Transaction Hash:
Block:
11669650 at Jan-17-2021 01:10:21 AM +UTC
Transaction Fee:
0.007256589550579203 ETH
$14.97
Gas Used:
35,391 Gas / 205.040534333 Gwei
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
| 0x00000000...438691c04 | |||||
| 0x0e2F2F9d...54d5BC841 |
0 Eth
Nonce: 0
|
0 Eth
Nonce: 0
| |||
|
0x5A0b54D5...D3E029c4c
Miner
| (Spark Pool) | 28.921883081946428033 Eth | 28.929139671497007236 Eth | 0.007256589550579203 | |
| 0x6C0439f6...ad43d08a4 |
93.117447208122414188 Eth
Nonce: 3022
|
93.110190618571834985 Eth
Nonce: 3023
| 0.007256589550579203 |
Execution Trace
0xa6d96991becb2fb6b3fc1820a7764802c44664ee.67424255( )
0x0000000000438f975cbde76d5fdd1aa76be46577.67424255( )-
UniswapV2Pair.CALL( )
DelegateCallProxyManyToOne.f8b2cb4f( )
-
ManyToOneImplementationHolder.STATICCALL( )
-
IndexPool.getBalance( token=0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9 ) => ( 2898787248090090857040 )
-
GasToken2.freeUpTo( value=1 ) => ( freed=1 )
0x0e2f2f9df9fda05b41ddb9d67596cbd54d5bc841.CALL( )-
GasToken2.SELFDESTRUCT( )
-
-
File 1 of 5: UniswapV2Pair
File 2 of 5: DelegateCallProxyManyToOne
File 3 of 5: ManyToOneImplementationHolder
File 4 of 5: IndexPool
File 5 of 5: GasToken2
// File: contracts/interfaces/IUniswapV2Pair.sol
pragma solidity >=0.5.0;
interface IUniswapV2Pair {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
function MINIMUM_LIQUIDITY() external pure returns (uint);
function factory() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function price0CumulativeLast() external view returns (uint);
function price1CumulativeLast() external view returns (uint);
function kLast() external view returns (uint);
function mint(address to) external returns (uint liquidity);
function burn(address to) external returns (uint amount0, uint amount1);
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
function skim(address to) external;
function sync() external;
function initialize(address, address) external;
}
// File: contracts/interfaces/IUniswapV2ERC20.sol
pragma solidity >=0.5.0;
interface IUniswapV2ERC20 {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
}
// File: contracts/libraries/SafeMath.sol
pragma solidity =0.5.16;
// a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
library SafeMath {
function add(uint x, uint y) internal pure returns (uint z) {
require((z = x + y) >= x, 'ds-math-add-overflow');
}
function sub(uint x, uint y) internal pure returns (uint z) {
require((z = x - y) <= x, 'ds-math-sub-underflow');
}
function mul(uint x, uint y) internal pure returns (uint z) {
require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
}
}
// File: contracts/UniswapV2ERC20.sol
pragma solidity =0.5.16;
contract UniswapV2ERC20 is IUniswapV2ERC20 {
using SafeMath for uint;
string public constant name = 'Uniswap V2';
string public constant symbol = 'UNI-V2';
uint8 public constant decimals = 18;
uint public totalSupply;
mapping(address => uint) public balanceOf;
mapping(address => mapping(address => uint)) public allowance;
bytes32 public DOMAIN_SEPARATOR;
// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
mapping(address => uint) public nonces;
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
constructor() public {
uint chainId;
assembly {
chainId := chainid
}
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
keccak256(bytes(name)),
keccak256(bytes('1')),
chainId,
address(this)
)
);
}
function _mint(address to, uint value) internal {
totalSupply = totalSupply.add(value);
balanceOf[to] = balanceOf[to].add(value);
emit Transfer(address(0), to, value);
}
function _burn(address from, uint value) internal {
balanceOf[from] = balanceOf[from].sub(value);
totalSupply = totalSupply.sub(value);
emit Transfer(from, address(0), value);
}
function _approve(address owner, address spender, uint value) private {
allowance[owner][spender] = value;
emit Approval(owner, spender, value);
}
function _transfer(address from, address to, uint value) private {
balanceOf[from] = balanceOf[from].sub(value);
balanceOf[to] = balanceOf[to].add(value);
emit Transfer(from, to, value);
}
function approve(address spender, uint value) external returns (bool) {
_approve(msg.sender, spender, value);
return true;
}
function transfer(address to, uint value) external returns (bool) {
_transfer(msg.sender, to, value);
return true;
}
function transferFrom(address from, address to, uint value) external returns (bool) {
if (allowance[from][msg.sender] != uint(-1)) {
allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
}
_transfer(from, to, value);
return true;
}
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
bytes32 digest = keccak256(
abi.encodePacked(
'\x19\x01',
DOMAIN_SEPARATOR,
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
)
);
address recoveredAddress = ecrecover(digest, v, r, s);
require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
_approve(owner, spender, value);
}
}
// File: contracts/libraries/Math.sol
pragma solidity =0.5.16;
// a library for performing various math operations
library Math {
function min(uint x, uint y) internal pure returns (uint z) {
z = x < y ? x : y;
}
// babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
function sqrt(uint y) internal pure returns (uint z) {
if (y > 3) {
z = y;
uint x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
}
// File: contracts/libraries/UQ112x112.sol
pragma solidity =0.5.16;
// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
// range: [0, 2**112 - 1]
// resolution: 1 / 2**112
library UQ112x112 {
uint224 constant Q112 = 2**112;
// encode a uint112 as a UQ112x112
function encode(uint112 y) internal pure returns (uint224 z) {
z = uint224(y) * Q112; // never overflows
}
// divide a UQ112x112 by a uint112, returning a UQ112x112
function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
z = x / uint224(y);
}
}
// File: contracts/interfaces/IERC20.sol
pragma solidity >=0.5.0;
interface IERC20 {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
}
// File: contracts/interfaces/IUniswapV2Factory.sol
pragma solidity >=0.5.0;
interface IUniswapV2Factory {
event PairCreated(address indexed token0, address indexed token1, address pair, uint);
function feeTo() external view returns (address);
function feeToSetter() external view returns (address);
function getPair(address tokenA, address tokenB) external view returns (address pair);
function allPairs(uint) external view returns (address pair);
function allPairsLength() external view returns (uint);
function createPair(address tokenA, address tokenB) external returns (address pair);
function setFeeTo(address) external;
function setFeeToSetter(address) external;
}
// File: contracts/interfaces/IUniswapV2Callee.sol
pragma solidity >=0.5.0;
interface IUniswapV2Callee {
function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external;
}
// File: contracts/UniswapV2Pair.sol
pragma solidity =0.5.16;
contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
using SafeMath for uint;
using UQ112x112 for uint224;
uint public constant MINIMUM_LIQUIDITY = 10**3;
bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
address public factory;
address public token0;
address public token1;
uint112 private reserve0; // uses single storage slot, accessible via getReserves
uint112 private reserve1; // uses single storage slot, accessible via getReserves
uint32 private blockTimestampLast; // uses single storage slot, accessible via getReserves
uint public price0CumulativeLast;
uint public price1CumulativeLast;
uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event
uint private unlocked = 1;
modifier lock() {
require(unlocked == 1, 'UniswapV2: LOCKED');
unlocked = 0;
_;
unlocked = 1;
}
function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
_reserve0 = reserve0;
_reserve1 = reserve1;
_blockTimestampLast = blockTimestampLast;
}
function _safeTransfer(address token, address to, uint value) private {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
}
event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
constructor() public {
factory = msg.sender;
}
// called once by the factory at time of deployment
function initialize(address _token0, address _token1) external {
require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
token0 = _token0;
token1 = _token1;
}
// update reserves and, on the first call per block, price accumulators
function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
uint32 blockTimestamp = uint32(block.timestamp % 2**32);
uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
// * never overflows, and + overflow is desired
price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
}
reserve0 = uint112(balance0);
reserve1 = uint112(balance1);
blockTimestampLast = blockTimestamp;
emit Sync(reserve0, reserve1);
}
// if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
address feeTo = IUniswapV2Factory(factory).feeTo();
feeOn = feeTo != address(0);
uint _kLast = kLast; // gas savings
if (feeOn) {
if (_kLast != 0) {
uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
uint rootKLast = Math.sqrt(_kLast);
if (rootK > rootKLast) {
uint numerator = totalSupply.mul(rootK.sub(rootKLast));
uint denominator = rootK.mul(5).add(rootKLast);
uint liquidity = numerator / denominator;
if (liquidity > 0) _mint(feeTo, liquidity);
}
}
} else if (_kLast != 0) {
kLast = 0;
}
}
// this low-level function should be called from a contract which performs important safety checks
function mint(address to) external lock returns (uint liquidity) {
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
uint balance0 = IERC20(token0).balanceOf(address(this));
uint balance1 = IERC20(token1).balanceOf(address(this));
uint amount0 = balance0.sub(_reserve0);
uint amount1 = balance1.sub(_reserve1);
bool feeOn = _mintFee(_reserve0, _reserve1);
uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
if (_totalSupply == 0) {
liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
_mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
} else {
liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
}
require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
_mint(to, liquidity);
_update(balance0, balance1, _reserve0, _reserve1);
if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
emit Mint(msg.sender, amount0, amount1);
}
// this low-level function should be called from a contract which performs important safety checks
function burn(address to) external lock returns (uint amount0, uint amount1) {
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
address _token0 = token0; // gas savings
address _token1 = token1; // gas savings
uint balance0 = IERC20(_token0).balanceOf(address(this));
uint balance1 = IERC20(_token1).balanceOf(address(this));
uint liquidity = balanceOf[address(this)];
bool feeOn = _mintFee(_reserve0, _reserve1);
uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
_burn(address(this), liquidity);
_safeTransfer(_token0, to, amount0);
_safeTransfer(_token1, to, amount1);
balance0 = IERC20(_token0).balanceOf(address(this));
balance1 = IERC20(_token1).balanceOf(address(this));
_update(balance0, balance1, _reserve0, _reserve1);
if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
emit Burn(msg.sender, amount0, amount1, to);
}
// this low-level function should be called from a contract which performs important safety checks
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
uint balance0;
uint balance1;
{ // scope for _token{0,1}, avoids stack too deep errors
address _token0 = token0;
address _token1 = token1;
require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
balance0 = IERC20(_token0).balanceOf(address(this));
balance1 = IERC20(_token1).balanceOf(address(this));
}
uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
{ // scope for reserve{0,1}Adjusted, avoids stack too deep errors
uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
}
_update(balance0, balance1, _reserve0, _reserve1);
emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
}
// force balances to match reserves
function skim(address to) external lock {
address _token0 = token0; // gas savings
address _token1 = token1; // gas savings
_safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
_safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
}
// force reserves to match balances
function sync() external lock {
_update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
}
}File 2 of 5: DelegateCallProxyManyToOne
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0;
/**
* @dev Because we use the code hashes of the proxy contracts for proxy address
* derivation, it is important that other packages have access to the correct
* values when they import the salt library.
*/
library CodeHashes {
bytes32 internal constant ONE_TO_ONE_CODEHASH = 0x63d9f7b5931b69188c8f6b806606f25892f1bb17b7f7e966fe3a32c04493aee4;
bytes32 internal constant MANY_TO_ONE_CODEHASH = 0xa035ad05a1663db5bfd455b99cd7c6ac6bd49269738458eda140e0b78ed53f79;
bytes32 internal constant IMPLEMENTATION_HOLDER_CODEHASH = 0x11c370493a726a0ffa93d42b399ad046f1b5a543b6e72f1a64f1488dc1c58f2c;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity =0.6.12;
/* ========== External Libraries ========== */
import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
/* ========== Proxy Contracts ========== */
import "./ManyToOneImplementationHolder.sol";
import { DelegateCallProxyManyToOne } from "./DelegateCallProxyManyToOne.sol";
import { DelegateCallProxyOneToOne } from "./DelegateCallProxyOneToOne.sol";
/* ========== Internal Libraries ========== */
import { SaltyLib as Salty } from "./SaltyLib.sol";
import { CodeHashes } from "./CodeHashes.sol";
/* ========== Inheritance ========== */
import "./interfaces/IDelegateCallProxyManager.sol";
/**
* @dev Contract that manages deployment and upgrades of delegatecall proxies.
*
* An implementation identifier can be created on the proxy manager which is
* used to specify the logic address for a particular contract type, and to
* upgrade the implementation as needed.
*
* ====== Proxy Types ======
* A one-to-one proxy is a single proxy contract with an upgradeable implementation
* address.
*
* A many-to-one proxy is a single upgradeable implementation address that may be
* used by many proxy contracts.
*
* ====== Access Control ======
* The proxy manager has a single address as its owner.
*
* The owner is the sole account with the following permissions:
* - Create new many-to-one implementations
* - Create new one-to-one proxies
* - Modify the implementation address of existing proxies
* - Lock proxies
* - Designate approved deployers
* - Remove approved deployers
* - Modify the owner address
*
* Approved deployers may only deploy many-to-one proxies.
*
* ====== Upgrades ======
* Proxies can be upgraded by the owner if they are not locked.
*
* Many-to-one proxy implementations are upgraded by calling the holder contract
* for the implementation ID being upgraded.
* One-to-one proxies are upgraded by calling the proxy contract directly.
*
* The owner can lock a one-to-one proxy or many-to-one implementation ID so that
* it becomes impossible to upgrade.
*/
contract DelegateCallProxyManager is Ownable, IDelegateCallProxyManager {
/* ========== Events ========== */
event DeploymentApprovalGranted(address deployer);
event DeploymentApprovalRevoked(address deployer);
event ManyToOne_ImplementationCreated(
bytes32 implementationID,
address implementationAddress
);
event ManyToOne_ImplementationUpdated(
bytes32 implementationID,
address implementationAddress
);
event ManyToOne_ImplementationLocked(bytes32 implementationID);
event ManyToOne_ProxyDeployed(
bytes32 implementationID,
address proxyAddress
);
event OneToOne_ProxyDeployed(
address proxyAddress,
address implementationAddress
);
event OneToOne_ImplementationUpdated(
address proxyAddress,
address implementationAddress
);
event OneToOne_ImplementationLocked(address proxyAddress);
/* ========== Storage ========== */
// Addresses allowed to deploy many-to-one proxies.
mapping(address => bool) internal _approvedDeployers;
// Maps implementation holders to their implementation IDs.
mapping(bytes32 => address) internal _implementationHolders;
// Maps implementation holders & proxy addresses to bool stating if they are locked.
mapping(address => bool) internal _lockedImplementations;
// Temporary value used in the many-to-one proxy constructor.
// The many-to-one proxy contract is deployed with create2 and
// uses static initialization code for simple address derivation,
// so it calls the proxy manager in the constructor to get this
// address in order to save it as an immutable in the bytecode.
address internal _implementationHolder;
/* ========== Modifiers ========== */
modifier onlyApprovedDeployer {
address sender = _msgSender();
require(_approvedDeployers[sender] || sender == owner(), "ERR_NOT_APPROVED");
_;
}
/* ========== Constructor ========== */
constructor() public Ownable() {}
/* ========== Access Control ========== */
/**
* @dev Allows `deployer` to deploy many-to-one proxies.
*/
function approveDeployer(address deployer) external override onlyOwner {
_approvedDeployers[deployer] = true;
emit DeploymentApprovalGranted(deployer);
}
/**
* @dev Prevents `deployer` from deploying many-to-one proxies.
*/
function revokeDeployerApproval(address deployer) external override onlyOwner {
_approvedDeployers[deployer] = false;
emit DeploymentApprovalRevoked(deployer);
}
/* ========== Implementation Management ========== */
/**
* @dev Creates a many-to-one proxy relationship.
*
* Deploys an implementation holder contract which stores the
* implementation address for many proxies. The implementation
* address can be updated on the holder to change the runtime
* code used by all its proxies.
*
* @param implementationID ID for the implementation, used to identify the
* proxies that use it. Also used as the salt in the create2 call when
* deploying the implementation holder contract.
* @param implementation Address with the runtime code the proxies
* should use.
*/
function createManyToOneProxyRelationship(
bytes32 implementationID,
address implementation
)
external
override
onlyOwner
{
// Deploy the implementation holder contract with the implementation
// ID as the create2 salt.
address implementationHolder = Create2.deploy(
0,
implementationID,
type(ManyToOneImplementationHolder).creationCode
);
// Store the implementation holder address
_implementationHolders[implementationID] = implementationHolder;
// Sets the implementation address.
_setImplementation(implementationHolder, implementation);
emit ManyToOne_ImplementationCreated(
implementationID,
implementation
);
}
/**
* @dev Lock the current implementation for `implementationID` so that it can never be upgraded again.
*/
function lockImplementationManyToOne(bytes32 implementationID) external override onlyOwner {
// Read the implementation holder address from storage.
address implementationHolder = _implementationHolders[implementationID];
// Verify that the implementation exists.
require(implementationHolder != address(0), "ERR_IMPLEMENTATION_ID");
_lockedImplementations[implementationHolder] = true;
emit ManyToOne_ImplementationLocked(implementationID);
}
/**
* @dev Lock the current implementation for `proxyAddress` so that it can never be upgraded again.
*/
function lockImplementationOneToOne(address proxyAddress) external override onlyOwner {
_lockedImplementations[proxyAddress] = true;
emit OneToOne_ImplementationLocked(proxyAddress);
}
/**
* @dev Updates the implementation address for a many-to-one
* proxy relationship.
*
* @param implementationID Identifier for the implementation.
* @param implementation Address with the runtime code the proxies
* should use.
*/
function setImplementationAddressManyToOne(
bytes32 implementationID,
address implementation
)
external
override
onlyOwner
{
// Read the implementation holder address from storage.
address implementationHolder = _implementationHolders[implementationID];
// Verify that the implementation exists.
require(implementationHolder != address(0), "ERR_IMPLEMENTATION_ID");
// Verify implementation is not locked
require(!_lockedImplementations[implementationHolder], "ERR_IMPLEMENTATION_LOCKED");
// Set the implementation address
_setImplementation(implementationHolder, implementation);
emit ManyToOne_ImplementationUpdated(
implementationID,
implementation
);
}
/**
* @dev Updates the implementation address for a one-to-one proxy.
*
* Note: This could work for many-to-one as well if the caller
* provides the implementation holder address in place of the
* proxy address, as they use the same access control and update
* mechanism.
*
* @param proxyAddress Address of the deployed proxy
* @param implementation Address with the runtime code for
* the proxy to use.
*/
function setImplementationAddressOneToOne(
address proxyAddress,
address implementation
)
external
override
onlyOwner
{
// Verify proxy is not locked
require(!_lockedImplementations[proxyAddress], "ERR_IMPLEMENTATION_LOCKED");
// Set the implementation address
_setImplementation(proxyAddress, implementation);
emit OneToOne_ImplementationUpdated(proxyAddress, implementation);
}
/* ========== Proxy Deployment ========== */
/**
* @dev Deploy a proxy contract with a one-to-one relationship
* with its implementation.
*
* The proxy will have its own implementation address which can
* be updated by the proxy manager.
*
* @param suppliedSalt Salt provided by the account requesting deployment.
* @param implementation Address of the contract with the runtime
* code that the proxy should use.
*/
function deployProxyOneToOne(
bytes32 suppliedSalt,
address implementation
)
external
override
onlyOwner
returns(address proxyAddress)
{
// Derive the create2 salt from the deployment requester's address
// and the requester-supplied salt.
bytes32 salt = Salty.deriveOneToOneSalt(_msgSender(), suppliedSalt);
// Deploy the proxy
proxyAddress = Create2.deploy(
0,
salt,
type(DelegateCallProxyOneToOne).creationCode
);
// Set the implementation address on the new proxy.
_setImplementation(proxyAddress, implementation);
emit OneToOne_ProxyDeployed(proxyAddress, implementation);
}
/**
* @dev Deploy a proxy with a many-to-one relationship with its implemenation.
*
* The proxy will call the implementation holder for every transaction to
* determine the address to use in calls.
*
* @param implementationID Identifier for the proxy's implementation.
* @param suppliedSalt Salt provided by the account requesting deployment.
*/
function deployProxyManyToOne(bytes32 implementationID, bytes32 suppliedSalt)
external
override
onlyApprovedDeployer
returns(address proxyAddress)
{
// Read the implementation holder address from storage.
address implementationHolder = _implementationHolders[implementationID];
// Verify that the implementation exists.
require(implementationHolder != address(0), "ERR_IMPLEMENTATION_ID");
// Derive the create2 salt from the deployment requester's address, the
// implementation ID and the requester-supplied salt.
bytes32 salt = Salty.deriveManyToOneSalt(
_msgSender(),
implementationID,
suppliedSalt
);
// Set the implementation holder address in storage so the proxy
// constructor can query it.
_implementationHolder = implementationHolder;
// Deploy the proxy, which will query the implementation holder address
// and save it as an immutable in the contract bytecode.
proxyAddress = Create2.deploy(
0,
salt,
type(DelegateCallProxyManyToOne).creationCode
);
// Remove the address from temporary storage.
_implementationHolder = address(0);
emit ManyToOne_ProxyDeployed(
implementationID,
proxyAddress
);
}
/* ========== Queries ========== */
/**
* @dev Returns a boolean stating whether `implementationID` is locked.
*/
function isImplementationLocked(bytes32 implementationID) external override view returns (bool) {
// Read the implementation holder address from storage.
address implementationHolder = _implementationHolders[implementationID];
// Verify that the implementation exists.
require(implementationHolder != address(0), "ERR_IMPLEMENTATION_ID");
return _lockedImplementations[implementationHolder];
}
/**
* @dev Returns a boolean stating whether `proxyAddress` is locked.
*/
function isImplementationLocked(address proxyAddress) external override view returns (bool) {
return _lockedImplementations[proxyAddress];
}
/**
* @dev Returns a boolean stating whether `deployer` is allowed to deploy many-to-one
* proxies.
*/
function isApprovedDeployer(address deployer) external override view returns (bool) {
return _approvedDeployers[deployer];
}
/**
* @dev Queries the temporary storage value `_implementationHolder`.
* This is used in the constructor of the many-to-one proxy contract
* so that the create2 address is static (adding constructor arguments
* would change the codehash) and the implementation holder can be
* stored as a constant.
*/
function getImplementationHolder()
external
override
view
returns (address)
{
return _implementationHolder;
}
/**
* @dev Returns the address of the implementation holder contract
* for `implementationID`.
*/
function getImplementationHolder(
bytes32 implementationID
)
external
override
view
returns (address)
{
return _implementationHolders[implementationID];
}
/**
* @dev Computes the create2 address for a one-to-one proxy requested
* by `originator` using `suppliedSalt`.
*
* @param originator Address of the account requesting deployment.
* @param suppliedSalt Salt provided by the account requesting deployment.
*/
function computeProxyAddressOneToOne(
address originator,
bytes32 suppliedSalt
)
external
override
view
returns (address)
{
bytes32 salt = Salty.deriveOneToOneSalt(originator, suppliedSalt);
return Create2.computeAddress(salt, CodeHashes.ONE_TO_ONE_CODEHASH);
}
/**
* @dev Computes the create2 address for a many-to-one proxy for the
* implementation `implementationID` requested by `originator` using
* `suppliedSalt`.
*
* @param originator Address of the account requesting deployment.
* @param implementationID The identifier for the contract implementation.
* @param suppliedSalt Salt provided by the account requesting deployment.
*/
function computeProxyAddressManyToOne(
address originator,
bytes32 implementationID,
bytes32 suppliedSalt
)
external
override
view
returns (address)
{
bytes32 salt = Salty.deriveManyToOneSalt(
originator,
implementationID,
suppliedSalt
);
return Create2.computeAddress(salt, CodeHashes.MANY_TO_ONE_CODEHASH);
}
/**
* @dev Computes the create2 address of the implementation holder
* for `implementationID`.
*
* @param implementationID The identifier for the contract implementation.
*/
function computeHolderAddressManyToOne(bytes32 implementationID)
public
override
view
returns (address)
{
return Create2.computeAddress(
implementationID,
CodeHashes.IMPLEMENTATION_HOLDER_CODEHASH
);
}
/* ========== Internal Functions ========== */
/**
* @dev Sets the implementation address for a one-to-one proxy or
* many-to-one implementation holder. Both use the same access
* control and update mechanism, which is the receipt of a call
* from the proxy manager with the abi-encoded implementation address
* as the only calldata.
*
* Note: Verifies that the implementation address is a contract.
*
* @param proxyOrHolder Address of the one-to-one proxy or
* many-to-one implementation holder contract.
* @param implementation Address of the contract with the runtime
* code that the proxy or proxies should use.
*/
function _setImplementation(
address proxyOrHolder,
address implementation
) internal {
// Verify that the implementation address is a contract.
require(Address.isContract(implementation), "ERR_NOT_CONTRACT");
// Set the implementation address on the contract.
// solium-disable-next-line security/no-low-level-calls
(bool success,) = proxyOrHolder.call(abi.encode(implementation));
require(success, "ERR_SET_ADDRESS_REVERT");
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/**
* @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
* `CREATE2` can be used to compute in advance the address where a smart
* contract will be deployed, which allows for interesting new mechanisms known
* as 'counterfactual interactions'.
*
* See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
* information.
*/
library Create2 {
/**
* @dev Deploys a contract using `CREATE2`. The address where the contract
* will be deployed can be known in advance via {computeAddress}.
*
* The bytecode for a contract can be obtained from Solidity with
* `type(contractName).creationCode`.
*
* Requirements:
*
* - `bytecode` must not be empty.
* - `salt` must have not been used for `bytecode` already.
* - the factory must have a balance of at least `amount`.
* - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
*/
function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address) {
address addr;
require(address(this).balance >= amount, "Create2: insufficient balance");
require(bytecode.length != 0, "Create2: bytecode length is zero");
// solhint-disable-next-line no-inline-assembly
assembly {
addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
}
require(addr != address(0), "Create2: Failed on deploy");
return addr;
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
* `bytecodeHash` or `salt` will result in a new destination address.
*/
function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
return computeAddress(salt, bytecodeHash, address(this));
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
* `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
*/
function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address) {
bytes32 _data = keccak256(
abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash)
);
return address(uint256(_data));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
/**
* @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) {
// This method relies in extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
/**
* @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);
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import "../GSN/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(_owner == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/*
* @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 payable) {
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;
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity =0.6.12;
/**
* @dev The ManyToOneImplementationHolder stores an upgradeable implementation address
* in storage, which many-to-one proxies query at execution time to determine which
* contract to delegate to.
*
* The manager can upgrade the implementation address by calling the holder with the
* abi-encoded address as calldata. If any other account calls the implementation holder,
* it will return the implementation address.
*
* This pattern was inspired by the DharmaUpgradeBeacon from 0age
* https://github.com/dharma-eng/dharma-smart-wallet/blob/master/contracts/upgradeability/smart-wallet/DharmaUpgradeBeacon.sol
*/
contract ManyToOneImplementationHolder {
/* --- Storage --- */
address internal immutable _manager;
address internal _implementation;
/* --- Constructor --- */
constructor() public {
_manager = msg.sender;
}
/**
* @dev Fallback function for the contract.
*
* Used by proxies to read the implementation address and used
* by the proxy manager to set the implementation address.
*
* If called by the owner, reads the implementation address from
* calldata (must be abi-encoded) and stores it to the first slot.
*
* Otherwise, returns the stored implementation address.
*/
fallback() external payable {
if (msg.sender != _manager) {
assembly {
mstore(0, sload(0))
return(0, 32)
}
}
assembly { sstore(0, calldataload(0)) }
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity =0.6.12;
import { Proxy } from "@openzeppelin/contracts/proxy/Proxy.sol";
/**
* @dev Proxy contract which uses an implementation address shared with many
* other proxies.
*
* An implementation holder contract stores the upgradeable implementation address.
* When the proxy is called, it queries the implementation address from the holder
* contract and delegatecalls the returned address, forwarding the received calldata
* and ether.
*
* Note: This contract does not verify that the implementation
* address is a valid delegation target. The manager must perform
* this safety check before updating the implementation on the holder.
*/
contract DelegateCallProxyManyToOne is Proxy {
/* ========== Constants ========== */
// Address that stores the implementation address.
address internal immutable _implementationHolder;
/* ========== Constructor ========== */
constructor() public {
// Calls the sender rather than receiving the address in the constructor
// arguments so that the address is computable using create2.
_implementationHolder = ProxyDeployer(msg.sender).getImplementationHolder();
}
/* ========== Internal Overrides ========== */
/**
* @dev Queries the implementation address from the implementation holder.
*/
function _implementation() internal override view returns (address) {
// Queries the implementation address from the implementation holder.
(bool success, bytes memory data) = _implementationHolder.staticcall("");
require(success, string(data));
address implementation = abi.decode((data), (address));
require(implementation != address(0), "ERR_NULL_IMPLEMENTATION");
return implementation;
}
}
interface ProxyDeployer {
function getImplementationHolder() external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/**
* @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
* instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
* be specified by overriding the virtual {_implementation} function.
*
* Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
* different contract through the {_delegate} function.
*
* The success and return data of the delegated call will be returned back to the caller of the proxy.
*/
abstract contract Proxy {
/**
* @dev Delegates the current call to `implementation`.
*
* This function does not return to its internall call site, it will return directly to the external caller.
*/
function _delegate(address implementation) internal {
// solhint-disable-next-line no-inline-assembly
assembly {
// Copy msg.data. We take full control of memory in this inline assembly
// block because it will not return to Solidity code. We overwrite the
// Solidity scratch pad at memory position 0.
calldatacopy(0, 0, calldatasize())
// Call the implementation.
// out and outsize are 0 because we don't know the size yet.
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// Copy the returned data.
returndatacopy(0, 0, returndatasize())
switch result
// delegatecall returns 0 on error.
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
/**
* @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
* and {_fallback} should delegate.
*/
function _implementation() internal virtual view returns (address);
/**
* @dev Delegates the current call to the address returned by `_implementation()`.
*
* This function does not return to its internall call site, it will return directly to the external caller.
*/
function _fallback() internal {
_beforeFallback();
_delegate(_implementation());
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
* function in the contract matches the call data.
*/
fallback () payable external {
_fallback();
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
* is empty.
*/
receive () payable external {
_fallback();
}
/**
* @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
* call, or as part of the Solidity `fallback` or `receive` functions.
*
* If overriden should call `super._beforeFallback()`.
*/
function _beforeFallback() internal virtual {
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity =0.6.12;
import { Proxy } from "@openzeppelin/contracts/proxy/Proxy.sol";
/**
* @dev Upgradeable delegatecall proxy for a single contract.
*
* This proxy stores an implementation address which can be upgraded by the proxy manager.
*
* To upgrade the implementation, the manager calls the proxy with the abi encoded implementation address.
*
* If any other account calls the proxy, it will delegatecall the implementation address with the received
* calldata and ether. If the call succeeds, it will return with the received returndata.
* If it reverts, it will revert with the received revert data.
*
* Note: The storage slot for the implementation address is:
* `bytes32(uint256(keccak256("IMPLEMENTATION_ADDRESS")) + 1)`
* This slot must not be used by the implementation contract.
*
* Note: This contract does not verify that the implementation address is a valid delegation target.
* The manager must perform this safety check.
*/
contract DelegateCallProxyOneToOne is Proxy {
/* ========== Constants ========== */
address internal immutable _manager;
/* ========== Constructor ========== */
constructor() public {
_manager = msg.sender ;
}
/* ========== Internal Overrides ========== */
/**
* @dev Reads the implementation address from storage.
*/
function _implementation() internal override view returns (address) {
address implementation;
assembly {
implementation := sload(
// bytes32(uint256(keccak256("IMPLEMENTATION_ADDRESS")) + 1)
0x913bd12b32b36f36cedaeb6e043912bceb97022755958701789d3108d33a045a
)
}
return implementation;
}
/**
* @dev Hook that is called before falling back to the implementation.
*
* Checks if the call is from the owner.
* If it is, reads the abi-encoded implementation address from calldata and stores
* it at the slot `bytes32(uint256(keccak256("IMPLEMENTATION_ADDRESS")) + 1)`,
* then returns with no data.
* If it is not, continues execution with the fallback function.
*/
function _beforeFallback() internal override {
if (msg.sender != _manager) {
super._beforeFallback();
} else {
assembly {
sstore(
// bytes32(uint256(keccak256("IMPLEMENTATION_ADDRESS")) + 1)
0x913bd12b32b36f36cedaeb6e043912bceb97022755958701789d3108d33a045a,
calldataload(0)
)
return(0, 0)
}
}
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0;
/* --- External Libraries --- */
import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol";
/* --- Proxy Contracts --- */
import { CodeHashes } from "./CodeHashes.sol";
/**
* @dev Library for computing create2 salts and addresses for proxies
* deployed by `DelegateCallProxyManager`.
*
* Because the proxy factory is meant to be used by multiple contracts,
* we use a salt derivation pattern that includes the address of the
* contract that requested the proxy deployment, a salt provided by that
* contract and the implementation ID used (for many-to-one proxies only).
*/
library SaltyLib {
/* --- Salt Derivation --- */
/**
* @dev Derives the create2 salt for a many-to-one proxy.
*
* Many different contracts in the Indexed framework may use the
* same implementation contract, and they all use the same init
* code, so we derive the actual create2 salt from a combination
* of the implementation ID, the address of the account requesting
* deployment and the user-supplied salt.
*
* @param originator Address of the account requesting deployment.
* @param implementationID The identifier for the contract implementation.
* @param suppliedSalt Salt provided by the account requesting deployment.
*/
function deriveManyToOneSalt(
address originator,
bytes32 implementationID,
bytes32 suppliedSalt
)
internal
pure
returns (bytes32)
{
return keccak256(
abi.encodePacked(
originator,
implementationID,
suppliedSalt
)
);
}
/**
* @dev Derives the create2 salt for a one-to-one proxy.
*
* @param originator Address of the account requesting deployment.
* @param suppliedSalt Salt provided by the account requesting deployment.
*/
function deriveOneToOneSalt(
address originator,
bytes32 suppliedSalt
)
internal
pure
returns (bytes32)
{
return keccak256(abi.encodePacked(originator, suppliedSalt));
}
/* --- Address Derivation --- */
/**
* @dev Computes the create2 address for a one-to-one proxy deployed
* by `deployer` (the factory) when requested by `originator` using
* `suppliedSalt`.
*
* @param deployer Address of the proxy factory.
* @param originator Address of the account requesting deployment.
* @param suppliedSalt Salt provided by the account requesting deployment.
*/
function computeProxyAddressOneToOne(
address deployer,
address originator,
bytes32 suppliedSalt
)
internal
pure
returns (address)
{
bytes32 salt = deriveOneToOneSalt(originator, suppliedSalt);
return Create2.computeAddress(salt, CodeHashes.ONE_TO_ONE_CODEHASH, deployer);
}
/**
* @dev Computes the create2 address for a many-to-one proxy for the
* implementation `implementationID` deployed by `deployer` (the factory)
* when requested by `originator` using `suppliedSalt`.
*
* @param deployer Address of the proxy factory.
* @param originator Address of the account requesting deployment.
* @param implementationID The identifier for the contract implementation.
* @param suppliedSalt Salt provided by the account requesting deployment.
*/
function computeProxyAddressManyToOne(
address deployer,
address originator,
bytes32 implementationID,
bytes32 suppliedSalt
)
internal
pure
returns (address)
{
bytes32 salt = deriveManyToOneSalt(
originator,
implementationID,
suppliedSalt
);
return Create2.computeAddress(salt, CodeHashes.MANY_TO_ONE_CODEHASH, deployer);
}
/**
* @dev Computes the create2 address of the implementation holder
* for `implementationID`.
*
* @param deployer Address of the proxy factory.
* @param implementationID The identifier for the contract implementation.
*/
function computeHolderAddressManyToOne(
address deployer,
bytes32 implementationID
)
internal
pure
returns (address)
{
return Create2.computeAddress(
implementationID,
CodeHashes.IMPLEMENTATION_HOLDER_CODEHASH,
deployer
);
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0;
/**
* @dev Contract that manages deployment and upgrades of delegatecall proxies.
*
* An implementation identifier can be created on the proxy manager which is
* used to specify the logic address for a particular contract type, and to
* upgrade the implementation as needed.
*
* A one-to-one proxy is a single proxy contract with an upgradeable implementation
* address.
*
* A many-to-one proxy is a single upgradeable implementation address that may be
* used by many proxy contracts.
*/
interface IDelegateCallProxyManager {
/* ========== Events ========== */
event DeploymentApprovalGranted(address deployer);
event DeploymentApprovalRevoked(address deployer);
event ManyToOne_ImplementationCreated(
bytes32 implementationID,
address implementationAddress
);
event ManyToOne_ImplementationUpdated(
bytes32 implementationID,
address implementationAddress
);
event ManyToOne_ProxyDeployed(
bytes32 implementationID,
address proxyAddress
);
event OneToOne_ProxyDeployed(
address proxyAddress,
address implementationAddress
);
event OneToOne_ImplementationUpdated(
address proxyAddress,
address implementationAddress
);
/* ========== Controls ========== */
/**
* @dev Allows `deployer` to deploy many-to-one proxies.
*/
function approveDeployer(address deployer) external;
/**
* @dev Prevents `deployer` from deploying many-to-one proxies.
*/
function revokeDeployerApproval(address deployer) external;
/* ========== Implementation Management ========== */
/**
* @dev Creates a many-to-one proxy relationship.
*
* Deploys an implementation holder contract which stores the
* implementation address for many proxies. The implementation
* address can be updated on the holder to change the runtime
* code used by all its proxies.
*
* @param implementationID ID for the implementation, used to identify the
* proxies that use it. Also used as the salt in the create2 call when
* deploying the implementation holder contract.
* @param implementation Address with the runtime code the proxies
* should use.
*/
function createManyToOneProxyRelationship(
bytes32 implementationID,
address implementation
) external;
/**
* @dev Lock the current implementation for `proxyAddress` so that it can never be upgraded again.
*/
function lockImplementationManyToOne(bytes32 implementationID) external;
/**
* @dev Lock the current implementation for `proxyAddress` so that it can never be upgraded again.
*/
function lockImplementationOneToOne(address proxyAddress) external;
/**
* @dev Updates the implementation address for a many-to-one
* proxy relationship.
*
* @param implementationID Identifier for the implementation.
* @param implementation Address with the runtime code the proxies
* should use.
*/
function setImplementationAddressManyToOne(
bytes32 implementationID,
address implementation
) external;
/**
* @dev Updates the implementation address for a one-to-one proxy.
*
* Note: This could work for many-to-one as well if the caller
* provides the implementation holder address in place of the
* proxy address, as they use the same access control and update
* mechanism.
*
* @param proxyAddress Address of the deployed proxy
* @param implementation Address with the runtime code for
* the proxy to use.
*/
function setImplementationAddressOneToOne(
address proxyAddress,
address implementation
) external;
/* ========== Proxy Deployment ========== */
/**
* @dev Deploy a proxy contract with a one-to-one relationship
* with its implementation.
*
* The proxy will have its own implementation address which can
* be updated by the proxy manager.
*
* @param suppliedSalt Salt provided by the account requesting deployment.
* @param implementation Address of the contract with the runtime
* code that the proxy should use.
*/
function deployProxyOneToOne(
bytes32 suppliedSalt,
address implementation
) external returns(address proxyAddress);
/**
* @dev Deploy a proxy with a many-to-one relationship with its implemenation.
*
* The proxy will call the implementation holder for every transaction to
* determine the address to use in calls.
*
* @param implementationID Identifier for the proxy's implementation.
* @param suppliedSalt Salt provided by the account requesting deployment.
*/
function deployProxyManyToOne(
bytes32 implementationID,
bytes32 suppliedSalt
) external returns(address proxyAddress);
/* ========== Queries ========== */
/**
* @dev Returns a boolean stating whether `implementationID` is locked.
*/
function isImplementationLocked(bytes32 implementationID) external view returns (bool);
/**
* @dev Returns a boolean stating whether `proxyAddress` is locked.
*/
function isImplementationLocked(address proxyAddress) external view returns (bool);
/**
* @dev Returns a boolean stating whether `deployer` is allowed to deploy many-to-one
* proxies.
*/
function isApprovedDeployer(address deployer) external view returns (bool);
/**
* @dev Queries the temporary storage value `_implementationHolder`.
* This is used in the constructor of the many-to-one proxy contract
* so that the create2 address is static (adding constructor arguments
* would change the codehash) and the implementation holder can be
* stored as a constant.
*/
function getImplementationHolder() external view returns (address);
/**
* @dev Returns the address of the implementation holder contract
* for `implementationID`.
*/
function getImplementationHolder(bytes32 implementationID) external view returns (address);
/**
* @dev Computes the create2 address for a one-to-one proxy requested
* by `originator` using `suppliedSalt`.
*
* @param originator Address of the account requesting deployment.
* @param suppliedSalt Salt provided by the account requesting deployment.
*/
function computeProxyAddressOneToOne(
address originator,
bytes32 suppliedSalt
) external view returns (address);
/**
* @dev Computes the create2 address for a many-to-one proxy for the
* implementation `implementationID` requested by `originator` using
* `suppliedSalt`.
*
* @param originator Address of the account requesting deployment.
* @param implementationID The identifier for the contract implementation.
* @param suppliedSalt Salt provided by the account requesting deployment.
*/
function computeProxyAddressManyToOne(
address originator,
bytes32 implementationID,
bytes32 suppliedSalt
) external view returns (address);
/**
* @dev Computes the create2 address of the implementation holder
* for `implementationID`.
*
* @param implementationID The identifier for the contract implementation.
*/
function computeHolderAddressManyToOne(bytes32 implementationID) external view returns (address);
}File 3 of 5: ManyToOneImplementationHolder
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0;
/**
* @dev Because we use the code hashes of the proxy contracts for proxy address
* derivation, it is important that other packages have access to the correct
* values when they import the salt library.
*/
library CodeHashes {
bytes32 internal constant ONE_TO_ONE_CODEHASH = 0x63d9f7b5931b69188c8f6b806606f25892f1bb17b7f7e966fe3a32c04493aee4;
bytes32 internal constant MANY_TO_ONE_CODEHASH = 0xa035ad05a1663db5bfd455b99cd7c6ac6bd49269738458eda140e0b78ed53f79;
bytes32 internal constant IMPLEMENTATION_HOLDER_CODEHASH = 0x11c370493a726a0ffa93d42b399ad046f1b5a543b6e72f1a64f1488dc1c58f2c;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity =0.6.12;
/* ========== External Libraries ========== */
import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
/* ========== Proxy Contracts ========== */
import "./ManyToOneImplementationHolder.sol";
import { DelegateCallProxyManyToOne } from "./DelegateCallProxyManyToOne.sol";
import { DelegateCallProxyOneToOne } from "./DelegateCallProxyOneToOne.sol";
/* ========== Internal Libraries ========== */
import { SaltyLib as Salty } from "./SaltyLib.sol";
import { CodeHashes } from "./CodeHashes.sol";
/* ========== Inheritance ========== */
import "./interfaces/IDelegateCallProxyManager.sol";
/**
* @dev Contract that manages deployment and upgrades of delegatecall proxies.
*
* An implementation identifier can be created on the proxy manager which is
* used to specify the logic address for a particular contract type, and to
* upgrade the implementation as needed.
*
* ====== Proxy Types ======
* A one-to-one proxy is a single proxy contract with an upgradeable implementation
* address.
*
* A many-to-one proxy is a single upgradeable implementation address that may be
* used by many proxy contracts.
*
* ====== Access Control ======
* The proxy manager has a single address as its owner.
*
* The owner is the sole account with the following permissions:
* - Create new many-to-one implementations
* - Create new one-to-one proxies
* - Modify the implementation address of existing proxies
* - Lock proxies
* - Designate approved deployers
* - Remove approved deployers
* - Modify the owner address
*
* Approved deployers may only deploy many-to-one proxies.
*
* ====== Upgrades ======
* Proxies can be upgraded by the owner if they are not locked.
*
* Many-to-one proxy implementations are upgraded by calling the holder contract
* for the implementation ID being upgraded.
* One-to-one proxies are upgraded by calling the proxy contract directly.
*
* The owner can lock a one-to-one proxy or many-to-one implementation ID so that
* it becomes impossible to upgrade.
*/
contract DelegateCallProxyManager is Ownable, IDelegateCallProxyManager {
/* ========== Events ========== */
event DeploymentApprovalGranted(address deployer);
event DeploymentApprovalRevoked(address deployer);
event ManyToOne_ImplementationCreated(
bytes32 implementationID,
address implementationAddress
);
event ManyToOne_ImplementationUpdated(
bytes32 implementationID,
address implementationAddress
);
event ManyToOne_ImplementationLocked(bytes32 implementationID);
event ManyToOne_ProxyDeployed(
bytes32 implementationID,
address proxyAddress
);
event OneToOne_ProxyDeployed(
address proxyAddress,
address implementationAddress
);
event OneToOne_ImplementationUpdated(
address proxyAddress,
address implementationAddress
);
event OneToOne_ImplementationLocked(address proxyAddress);
/* ========== Storage ========== */
// Addresses allowed to deploy many-to-one proxies.
mapping(address => bool) internal _approvedDeployers;
// Maps implementation holders to their implementation IDs.
mapping(bytes32 => address) internal _implementationHolders;
// Maps implementation holders & proxy addresses to bool stating if they are locked.
mapping(address => bool) internal _lockedImplementations;
// Temporary value used in the many-to-one proxy constructor.
// The many-to-one proxy contract is deployed with create2 and
// uses static initialization code for simple address derivation,
// so it calls the proxy manager in the constructor to get this
// address in order to save it as an immutable in the bytecode.
address internal _implementationHolder;
/* ========== Modifiers ========== */
modifier onlyApprovedDeployer {
address sender = _msgSender();
require(_approvedDeployers[sender] || sender == owner(), "ERR_NOT_APPROVED");
_;
}
/* ========== Constructor ========== */
constructor() public Ownable() {}
/* ========== Access Control ========== */
/**
* @dev Allows `deployer` to deploy many-to-one proxies.
*/
function approveDeployer(address deployer) external override onlyOwner {
_approvedDeployers[deployer] = true;
emit DeploymentApprovalGranted(deployer);
}
/**
* @dev Prevents `deployer` from deploying many-to-one proxies.
*/
function revokeDeployerApproval(address deployer) external override onlyOwner {
_approvedDeployers[deployer] = false;
emit DeploymentApprovalRevoked(deployer);
}
/* ========== Implementation Management ========== */
/**
* @dev Creates a many-to-one proxy relationship.
*
* Deploys an implementation holder contract which stores the
* implementation address for many proxies. The implementation
* address can be updated on the holder to change the runtime
* code used by all its proxies.
*
* @param implementationID ID for the implementation, used to identify the
* proxies that use it. Also used as the salt in the create2 call when
* deploying the implementation holder contract.
* @param implementation Address with the runtime code the proxies
* should use.
*/
function createManyToOneProxyRelationship(
bytes32 implementationID,
address implementation
)
external
override
onlyOwner
{
// Deploy the implementation holder contract with the implementation
// ID as the create2 salt.
address implementationHolder = Create2.deploy(
0,
implementationID,
type(ManyToOneImplementationHolder).creationCode
);
// Store the implementation holder address
_implementationHolders[implementationID] = implementationHolder;
// Sets the implementation address.
_setImplementation(implementationHolder, implementation);
emit ManyToOne_ImplementationCreated(
implementationID,
implementation
);
}
/**
* @dev Lock the current implementation for `implementationID` so that it can never be upgraded again.
*/
function lockImplementationManyToOne(bytes32 implementationID) external override onlyOwner {
// Read the implementation holder address from storage.
address implementationHolder = _implementationHolders[implementationID];
// Verify that the implementation exists.
require(implementationHolder != address(0), "ERR_IMPLEMENTATION_ID");
_lockedImplementations[implementationHolder] = true;
emit ManyToOne_ImplementationLocked(implementationID);
}
/**
* @dev Lock the current implementation for `proxyAddress` so that it can never be upgraded again.
*/
function lockImplementationOneToOne(address proxyAddress) external override onlyOwner {
_lockedImplementations[proxyAddress] = true;
emit OneToOne_ImplementationLocked(proxyAddress);
}
/**
* @dev Updates the implementation address for a many-to-one
* proxy relationship.
*
* @param implementationID Identifier for the implementation.
* @param implementation Address with the runtime code the proxies
* should use.
*/
function setImplementationAddressManyToOne(
bytes32 implementationID,
address implementation
)
external
override
onlyOwner
{
// Read the implementation holder address from storage.
address implementationHolder = _implementationHolders[implementationID];
// Verify that the implementation exists.
require(implementationHolder != address(0), "ERR_IMPLEMENTATION_ID");
// Verify implementation is not locked
require(!_lockedImplementations[implementationHolder], "ERR_IMPLEMENTATION_LOCKED");
// Set the implementation address
_setImplementation(implementationHolder, implementation);
emit ManyToOne_ImplementationUpdated(
implementationID,
implementation
);
}
/**
* @dev Updates the implementation address for a one-to-one proxy.
*
* Note: This could work for many-to-one as well if the caller
* provides the implementation holder address in place of the
* proxy address, as they use the same access control and update
* mechanism.
*
* @param proxyAddress Address of the deployed proxy
* @param implementation Address with the runtime code for
* the proxy to use.
*/
function setImplementationAddressOneToOne(
address proxyAddress,
address implementation
)
external
override
onlyOwner
{
// Verify proxy is not locked
require(!_lockedImplementations[proxyAddress], "ERR_IMPLEMENTATION_LOCKED");
// Set the implementation address
_setImplementation(proxyAddress, implementation);
emit OneToOne_ImplementationUpdated(proxyAddress, implementation);
}
/* ========== Proxy Deployment ========== */
/**
* @dev Deploy a proxy contract with a one-to-one relationship
* with its implementation.
*
* The proxy will have its own implementation address which can
* be updated by the proxy manager.
*
* @param suppliedSalt Salt provided by the account requesting deployment.
* @param implementation Address of the contract with the runtime
* code that the proxy should use.
*/
function deployProxyOneToOne(
bytes32 suppliedSalt,
address implementation
)
external
override
onlyOwner
returns(address proxyAddress)
{
// Derive the create2 salt from the deployment requester's address
// and the requester-supplied salt.
bytes32 salt = Salty.deriveOneToOneSalt(_msgSender(), suppliedSalt);
// Deploy the proxy
proxyAddress = Create2.deploy(
0,
salt,
type(DelegateCallProxyOneToOne).creationCode
);
// Set the implementation address on the new proxy.
_setImplementation(proxyAddress, implementation);
emit OneToOne_ProxyDeployed(proxyAddress, implementation);
}
/**
* @dev Deploy a proxy with a many-to-one relationship with its implemenation.
*
* The proxy will call the implementation holder for every transaction to
* determine the address to use in calls.
*
* @param implementationID Identifier for the proxy's implementation.
* @param suppliedSalt Salt provided by the account requesting deployment.
*/
function deployProxyManyToOne(bytes32 implementationID, bytes32 suppliedSalt)
external
override
onlyApprovedDeployer
returns(address proxyAddress)
{
// Read the implementation holder address from storage.
address implementationHolder = _implementationHolders[implementationID];
// Verify that the implementation exists.
require(implementationHolder != address(0), "ERR_IMPLEMENTATION_ID");
// Derive the create2 salt from the deployment requester's address, the
// implementation ID and the requester-supplied salt.
bytes32 salt = Salty.deriveManyToOneSalt(
_msgSender(),
implementationID,
suppliedSalt
);
// Set the implementation holder address in storage so the proxy
// constructor can query it.
_implementationHolder = implementationHolder;
// Deploy the proxy, which will query the implementation holder address
// and save it as an immutable in the contract bytecode.
proxyAddress = Create2.deploy(
0,
salt,
type(DelegateCallProxyManyToOne).creationCode
);
// Remove the address from temporary storage.
_implementationHolder = address(0);
emit ManyToOne_ProxyDeployed(
implementationID,
proxyAddress
);
}
/* ========== Queries ========== */
/**
* @dev Returns a boolean stating whether `implementationID` is locked.
*/
function isImplementationLocked(bytes32 implementationID) external override view returns (bool) {
// Read the implementation holder address from storage.
address implementationHolder = _implementationHolders[implementationID];
// Verify that the implementation exists.
require(implementationHolder != address(0), "ERR_IMPLEMENTATION_ID");
return _lockedImplementations[implementationHolder];
}
/**
* @dev Returns a boolean stating whether `proxyAddress` is locked.
*/
function isImplementationLocked(address proxyAddress) external override view returns (bool) {
return _lockedImplementations[proxyAddress];
}
/**
* @dev Returns a boolean stating whether `deployer` is allowed to deploy many-to-one
* proxies.
*/
function isApprovedDeployer(address deployer) external override view returns (bool) {
return _approvedDeployers[deployer];
}
/**
* @dev Queries the temporary storage value `_implementationHolder`.
* This is used in the constructor of the many-to-one proxy contract
* so that the create2 address is static (adding constructor arguments
* would change the codehash) and the implementation holder can be
* stored as a constant.
*/
function getImplementationHolder()
external
override
view
returns (address)
{
return _implementationHolder;
}
/**
* @dev Returns the address of the implementation holder contract
* for `implementationID`.
*/
function getImplementationHolder(
bytes32 implementationID
)
external
override
view
returns (address)
{
return _implementationHolders[implementationID];
}
/**
* @dev Computes the create2 address for a one-to-one proxy requested
* by `originator` using `suppliedSalt`.
*
* @param originator Address of the account requesting deployment.
* @param suppliedSalt Salt provided by the account requesting deployment.
*/
function computeProxyAddressOneToOne(
address originator,
bytes32 suppliedSalt
)
external
override
view
returns (address)
{
bytes32 salt = Salty.deriveOneToOneSalt(originator, suppliedSalt);
return Create2.computeAddress(salt, CodeHashes.ONE_TO_ONE_CODEHASH);
}
/**
* @dev Computes the create2 address for a many-to-one proxy for the
* implementation `implementationID` requested by `originator` using
* `suppliedSalt`.
*
* @param originator Address of the account requesting deployment.
* @param implementationID The identifier for the contract implementation.
* @param suppliedSalt Salt provided by the account requesting deployment.
*/
function computeProxyAddressManyToOne(
address originator,
bytes32 implementationID,
bytes32 suppliedSalt
)
external
override
view
returns (address)
{
bytes32 salt = Salty.deriveManyToOneSalt(
originator,
implementationID,
suppliedSalt
);
return Create2.computeAddress(salt, CodeHashes.MANY_TO_ONE_CODEHASH);
}
/**
* @dev Computes the create2 address of the implementation holder
* for `implementationID`.
*
* @param implementationID The identifier for the contract implementation.
*/
function computeHolderAddressManyToOne(bytes32 implementationID)
public
override
view
returns (address)
{
return Create2.computeAddress(
implementationID,
CodeHashes.IMPLEMENTATION_HOLDER_CODEHASH
);
}
/* ========== Internal Functions ========== */
/**
* @dev Sets the implementation address for a one-to-one proxy or
* many-to-one implementation holder. Both use the same access
* control and update mechanism, which is the receipt of a call
* from the proxy manager with the abi-encoded implementation address
* as the only calldata.
*
* Note: Verifies that the implementation address is a contract.
*
* @param proxyOrHolder Address of the one-to-one proxy or
* many-to-one implementation holder contract.
* @param implementation Address of the contract with the runtime
* code that the proxy or proxies should use.
*/
function _setImplementation(
address proxyOrHolder,
address implementation
) internal {
// Verify that the implementation address is a contract.
require(Address.isContract(implementation), "ERR_NOT_CONTRACT");
// Set the implementation address on the contract.
// solium-disable-next-line security/no-low-level-calls
(bool success,) = proxyOrHolder.call(abi.encode(implementation));
require(success, "ERR_SET_ADDRESS_REVERT");
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/**
* @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
* `CREATE2` can be used to compute in advance the address where a smart
* contract will be deployed, which allows for interesting new mechanisms known
* as 'counterfactual interactions'.
*
* See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
* information.
*/
library Create2 {
/**
* @dev Deploys a contract using `CREATE2`. The address where the contract
* will be deployed can be known in advance via {computeAddress}.
*
* The bytecode for a contract can be obtained from Solidity with
* `type(contractName).creationCode`.
*
* Requirements:
*
* - `bytecode` must not be empty.
* - `salt` must have not been used for `bytecode` already.
* - the factory must have a balance of at least `amount`.
* - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
*/
function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address) {
address addr;
require(address(this).balance >= amount, "Create2: insufficient balance");
require(bytecode.length != 0, "Create2: bytecode length is zero");
// solhint-disable-next-line no-inline-assembly
assembly {
addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
}
require(addr != address(0), "Create2: Failed on deploy");
return addr;
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
* `bytecodeHash` or `salt` will result in a new destination address.
*/
function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
return computeAddress(salt, bytecodeHash, address(this));
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
* `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
*/
function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address) {
bytes32 _data = keccak256(
abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash)
);
return address(uint256(_data));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
/**
* @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) {
// This method relies in extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
/**
* @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);
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import "../GSN/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(_owner == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/*
* @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 payable) {
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;
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity =0.6.12;
/**
* @dev The ManyToOneImplementationHolder stores an upgradeable implementation address
* in storage, which many-to-one proxies query at execution time to determine which
* contract to delegate to.
*
* The manager can upgrade the implementation address by calling the holder with the
* abi-encoded address as calldata. If any other account calls the implementation holder,
* it will return the implementation address.
*
* This pattern was inspired by the DharmaUpgradeBeacon from 0age
* https://github.com/dharma-eng/dharma-smart-wallet/blob/master/contracts/upgradeability/smart-wallet/DharmaUpgradeBeacon.sol
*/
contract ManyToOneImplementationHolder {
/* --- Storage --- */
address internal immutable _manager;
address internal _implementation;
/* --- Constructor --- */
constructor() public {
_manager = msg.sender;
}
/**
* @dev Fallback function for the contract.
*
* Used by proxies to read the implementation address and used
* by the proxy manager to set the implementation address.
*
* If called by the owner, reads the implementation address from
* calldata (must be abi-encoded) and stores it to the first slot.
*
* Otherwise, returns the stored implementation address.
*/
fallback() external payable {
if (msg.sender != _manager) {
assembly {
mstore(0, sload(0))
return(0, 32)
}
}
assembly { sstore(0, calldataload(0)) }
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity =0.6.12;
import { Proxy } from "@openzeppelin/contracts/proxy/Proxy.sol";
/**
* @dev Proxy contract which uses an implementation address shared with many
* other proxies.
*
* An implementation holder contract stores the upgradeable implementation address.
* When the proxy is called, it queries the implementation address from the holder
* contract and delegatecalls the returned address, forwarding the received calldata
* and ether.
*
* Note: This contract does not verify that the implementation
* address is a valid delegation target. The manager must perform
* this safety check before updating the implementation on the holder.
*/
contract DelegateCallProxyManyToOne is Proxy {
/* ========== Constants ========== */
// Address that stores the implementation address.
address internal immutable _implementationHolder;
/* ========== Constructor ========== */
constructor() public {
// Calls the sender rather than receiving the address in the constructor
// arguments so that the address is computable using create2.
_implementationHolder = ProxyDeployer(msg.sender).getImplementationHolder();
}
/* ========== Internal Overrides ========== */
/**
* @dev Queries the implementation address from the implementation holder.
*/
function _implementation() internal override view returns (address) {
// Queries the implementation address from the implementation holder.
(bool success, bytes memory data) = _implementationHolder.staticcall("");
require(success, string(data));
address implementation = abi.decode((data), (address));
require(implementation != address(0), "ERR_NULL_IMPLEMENTATION");
return implementation;
}
}
interface ProxyDeployer {
function getImplementationHolder() external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/**
* @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
* instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
* be specified by overriding the virtual {_implementation} function.
*
* Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
* different contract through the {_delegate} function.
*
* The success and return data of the delegated call will be returned back to the caller of the proxy.
*/
abstract contract Proxy {
/**
* @dev Delegates the current call to `implementation`.
*
* This function does not return to its internall call site, it will return directly to the external caller.
*/
function _delegate(address implementation) internal {
// solhint-disable-next-line no-inline-assembly
assembly {
// Copy msg.data. We take full control of memory in this inline assembly
// block because it will not return to Solidity code. We overwrite the
// Solidity scratch pad at memory position 0.
calldatacopy(0, 0, calldatasize())
// Call the implementation.
// out and outsize are 0 because we don't know the size yet.
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// Copy the returned data.
returndatacopy(0, 0, returndatasize())
switch result
// delegatecall returns 0 on error.
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
/**
* @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
* and {_fallback} should delegate.
*/
function _implementation() internal virtual view returns (address);
/**
* @dev Delegates the current call to the address returned by `_implementation()`.
*
* This function does not return to its internall call site, it will return directly to the external caller.
*/
function _fallback() internal {
_beforeFallback();
_delegate(_implementation());
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
* function in the contract matches the call data.
*/
fallback () payable external {
_fallback();
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
* is empty.
*/
receive () payable external {
_fallback();
}
/**
* @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
* call, or as part of the Solidity `fallback` or `receive` functions.
*
* If overriden should call `super._beforeFallback()`.
*/
function _beforeFallback() internal virtual {
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity =0.6.12;
import { Proxy } from "@openzeppelin/contracts/proxy/Proxy.sol";
/**
* @dev Upgradeable delegatecall proxy for a single contract.
*
* This proxy stores an implementation address which can be upgraded by the proxy manager.
*
* To upgrade the implementation, the manager calls the proxy with the abi encoded implementation address.
*
* If any other account calls the proxy, it will delegatecall the implementation address with the received
* calldata and ether. If the call succeeds, it will return with the received returndata.
* If it reverts, it will revert with the received revert data.
*
* Note: The storage slot for the implementation address is:
* `bytes32(uint256(keccak256("IMPLEMENTATION_ADDRESS")) + 1)`
* This slot must not be used by the implementation contract.
*
* Note: This contract does not verify that the implementation address is a valid delegation target.
* The manager must perform this safety check.
*/
contract DelegateCallProxyOneToOne is Proxy {
/* ========== Constants ========== */
address internal immutable _manager;
/* ========== Constructor ========== */
constructor() public {
_manager = msg.sender ;
}
/* ========== Internal Overrides ========== */
/**
* @dev Reads the implementation address from storage.
*/
function _implementation() internal override view returns (address) {
address implementation;
assembly {
implementation := sload(
// bytes32(uint256(keccak256("IMPLEMENTATION_ADDRESS")) + 1)
0x913bd12b32b36f36cedaeb6e043912bceb97022755958701789d3108d33a045a
)
}
return implementation;
}
/**
* @dev Hook that is called before falling back to the implementation.
*
* Checks if the call is from the owner.
* If it is, reads the abi-encoded implementation address from calldata and stores
* it at the slot `bytes32(uint256(keccak256("IMPLEMENTATION_ADDRESS")) + 1)`,
* then returns with no data.
* If it is not, continues execution with the fallback function.
*/
function _beforeFallback() internal override {
if (msg.sender != _manager) {
super._beforeFallback();
} else {
assembly {
sstore(
// bytes32(uint256(keccak256("IMPLEMENTATION_ADDRESS")) + 1)
0x913bd12b32b36f36cedaeb6e043912bceb97022755958701789d3108d33a045a,
calldataload(0)
)
return(0, 0)
}
}
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0;
/* --- External Libraries --- */
import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol";
/* --- Proxy Contracts --- */
import { CodeHashes } from "./CodeHashes.sol";
/**
* @dev Library for computing create2 salts and addresses for proxies
* deployed by `DelegateCallProxyManager`.
*
* Because the proxy factory is meant to be used by multiple contracts,
* we use a salt derivation pattern that includes the address of the
* contract that requested the proxy deployment, a salt provided by that
* contract and the implementation ID used (for many-to-one proxies only).
*/
library SaltyLib {
/* --- Salt Derivation --- */
/**
* @dev Derives the create2 salt for a many-to-one proxy.
*
* Many different contracts in the Indexed framework may use the
* same implementation contract, and they all use the same init
* code, so we derive the actual create2 salt from a combination
* of the implementation ID, the address of the account requesting
* deployment and the user-supplied salt.
*
* @param originator Address of the account requesting deployment.
* @param implementationID The identifier for the contract implementation.
* @param suppliedSalt Salt provided by the account requesting deployment.
*/
function deriveManyToOneSalt(
address originator,
bytes32 implementationID,
bytes32 suppliedSalt
)
internal
pure
returns (bytes32)
{
return keccak256(
abi.encodePacked(
originator,
implementationID,
suppliedSalt
)
);
}
/**
* @dev Derives the create2 salt for a one-to-one proxy.
*
* @param originator Address of the account requesting deployment.
* @param suppliedSalt Salt provided by the account requesting deployment.
*/
function deriveOneToOneSalt(
address originator,
bytes32 suppliedSalt
)
internal
pure
returns (bytes32)
{
return keccak256(abi.encodePacked(originator, suppliedSalt));
}
/* --- Address Derivation --- */
/**
* @dev Computes the create2 address for a one-to-one proxy deployed
* by `deployer` (the factory) when requested by `originator` using
* `suppliedSalt`.
*
* @param deployer Address of the proxy factory.
* @param originator Address of the account requesting deployment.
* @param suppliedSalt Salt provided by the account requesting deployment.
*/
function computeProxyAddressOneToOne(
address deployer,
address originator,
bytes32 suppliedSalt
)
internal
pure
returns (address)
{
bytes32 salt = deriveOneToOneSalt(originator, suppliedSalt);
return Create2.computeAddress(salt, CodeHashes.ONE_TO_ONE_CODEHASH, deployer);
}
/**
* @dev Computes the create2 address for a many-to-one proxy for the
* implementation `implementationID` deployed by `deployer` (the factory)
* when requested by `originator` using `suppliedSalt`.
*
* @param deployer Address of the proxy factory.
* @param originator Address of the account requesting deployment.
* @param implementationID The identifier for the contract implementation.
* @param suppliedSalt Salt provided by the account requesting deployment.
*/
function computeProxyAddressManyToOne(
address deployer,
address originator,
bytes32 implementationID,
bytes32 suppliedSalt
)
internal
pure
returns (address)
{
bytes32 salt = deriveManyToOneSalt(
originator,
implementationID,
suppliedSalt
);
return Create2.computeAddress(salt, CodeHashes.MANY_TO_ONE_CODEHASH, deployer);
}
/**
* @dev Computes the create2 address of the implementation holder
* for `implementationID`.
*
* @param deployer Address of the proxy factory.
* @param implementationID The identifier for the contract implementation.
*/
function computeHolderAddressManyToOne(
address deployer,
bytes32 implementationID
)
internal
pure
returns (address)
{
return Create2.computeAddress(
implementationID,
CodeHashes.IMPLEMENTATION_HOLDER_CODEHASH,
deployer
);
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0;
/**
* @dev Contract that manages deployment and upgrades of delegatecall proxies.
*
* An implementation identifier can be created on the proxy manager which is
* used to specify the logic address for a particular contract type, and to
* upgrade the implementation as needed.
*
* A one-to-one proxy is a single proxy contract with an upgradeable implementation
* address.
*
* A many-to-one proxy is a single upgradeable implementation address that may be
* used by many proxy contracts.
*/
interface IDelegateCallProxyManager {
/* ========== Events ========== */
event DeploymentApprovalGranted(address deployer);
event DeploymentApprovalRevoked(address deployer);
event ManyToOne_ImplementationCreated(
bytes32 implementationID,
address implementationAddress
);
event ManyToOne_ImplementationUpdated(
bytes32 implementationID,
address implementationAddress
);
event ManyToOne_ProxyDeployed(
bytes32 implementationID,
address proxyAddress
);
event OneToOne_ProxyDeployed(
address proxyAddress,
address implementationAddress
);
event OneToOne_ImplementationUpdated(
address proxyAddress,
address implementationAddress
);
/* ========== Controls ========== */
/**
* @dev Allows `deployer` to deploy many-to-one proxies.
*/
function approveDeployer(address deployer) external;
/**
* @dev Prevents `deployer` from deploying many-to-one proxies.
*/
function revokeDeployerApproval(address deployer) external;
/* ========== Implementation Management ========== */
/**
* @dev Creates a many-to-one proxy relationship.
*
* Deploys an implementation holder contract which stores the
* implementation address for many proxies. The implementation
* address can be updated on the holder to change the runtime
* code used by all its proxies.
*
* @param implementationID ID for the implementation, used to identify the
* proxies that use it. Also used as the salt in the create2 call when
* deploying the implementation holder contract.
* @param implementation Address with the runtime code the proxies
* should use.
*/
function createManyToOneProxyRelationship(
bytes32 implementationID,
address implementation
) external;
/**
* @dev Lock the current implementation for `proxyAddress` so that it can never be upgraded again.
*/
function lockImplementationManyToOne(bytes32 implementationID) external;
/**
* @dev Lock the current implementation for `proxyAddress` so that it can never be upgraded again.
*/
function lockImplementationOneToOne(address proxyAddress) external;
/**
* @dev Updates the implementation address for a many-to-one
* proxy relationship.
*
* @param implementationID Identifier for the implementation.
* @param implementation Address with the runtime code the proxies
* should use.
*/
function setImplementationAddressManyToOne(
bytes32 implementationID,
address implementation
) external;
/**
* @dev Updates the implementation address for a one-to-one proxy.
*
* Note: This could work for many-to-one as well if the caller
* provides the implementation holder address in place of the
* proxy address, as they use the same access control and update
* mechanism.
*
* @param proxyAddress Address of the deployed proxy
* @param implementation Address with the runtime code for
* the proxy to use.
*/
function setImplementationAddressOneToOne(
address proxyAddress,
address implementation
) external;
/* ========== Proxy Deployment ========== */
/**
* @dev Deploy a proxy contract with a one-to-one relationship
* with its implementation.
*
* The proxy will have its own implementation address which can
* be updated by the proxy manager.
*
* @param suppliedSalt Salt provided by the account requesting deployment.
* @param implementation Address of the contract with the runtime
* code that the proxy should use.
*/
function deployProxyOneToOne(
bytes32 suppliedSalt,
address implementation
) external returns(address proxyAddress);
/**
* @dev Deploy a proxy with a many-to-one relationship with its implemenation.
*
* The proxy will call the implementation holder for every transaction to
* determine the address to use in calls.
*
* @param implementationID Identifier for the proxy's implementation.
* @param suppliedSalt Salt provided by the account requesting deployment.
*/
function deployProxyManyToOne(
bytes32 implementationID,
bytes32 suppliedSalt
) external returns(address proxyAddress);
/* ========== Queries ========== */
/**
* @dev Returns a boolean stating whether `implementationID` is locked.
*/
function isImplementationLocked(bytes32 implementationID) external view returns (bool);
/**
* @dev Returns a boolean stating whether `proxyAddress` is locked.
*/
function isImplementationLocked(address proxyAddress) external view returns (bool);
/**
* @dev Returns a boolean stating whether `deployer` is allowed to deploy many-to-one
* proxies.
*/
function isApprovedDeployer(address deployer) external view returns (bool);
/**
* @dev Queries the temporary storage value `_implementationHolder`.
* This is used in the constructor of the many-to-one proxy contract
* so that the create2 address is static (adding constructor arguments
* would change the codehash) and the implementation holder can be
* stored as a constant.
*/
function getImplementationHolder() external view returns (address);
/**
* @dev Returns the address of the implementation holder contract
* for `implementationID`.
*/
function getImplementationHolder(bytes32 implementationID) external view returns (address);
/**
* @dev Computes the create2 address for a one-to-one proxy requested
* by `originator` using `suppliedSalt`.
*
* @param originator Address of the account requesting deployment.
* @param suppliedSalt Salt provided by the account requesting deployment.
*/
function computeProxyAddressOneToOne(
address originator,
bytes32 suppliedSalt
) external view returns (address);
/**
* @dev Computes the create2 address for a many-to-one proxy for the
* implementation `implementationID` requested by `originator` using
* `suppliedSalt`.
*
* @param originator Address of the account requesting deployment.
* @param implementationID The identifier for the contract implementation.
* @param suppliedSalt Salt provided by the account requesting deployment.
*/
function computeProxyAddressManyToOne(
address originator,
bytes32 implementationID,
bytes32 suppliedSalt
) external view returns (address);
/**
* @dev Computes the create2 address of the implementation holder
* for `implementationID`.
*
* @param implementationID The identifier for the contract implementation.
*/
function computeHolderAddressManyToOne(bytes32 implementationID) external view returns (address);
}File 4 of 5: IndexPool
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0;
/************************************************************************************************
Originally from https://github.com/balancer-labs/balancer-core/blob/master/contracts/BConst.sol
This source code has been modified from the original, which was copied from the github repository
at commit hash f4ed5d65362a8d6cec21662fb6eae233b0babc1f.
Subject to the GPL-3.0 license
*************************************************************************************************/
contract BConst {
uint256 public constant VERSION_NUMBER = 0;
/* --- Weight Updates --- */
// Minimum time passed between each weight update for a token.
uint256 internal constant WEIGHT_UPDATE_DELAY = 1 hours;
// Maximum percent by which a weight can adjust at a time
// relative to the current weight.
// The number of iterations needed to move from weight A to weight B is the floor of:
// (A > B): (ln(A) - ln(B)) / ln(1.01)
// (B > A): (ln(A) - ln(B)) / ln(0.99)
uint256 internal constant WEIGHT_CHANGE_PCT = BONE/100;
uint256 internal constant BONE = 10**18;
uint256 internal constant MIN_BOUND_TOKENS = 2;
uint256 internal constant MAX_BOUND_TOKENS = 10;
// Minimum swap fee.
uint256 internal constant MIN_FEE = BONE / 10**6;
// Maximum swap or exit fee.
uint256 internal constant MAX_FEE = BONE / 10;
// Actual exit fee.
uint256 internal constant EXIT_FEE = 0;
// Default total of all desired weights. Can differ by up to BONE.
uint256 internal constant DEFAULT_TOTAL_WEIGHT = BONE * 25;
// Minimum weight for any token (1/100).
uint256 internal constant MIN_WEIGHT = BONE / 4;
uint256 internal constant MAX_WEIGHT = BONE * 25;
// Maximum total weight.
uint256 internal constant MAX_TOTAL_WEIGHT = BONE * 26;
// Minimum balance for a token (only applied at initialization)
uint256 internal constant MIN_BALANCE = BONE / 10**12;
// Initial pool tokens
uint256 internal constant INIT_POOL_SUPPLY = BONE * 100;
uint256 internal constant MIN_BPOW_BASE = 1 wei;
uint256 internal constant MAX_BPOW_BASE = (2 * BONE) - 1 wei;
uint256 internal constant BPOW_PRECISION = BONE / 10**10;
// Maximum ratio of input tokens to balance for swaps.
uint256 internal constant MAX_IN_RATIO = BONE / 2;
// Maximum ratio of output tokens to balance for swaps.
uint256 internal constant MAX_OUT_RATIO = (BONE / 3) + 1 wei;
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0;
import "./BNum.sol";
/************************************************************************************************
Originally from https://github.com/balancer-labs/balancer-core/blob/master/contracts/BMath.sol
This source code has been modified from the original, which was copied from the github repository
at commit hash f4ed5d65362a8d6cec21662fb6eae233b0babc1f.
Subject to the GPL-3.0 license
*************************************************************************************************/
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 //
**********************************************************************************************/
function calcSpotPrice(
uint256 tokenBalanceIn,
uint256 tokenWeightIn,
uint256 tokenBalanceOut,
uint256 tokenWeightOut,
uint256 swapFee
) internal pure returns (uint256 spotPrice) {
uint256 numer = bdiv(tokenBalanceIn, tokenWeightIn);
uint256 denom = bdiv(tokenBalanceOut, tokenWeightOut);
uint256 ratio = bdiv(numer, denom);
uint256 scale = bdiv(BONE, bsub(BONE, swapFee));
return (spotPrice = bmul(ratio, scale));
}
/**********************************************************************************************
// 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 //
**********************************************************************************************/
function calcOutGivenIn(
uint256 tokenBalanceIn,
uint256 tokenWeightIn,
uint256 tokenBalanceOut,
uint256 tokenWeightOut,
uint256 tokenAmountIn,
uint256 swapFee
) internal pure returns (uint256 tokenAmountOut) {
uint256 weightRatio = bdiv(tokenWeightIn, tokenWeightOut);
uint256 adjustedIn = bsub(BONE, swapFee);
adjustedIn = bmul(tokenAmountIn, adjustedIn);
uint256 y = bdiv(tokenBalanceIn, badd(tokenBalanceIn, adjustedIn));
uint256 foo = bpow(y, weightRatio);
uint256 bar = bsub(BONE, foo);
tokenAmountOut = bmul(tokenBalanceOut, bar);
return tokenAmountOut;
}
/**********************************************************************************************
// 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 //
**********************************************************************************************/
function calcInGivenOut(
uint256 tokenBalanceIn,
uint256 tokenWeightIn,
uint256 tokenBalanceOut,
uint256 tokenWeightOut,
uint256 tokenAmountOut,
uint256 swapFee
) internal pure returns (uint256 tokenAmountIn) {
uint256 weightRatio = bdiv(tokenWeightOut, tokenWeightIn);
uint256 diff = bsub(tokenBalanceOut, tokenAmountOut);
uint256 y = bdiv(tokenBalanceOut, diff);
uint256 foo = bpow(y, weightRatio);
foo = bsub(foo, BONE);
tokenAmountIn = bsub(BONE, swapFee);
tokenAmountIn = bdiv(bmul(tokenBalanceIn, foo), tokenAmountIn);
return tokenAmountIn;
}
/**********************************************************************************************
// 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 \\ / //
**********************************************************************************************/
function calcPoolOutGivenSingleIn(
uint256 tokenBalanceIn,
uint256 tokenWeightIn,
uint256 poolSupply,
uint256 totalWeight,
uint256 tokenAmountIn,
uint256 swapFee
) internal pure returns (uint256 poolAmountOut) {
// Charge the trading fee for the proportion of tokenAi
/// which is implicitly traded to the other pool tokens.
// That proportion is (1- weightTokenIn)
// tokenAiAfterFee = tAi * (1 - (1-weightTi) * poolFee);
uint256 normalizedWeight = bdiv(tokenWeightIn, totalWeight);
uint256 zaz = bmul(bsub(BONE, normalizedWeight), swapFee);
uint256 tokenAmountInAfterFee = bmul(tokenAmountIn, bsub(BONE, zaz));
uint256 newTokenBalanceIn = badd(tokenBalanceIn, tokenAmountInAfterFee);
uint256 tokenInRatio = bdiv(newTokenBalanceIn, tokenBalanceIn);
// uint newPoolSupply = (ratioTi ^ weightTi) * poolSupply;
uint256 poolRatio = bpow(tokenInRatio, normalizedWeight);
uint256 newPoolSupply = bmul(poolRatio, poolSupply);
poolAmountOut = bsub(newPoolSupply, poolSupply);
return poolAmountOut;
}
/**********************************************************************************************
// 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 / //
**********************************************************************************************/
function calcSingleInGivenPoolOut(
uint256 tokenBalanceIn,
uint256 tokenWeightIn,
uint256 poolSupply,
uint256 totalWeight,
uint256 poolAmountOut,
uint256 swapFee
) internal pure returns (uint256 tokenAmountIn) {
uint256 normalizedWeight = bdiv(tokenWeightIn, totalWeight);
uint256 newPoolSupply = badd(poolSupply, poolAmountOut);
uint256 poolRatio = bdiv(newPoolSupply, poolSupply);
//uint newBalTi = poolRatio^(1/weightTi) * balTi;
uint256 boo = bdiv(BONE, normalizedWeight);
uint256 tokenInRatio = bpow(poolRatio, boo);
uint256 newTokenBalanceIn = bmul(tokenInRatio, tokenBalanceIn);
uint256 tokenAmountInAfterFee = bsub(newTokenBalanceIn, tokenBalanceIn);
// Do reverse order of fees charged in joinswap_ExternAmountIn, this way
// ``` pAo == joinswap_ExternAmountIn(Ti, joinswap_PoolAmountOut(pAo, Ti)) ```
//uint tAi = tAiAfterFee / (1 - (1-weightTi) * swapFee) ;
uint256 zar = bmul(bsub(BONE, normalizedWeight), swapFee);
tokenAmountIn = bdiv(tokenAmountInAfterFee, bsub(BONE, zar));
return tokenAmountIn;
}
/**********************************************************************************************
// 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 / / //
**********************************************************************************************/
function calcSingleOutGivenPoolIn(
uint256 tokenBalanceOut,
uint256 tokenWeightOut,
uint256 poolSupply,
uint256 totalWeight,
uint256 poolAmountIn,
uint256 swapFee
) internal pure returns (uint256 tokenAmountOut) {
uint256 normalizedWeight = bdiv(tokenWeightOut, totalWeight);
// charge exit fee on the pool token side
// pAiAfterExitFee = pAi*(1-exitFee)
uint256 poolAmountInAfterExitFee = bmul(poolAmountIn, bsub(BONE, EXIT_FEE));
uint256 newPoolSupply = bsub(poolSupply, poolAmountInAfterExitFee);
uint256 poolRatio = bdiv(newPoolSupply, poolSupply);
// newBalTo = poolRatio^(1/weightTo) * balTo;
uint256 tokenOutRatio = bpow(poolRatio, bdiv(BONE, normalizedWeight));
uint256 newTokenBalanceOut = bmul(tokenOutRatio, tokenBalanceOut);
uint256 tokenAmountOutBeforeSwapFee = bsub(
tokenBalanceOut,
newTokenBalanceOut
);
// charge swap fee on the output token side
//uint tAo = tAoBeforeSwapFee * (1 - (1-weightTo) * swapFee)
uint256 zaz = bmul(bsub(BONE, normalizedWeight), swapFee);
tokenAmountOut = bmul(tokenAmountOutBeforeSwapFee, bsub(BONE, zaz));
return tokenAmountOut;
}
/**********************************************************************************************
// 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 //
**********************************************************************************************/
function calcPoolInGivenSingleOut(
uint256 tokenBalanceOut,
uint256 tokenWeightOut,
uint256 poolSupply,
uint256 totalWeight,
uint256 tokenAmountOut,
uint256 swapFee
) internal pure returns (uint256 poolAmountIn) {
// charge swap fee on the output token side
uint256 normalizedWeight = bdiv(tokenWeightOut, totalWeight);
//uint tAoBeforeSwapFee = tAo / (1 - (1-weightTo) * swapFee) ;
uint256 zoo = bsub(BONE, normalizedWeight);
uint256 zar = bmul(zoo, swapFee);
uint256 tokenAmountOutBeforeSwapFee = bdiv(tokenAmountOut, bsub(BONE, zar));
uint256 newTokenBalanceOut = bsub(
tokenBalanceOut,
tokenAmountOutBeforeSwapFee
);
uint256 tokenOutRatio = bdiv(newTokenBalanceOut, tokenBalanceOut);
//uint newPoolSupply = (ratioTo ^ weightTo) * poolSupply;
uint256 poolRatio = bpow(tokenOutRatio, normalizedWeight);
uint256 newPoolSupply = bmul(poolRatio, poolSupply);
uint256 poolAmountInAfterExitFee = bsub(poolSupply, newPoolSupply);
// charge exit fee on the pool token side
// pAi = pAiAfterExitFee/(1-exitFee)
poolAmountIn = bdiv(poolAmountInAfterExitFee, bsub(BONE, EXIT_FEE));
return poolAmountIn;
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0;
import "./BConst.sol";
/************************************************************************************************
Originally from https://github.com/balancer-labs/balancer-core/blob/master/contracts/BNum.sol
This source code has been modified from the original, which was copied from the github repository
at commit hash f4ed5d65362a8d6cec21662fb6eae233b0babc1f.
Subject to the GPL-3.0 license
*************************************************************************************************/
contract BNum is BConst {
function btoi(uint256 a) internal pure returns (uint256) {
return a / BONE;
}
function bfloor(uint256 a) internal pure returns (uint256) {
return btoi(a) * BONE;
}
function badd(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "ERR_ADD_OVERFLOW");
return c;
}
function bsub(uint256 a, uint256 b) internal pure returns (uint256) {
(uint256 c, bool flag) = bsubSign(a, b);
require(!flag, "ERR_SUB_UNDERFLOW");
return c;
}
function bsubSign(uint256 a, uint256 b)
internal
pure
returns (uint256, bool)
{
if (a >= b) {
return (a - b, false);
} else {
return (b - a, true);
}
}
function bmul(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c0 = a * b;
require(a == 0 || c0 / a == b, "ERR_MUL_OVERFLOW");
uint256 c1 = c0 + (BONE / 2);
require(c1 >= c0, "ERR_MUL_OVERFLOW");
uint256 c2 = c1 / BONE;
return c2;
}
function bdiv(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "ERR_DIV_ZERO");
uint256 c0 = a * BONE;
require(a == 0 || c0 / a == BONE, "ERR_DIV_INTERNAL"); // bmul overflow
uint256 c1 = c0 + (b / 2);
require(c1 >= c0, "ERR_DIV_INTERNAL"); // badd require
uint256 c2 = c1 / b;
return c2;
}
// DSMath.wpow
function bpowi(uint256 a, uint256 n) internal pure returns (uint256) {
uint256 z = n % 2 != 0 ? a : BONE;
for (n /= 2; n != 0; n /= 2) {
a = bmul(a, a);
if (n % 2 != 0) {
z = bmul(z, a);
}
}
return z;
}
// Compute b^(e.w) by splitting it into (b^e)*(b^0.w).
// Use `bpowi` for `b^e` and `bpowK` for k iterations
// of approximation of b^0.w
function bpow(uint256 base, uint256 exp) internal pure returns (uint256) {
require(base >= MIN_BPOW_BASE, "ERR_BPOW_BASE_TOO_LOW");
require(base <= MAX_BPOW_BASE, "ERR_BPOW_BASE_TOO_HIGH");
uint256 whole = bfloor(exp);
uint256 remain = bsub(exp, whole);
uint256 wholePow = bpowi(base, btoi(whole));
if (remain == 0) {
return wholePow;
}
uint256 partialResult = bpowApprox(base, remain, BPOW_PRECISION);
return bmul(wholePow, partialResult);
}
function bpowApprox(
uint256 base,
uint256 exp,
uint256 precision
) internal pure returns (uint256) {
// term 0:
uint256 a = exp;
(uint256 x, bool xneg) = bsubSign(base, BONE);
uint256 term = BONE;
uint256 sum = term;
bool negative = false;
// term(k) = numer / denom
// = (product(a - i - 1, i=1-->k) * x^k) / (k!)
// each iteration, multiply previous term by (a-(k-1)) * x / k
// continue until term is less than precision
for (uint256 i = 1; term >= precision; i++) {
uint256 bigK = i * BONE;
(uint256 c, bool cneg) = bsubSign(a, bsub(bigK, BONE));
term = bmul(term, bmul(c, x));
term = bdiv(term, bigK);
if (term == 0) break;
if (xneg) negative = !negative;
if (cneg) negative = !negative;
if (negative) {
sum = bsub(sum, term);
} else {
sum = badd(sum, term);
}
}
return sum;
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0;
import "./BNum.sol";
/************************************************************************************************
Originally from https://github.com/balancer-labs/balancer-core/blob/master/contracts/BToken.sol
This source code has been modified from the original, which was copied from the github repository
at commit hash f4ed5d65362a8d6cec21662fb6eae233b0babc1f.
Subject to the GPL-3.0 license
*************************************************************************************************/
// Highly opinionated token implementation
interface IERC20 {
event Approval(address indexed src, address indexed dst, uint256 amt);
event Transfer(address indexed src, address indexed dst, uint256 amt);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address whom) external view returns (uint256);
function allowance(address src, address dst) external view returns (uint256);
function approve(address dst, uint256 amt) external returns (bool);
function transfer(address dst, uint256 amt) external returns (bool);
function transferFrom(
address src,
address dst,
uint256 amt
) external returns (bool);
}
contract BTokenBase is BNum {
mapping(address => uint256) internal _balance;
mapping(address => mapping(address => uint256)) internal _allowance;
uint256 internal _totalSupply;
event Approval(address indexed src, address indexed dst, uint256 amt);
event Transfer(address indexed src, address indexed dst, uint256 amt);
function _mint(uint256 amt) internal {
_balance[address(this)] = badd(_balance[address(this)], amt);
_totalSupply = badd(_totalSupply, amt);
emit Transfer(address(0), address(this), amt);
}
function _burn(uint256 amt) internal {
require(_balance[address(this)] >= amt, "ERR_INSUFFICIENT_BAL");
_balance[address(this)] = bsub(_balance[address(this)], amt);
_totalSupply = bsub(_totalSupply, amt);
emit Transfer(address(this), address(0), amt);
}
function _move(
address src,
address dst,
uint256 amt
) internal {
require(_balance[src] >= amt, "ERR_INSUFFICIENT_BAL");
_balance[src] = bsub(_balance[src], amt);
_balance[dst] = badd(_balance[dst], amt);
emit Transfer(src, dst, amt);
}
function _push(address to, uint256 amt) internal {
_move(address(this), to, amt);
}
function _pull(address from, uint256 amt) internal {
_move(from, address(this), amt);
}
}
contract BToken is BTokenBase, IERC20 {
uint8 private constant DECIMALS = 18;
string private _name;
string private _symbol;
function _initializeToken(string memory name, string memory symbol) internal {
require(
bytes(_name).length == 0 &&
bytes(name).length != 0 &&
bytes(symbol).length != 0,
"ERR_BTOKEN_INITIALIZED"
);
_name = name;
_symbol = symbol;
}
function name()
external
override
view
returns (string memory)
{
return _name;
}
function symbol()
external
override
view
returns (string memory)
{
return _symbol;
}
function decimals()
external
override
view
returns (uint8)
{
return DECIMALS;
}
function allowance(address src, address dst)
external
override
view
returns (uint256)
{
return _allowance[src][dst];
}
function balanceOf(address whom) external override view returns (uint256) {
return _balance[whom];
}
function totalSupply() public override view returns (uint256) {
return _totalSupply;
}
function approve(address dst, uint256 amt) external override returns (bool) {
_allowance[msg.sender][dst] = amt;
emit Approval(msg.sender, dst, amt);
return true;
}
function increaseApproval(address dst, uint256 amt) external returns (bool) {
_allowance[msg.sender][dst] = badd(_allowance[msg.sender][dst], amt);
emit Approval(msg.sender, dst, _allowance[msg.sender][dst]);
return true;
}
function decreaseApproval(address dst, uint256 amt) external returns (bool) {
uint256 oldValue = _allowance[msg.sender][dst];
if (amt > oldValue) {
_allowance[msg.sender][dst] = 0;
} else {
_allowance[msg.sender][dst] = bsub(oldValue, amt);
}
emit Approval(msg.sender, dst, _allowance[msg.sender][dst]);
return true;
}
function transfer(address dst, uint256 amt) external override returns (bool) {
_move(msg.sender, dst, amt);
return true;
}
function transferFrom(
address src,
address dst,
uint256 amt
) external override returns (bool) {
require(
msg.sender == src || amt <= _allowance[src][msg.sender],
"ERR_BTOKEN_BAD_CALLER"
);
_move(src, dst, amt);
if (msg.sender != src && _allowance[src][msg.sender] != uint256(-1)) {
_allowance[src][msg.sender] = bsub(_allowance[src][msg.sender], amt);
emit Approval(msg.sender, dst, _allowance[src][msg.sender]);
}
return true;
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
/* ========== Internal Inheritance ========== */
import "./BToken.sol";
import "./BMath.sol";
/* ========== Internal Interfaces ========== */
import "../interfaces/IFlashLoanRecipient.sol";
import "../interfaces/IIndexPool.sol";
/************************************************************************************************
Originally from https://github.com/balancer-labs/balancer-core/blob/master/contracts/BPool.sol
This source code has been modified from the original, which was copied from the github repository
at commit hash f4ed5d65362a8d6cec21662fb6eae233b0babc1f.
Subject to the GPL-3.0 license
*************************************************************************************************/
contract IndexPool is BToken, BMath, IIndexPool {
/* ========== EVENTS ========== */
/** @dev Emitted when tokens are swapped. */
event LOG_SWAP(
address indexed caller,
address indexed tokenIn,
address indexed tokenOut,
uint256 tokenAmountIn,
uint256 tokenAmountOut
);
/** @dev Emitted when underlying tokens are deposited for pool tokens. */
event LOG_JOIN(
address indexed caller,
address indexed tokenIn,
uint256 tokenAmountIn
);
/** @dev Emitted when pool tokens are burned for underlying. */
event LOG_EXIT(
address indexed caller,
address indexed tokenOut,
uint256 tokenAmountOut
);
/** @dev Emitted when a token's weight updates. */
event LOG_DENORM_UPDATED(address indexed token, uint256 newDenorm);
/** @dev Emitted when a token's desired weight is set. */
event LOG_DESIRED_DENORM_SET(address indexed token, uint256 desiredDenorm);
/** @dev Emitted when a token is unbound from the pool. */
event LOG_TOKEN_REMOVED(address token);
/** @dev Emitted when a token is unbound from the pool. */
event LOG_TOKEN_ADDED(
address indexed token,
uint256 desiredDenorm,
uint256 minimumBalance
);
/** @dev Emitted when a token's minimum balance is updated. */
event LOG_MINIMUM_BALANCE_UPDATED(address token, uint256 minimumBalance);
/** @dev Emitted when a token reaches its minimum balance. */
event LOG_TOKEN_READY(address indexed token);
/** @dev Emitted when public trades are enabled. */
event LOG_PUBLIC_SWAP_ENABLED();
/** @dev Emitted when the maximum tokens value is updated. */
event LOG_MAX_TOKENS_UPDATED(uint256 maxPoolTokens);
/** @dev Emitted when the swap fee is updated. */
event LOG_SWAP_FEE_UPDATED(uint256 swapFee);
/* ========== Modifiers ========== */
modifier _lock_ {
require(!_mutex, "ERR_REENTRY");
_mutex = true;
_;
_mutex = false;
}
modifier _viewlock_() {
require(!_mutex, "ERR_REENTRY");
_;
}
modifier _control_ {
require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
_;
}
modifier _public_ {
require(_publicSwap, "ERR_NOT_PUBLIC");
_;
}
/* ========== Storage ========== */
bool internal _mutex;
// Account with CONTROL role. Able to modify the swap fee,
// adjust token weights, bind and unbind tokens and lock
// public swaps & joins.
address internal _controller;
// Contract that handles unbound tokens.
TokenUnbindHandler internal _unbindHandler;
// True if PUBLIC can call SWAP & JOIN functions
bool internal _publicSwap;
// `setSwapFee` requires CONTROL
uint256 internal _swapFee;
// Array of underlying tokens in the pool.
address[] internal _tokens;
// Internal records of the pool's underlying tokens
mapping(address => Record) internal _records;
// Total denormalized weight of the pool.
uint256 internal _totalWeight;
// Minimum balances for tokens which have been added without the
// requisite initial balance.
mapping(address => uint256) internal _minimumBalances;
// Maximum LP tokens that can be bound.
// Used in alpha to restrict the economic impact of a catastrophic
// failure. It can be gradually increased as the pool continues to
// not be exploited.
uint256 internal _maxPoolTokens;
/* ========== Controls ========== */
/**
* @dev Sets the controller address and the token name & symbol.
*
* Note: This saves on storage costs for multi-step pool deployment.
*
* @param controller Controller of the pool
* @param name Name of the pool token
* @param symbol Symbol of the pool token
*/
function configure(
address controller,
string calldata name,
string calldata symbol
) external override {
require(_controller == address(0), "ERR_CONFIGURED");
require(controller != address(0), "ERR_NULL_ADDRESS");
_controller = controller;
// default fee is 2.5%
_swapFee = BONE / 40;
_initializeToken(name, symbol);
}
/**
* @dev Sets up the initial assets for the pool.
*
* Note: `tokenProvider` must have approved the pool to transfer the
* corresponding `balances` of `tokens`.
*
* @param tokens Underlying tokens to initialize the pool with
* @param balances Initial balances to transfer
* @param denorms Initial denormalized weights for the tokens
* @param tokenProvider Address to transfer the balances from
*/
function initialize(
address[] calldata tokens,
uint256[] calldata balances,
uint96[] calldata denorms,
address tokenProvider,
address unbindHandler
)
external
override
_control_
{
require(_tokens.length == 0, "ERR_INITIALIZED");
uint256 len = tokens.length;
require(len >= MIN_BOUND_TOKENS, "ERR_MIN_TOKENS");
require(len <= MAX_BOUND_TOKENS, "ERR_MAX_TOKENS");
require(balances.length == len && denorms.length == len, "ERR_ARR_LEN");
uint256 totalWeight = 0;
for (uint256 i = 0; i < len; i++) {
address token = tokens[i];
uint96 denorm = denorms[i];
uint256 balance = balances[i];
require(denorm >= MIN_WEIGHT, "ERR_MIN_WEIGHT");
require(denorm <= MAX_WEIGHT, "ERR_MAX_WEIGHT");
require(balance >= MIN_BALANCE, "ERR_MIN_BALANCE");
_records[token] = Record({
bound: true,
ready: true,
lastDenormUpdate: uint40(now),
denorm: denorm,
desiredDenorm: denorm,
index: uint8(i),
balance: balance
});
_tokens.push(token);
totalWeight = badd(totalWeight, denorm);
_pullUnderlying(token, tokenProvider, balance);
}
require(totalWeight <= MAX_TOTAL_WEIGHT, "ERR_MAX_TOTAL_WEIGHT");
_totalWeight = totalWeight;
_publicSwap = true;
emit LOG_PUBLIC_SWAP_ENABLED();
_mintPoolShare(INIT_POOL_SUPPLY);
_pushPoolShare(tokenProvider, INIT_POOL_SUPPLY);
_unbindHandler = TokenUnbindHandler(unbindHandler);
}
/**
* @dev Sets the maximum number of pool tokens that can be minted.
*
* This value will be used in the alpha to limit the maximum damage
* that can be caused by a catastrophic error. It can be gradually
* increased as the pool continues to not be exploited.
*
* If it is set to 0, the limit will be removed.
*/
function setMaxPoolTokens(uint256 maxPoolTokens) external override _control_ {
_maxPoolTokens = maxPoolTokens;
emit LOG_MAX_TOKENS_UPDATED(maxPoolTokens);
}
/* ========== Configuration Actions ========== */
/**
* @dev Set the swap fee.
* Note: Swap fee must be between 0.0001% and 10%
*/
function setSwapFee(uint256 swapFee) external override _control_ {
require(swapFee >= MIN_FEE, "ERR_MIN_FEE");
require(swapFee <= MAX_FEE, "ERR_MAX_FEE");
_swapFee = swapFee;
emit LOG_SWAP_FEE_UPDATED(swapFee);
}
/* ========== Token Management Actions ========== */
/**
* @dev Sets the desired weights for the pool tokens, which
* will be adjusted over time as they are swapped.
*
* Note: This does not check for duplicate tokens or that the total
* of the desired weights is equal to the target total weight (25).
* Those assumptions should be met in the controller. Further, the
* provided tokens should only include the tokens which are not set
* for removal.
*/
function reweighTokens(
address[] calldata tokens,
uint96[] calldata desiredDenorms
)
external
override
_lock_
_control_
{
uint256 len = tokens.length;
require(desiredDenorms.length == len, "ERR_ARR_LEN");
for (uint256 i = 0; i < len; i++)
_setDesiredDenorm(tokens[i], desiredDenorms[i]);
}
/**
* @dev Update the underlying assets held by the pool and their associated
* weights. Tokens which are not currently bound will be gradually added
* as they are swapped in to reach the provided minimum balances, which must
* be an amount of tokens worth the minimum weight of the total pool value.
* If a currently bound token is not received in this call, the token's
* desired weight will be set to 0.
*/
function reindexTokens(
address[] calldata tokens,
uint96[] calldata desiredDenorms,
uint256[] calldata minimumBalances
)
external
override
_lock_
_control_
{
uint256 len = tokens.length;
require(
desiredDenorms.length == len && minimumBalances.length == len,
"ERR_ARR_LEN"
);
// This size may not be the same as the input size, as it is possible
// to temporarily exceed the index size while tokens are being phased in
// or out.
uint256 tLen = _tokens.length;
bool[] memory receivedIndices = new bool[](tLen);
// We need to read token records in two separate loops, so
// write them to memory to avoid duplicate storage reads.
Record[] memory records = new Record[](len);
// Read all the records from storage and mark which of the existing tokens
// were represented in the reindex call.
for (uint256 i = 0; i < len; i++) {
records[i] = _records[tokens[i]];
if (records[i].bound) receivedIndices[records[i].index] = true;
}
// If any bound tokens were not sent in this call, set their desired weights to 0.
for (uint256 i = 0; i < tLen; i++) {
if (!receivedIndices[i]) {
_setDesiredDenorm(_tokens[i], 0);
}
}
for (uint256 i = 0; i < len; i++) {
address token = tokens[i];
// If an input weight is less than the minimum weight, use that instead.
uint96 denorm = desiredDenorms[i];
if (denorm < MIN_WEIGHT) denorm = uint96(MIN_WEIGHT);
if (!records[i].bound) {
// If the token is not bound, bind it.
_bind(token, minimumBalances[i], denorm);
} else {
_setDesiredDenorm(token, denorm);
}
}
}
/**
* @dev Updates the minimum balance for an uninitialized token.
* This becomes useful if a token's external price significantly
* rises after being bound, since the pool can not send a token
* out until it reaches the minimum balance.
*/
function setMinimumBalance(
address token,
uint256 minimumBalance
)
external
override
_control_
{
Record storage record = _records[token];
require(record.bound, "ERR_NOT_BOUND");
require(!record.ready, "ERR_READY");
_minimumBalances[token] = minimumBalance;
emit LOG_MINIMUM_BALANCE_UPDATED(token, minimumBalance);
}
/* ========== Liquidity Provider Actions ========== */
/**
* @dev Mint new pool tokens by providing the proportional amount of each
* underlying token's balance relative to the proportion of pool tokens minted.
*
* For any underlying tokens which are not initialized, the caller must provide
* the proportional share of the minimum balance for the token rather than the
* actual balance.
*
* @param poolAmountOut Amount of pool tokens to mint
* @param maxAmountsIn Maximum amount of each token to pay in the same
* order as the pool's _tokens list.
*/
function joinPool(uint256 poolAmountOut, uint256[] calldata maxAmountsIn)
external
override
_lock_
_public_
{
uint256 poolTotal = totalSupply();
uint256 ratio = bdiv(poolAmountOut, poolTotal);
require(ratio != 0, "ERR_MATH_APPROX");
require(maxAmountsIn.length == _tokens.length, "ERR_ARR_LEN");
uint256 maxPoolTokens = _maxPoolTokens;
if (maxPoolTokens > 0) {
require(
badd(poolTotal, poolAmountOut) <= maxPoolTokens,
"ERR_MAX_POOL_TOKENS"
);
}
for (uint256 i = 0; i < maxAmountsIn.length; i++) {
address t = _tokens[i];
(Record memory record, uint256 realBalance) = _getInputToken(t);
uint256 tokenAmountIn = bmul(ratio, record.balance);
require(tokenAmountIn != 0, "ERR_MATH_APPROX");
require(tokenAmountIn <= maxAmountsIn[i], "ERR_LIMIT_IN");
_updateInputToken(t, record, badd(realBalance, tokenAmountIn));
emit LOG_JOIN(msg.sender, t, tokenAmountIn);
_pullUnderlying(t, msg.sender, tokenAmountIn);
}
_mintPoolShare(poolAmountOut);
_pushPoolShare(msg.sender, poolAmountOut);
}
/**
* @dev Pay `tokenAmountIn` of `tokenIn` to mint at least `minPoolAmountOut`
* pool tokens.
*
* The pool implicitly swaps `(1- weightTokenIn) * tokenAmountIn` to the other
* underlying tokens. Thus a swap fee is charged against the input tokens.
*
* @param tokenIn Token to send the pool
* @param tokenAmountIn Exact amount of `tokenIn` to pay
* @param minPoolAmountOut Minimum amount of pool tokens to mint
* @return poolAmountOut - Amount of pool tokens minted
*/
function joinswapExternAmountIn(
address tokenIn,
uint256 tokenAmountIn,
uint256 minPoolAmountOut
)
external
override
_lock_
_public_
returns (uint256/* poolAmountOut */)
{
(Record memory inRecord, uint256 realInBalance) = _getInputToken(tokenIn);
require(tokenAmountIn != 0, "ERR_ZERO_IN");
require(
tokenAmountIn <= bmul(inRecord.balance, MAX_IN_RATIO),
"ERR_MAX_IN_RATIO"
);
uint256 poolAmountOut = calcPoolOutGivenSingleIn(
inRecord.balance,
inRecord.denorm,
_totalSupply,
_totalWeight,
tokenAmountIn,
_swapFee
);
uint256 maxPoolTokens = _maxPoolTokens;
if (maxPoolTokens > 0) {
require(
badd(_totalSupply, poolAmountOut) <= maxPoolTokens,
"ERR_MAX_POOL_TOKENS"
);
}
require(poolAmountOut >= minPoolAmountOut, "ERR_LIMIT_OUT");
_updateInputToken(tokenIn, inRecord, badd(realInBalance, tokenAmountIn));
emit LOG_JOIN(msg.sender, tokenIn, tokenAmountIn);
_mintPoolShare(poolAmountOut);
_pushPoolShare(msg.sender, poolAmountOut);
_pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
return poolAmountOut;
}
/**
* @dev Pay up to `maxAmountIn` of `tokenIn` to mint exactly `poolAmountOut`.
*
* The pool implicitly swaps `(1- weightTokenIn) * tokenAmountIn` to the other
* underlying tokens. Thus a swap fee is charged against the input tokens.
*
* @param tokenIn Token to send the pool
* @param poolAmountOut Exact amount of pool tokens to mint
* @param maxAmountIn Maximum amount of `tokenIn` to pay
* @return tokenAmountIn - Amount of `tokenIn` paid
*/
function joinswapPoolAmountOut(
address tokenIn,
uint256 poolAmountOut,
uint256 maxAmountIn
)
external
override
_lock_
_public_
returns (uint256/* tokenAmountIn */)
{
uint256 maxPoolTokens = _maxPoolTokens;
if (maxPoolTokens > 0) {
require(
badd(_totalSupply, poolAmountOut) <= maxPoolTokens,
"ERR_MAX_POOL_TOKENS"
);
}
(Record memory inRecord, uint256 realInBalance) = _getInputToken(tokenIn);
uint256 tokenAmountIn = calcSingleInGivenPoolOut(
inRecord.balance,
inRecord.denorm,
_totalSupply,
_totalWeight,
poolAmountOut,
_swapFee
);
require(tokenAmountIn != 0, "ERR_MATH_APPROX");
require(tokenAmountIn <= maxAmountIn, "ERR_LIMIT_IN");
require(
tokenAmountIn <= bmul(inRecord.balance, MAX_IN_RATIO),
"ERR_MAX_IN_RATIO"
);
_updateInputToken(tokenIn, inRecord, badd(realInBalance, tokenAmountIn));
emit LOG_JOIN(msg.sender, tokenIn, tokenAmountIn);
_mintPoolShare(poolAmountOut);
_pushPoolShare(msg.sender, poolAmountOut);
_pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
return tokenAmountIn;
}
/**
* @dev Burns `poolAmountIn` pool tokens in exchange for the amounts of each
* underlying token's balance proportional to the ratio of tokens burned to
* total pool supply. The amount of each token transferred to the caller must
* be greater than or equal to the associated minimum output amount from the
* `minAmountsOut` array.
*
* @param poolAmountIn Exact amount of pool tokens to burn
* @param minAmountsOut Minimum amount of each token to receive, in the same
* order as the pool's _tokens list.
*/
function exitPool(uint256 poolAmountIn, uint256[] calldata minAmountsOut)
external
override
_lock_
{
require(minAmountsOut.length == _tokens.length, "ERR_ARR_LEN");
uint256 poolTotal = totalSupply();
uint256 exitFee = bmul(poolAmountIn, EXIT_FEE);
uint256 pAiAfterExitFee = bsub(poolAmountIn, exitFee);
uint256 ratio = bdiv(pAiAfterExitFee, poolTotal);
require(ratio != 0, "ERR_MATH_APPROX");
_pullPoolShare(msg.sender, poolAmountIn);
_pushPoolShare(_controller, exitFee);
_burnPoolShare(pAiAfterExitFee);
for (uint256 i = 0; i < minAmountsOut.length; i++) {
address t = _tokens[i];
Record memory record = _records[t];
if (record.ready) {
uint256 tokenAmountOut = bmul(ratio, record.balance);
require(tokenAmountOut != 0, "ERR_MATH_APPROX");
require(tokenAmountOut >= minAmountsOut[i], "ERR_LIMIT_OUT");
_records[t].balance = bsub(record.balance, tokenAmountOut);
emit LOG_EXIT(msg.sender, t, tokenAmountOut);
_pushUnderlying(t, msg.sender, tokenAmountOut);
} else {
// If the token is not initialized, it can not exit the pool.
require(minAmountsOut[i] == 0, "ERR_OUT_NOT_READY");
}
}
}
/**
* @dev Burns `poolAmountIn` pool tokens in exchange for at least `minAmountOut`
* of `tokenOut`. Returns the number of tokens sent to the caller.
*
* The pool implicitly burns the tokens for all underlying tokens and swaps them
* to the desired output token. A swap fee is charged against the output tokens.
*
* @param tokenOut Token to receive
* @param poolAmountIn Exact amount of pool tokens to burn
* @param minAmountOut Minimum amount of `tokenOut` to receive
* @return tokenAmountOut - Amount of `tokenOut` received
*/
function exitswapPoolAmountIn(
address tokenOut,
uint256 poolAmountIn,
uint256 minAmountOut
)
external
override
_lock_
returns (uint256/* tokenAmountOut */)
{
Record memory outRecord = _getOutputToken(tokenOut);
uint256 tokenAmountOut = calcSingleOutGivenPoolIn(
outRecord.balance,
outRecord.denorm,
_totalSupply,
_totalWeight,
poolAmountIn,
_swapFee
);
require(tokenAmountOut >= minAmountOut, "ERR_LIMIT_OUT");
require(
tokenAmountOut <= bmul(outRecord.balance, MAX_OUT_RATIO),
"ERR_MAX_OUT_RATIO"
);
_pushUnderlying(tokenOut, msg.sender, tokenAmountOut);
_records[tokenOut].balance = bsub(outRecord.balance, tokenAmountOut);
_decreaseDenorm(outRecord, tokenOut);
uint256 exitFee = bmul(poolAmountIn, EXIT_FEE);
emit LOG_EXIT(msg.sender, tokenOut, tokenAmountOut);
_pullPoolShare(msg.sender, poolAmountIn);
_burnPoolShare(bsub(poolAmountIn, exitFee));
_pushPoolShare(_controller, exitFee);
return tokenAmountOut;
}
/**
* @dev Burn up to `maxPoolAmountIn` for exactly `tokenAmountOut` of `tokenOut`.
* Returns the number of pool tokens burned.
*
* The pool implicitly burns the tokens for all underlying tokens and swaps them
* to the desired output token. A swap fee is charged against the output tokens.
*
* @param tokenOut Token to receive
* @param tokenAmountOut Exact amount of `tokenOut` to receive
* @param maxPoolAmountIn Maximum amount of pool tokens to burn
* @return poolAmountIn - Amount of pool tokens burned
*/
function exitswapExternAmountOut(
address tokenOut,
uint256 tokenAmountOut,
uint256 maxPoolAmountIn
)
external
override
_lock_
returns (uint256/* poolAmountIn */)
{
Record memory outRecord = _getOutputToken(tokenOut);
require(
tokenAmountOut <= bmul(outRecord.balance, MAX_OUT_RATIO),
"ERR_MAX_OUT_RATIO"
);
uint256 poolAmountIn = calcPoolInGivenSingleOut(
outRecord.balance,
outRecord.denorm,
_totalSupply,
_totalWeight,
tokenAmountOut,
_swapFee
);
require(poolAmountIn != 0, "ERR_MATH_APPROX");
require(poolAmountIn <= maxPoolAmountIn, "ERR_LIMIT_IN");
_pushUnderlying(tokenOut, msg.sender, tokenAmountOut);
_records[tokenOut].balance = bsub(outRecord.balance, tokenAmountOut);
_decreaseDenorm(outRecord, tokenOut);
uint256 exitFee = bmul(poolAmountIn, EXIT_FEE);
emit LOG_EXIT(msg.sender, tokenOut, tokenAmountOut);
_pullPoolShare(msg.sender, poolAmountIn);
_burnPoolShare(bsub(poolAmountIn, exitFee));
_pushPoolShare(_controller, exitFee);
return poolAmountIn;
}
/* ========== Other ========== */
/**
* @dev Absorb any tokens that have been sent to the pool.
* If the token is not bound, it will be sent to the unbound
* token handler.
*/
function gulp(address token) external override _lock_ {
Record storage record = _records[token];
uint256 balance = IERC20(token).balanceOf(address(this));
if (record.bound) {
if (!record.ready) {
uint256 minimumBalance = _minimumBalances[token];
if (balance >= minimumBalance) {
_minimumBalances[token] = 0;
record.ready = true;
emit LOG_TOKEN_READY(token);
uint256 additionalBalance = bsub(balance, minimumBalance);
uint256 balRatio = bdiv(additionalBalance, minimumBalance);
uint96 newDenorm = uint96(badd(MIN_WEIGHT, bmul(MIN_WEIGHT, balRatio)));
record.denorm = newDenorm;
record.lastDenormUpdate = uint40(now);
_totalWeight = badd(_totalWeight, newDenorm);
emit LOG_DENORM_UPDATED(token, record.denorm);
}
}
_records[token].balance = balance;
} else {
_pushUnderlying(token, address(_unbindHandler), balance);
_unbindHandler.handleUnbindToken(token, balance);
}
}
/* ========== Flash Loan ========== */
/**
* @dev Execute a flash loan, transferring `amount` of `token` to `recipient`.
* `amount` must be repaid with `swapFee` interest by the end of the transaction.
*
* @param recipient Must implement the IFlashLoanRecipient interface
* @param token Token to borrow
* @param amount Amount to borrow
* @param data Data to send to the recipient in `receiveFlashLoan` call
*/
function flashBorrow(
address recipient,
address token,
uint256 amount,
bytes calldata data
)
external
override
_lock_
{
Record storage record = _records[token];
require(record.bound, "ERR_NOT_BOUND");
uint256 balStart = IERC20(token).balanceOf(address(this));
require(balStart >= amount, "ERR_INSUFFICIENT_BAL");
_pushUnderlying(token, address(recipient), amount);
uint256 fee = bmul(balStart, _swapFee);
uint256 amountDue = badd(amount, fee);
IFlashLoanRecipient(recipient).receiveFlashLoan(token, amount, amountDue, data);
uint256 balEnd = IERC20(token).balanceOf(address(this));
require(
balEnd > balStart && balEnd >= amountDue,
"ERR_INSUFFICIENT_PAYMENT"
);
record.balance = balEnd;
// If the payment brings the token above its minimum balance,
// clear the minimum and mark the token as ready.
if (!record.ready) {
uint256 minimumBalance = _minimumBalances[token];
if (balEnd >= minimumBalance) {
_minimumBalances[token] = 0;
record.ready = true;
emit LOG_TOKEN_READY(token);
uint256 additionalBalance = bsub(balEnd, minimumBalance);
uint256 balRatio = bdiv(additionalBalance, minimumBalance);
uint96 newDenorm = uint96(badd(MIN_WEIGHT, bmul(MIN_WEIGHT, balRatio)));
record.denorm = newDenorm;
record.lastDenormUpdate = uint40(now);
_totalWeight = badd(_totalWeight, newDenorm);
emit LOG_DENORM_UPDATED(token, record.denorm);
}
}
}
/* ========== Token Swaps ========== */
/**
* @dev Execute a token swap with a specified amount of input
* tokens and a minimum amount of output tokens.
*
* Note: Will revert if `tokenOut` is uninitialized.
*
* @param tokenIn Token to swap in
* @param tokenAmountIn Exact amount of `tokenIn` to swap in
* @param tokenOut Token to swap out
* @param minAmountOut Minimum amount of `tokenOut` to receive
* @param maxPrice Maximum ratio of input to output tokens
* @return (tokenAmountOut, spotPriceAfter)
*/
function swapExactAmountIn(
address tokenIn,
uint256 tokenAmountIn,
address tokenOut,
uint256 minAmountOut,
uint256 maxPrice
)
external
override
_lock_
_public_
returns (uint256/* tokenAmountOut */, uint256/* spotPriceAfter */)
{
(Record memory inRecord, uint256 realInBalance) = _getInputToken(tokenIn);
Record memory outRecord = _getOutputToken(tokenOut);
require(
tokenAmountIn <= bmul(inRecord.balance, MAX_IN_RATIO),
"ERR_MAX_IN_RATIO"
);
uint256 spotPriceBefore = calcSpotPrice(
inRecord.balance,
inRecord.denorm,
outRecord.balance,
outRecord.denorm,
_swapFee
);
require(spotPriceBefore <= maxPrice, "ERR_BAD_LIMIT_PRICE");
uint256 tokenAmountOut = calcOutGivenIn(
inRecord.balance,
inRecord.denorm,
outRecord.balance,
outRecord.denorm,
tokenAmountIn,
_swapFee
);
require(tokenAmountOut >= minAmountOut, "ERR_LIMIT_OUT");
_pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
_pushUnderlying(tokenOut, msg.sender, tokenAmountOut);
realInBalance = badd(realInBalance, tokenAmountIn);
_updateInputToken(tokenIn, inRecord, realInBalance);
if (inRecord.ready) {
inRecord.balance = realInBalance;
}
// Update the in-memory record for the spotPriceAfter calculation,
// then update the storage record with the local balance.
outRecord.balance = bsub(outRecord.balance, tokenAmountOut);
_records[tokenOut].balance = outRecord.balance;
// If needed, update the output token's weight.
_decreaseDenorm(outRecord, tokenOut);
uint256 spotPriceAfter = calcSpotPrice(
inRecord.balance,
inRecord.denorm,
outRecord.balance,
outRecord.denorm,
_swapFee
);
require(spotPriceAfter >= spotPriceBefore, "ERR_MATH_APPROX_2");
require(spotPriceAfter <= maxPrice, "ERR_LIMIT_PRICE");
require(
spotPriceBefore <= bdiv(tokenAmountIn, tokenAmountOut),
"ERR_MATH_APPROX"
);
emit LOG_SWAP(msg.sender, tokenIn, tokenOut, tokenAmountIn, tokenAmountOut);
return (tokenAmountOut, spotPriceAfter);
}
/**
* @dev Trades at most `maxAmountIn` of `tokenIn` for exactly `tokenAmountOut`
* of `tokenOut`.
*
* Returns the actual input amount and the new spot price after the swap,
* which can not exceed `maxPrice`.
*
* @param tokenIn Token to swap in
* @param maxAmountIn Maximum amount of `tokenIn` to pay
* @param tokenOut Token to swap out
* @param tokenAmountOut Exact amount of `tokenOut` to receive
* @param maxPrice Maximum ratio of input to output tokens
* @return (tokenAmountIn, spotPriceAfter)
*/
function swapExactAmountOut(
address tokenIn,
uint256 maxAmountIn,
address tokenOut,
uint256 tokenAmountOut,
uint256 maxPrice
)
external
override
_lock_
_public_
returns (uint256 /* tokenAmountIn */, uint256 /* spotPriceAfter */)
{
(Record memory inRecord, uint256 realInBalance) = _getInputToken(tokenIn);
Record memory outRecord = _getOutputToken(tokenOut);
require(
tokenAmountOut <= bmul(outRecord.balance, MAX_OUT_RATIO),
"ERR_MAX_OUT_RATIO"
);
uint256 spotPriceBefore = calcSpotPrice(
inRecord.balance,
inRecord.denorm,
outRecord.balance,
outRecord.denorm,
_swapFee
);
require(spotPriceBefore <= maxPrice, "ERR_BAD_LIMIT_PRICE");
uint256 tokenAmountIn = calcInGivenOut(
inRecord.balance,
inRecord.denorm,
outRecord.balance,
outRecord.denorm,
tokenAmountOut,
_swapFee
);
require(tokenAmountIn <= maxAmountIn, "ERR_LIMIT_IN");
_pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
_pushUnderlying(tokenOut, msg.sender, tokenAmountOut);
// Update the balance and (if necessary) weight of the input token.
realInBalance = badd(realInBalance, tokenAmountIn);
_updateInputToken(tokenIn, inRecord, realInBalance);
if (inRecord.ready) {
inRecord.balance = realInBalance;
}
// Update the in-memory record for the spotPriceAfter calculation,
// then update the storage record with the local balance.
outRecord.balance = bsub(outRecord.balance, tokenAmountOut);
_records[tokenOut].balance = outRecord.balance;
// If needed, update the output token's weight.
_decreaseDenorm(outRecord, tokenOut);
uint256 spotPriceAfter = calcSpotPrice(
inRecord.balance,
inRecord.denorm,
outRecord.balance,
outRecord.denorm,
_swapFee
);
require(spotPriceAfter >= spotPriceBefore, "ERR_MATH_APPROX");
require(spotPriceAfter <= maxPrice, "ERR_LIMIT_PRICE");
require(
spotPriceBefore <= bdiv(tokenAmountIn, tokenAmountOut),
"ERR_MATH_APPROX"
);
emit LOG_SWAP(msg.sender, tokenIn, tokenOut, tokenAmountIn, tokenAmountOut);
return (tokenAmountIn, spotPriceAfter);
}
/* ========== Config Queries ========== */
/**
* @dev Check if swapping tokens and joining the pool is allowed.
*/
function isPublicSwap() external view override returns (bool) {
return _publicSwap;
}
function getSwapFee() external view override _viewlock_ returns (uint256/* swapFee */) {
return _swapFee;
}
/**
* @dev Returns the controller address.
*/
function getController() external view override returns (address)
{
return _controller;
}
/* ========== Token Queries ========== */
function getMaxPoolTokens() external view override returns (uint256) {
return _maxPoolTokens;
}
/**
* @dev Check if a token is bound to the pool.
*/
function isBound(address t) external view override returns (bool) {
return _records[t].bound;
}
/**
* @dev Get the number of tokens bound to the pool.
*/
function getNumTokens() external view override returns (uint256) {
return _tokens.length;
}
/**
* @dev Get all bound tokens.
*/
function getCurrentTokens()
external
view
override
_viewlock_
returns (address[] memory tokens)
{
tokens = _tokens;
}
/**
* @dev Returns the list of tokens which have a desired weight above 0.
* Tokens with a desired weight of 0 are set to be phased out of the pool.
*/
function getCurrentDesiredTokens()
external
view
override
_viewlock_
returns (address[] memory tokens)
{
address[] memory tempTokens = _tokens;
tokens = new address[](tempTokens.length);
uint256 usedIndex = 0;
for (uint256 i = 0; i < tokens.length; i++) {
address token = tempTokens[i];
if (_records[token].desiredDenorm > 0) {
tokens[usedIndex++] = token;
}
}
assembly { mstore(tokens, usedIndex) }
}
/**
* @dev Returns the denormalized weight of a bound token.
*/
function getDenormalizedWeight(address token)
external
view
override
_viewlock_
returns (uint256/* denorm */)
{
require(_records[token].bound, "ERR_NOT_BOUND");
return _records[token].denorm;
}
/**
* @dev Returns the record for a token bound to the pool.
*/
function getTokenRecord(address token)
external
view
override
_viewlock_
returns (Record memory record)
{
record = _records[token];
require(record.bound, "ERR_NOT_BOUND");
}
/**
* @dev Finds the first token which is both initialized and has a
* desired weight above 0, then returns the address of that token
* and the extrapolated value of the pool in terms of that token.
*
* The value is extrapolated by multiplying the token's
* balance by the reciprocal of its normalized weight.
* @return (token, extrapolatedValue)
*/
function extrapolatePoolValueFromToken()
external
view
override
_viewlock_
returns (address/* token */, uint256/* extrapolatedValue */)
{
address token;
uint256 extrapolatedValue;
uint256 len = _tokens.length;
for (uint256 i = 0; i < len; i++) {
token = _tokens[i];
Record storage record = _records[token];
if (record.ready && record.desiredDenorm > 0) {
extrapolatedValue = bmul(
record.balance,
bdiv(_totalWeight, record.denorm)
);
break;
}
}
require(extrapolatedValue > 0, "ERR_NONE_READY");
return (token, extrapolatedValue);
}
/**
* @dev Get the total denormalized weight of the pool.
*/
function getTotalDenormalizedWeight()
external
view
override
_viewlock_
returns (uint256)
{
return _totalWeight;
}
/**
* @dev Returns the stored balance of a bound token.
*/
function getBalance(address token) external view override _viewlock_ returns (uint256) {
Record storage record = _records[token];
require(record.bound, "ERR_NOT_BOUND");
return record.balance;
}
/**
* @dev Get the minimum balance of an uninitialized token.
* Note: Throws if the token is initialized.
*/
function getMinimumBalance(address token) external view override _viewlock_ returns (uint256) {
Record memory record = _records[token];
require(record.bound, "ERR_NOT_BOUND");
require(!record.ready, "ERR_READY");
return _minimumBalances[token];
}
/**
* @dev Returns the balance of a token which is used in price
* calculations. If the token is initialized, this is the
* stored balance; if not, this is the minimum balance.
*/
function getUsedBalance(address token) external view override _viewlock_ returns (uint256) {
Record memory record = _records[token];
require(record.bound, "ERR_NOT_BOUND");
if (!record.ready) {
return _minimumBalances[token];
}
return record.balance;
}
/* ========== Price Queries ========== */
/**
* @dev Returns the spot price for `tokenOut` in terms of `tokenIn`.
*/
function getSpotPrice(address tokenIn, address tokenOut)
external
view
override
_viewlock_
returns (uint256)
{
(Record memory inRecord,) = _getInputToken(tokenIn);
Record memory outRecord = _getOutputToken(tokenOut);
return
calcSpotPrice(
inRecord.balance,
inRecord.denorm,
outRecord.balance,
outRecord.denorm,
_swapFee
);
}
/* ========== Pool Share Internal Functions ========== */
function _pullPoolShare(address from, uint256 amount) internal {
_pull(from, amount);
}
function _pushPoolShare(address to, uint256 amount) internal {
_push(to, amount);
}
function _mintPoolShare(uint256 amount) internal {
_mint(amount);
}
function _burnPoolShare(uint256 amount) internal {
_burn(amount);
}
/* ========== Underlying Token Internal Functions ========== */
// 'Underlying' token-manipulation functions make external calls but are NOT locked
// You must `_lock_` or otherwise ensure reentry-safety
function _pullUnderlying(
address erc20,
address from,
uint256 amount
) internal {
(bool success, bytes memory data) = erc20.call(
abi.encodeWithSelector(
IERC20.transferFrom.selector,
from,
address(this),
amount
)
);
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"ERR_ERC20_FALSE"
);
}
function _pushUnderlying(
address erc20,
address to,
uint256 amount
) internal {
(bool success, bytes memory data) = erc20.call(
abi.encodeWithSelector(
IERC20.transfer.selector,
to,
amount
)
);
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"ERR_ERC20_FALSE"
);
}
/* ========== Token Management Internal Functions ========== */
/**
* @dev Bind a token by address without actually depositing a balance.
* The token will be unable to be swapped out until it reaches the minimum balance.
* Note: Token must not already be bound.
* Note: `minimumBalance` should represent an amount of the token which is worth
* the portion of the current pool value represented by the minimum weight.
* @param token Address of the token to bind
* @param minimumBalance minimum balance to reach before the token can be swapped out
* @param desiredDenorm Desired weight for the token.
*/
function _bind(
address token,
uint256 minimumBalance,
uint96 desiredDenorm
) internal {
require(!_records[token].bound, "ERR_IS_BOUND");
require(desiredDenorm >= MIN_WEIGHT, "ERR_MIN_WEIGHT");
require(desiredDenorm <= MAX_WEIGHT, "ERR_MAX_WEIGHT");
require(minimumBalance >= MIN_BALANCE, "ERR_MIN_BALANCE");
_records[token] = Record({
bound: true,
ready: false,
lastDenormUpdate: 0,
denorm: 0,
desiredDenorm: desiredDenorm,
index: uint8(_tokens.length),
balance: 0
});
_tokens.push(token);
_minimumBalances[token] = minimumBalance;
emit LOG_TOKEN_ADDED(token, desiredDenorm, minimumBalance);
}
/**
* @dev Remove a token from the pool.
* Replaces the address in the tokens array with the last address,
* then removes it from the array.
* Note: This should only be called after the total weight has been adjusted.
* Note: Must be called in a function with:
* - _lock_ modifier to prevent reentrance
* - requirement that the token is bound
*/
function _unbind(address token) internal {
Record memory record = _records[token];
uint256 tokenBalance = record.balance;
// Swap the token-to-unbind with the last token,
// then delete the last token
uint256 index = record.index;
uint256 last = _tokens.length - 1;
// Only swap the token with the last token if it is not
// already at the end of the array.
if (index != last) {
_tokens[index] = _tokens[last];
_records[_tokens[index]].index = uint8(index);
}
_tokens.pop();
_records[token] = Record({
bound: false,
ready: false,
lastDenormUpdate: 0,
denorm: 0,
desiredDenorm: 0,
index: 0,
balance: 0
});
// transfer any remaining tokens out
_pushUnderlying(token, address(_unbindHandler), tokenBalance);
_unbindHandler.handleUnbindToken(token, tokenBalance);
emit LOG_TOKEN_REMOVED(token);
}
function _setDesiredDenorm(address token, uint96 desiredDenorm) internal {
Record storage record = _records[token];
require(record.bound, "ERR_NOT_BOUND");
// If the desired weight is 0, this will trigger a gradual unbinding of the token.
// Therefore the weight only needs to be above the minimum weight if it isn't 0.
require(
desiredDenorm >= MIN_WEIGHT || desiredDenorm == 0,
"ERR_MIN_WEIGHT"
);
require(desiredDenorm <= MAX_WEIGHT, "ERR_MAX_WEIGHT");
record.desiredDenorm = desiredDenorm;
emit LOG_DESIRED_DENORM_SET(token, desiredDenorm);
}
function _increaseDenorm(Record memory record, address token) internal {
// If the weight does not need to increase or the token is not
// initialized, don't do anything.
if (
record.denorm >= record.desiredDenorm ||
!record.ready ||
now - record.lastDenormUpdate < WEIGHT_UPDATE_DELAY
) return;
uint96 oldWeight = record.denorm;
uint96 denorm = record.desiredDenorm;
uint256 maxDiff = bmul(oldWeight, WEIGHT_CHANGE_PCT);
uint256 diff = bsub(denorm, oldWeight);
if (diff > maxDiff) {
denorm = uint96(badd(oldWeight, maxDiff));
diff = maxDiff;
}
_totalWeight = badd(_totalWeight, diff);
require(_totalWeight <= MAX_TOTAL_WEIGHT, "ERR_MAX_TOTAL_WEIGHT");
// Update the in-memory denorm value for spot-price computations.
record.denorm = denorm;
// Update the storage record
_records[token].denorm = denorm;
_records[token].lastDenormUpdate = uint40(now);
emit LOG_DENORM_UPDATED(token, denorm);
}
function _decreaseDenorm(Record memory record, address token) internal {
// If the weight does not need to decrease, don't do anything.
if (
record.denorm <= record.desiredDenorm ||
!record.ready ||
now - record.lastDenormUpdate < WEIGHT_UPDATE_DELAY
) return;
uint96 oldWeight = record.denorm;
uint96 denorm = record.desiredDenorm;
uint256 maxDiff = bmul(oldWeight, WEIGHT_CHANGE_PCT);
uint256 diff = bsub(oldWeight, denorm);
if (diff > maxDiff) {
denorm = uint96(bsub(oldWeight, maxDiff));
diff = maxDiff;
}
if (denorm <= MIN_WEIGHT) {
denorm = 0;
_totalWeight = bsub(_totalWeight, denorm);
// Because this is removing the token from the pool, the
// in-memory denorm value is irrelevant, as it is only used
// to calculate the new spot price, but the spot price calc
// will throw if it is passed 0 for the denorm.
_unbind(token);
} else {
_totalWeight = bsub(_totalWeight, diff);
// Update the in-memory denorm value for spot-price computations.
record.denorm = denorm;
// Update the stored denorm value
_records[token].denorm = denorm;
_records[token].lastDenormUpdate = uint40(now);
emit LOG_DENORM_UPDATED(token, denorm);
}
}
/**
* @dev Handles weight changes and initialization of an
* input token.
*
* If the token is not initialized and the new balance is
* still below the minimum, this will not do anything.
*
* If the token is not initialized but the new balance will
* bring the token above the minimum balance, this will
* mark the token as initialized, remove the minimum
* balance and set the weight to the minimum weight plus
* 1%.
*
*
* @param token Address of the input token
* @param record Token record with minimums applied to the balance
* and weight if the token was uninitialized.
*/
function _updateInputToken(
address token,
Record memory record,
uint256 realBalance
)
internal
{
if (!record.ready) {
// Check if the minimum balance has been reached
if (realBalance >= record.balance) {
// Remove the minimum balance record
_minimumBalances[token] = 0;
// Mark the token as initialized
_records[token].ready = true;
record.ready = true;
emit LOG_TOKEN_READY(token);
// Set the initial denorm value to the minimum weight times one plus
// the ratio of the increase in balance over the minimum to the minimum
// balance.
// weight = (1 + ((bal - min_bal) / min_bal)) * min_weight
uint256 additionalBalance = bsub(realBalance, record.balance);
uint256 balRatio = bdiv(additionalBalance, record.balance);
record.denorm = uint96(badd(MIN_WEIGHT, bmul(MIN_WEIGHT, balRatio)));
_records[token].denorm = record.denorm;
_records[token].lastDenormUpdate = uint40(now);
_totalWeight = badd(_totalWeight, record.denorm);
emit LOG_DENORM_UPDATED(token, record.denorm);
} else {
uint256 realToMinRatio = bdiv(
bsub(record.balance, realBalance),
record.balance
);
uint256 weightPremium = bmul(MIN_WEIGHT / 10, realToMinRatio);
record.denorm = uint96(badd(MIN_WEIGHT, weightPremium));
}
// If the token is still not ready, do not adjust the weight.
} else {
// If the token is already initialized, update the weight (if any adjustment
// is needed).
_increaseDenorm(record, token);
}
// Regardless of whether the token is initialized, store the actual new balance.
_records[token].balance = realBalance;
}
/* ========== Token Query Internal Functions ========== */
/**
* @dev Get the record for a token which is being swapped in.
* The token must be bound to the pool. If the token is not
* initialized (meaning it does not have the minimum balance)
* this function will return the actual balance of the token
* which the pool holds, but set the record's balance and weight
* to the token's minimum balance and the pool's minimum weight.
* This allows the token swap to be priced correctly even if the
* pool does not own any of the tokens.
*/
function _getInputToken(address token)
internal
view
returns (Record memory record, uint256 realBalance)
{
record = _records[token];
require(record.bound, "ERR_NOT_BOUND");
realBalance = record.balance;
// If the input token is not initialized, we use the minimum
// initial weight and minimum initial balance instead of the
// real values for price and output calculations.
if (!record.ready) {
record.balance = _minimumBalances[token];
uint256 realToMinRatio = bdiv(
bsub(record.balance, realBalance),
record.balance
);
uint256 weightPremium = bmul(MIN_WEIGHT / 10, realToMinRatio);
record.denorm = uint96(badd(MIN_WEIGHT, weightPremium));
}
}
function _getOutputToken(address token)
internal
view
returns (Record memory record)
{
record = _records[token];
require(record.bound, "ERR_NOT_BOUND");
// Tokens which have not reached their minimum balance can not be
// swapped out.
require(record.ready, "ERR_OUT_NOT_READY");
}
}
interface TokenUnbindHandler {
/**
* @dev Receive `amount` of `token` from the pool.
*/
function handleUnbindToken(address token, uint256 amount) external;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0;
interface IFlashLoanRecipient {
function receiveFlashLoan(
address tokenBorrowed,
uint256 amountBorrowed,
uint256 amountDue,
bytes calldata data
) external;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
interface IIndexPool {
/**
* @dev Token record data structure
* @param bound is token bound to pool
* @param ready has token been initialized
* @param lastDenormUpdate timestamp of last denorm change
* @param denorm denormalized weight
* @param desiredDenorm desired denormalized weight (used for incremental changes)
* @param index index of address in tokens array
* @param balance token balance
*/
struct Record {
bool bound;
bool ready;
uint40 lastDenormUpdate;
uint96 denorm;
uint96 desiredDenorm;
uint8 index;
uint256 balance;
}
event LOG_SWAP(
address indexed caller,
address indexed tokenIn,
address indexed tokenOut,
uint256 tokenAmountIn,
uint256 tokenAmountOut
);
event LOG_JOIN(
address indexed caller,
address indexed tokenIn,
uint256 tokenAmountIn
);
event LOG_EXIT(
address indexed caller,
address indexed tokenOut,
uint256 tokenAmountOut
);
event LOG_DENORM_UPDATED(address indexed token, uint256 newDenorm);
event LOG_DESIRED_DENORM_SET(address indexed token, uint256 desiredDenorm);
event LOG_TOKEN_REMOVED(address token);
event LOG_TOKEN_ADDED(
address indexed token,
uint256 desiredDenorm,
uint256 minimumBalance
);
event LOG_MINIMUM_BALANCE_UPDATED(address token, uint256 minimumBalance);
event LOG_TOKEN_READY(address indexed token);
event LOG_PUBLIC_SWAP_ENABLED();
event LOG_MAX_TOKENS_UPDATED(uint256 maxPoolTokens);
event LOG_SWAP_FEE_UPDATED(uint256 swapFee);
function configure(
address controller,
string calldata name,
string calldata symbol
) external;
function initialize(
address[] calldata tokens,
uint256[] calldata balances,
uint96[] calldata denorms,
address tokenProvider,
address unbindHandler
) external;
function setMaxPoolTokens(uint256 maxPoolTokens) external;
function setSwapFee(uint256 swapFee) external;
function reweighTokens(
address[] calldata tokens,
uint96[] calldata desiredDenorms
) external;
function reindexTokens(
address[] calldata tokens,
uint96[] calldata desiredDenorms,
uint256[] calldata minimumBalances
) external;
function setMinimumBalance(address token, uint256 minimumBalance) external;
function joinPool(uint256 poolAmountOut, uint256[] calldata maxAmountsIn) external;
function joinswapExternAmountIn(
address tokenIn,
uint256 tokenAmountIn,
uint256 minPoolAmountOut
) external returns (uint256/* poolAmountOut */);
function joinswapPoolAmountOut(
address tokenIn,
uint256 poolAmountOut,
uint256 maxAmountIn
) external returns (uint256/* tokenAmountIn */);
function exitPool(uint256 poolAmountIn, uint256[] calldata minAmountsOut) external;
function exitswapPoolAmountIn(
address tokenOut,
uint256 poolAmountIn,
uint256 minAmountOut
)
external returns (uint256/* tokenAmountOut */);
function exitswapExternAmountOut(
address tokenOut,
uint256 tokenAmountOut,
uint256 maxPoolAmountIn
) external returns (uint256/* poolAmountIn */);
function gulp(address token) external;
function flashBorrow(
address recipient,
address token,
uint256 amount,
bytes calldata data
) external;
function swapExactAmountIn(
address tokenIn,
uint256 tokenAmountIn,
address tokenOut,
uint256 minAmountOut,
uint256 maxPrice
) external returns (uint256/* tokenAmountOut */, uint256/* spotPriceAfter */);
function swapExactAmountOut(
address tokenIn,
uint256 maxAmountIn,
address tokenOut,
uint256 tokenAmountOut,
uint256 maxPrice
) external returns (uint256 /* tokenAmountIn */, uint256 /* spotPriceAfter */);
function isPublicSwap() external view returns (bool);
function getSwapFee() external view returns (uint256/* swapFee */);
function getController() external view returns (address);
function getMaxPoolTokens() external view returns (uint256);
function isBound(address t) external view returns (bool);
function getNumTokens() external view returns (uint256);
function getCurrentTokens() external view returns (address[] memory tokens);
function getCurrentDesiredTokens() external view returns (address[] memory tokens);
function getDenormalizedWeight(address token) external view returns (uint256/* denorm */);
function getTokenRecord(address token) external view returns (Record memory record);
function extrapolatePoolValueFromToken() external view returns (address/* token */, uint256/* extrapolatedValue */);
function getTotalDenormalizedWeight() external view returns (uint256);
function getBalance(address token) external view returns (uint256);
function getMinimumBalance(address token) external view returns (uint256);
function getUsedBalance(address token) external view returns (uint256);
function getSpotPrice(address tokenIn, address tokenOut) external view returns (uint256);
}File 5 of 5: GasToken2
pragma solidity ^0.4.10;
contract GasToken2 {
//////////////////////////////////////////////////////////////////////////
// RLP.sol
// Due to some unexplained bug, we get a slightly different bytecode if
// we use an import, and are then unable to verify the code in Etherscan
//////////////////////////////////////////////////////////////////////////
uint256 constant ADDRESS_BYTES = 20;
uint256 constant MAX_SINGLE_BYTE = 128;
uint256 constant MAX_NONCE = 256**9 - 1;
// count number of bytes required to represent an unsigned integer
function count_bytes(uint256 n) constant internal returns (uint256 c) {
uint i = 0;
uint mask = 1;
while (n >= mask) {
i += 1;
mask *= 256;
}
return i;
}
function mk_contract_address(address a, uint256 n) constant internal returns (address rlp) {
/*
* make sure the RLP encoding fits in one word:
* total_length 1 byte
* address_length 1 byte
* address 20 bytes
* nonce_length 1 byte (or 0)
* nonce 1-9 bytes
* ==========
* 24-32 bytes
*/
require(n <= MAX_NONCE);
// number of bytes required to write down the nonce
uint256 nonce_bytes;
// length in bytes of the RLP encoding of the nonce
uint256 nonce_rlp_len;
if (0 < n && n < MAX_SINGLE_BYTE) {
// nonce fits in a single byte
// RLP(nonce) = nonce
nonce_bytes = 1;
nonce_rlp_len = 1;
} else {
// RLP(nonce) = [num_bytes_in_nonce nonce]
nonce_bytes = count_bytes(n);
nonce_rlp_len = nonce_bytes + 1;
}
// [address_length(1) address(20) nonce_length(0 or 1) nonce(1-9)]
uint256 tot_bytes = 1 + ADDRESS_BYTES + nonce_rlp_len;
// concatenate all parts of the RLP encoding in the leading bytes of
// one 32-byte word
uint256 word = ((192 + tot_bytes) * 256**31) +
((128 + ADDRESS_BYTES) * 256**30) +
(uint256(a) * 256**10);
if (0 < n && n < MAX_SINGLE_BYTE) {
word += n * 256**9;
} else {
word += (128 + nonce_bytes) * 256**9;
word += n * 256**(9 - nonce_bytes);
}
uint256 hash;
assembly {
let mem_start := mload(0x40) // get a pointer to free memory
mstore(0x40, add(mem_start, 0x20)) // update the pointer
mstore(mem_start, word) // store the rlp encoding
hash := sha3(mem_start,
add(tot_bytes, 1)) // hash the rlp encoding
}
// interpret hash as address (20 least significant bytes)
return address(hash);
}
//////////////////////////////////////////////////////////////////////////
// Generic ERC20
//////////////////////////////////////////////////////////////////////////
// owner -> amount
mapping(address => uint256) s_balances;
// owner -> spender -> max amount
mapping(address => mapping(address => uint256)) s_allowances;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
// Spec: Get the account balance of another account with address `owner`
function balanceOf(address owner) public constant returns (uint256 balance) {
return s_balances[owner];
}
function internalTransfer(address from, address to, uint256 value) internal returns (bool success) {
if (value <= s_balances[from]) {
s_balances[from] -= value;
s_balances[to] += value;
Transfer(from, to, value);
return true;
} else {
return false;
}
}
// Spec: Send `value` amount of tokens to address `to`
function transfer(address to, uint256 value) public returns (bool success) {
address from = msg.sender;
return internalTransfer(from, to, value);
}
// Spec: Send `value` amount of tokens from address `from` to address `to`
function transferFrom(address from, address to, uint256 value) public returns (bool success) {
address spender = msg.sender;
if(value <= s_allowances[from][spender] && internalTransfer(from, to, value)) {
s_allowances[from][spender] -= value;
return true;
} else {
return false;
}
}
// Spec: Allow `spender` to withdraw from your account, multiple times, up
// to the `value` amount. If this function is called again it overwrites the
// current allowance with `value`.
function approve(address spender, uint256 value) public returns (bool success) {
address owner = msg.sender;
if (value != 0 && s_allowances[owner][spender] != 0) {
return false;
}
s_allowances[owner][spender] = value;
Approval(owner, spender, value);
return true;
}
// Spec: Returns the `amount` which `spender` is still allowed to withdraw
// from `owner`.
// What if the allowance is higher than the balance of the `owner`?
// Callers should be careful to use min(allowance, balanceOf) to make sure
// that the allowance is actually present in the account!
function allowance(address owner, address spender) public constant returns (uint256 remaining) {
return s_allowances[owner][spender];
}
//////////////////////////////////////////////////////////////////////////
// GasToken specifics
//////////////////////////////////////////////////////////////////////////
uint8 constant public decimals = 2;
string constant public name = "Gastoken.io";
string constant public symbol = "GST2";
// We build a queue of nonces at which child contracts are stored. s_head is
// the nonce at the head of the queue, s_tail is the nonce behind the tail
// of the queue. The queue grows at the head and shrinks from the tail.
// Note that when and only when a contract CREATEs another contract, the
// creating contract's nonce is incremented.
// The first child contract is created with nonce == 1, the second child
// contract is created with nonce == 2, and so on...
// For example, if there are child contracts at nonces [2,3,4],
// then s_head == 4 and s_tail == 1. If there are no child contracts,
// s_head == s_tail.
uint256 s_head;
uint256 s_tail;
// totalSupply gives the number of tokens currently in existence
// Each token corresponds to one child contract that can be SELFDESTRUCTed
// for a gas refund.
function totalSupply() public constant returns (uint256 supply) {
return s_head - s_tail;
}
// Creates a child contract that can only be destroyed by this contract.
function makeChild() internal returns (address addr) {
assembly {
// EVM assembler of runtime portion of child contract:
// ;; Pseudocode: if (msg.sender != 0x0000000000b3f879cb30fe243b4dfee438691c04) { throw; }
// ;; suicide(msg.sender)
// PUSH15 0xb3f879cb30fe243b4dfee438691c04 ;; hardcoded address of this contract
// CALLER
// XOR
// PC
// JUMPI
// CALLER
// SELFDESTRUCT
// Or in binary: 6eb3f879cb30fe243b4dfee438691c043318585733ff
// Since the binary is so short (22 bytes), we can get away
// with a very simple initcode:
// PUSH22 0x6eb3f879cb30fe243b4dfee438691c043318585733ff
// PUSH1 0
// MSTORE ;; at this point, memory locations mem[10] through
// ;; mem[31] contain the runtime portion of the child
// ;; contract. all that's left to do is to RETURN this
// ;; chunk of memory.
// PUSH1 22 ;; length
// PUSH1 10 ;; offset
// RETURN
// Or in binary: 756eb3f879cb30fe243b4dfee438691c043318585733ff6000526016600af3
// Almost done! All we have to do is put this short (31 bytes) blob into
// memory and call CREATE with the appropriate offsets.
let solidity_free_mem_ptr := mload(0x40)
mstore(solidity_free_mem_ptr, 0x00756eb3f879cb30fe243b4dfee438691c043318585733ff6000526016600af3)
addr := create(0, add(solidity_free_mem_ptr, 1), 31)
}
}
// Mints `value` new sub-tokens (e.g. cents, pennies, ...) by creating `value`
// new child contracts. The minted tokens are owned by the caller of this
// function.
function mint(uint256 value) public {
for (uint256 i = 0; i < value; i++) {
makeChild();
}
s_head += value;
s_balances[msg.sender] += value;
}
// Destroys `value` child contracts and updates s_tail.
//
// This function is affected by an issue in solc: https://github.com/ethereum/solidity/issues/2999
// The `mk_contract_address(this, i).call();` doesn't forward all available gas, but only GAS - 25710.
// As a result, when this line is executed with e.g. 30000 gas, the callee will have less than 5000 gas
// available and its SELFDESTRUCT operation will fail leading to no gas refund occurring.
// The remaining ~29000 gas left after the call is enough to update s_tail and the caller's balance.
// Hence tokens will have been destroyed without a commensurate gas refund.
// Fortunately, there is a simple workaround:
// Whenever you call free, freeUpTo, freeFrom, or freeUpToFrom, ensure that you pass at least
// 25710 + `value` * (1148 + 5722 + 150) gas. (It won't all be used)
function destroyChildren(uint256 value) internal {
uint256 tail = s_tail;
// tail points to slot behind the last contract in the queue
for (uint256 i = tail + 1; i <= tail + value; i++) {
mk_contract_address(this, i).call();
}
s_tail = tail + value;
}
// Frees `value` sub-tokens (e.g. cents, pennies, ...) belonging to the
// caller of this function by destroying `value` child contracts, which
// will trigger a partial gas refund.
// You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas
// when calling this function. For details, see the comment above `destroyChilden`.
function free(uint256 value) public returns (bool success) {
uint256 from_balance = s_balances[msg.sender];
if (value > from_balance) {
return false;
}
destroyChildren(value);
s_balances[msg.sender] = from_balance - value;
return true;
}
// Frees up to `value` sub-tokens. Returns how many tokens were freed.
// Otherwise, identical to free.
// You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas
// when calling this function. For details, see the comment above `destroyChilden`.
function freeUpTo(uint256 value) public returns (uint256 freed) {
uint256 from_balance = s_balances[msg.sender];
if (value > from_balance) {
value = from_balance;
}
destroyChildren(value);
s_balances[msg.sender] = from_balance - value;
return value;
}
// Frees `value` sub-tokens owned by address `from`. Requires that `msg.sender`
// has been approved by `from`.
// You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas
// when calling this function. For details, see the comment above `destroyChilden`.
function freeFrom(address from, uint256 value) public returns (bool success) {
address spender = msg.sender;
uint256 from_balance = s_balances[from];
if (value > from_balance) {
return false;
}
mapping(address => uint256) from_allowances = s_allowances[from];
uint256 spender_allowance = from_allowances[spender];
if (value > spender_allowance) {
return false;
}
destroyChildren(value);
s_balances[from] = from_balance - value;
from_allowances[spender] = spender_allowance - value;
return true;
}
// Frees up to `value` sub-tokens owned by address `from`. Returns how many tokens were freed.
// Otherwise, identical to `freeFrom`.
// You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas
// when calling this function. For details, see the comment above `destroyChilden`.
function freeFromUpTo(address from, uint256 value) public returns (uint256 freed) {
address spender = msg.sender;
uint256 from_balance = s_balances[from];
if (value > from_balance) {
value = from_balance;
}
mapping(address => uint256) from_allowances = s_allowances[from];
uint256 spender_allowance = from_allowances[spender];
if (value > spender_allowance) {
value = spender_allowance;
}
destroyChildren(value);
s_balances[from] = from_balance - value;
from_allowances[spender] = spender_allowance - value;
return value;
}
}