Transaction Hash:
Block:
11361957 at Nov-30-2020 07:15:55 PM +UTC
Transaction Fee:
0.002896286 ETH
$7.57
Gas Used:
39,139 Gas / 74 Gwei
Emitted Events:
225 |
Api3Token.Transfer( from=[Receiver] BatchExchange, to=[Sender] 0x93f5af632ce523286e033f0510e9b3c9710f4489, value=2765201279147278924764 )
|
226 |
BatchExchange.Withdraw( user=[Sender] 0x93f5af632ce523286e033f0510e9b3c9710f4489, token=Api3Token, amount=2765201279147278924764 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x0b38210e...ea731B88a | |||||
0x6F400810...93eaa841F | (DXdao: Mesa) | ||||
0x829BD824...93333A830
Miner
| (F2Pool Old) | 7,394.717080456883095274 Eth | 7,394.719976742883095274 Eth | 0.002896286 | |
0x93f5af63...9710F4489 |
3.809369779698365356 Eth
Nonce: 9672
|
3.806473493698365356 Eth
Nonce: 9673
| 0.002896286 |
Execution Trace
withdraw[EpochTokenLocker (ln:431)]
updateDepositsBalance[EpochTokenLocker (ln:432)]
getCurrentBatchId[EpochTokenLocker (ln:569)]
add[EpochTokenLocker (ln:571)]
getCurrentBatchId[EpochTokenLocker (ln:434)]
getCurrentBatchId[EpochTokenLocker (ln:438)]
min[EpochTokenLocker (ln:441)]
sub[EpochTokenLocker (ln:443)]
safeTransfer[EpochTokenLocker (ln:446)]
callOptionalReturn[SafeERC20 (ln:264)]
isContract[SafeERC20 (ln:307)]
call[SafeERC20 (ln:310)]
decode[SafeERC20 (ln:315)]
encodeWithSelector[SafeERC20 (ln:264)]
Withdraw[EpochTokenLocker (ln:447)]
File 1 of 2: BatchExchange
File 2 of 2: Api3Token
pragma solidity ^0.5.0; interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. * * NOTE: This is a feature of the next version of OpenZeppelin Contracts. * @dev Get it via `npm install @openzeppelin/contracts@next`. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. * NOTE: This is a feature of the next version of OpenZeppelin Contracts. * @dev Get it via `npm install @openzeppelin/contracts@next`. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. * * NOTE: This is a feature of the next version of OpenZeppelin Contracts. * @dev Get it via `npm install @openzeppelin/contracts@next`. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } library Address { /** * @dev Returns true if `account` is a contract. * * This test is non-exhaustive, and there may be false-negatives: during the * execution of a contract's constructor, its address will be reported as * not containing 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. */ 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. // According to EIP-1052, 0x0 is the value returned for not-yet created accounts // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // for accounts without code, i.e. `keccak256('')` bytes32 codehash; bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(account) } return (codehash != 0x0 && codehash != accountHash); } /** * @dev Converts an `address` into `address payable`. Note that this is * simply a type cast: the actual underlying value is not changed. * * NOTE: This is a feature of the next version of OpenZeppelin Contracts. * @dev Get it via `npm install @openzeppelin/contracts@next`. */ function toPayable(address account) internal pure returns (address payable) { return address(uint160(account)); } } library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. // A Solidity high level call has three parts: // 1. The target address is checked to verify it contains contract code // 2. The call itself is made, and success asserted // 3. The return value is decoded, which in turn checks the size of the returned data. // solhint-disable-next-line max-line-length require(address(token).isContract(), "SafeERC20: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = address(token).call(data); require(success, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow, so we distribute return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2); } } contract EpochTokenLocker { using SafeMath for uint256; /** @dev Number of seconds a batch is lasting*/ uint32 public constant BATCH_TIME = 300; // User => Token => BalanceState mapping(address => mapping(address => BalanceState)) private balanceStates; // user => token => lastCreditBatchId mapping(address => mapping(address => uint32)) public lastCreditBatchId; struct BalanceState { uint256 balance; PendingFlux pendingDeposits; // deposits will be credited in any future epoch, i.e. currentStateIndex > batchId PendingFlux pendingWithdraws; // withdraws are allowed in any future epoch, i.e. currentStateIndex > batchId } struct PendingFlux { uint256 amount; uint32 batchId; } event Deposit(address indexed user, address indexed token, uint256 amount, uint32 batchId); event WithdrawRequest(address indexed user, address indexed token, uint256 amount, uint32 batchId); event Withdraw(address indexed user, address indexed token, uint256 amount); /** @dev credits user with deposit amount on next epoch (given by getCurrentBatchId) * @param token address of token to be deposited * @param amount number of token(s) to be credited to user's account * * Emits an {Deposit} event with relevent deposit information. * * Requirements: * - token transfer to contract is successfull */ function deposit(address token, uint256 amount) public { updateDepositsBalance(msg.sender, token); SafeERC20.safeTransferFrom(IERC20(token), msg.sender, address(this), amount); // solhint-disable-next-line max-line-length balanceStates[msg.sender][token].pendingDeposits.amount = balanceStates[msg.sender][token].pendingDeposits.amount.add( amount ); balanceStates[msg.sender][token].pendingDeposits.batchId = getCurrentBatchId(); emit Deposit(msg.sender, token, amount, getCurrentBatchId()); } /** @dev Signals and initiates user's intent to withdraw. * @param token address of token to be withdrawn * @param amount number of token(s) to be withdrawn * * Emits an {WithdrawRequest} event with relevent request information. */ function requestWithdraw(address token, uint256 amount) public { requestFutureWithdraw(token, amount, getCurrentBatchId()); } /** @dev Signals and initiates user's intent to withdraw. * @param token address of token to be withdrawn * @param amount number of token(s) to be withdrawn * @param batchId state index at which request is to be made. * * Emits an {WithdrawRequest} event with relevent request information. */ function requestFutureWithdraw(address token, uint256 amount, uint32 batchId) public { // First process pendingWithdraw (if any), as otherwise balances might increase for currentBatchId - 1 if (hasValidWithdrawRequest(msg.sender, token)) { withdraw(msg.sender, token); } require(batchId >= getCurrentBatchId(), "Request cannot be made in the past"); balanceStates[msg.sender][token].pendingWithdraws = PendingFlux({amount: amount, batchId: batchId}); emit WithdrawRequest(msg.sender, token, amount, batchId); } /** @dev Claims pending withdraw - can be called on behalf of others * @param token address of token to be withdrawn * @param user address of user who withdraw is being claimed. * * Emits an {Withdraw} event stating that `user` withdrew `amount` of `token` * * Requirements: * - withdraw was requested in previous epoch * - token was received from exchange in current auction batch */ function withdraw(address user, address token) public { updateDepositsBalance(user, token); // withdrawn amount may have been deposited in previous epoch require( balanceStates[user][token].pendingWithdraws.batchId < getCurrentBatchId(), "withdraw was not registered previously" ); require( lastCreditBatchId[user][token] < getCurrentBatchId(), "Withdraw not possible for token that is traded in the current auction" ); uint256 amount = Math.min(balanceStates[user][token].balance, balanceStates[user][token].pendingWithdraws.amount); balanceStates[user][token].balance = balanceStates[user][token].balance.sub(amount); delete balanceStates[user][token].pendingWithdraws; SafeERC20.safeTransfer(IERC20(token), user, amount); emit Withdraw(user, token, amount); } /** * Public view functions */ /** @dev getter function used to display pending deposit * @param user address of user * @param token address of ERC20 token * return amount and batchId of deposit's transfer if any (else 0) */ function getPendingDeposit(address user, address token) public view returns (uint256, uint32) { PendingFlux memory pendingDeposit = balanceStates[user][token].pendingDeposits; return (pendingDeposit.amount, pendingDeposit.batchId); } /** @dev getter function used to display pending withdraw * @param user address of user * @param token address of ERC20 token * return amount and batchId when withdraw was requested if any (else 0) */ function getPendingWithdraw(address user, address token) public view returns (uint256, uint32) { PendingFlux memory pendingWithdraw = balanceStates[user][token].pendingWithdraws; return (pendingWithdraw.amount, pendingWithdraw.batchId); } /** @dev getter function to determine current auction id. * return current batchId */ function getCurrentBatchId() public view returns (uint32) { // solhint-disable-next-line not-rely-on-time return uint32(now / BATCH_TIME); } /** @dev used to determine how much time is left in a batch * return seconds remaining in current batch */ function getSecondsRemainingInBatch() public view returns (uint256) { // solhint-disable-next-line not-rely-on-time return BATCH_TIME - (now % BATCH_TIME); } /** @dev fetches and returns user's balance * @param user address of user * @param token address of ERC20 token * return Current `token` balance of `user`'s account */ function getBalance(address user, address token) public view returns (uint256) { uint256 balance = balanceStates[user][token].balance; if (balanceStates[user][token].pendingDeposits.batchId < getCurrentBatchId()) { balance = balance.add(balanceStates[user][token].pendingDeposits.amount); } if (balanceStates[user][token].pendingWithdraws.batchId < getCurrentBatchId()) { balance = balance.sub(Math.min(balanceStates[user][token].pendingWithdraws.amount, balance)); } return balance; } /** @dev Used to determine if user has a valid pending withdraw request of specific token * @param user address of user * @param token address of ERC20 token * return true if `user` has valid withdraw request for `token`, otherwise false */ function hasValidWithdrawRequest(address user, address token) public view returns (bool) { return balanceStates[user][token].pendingWithdraws.batchId < getCurrentBatchId() && balanceStates[user][token].pendingWithdraws.batchId > 0; } /** * internal functions */ /** * The following function should be used to update any balances within an epoch, which * will not be immediately final. E.g. the BatchExchange credits new balances to * the buyers in an auction, but as there are might be better solutions, the updates are * not final. In order to prevent withdraws from non-final updates, we disallow withdraws * by setting lastCreditBatchId to the current batchId and allow only withdraws in batches * with a higher batchId. */ function addBalanceAndBlockWithdrawForThisBatch(address user, address token, uint256 amount) internal { if (hasValidWithdrawRequest(user, token)) { lastCreditBatchId[user][token] = getCurrentBatchId(); } addBalance(user, token, amount); } function addBalance(address user, address token, uint256 amount) internal { updateDepositsBalance(user, token); balanceStates[user][token].balance = balanceStates[user][token].balance.add(amount); } /** * The following function should be used to subtract amounts from the current balances state. * For the substraction the current withdrawRequests are considered and they are effectively reducing * the available balance. */ function subtractBalance(address user, address token, uint256 amount) internal { require(amount <= getBalance(user, token), "Amount exceeds user's balance."); subtractBalanceUnchecked(user, token, amount); } /** * The following function should be used to substract amounts from the current balance * state, if the pending withdrawRequests are not considered and should not effectively reduce * the available balance. * For example, the reversion of trades from a previous batch-solution do not * need to consider withdrawRequests. This is the case as withdraws are blocked for one * batch for accounts having credited funds in a previous submission. * PendingWithdraws must also be ignored since otherwise for the reversion of trades, * a solution reversion could be blocked: A bigger withdrawRequest could set the return value of * getBalance(user, token) to zero, although the user was just credited tokens in * the last submission. In this situation, during the unwinding of the previous orders, * the check `amount <= getBalance(user, token)` would fail and the reversion would be blocked. */ function subtractBalanceUnchecked(address user, address token, uint256 amount) internal { updateDepositsBalance(user, token); balanceStates[user][token].balance = balanceStates[user][token].balance.sub(amount); } function updateDepositsBalance(address user, address token) private { uint256 batchId = balanceStates[user][token].pendingDeposits.batchId; if (batchId > 0 && batchId < getCurrentBatchId()) { // batchId > 0 is checked in order save an SSTORE in case there is no pending deposit balanceStates[user][token].balance = balanceStates[user][token].balance.add( balanceStates[user][token].pendingDeposits.amount ); delete balanceStates[user][token].pendingDeposits; } } } library IdToAddressBiMap { struct Data { mapping(uint16 => address) idToAddress; mapping(address => uint16) addressToId; } function hasId(Data storage self, uint16 id) public view returns (bool) { return self.idToAddress[id + 1] != address(0); } function hasAddress(Data storage self, address addr) public view returns (bool) { return self.addressToId[addr] != 0; } function getAddressAt(Data storage self, uint16 id) public view returns (address) { require(hasId(self, id), "Must have ID to get Address"); return self.idToAddress[id + 1]; } function getId(Data storage self, address addr) public view returns (uint16) { require(hasAddress(self, addr), "Must have Address to get ID"); return self.addressToId[addr] - 1; } function insert(Data storage self, uint16 id, address addr) public returns (bool) { require(addr != address(0), "Cannot insert zero address"); require(id != uint16(-1), "Cannot insert max uint16"); // Ensure bijectivity of the mappings if (self.addressToId[addr] != 0 || self.idToAddress[id + 1] != address(0)) { return false; } self.idToAddress[id + 1] = addr; self.addressToId[addr] = id + 1; return true; } } library IterableAppendOnlySet { struct Data { mapping(address => address) nextMap; address last; } function insert(Data storage self, address value) public returns (bool) { if (contains(self, value)) { return false; } self.nextMap[self.last] = value; self.last = value; return true; } function contains(Data storage self, address value) public view returns (bool) { require(value != address(0), "Inserting address(0) is not supported"); return self.nextMap[value] != address(0) || (self.last == value); } function first(Data storage self) public view returns (address) { require(self.last != address(0), "Trying to get first from empty set"); return self.nextMap[address(0)]; } function next(Data storage self, address value) public view returns (address) { require(contains(self, value), "Trying to get next of non-existent element"); require(value != self.last, "Trying to get next of last element"); return self.nextMap[value]; } function size(Data storage self) public view returns (uint256) { if (self.last == address(0)) { return 0; } uint256 count = 1; address current = first(self); while (current != self.last) { current = next(self, current); count++; } return count; } function atIndex(Data storage self, uint256 index) public view returns (address) { require(index < size(self), "requested index too large"); address res = first(self); for (uint256 i = 0; i < index; i++) { res = next(self, res); } return res; } } library GnosisMath { /* * Constants */ // This is equal to 1 in our calculations uint public constant ONE = 0x10000000000000000; uint public constant LN2 = 0xb17217f7d1cf79ac; uint public constant LOG2_E = 0x171547652b82fe177; /* * Public functions */ /// @dev Returns natural exponential function value of given x /// @param x x /// @return e**x function exp(int x) public pure returns (uint) { // revert if x is > MAX_POWER, where // MAX_POWER = int(mp.floor(mp.log(mpf(2**256 - 1) / ONE) * ONE)) require(x <= 2454971259878909886679); // return 0 if exp(x) is tiny, using // MIN_POWER = int(mp.floor(mp.log(mpf(1) / ONE) * ONE)) if (x < -818323753292969962227) return 0; // Transform so that e^x -> 2^x x = x * int(ONE) / int(LN2); // 2^x = 2^whole(x) * 2^frac(x) // ^^^^^^^^^^ is a bit shift // so Taylor expand on z = frac(x) int shift; uint z; if (x >= 0) { shift = x / int(ONE); z = uint(x % int(ONE)); } else { shift = x / int(ONE) - 1; z = ONE - uint(-x % int(ONE)); } // 2^x = 1 + (ln 2) x + (ln 2)^2/2! x^2 + ... // // Can generate the z coefficients using mpmath and the following lines // >>> from mpmath import mp // >>> mp.dps = 100 // >>> ONE = 0x10000000000000000 // >>> print('\n'.join(hex(int(mp.log(2)**i / mp.factorial(i) * ONE)) for i in range(1, 7))) // 0xb17217f7d1cf79ab // 0x3d7f7bff058b1d50 // 0xe35846b82505fc5 // 0x276556df749cee5 // 0x5761ff9e299cc4 // 0xa184897c363c3 uint zpow = z; uint result = ONE; result += 0xb17217f7d1cf79ab * zpow / ONE; zpow = zpow * z / ONE; result += 0x3d7f7bff058b1d50 * zpow / ONE; zpow = zpow * z / ONE; result += 0xe35846b82505fc5 * zpow / ONE; zpow = zpow * z / ONE; result += 0x276556df749cee5 * zpow / ONE; zpow = zpow * z / ONE; result += 0x5761ff9e299cc4 * zpow / ONE; zpow = zpow * z / ONE; result += 0xa184897c363c3 * zpow / ONE; zpow = zpow * z / ONE; result += 0xffe5fe2c4586 * zpow / ONE; zpow = zpow * z / ONE; result += 0x162c0223a5c8 * zpow / ONE; zpow = zpow * z / ONE; result += 0x1b5253d395e * zpow / ONE; zpow = zpow * z / ONE; result += 0x1e4cf5158b * zpow / ONE; zpow = zpow * z / ONE; result += 0x1e8cac735 * zpow / ONE; zpow = zpow * z / ONE; result += 0x1c3bd650 * zpow / ONE; zpow = zpow * z / ONE; result += 0x1816193 * zpow / ONE; zpow = zpow * z / ONE; result += 0x131496 * zpow / ONE; zpow = zpow * z / ONE; result += 0xe1b7 * zpow / ONE; zpow = zpow * z / ONE; result += 0x9c7 * zpow / ONE; if (shift >= 0) { if (result >> (256 - shift) > 0) return (2 ** 256 - 1); return result << shift; } else return result >> (-shift); } /// @dev Returns natural logarithm value of given x /// @param x x /// @return ln(x) function ln(uint x) public pure returns (int) { require(x > 0); // binary search for floor(log2(x)) int ilog2 = floorLog2(x); int z; if (ilog2 < 0) z = int(x << uint(-ilog2)); else z = int(x >> uint(ilog2)); // z = x * 2^-⌊log₂x⌋ // so 1 <= z < 2 // and ln z = ln x - ⌊log₂x⌋/log₂e // so just compute ln z using artanh series // and calculate ln x from that int term = (z - int(ONE)) * int(ONE) / (z + int(ONE)); int halflnz = term; int termpow = term * term / int(ONE) * term / int(ONE); halflnz += termpow / 3; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 5; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 7; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 9; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 11; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 13; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 15; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 17; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 19; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 21; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 23; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 25; return (ilog2 * int(ONE)) * int(ONE) / int(LOG2_E) + 2 * halflnz; } /// @dev Returns base 2 logarithm value of given x /// @param x x /// @return logarithmic value function floorLog2(uint x) public pure returns (int lo) { lo = -64; int hi = 193; // I use a shift here instead of / 2 because it floors instead of rounding towards 0 int mid = (hi + lo) >> 1; while ((lo + 1) < hi) { if (mid < 0 && x << uint(-mid) < ONE || mid >= 0 && x >> uint(mid) < ONE) hi = mid; else lo = mid; mid = (hi + lo) >> 1; } } /// @dev Returns maximum of an array /// @param nums Numbers to look through /// @return Maximum number function max(int[] memory nums) public pure returns (int maxNum) { require(nums.length > 0); maxNum = -2 ** 255; for (uint i = 0; i < nums.length; i++) if (nums[i] > maxNum) maxNum = nums[i]; } /// @dev Returns whether an add operation causes an overflow /// @param a First addend /// @param b Second addend /// @return Did no overflow occur? function safeToAdd(uint a, uint b) internal pure returns (bool) { return a + b >= a; } /// @dev Returns whether a subtraction operation causes an underflow /// @param a Minuend /// @param b Subtrahend /// @return Did no underflow occur? function safeToSub(uint a, uint b) internal pure returns (bool) { return a >= b; } /// @dev Returns whether a multiply operation causes an overflow /// @param a First factor /// @param b Second factor /// @return Did no overflow occur? function safeToMul(uint a, uint b) internal pure returns (bool) { return b == 0 || a * b / b == a; } /// @dev Returns sum if no overflow occurred /// @param a First addend /// @param b Second addend /// @return Sum function add(uint a, uint b) internal pure returns (uint) { require(safeToAdd(a, b)); return a + b; } /// @dev Returns difference if no overflow occurred /// @param a Minuend /// @param b Subtrahend /// @return Difference function sub(uint a, uint b) internal pure returns (uint) { require(safeToSub(a, b)); return a - b; } /// @dev Returns product if no overflow occurred /// @param a First factor /// @param b Second factor /// @return Product function mul(uint a, uint b) internal pure returns (uint) { require(safeToMul(a, b)); return a * b; } /// @dev Returns whether an add operation causes an overflow /// @param a First addend /// @param b Second addend /// @return Did no overflow occur? function safeToAdd(int a, int b) internal pure returns (bool) { return (b >= 0 && a + b >= a) || (b < 0 && a + b < a); } /// @dev Returns whether a subtraction operation causes an underflow /// @param a Minuend /// @param b Subtrahend /// @return Did no underflow occur? function safeToSub(int a, int b) internal pure returns (bool) { return (b >= 0 && a - b <= a) || (b < 0 && a - b > a); } /// @dev Returns whether a multiply operation causes an overflow /// @param a First factor /// @param b Second factor /// @return Did no overflow occur? function safeToMul(int a, int b) internal pure returns (bool) { return (b == 0) || (a * b / b == a); } /// @dev Returns sum if no overflow occurred /// @param a First addend /// @param b Second addend /// @return Sum function add(int a, int b) internal pure returns (int) { require(safeToAdd(a, b)); return a + b; } /// @dev Returns difference if no overflow occurred /// @param a Minuend /// @param b Subtrahend /// @return Difference function sub(int a, int b) internal pure returns (int) { require(safeToSub(a, b)); return a - b; } /// @dev Returns product if no overflow occurred /// @param a First factor /// @param b Second factor /// @return Product function mul(int a, int b) internal pure returns (int) { require(safeToMul(a, b)); return a * b; } } contract Token { /* * Events */ event Transfer(address indexed from, address indexed to, uint value); event Approval(address indexed owner, address indexed spender, uint value); /* * Public functions */ function transfer(address to, uint value) public returns (bool); function transferFrom(address from, address to, uint value) public returns (bool); function approve(address spender, uint value) public returns (bool); function balanceOf(address owner) public view returns (uint); function allowance(address owner, address spender) public view returns (uint); function totalSupply() public view returns (uint); } contract Proxied { address public masterCopy; } contract Proxy is Proxied { /// @dev Constructor function sets address of master copy contract. /// @param _masterCopy Master copy address. constructor(address _masterCopy) public { require(_masterCopy != address(0), "The master copy is required"); masterCopy = _masterCopy; } /// @dev Fallback function forwards all transactions and returns all received return data. function() external payable { address _masterCopy = masterCopy; assembly { calldatacopy(0, 0, calldatasize) let success := delegatecall(not(0), _masterCopy, 0, calldatasize, 0, 0) returndatacopy(0, 0, returndatasize) switch success case 0 { revert(0, returndatasize) } default { return(0, returndatasize) } } } } contract StandardTokenData { /* * Storage */ mapping(address => uint) balances; mapping(address => mapping(address => uint)) allowances; uint totalTokens; } contract GnosisStandardToken is Token, StandardTokenData { using GnosisMath for *; /* * Public functions */ /// @dev Transfers sender's tokens to a given address. Returns success /// @param to Address of token receiver /// @param value Number of tokens to transfer /// @return Was transfer successful? function transfer(address to, uint value) public returns (bool) { if (!balances[msg.sender].safeToSub(value) || !balances[to].safeToAdd(value)) { return false; } balances[msg.sender] -= value; balances[to] += value; emit Transfer(msg.sender, to, value); return true; } /// @dev Allows allowed third party to transfer tokens from one address to another. Returns success /// @param from Address from where tokens are withdrawn /// @param to Address to where tokens are sent /// @param value Number of tokens to transfer /// @return Was transfer successful? function transferFrom(address from, address to, uint value) public returns (bool) { if (!balances[from].safeToSub(value) || !allowances[from][msg.sender].safeToSub( value ) || !balances[to].safeToAdd(value)) { return false; } balances[from] -= value; allowances[from][msg.sender] -= value; balances[to] += value; emit Transfer(from, to, value); return true; } /// @dev Sets approved amount of tokens for spender. Returns success /// @param spender Address of allowed account /// @param value Number of approved tokens /// @return Was approval successful? function approve(address spender, uint value) public returns (bool) { allowances[msg.sender][spender] = value; emit Approval(msg.sender, spender, value); return true; } /// @dev Returns number of allowed tokens for given address /// @param owner Address of token owner /// @param spender Address of token spender /// @return Remaining allowance for spender function allowance(address owner, address spender) public view returns (uint) { return allowances[owner][spender]; } /// @dev Returns number of tokens owned by given address /// @param owner Address of token owner /// @return Balance of owner function balanceOf(address owner) public view returns (uint) { return balances[owner]; } /// @dev Returns total supply of tokens /// @return Total supply function totalSupply() public view returns (uint) { return totalTokens; } } contract TokenOWL is Proxied, GnosisStandardToken { using GnosisMath for *; string public constant name = "OWL Token"; string public constant symbol = "OWL"; uint8 public constant decimals = 18; struct masterCopyCountdownType { address masterCopy; uint timeWhenAvailable; } masterCopyCountdownType masterCopyCountdown; address public creator; address public minter; event Minted(address indexed to, uint256 amount); event Burnt(address indexed from, address indexed user, uint256 amount); modifier onlyCreator() { // R1 require(msg.sender == creator, "Only the creator can perform the transaction"); _; } /// @dev trickers the update process via the proxyMaster for a new address _masterCopy /// updating is only possible after 30 days function startMasterCopyCountdown(address _masterCopy) public onlyCreator { require(address(_masterCopy) != address(0), "The master copy must be a valid address"); // Update masterCopyCountdown masterCopyCountdown.masterCopy = _masterCopy; masterCopyCountdown.timeWhenAvailable = now + 30 days; } /// @dev executes the update process via the proxyMaster for a new address _masterCopy function updateMasterCopy() public onlyCreator { require(address(masterCopyCountdown.masterCopy) != address(0), "The master copy must be a valid address"); require( block.timestamp >= masterCopyCountdown.timeWhenAvailable, "It's not possible to update the master copy during the waiting period" ); // Update masterCopy masterCopy = masterCopyCountdown.masterCopy; } function getMasterCopy() public view returns (address) { return masterCopy; } /// @dev Set minter. Only the creator of this contract can call this. /// @param newMinter The new address authorized to mint this token function setMinter(address newMinter) public onlyCreator { minter = newMinter; } /// @dev change owner/creator of the contract. Only the creator/owner of this contract can call this. /// @param newOwner The new address, which should become the owner function setNewOwner(address newOwner) public onlyCreator { creator = newOwner; } /// @dev Mints OWL. /// @param to Address to which the minted token will be given /// @param amount Amount of OWL to be minted function mintOWL(address to, uint amount) public { require(minter != address(0), "The minter must be initialized"); require(msg.sender == minter, "Only the minter can mint OWL"); balances[to] = balances[to].add(amount); totalTokens = totalTokens.add(amount); emit Minted(to, amount); emit Transfer(address(0), to, amount); } /// @dev Burns OWL. /// @param user Address of OWL owner /// @param amount Amount of OWL to be burnt function burnOWL(address user, uint amount) public { allowances[user][msg.sender] = allowances[user][msg.sender].sub(amount); balances[user] = balances[user].sub(amount); totalTokens = totalTokens.sub(amount); emit Burnt(msg.sender, user, amount); emit Transfer(user, address(0), amount); } function getMasterCopyCountdown() public view returns (address, uint) { return (masterCopyCountdown.masterCopy, masterCopyCountdown.timeWhenAvailable); } } library SafeCast { /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits */ function toUint128(uint256 value) internal pure returns (uint128) { require(value < 2**128, "SafeCast: value doesn\'t fit in 128 bits"); return uint128(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits */ function toUint64(uint256 value) internal pure returns (uint64) { require(value < 2**64, "SafeCast: value doesn\'t fit in 64 bits"); return uint64(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits */ function toUint32(uint256 value) internal pure returns (uint32) { require(value < 2**32, "SafeCast: value doesn\'t fit in 32 bits"); return uint32(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits */ function toUint16(uint256 value) internal pure returns (uint16) { require(value < 2**16, "SafeCast: value doesn\'t fit in 16 bits"); return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits. */ function toUint8(uint256 value) internal pure returns (uint8) { require(value < 2**8, "SafeCast: value doesn\'t fit in 8 bits"); return uint8(value); } } library BytesLib { function concat( bytes memory _preBytes, bytes memory _postBytes ) internal pure returns (bytes memory) { bytes memory tempBytes; assembly { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // Store the length of the first bytes array at the beginning of // the memory for tempBytes. let length := mload(_preBytes) mstore(tempBytes, length) // Maintain a memory counter for the current write location in the // temp bytes array by adding the 32 bytes for the array length to // the starting location. let mc := add(tempBytes, 0x20) // Stop copying when the memory counter reaches the length of the // first bytes array. let end := add(mc, length) for { // Initialize a copy counter to the start of the _preBytes data, // 32 bytes into its memory. let cc := add(_preBytes, 0x20) } lt(mc, end) { // Increase both counters by 32 bytes each iteration. mc := add(mc, 0x20) cc := add(cc, 0x20) } { // Write the _preBytes data into the tempBytes memory 32 bytes // at a time. mstore(mc, mload(cc)) } // Add the length of _postBytes to the current length of tempBytes // and store it as the new length in the first 32 bytes of the // tempBytes memory. length := mload(_postBytes) mstore(tempBytes, add(length, mload(tempBytes))) // Move the memory counter back from a multiple of 0x20 to the // actual end of the _preBytes data. mc := end // Stop copying when the memory counter reaches the new combined // length of the arrays. end := add(mc, length) for { let cc := add(_postBytes, 0x20) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } // Update the free-memory pointer by padding our last write location // to 32 bytes: add 31 bytes to the end of tempBytes to move to the // next 32 byte block, then round down to the nearest multiple of // 32. If the sum of the length of the two arrays is zero then add // one before rounding down to leave a blank 32 bytes (the length block with 0). mstore(0x40, and( add(add(end, iszero(add(length, mload(_preBytes)))), 31), not(31) // Round down to the nearest 32 bytes. )) } return tempBytes; } function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal { assembly { // Read the first 32 bytes of _preBytes storage, which is the length // of the array. (We don't need to use the offset into the slot // because arrays use the entire slot.) let fslot := sload(_preBytes_slot) // Arrays of 31 bytes or less have an even value in their slot, // while longer arrays have an odd value. The actual length is // the slot divided by two for odd values, and the lowest order // byte divided by two for even values. // If the slot is even, bitwise and the slot with 255 and divide by // two to get the length. If the slot is odd, bitwise and the slot // with -1 and divide by two. let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) let mlength := mload(_postBytes) let newlength := add(slength, mlength) // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage switch add(lt(slength, 32), lt(newlength, 32)) case 2 { // Since the new array still fits in the slot, we just need to // update the contents of the slot. // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length sstore( _preBytes_slot, // all the modifications to the slot are inside this // next block add( // we can just add to the slot contents because the // bytes we want to change are the LSBs fslot, add( mul( div( // load the bytes from memory mload(add(_postBytes, 0x20)), // zero all bytes to the right exp(0x100, sub(32, mlength)) ), // and now shift left the number of bytes to // leave space for the length in the slot exp(0x100, sub(32, newlength)) ), // increase length by the double of the memory // bytes length mul(mlength, 2) ) ) ) } case 1 { // The stored value fits in the slot, but the combined value // will exceed it. // get the keccak hash to get the contents of the array mstore(0x0, _preBytes_slot) let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes_slot, add(mul(newlength, 2), 1)) // The contents of the _postBytes array start 32 bytes into // the structure. Our first read should obtain the `submod` // bytes that can fit into the unused space in the last word // of the stored array. To get this, we read 32 bytes starting // from `submod`, so the data we read overlaps with the array // contents by `submod` bytes. Masking the lowest-order // `submod` bytes allows us to add that value directly to the // stored value. let submod := sub(32, slength) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore( sc, add( and( fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 ), and(mload(mc), mask) ) ) for { mc := add(mc, 0x20) sc := add(sc, 1) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } default { // get the keccak hash to get the contents of the array mstore(0x0, _preBytes_slot) // Start copying to the last used word of the stored array. let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes_slot, add(mul(newlength, 2), 1)) // Copy over the first `submod` bytes of the new data as in // case 1 above. let slengthmod := mod(slength, 32) let mlengthmod := mod(mlength, 32) let submod := sub(32, slengthmod) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore(sc, add(sload(sc), and(mload(mc), mask))) for { sc := add(sc, 1) mc := add(mc, 0x20) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } } } function slice( bytes memory _bytes, uint _start, uint _length ) internal pure returns (bytes memory) { require(_bytes.length >= (_start + _length)); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } //if we want a zero-length slice let's just return a zero-length array default { tempBytes := mload(0x40) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } function toAddress(bytes memory _bytes, uint _start) internal pure returns (address) { require(_bytes.length >= (_start + 20)); address tempAddress; assembly { tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) } return tempAddress; } function toUint8(bytes memory _bytes, uint _start) internal pure returns (uint8) { require(_bytes.length >= (_start + 1)); uint8 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x1), _start)) } return tempUint; } function toUint16(bytes memory _bytes, uint _start) internal pure returns (uint16) { require(_bytes.length >= (_start + 2)); uint16 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x2), _start)) } return tempUint; } function toUint32(bytes memory _bytes, uint _start) internal pure returns (uint32) { require(_bytes.length >= (_start + 4)); uint32 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x4), _start)) } return tempUint; } function toUint64(bytes memory _bytes, uint _start) internal pure returns (uint64) { require(_bytes.length >= (_start + 8)); uint64 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x8), _start)) } return tempUint; } function toUint96(bytes memory _bytes, uint _start) internal pure returns (uint96) { require(_bytes.length >= (_start + 12)); uint96 tempUint; assembly { tempUint := mload(add(add(_bytes, 0xc), _start)) } return tempUint; } function toUint128(bytes memory _bytes, uint _start) internal pure returns (uint128) { require(_bytes.length >= (_start + 16)); uint128 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x10), _start)) } return tempUint; } function toUint(bytes memory _bytes, uint _start) internal pure returns (uint256) { require(_bytes.length >= (_start + 32)); uint256 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x20), _start)) } return tempUint; } function toBytes32(bytes memory _bytes, uint _start) internal pure returns (bytes32) { require(_bytes.length >= (_start + 32)); bytes32 tempBytes32; assembly { tempBytes32 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes32; } function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { bool success = true; assembly { let length := mload(_preBytes) // if lengths don't match the arrays are not equal switch eq(length, mload(_postBytes)) case 1 { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 let mc := add(_preBytes, 0x20) let end := add(mc, length) for { let cc := add(_postBytes, 0x20) // the next line is the loop condition: // while(uint(mc < end) + cb == 2) } eq(add(lt(mc, end), cb), 2) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { // if any of these checks fails then arrays are not equal if iszero(eq(mload(mc), mload(cc))) { // unsuccess: success := 0 cb := 0 } } } default { // unsuccess: success := 0 } } return success; } function equalStorage( bytes storage _preBytes, bytes memory _postBytes ) internal view returns (bool) { bool success = true; assembly { // we know _preBytes_offset is 0 let fslot := sload(_preBytes_slot) // Decode the length of the stored array like in concatStorage(). let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) let mlength := mload(_postBytes) // if lengths don't match the arrays are not equal switch eq(slength, mlength) case 1 { // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage if iszero(iszero(slength)) { switch lt(slength, 32) case 1 { // blank the last byte which is the length fslot := mul(div(fslot, 0x100), 0x100) if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) { // unsuccess: success := 0 } } default { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 // get the keccak hash to get the contents of the array mstore(0x0, _preBytes_slot) let sc := keccak256(0x0, 0x20) let mc := add(_postBytes, 0x20) let end := add(mc, mlength) // the next line is the loop condition: // while(uint(mc < end) + cb == 2) for {} eq(add(lt(mc, end), cb), 2) { sc := add(sc, 1) mc := add(mc, 0x20) } { if iszero(eq(sload(sc), mload(mc))) { // unsuccess: success := 0 cb := 0 } } } } } default { // unsuccess: success := 0 } } return success; } } library SignedSafeMath { int256 constant private INT256_MIN = -2**255; /** * @dev Multiplies two signed integers, reverts on overflow. */ function mul(int256 a, int256 b) internal pure returns (int256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } require(!(a == -1 && b == INT256_MIN), "SignedSafeMath: multiplication overflow"); int256 c = a * b; require(c / a == b, "SignedSafeMath: multiplication overflow"); return c; } /** * @dev Integer division of two signed integers truncating the quotient, reverts on division by zero. */ function div(int256 a, int256 b) internal pure returns (int256) { require(b != 0, "SignedSafeMath: division by zero"); require(!(b == -1 && a == INT256_MIN), "SignedSafeMath: division overflow"); int256 c = a / b; return c; } /** * @dev Subtracts two signed integers, reverts on overflow. */ function sub(int256 a, int256 b) internal pure returns (int256) { int256 c = a - b; require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow"); return c; } /** * @dev Adds two signed integers, reverts on overflow. */ function add(int256 a, int256 b) internal pure returns (int256) { int256 c = a + b; require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow"); return c; } } library TokenConservation { using SignedSafeMath for int256; /** @dev initialize the token conservation data structure * @param tokenIdsForPrice sorted list of tokenIds for which token conservation should be checked */ function init(uint16[] memory tokenIdsForPrice) internal pure returns (int256[] memory) { return new int256[](tokenIdsForPrice.length + 1); } /** @dev returns the token imbalance of the fee token * @param self internal datastructure created by TokenConservation.init() */ function feeTokenImbalance(int256[] memory self) internal pure returns (int256) { return self[0]; } /** @dev updated token conservation array. * @param self internal datastructure created by TokenConservation.init() * @param buyToken id of token whose imbalance should be subtracted from * @param sellToken id of token whose imbalance should be added to * @param tokenIdsForPrice sorted list of tokenIds * @param buyAmount amount to be subtracted at `self[buyTokenIndex]` * @param sellAmount amount to be added at `self[sellTokenIndex]` */ function updateTokenConservation( int256[] memory self, uint16 buyToken, uint16 sellToken, uint16[] memory tokenIdsForPrice, uint128 buyAmount, uint128 sellAmount ) internal pure { uint256 buyTokenIndex = findPriceIndex(buyToken, tokenIdsForPrice); uint256 sellTokenIndex = findPriceIndex(sellToken, tokenIdsForPrice); self[buyTokenIndex] = self[buyTokenIndex].sub(int256(buyAmount)); self[sellTokenIndex] = self[sellTokenIndex].add(int256(sellAmount)); } /** @dev Ensures all array's elements are zero except the first. * @param self internal datastructure created by TokenConservation.init() * @return true if all, but first element of self are zero else false */ function checkTokenConservation(int256[] memory self) internal pure { require(self[0] > 0, "Token conservation at 0 must be positive."); for (uint256 i = 1; i < self.length; i++) { require(self[i] == 0, "Token conservation does not hold"); } } /** @dev Token ordering is verified by submitSolution. Required because binary search is used to fetch token info. * @param tokenIdsForPrice list of tokenIds * @return true if tokenIdsForPrice is sorted else false */ function checkPriceOrdering(uint16[] memory tokenIdsForPrice) internal pure returns (bool) { for (uint256 i = 1; i < tokenIdsForPrice.length; i++) { if (tokenIdsForPrice[i] <= tokenIdsForPrice[i - 1]) { return false; } } return true; } /** @dev implementation of binary search on sorted list returns token id * @param tokenId element whose index is to be found * @param tokenIdsForPrice list of (sorted) tokenIds for which binary search is applied. * @return `index` in `tokenIdsForPrice` where `tokenId` appears (reverts if not found). */ function findPriceIndex(uint16 tokenId, uint16[] memory tokenIdsForPrice) private pure returns (uint256) { // Fee token is not included in tokenIdsForPrice if (tokenId == 0) { return 0; } // binary search for the other tokens uint256 leftValue = 0; uint256 rightValue = tokenIdsForPrice.length - 1; while (rightValue >= leftValue) { uint256 middleValue = (leftValue + rightValue) / 2; if (tokenIdsForPrice[middleValue] == tokenId) { // shifted one to the right to account for fee token at index 0 return middleValue + 1; } else if (tokenIdsForPrice[middleValue] < tokenId) { leftValue = middleValue + 1; } else { rightValue = middleValue - 1; } } revert("Price not provided for token"); } } contract BatchExchange is EpochTokenLocker { using SafeCast for uint256; using SafeMath for uint128; using BytesLib for bytes32; using BytesLib for bytes; using TokenConservation for int256[]; using TokenConservation for uint16[]; using IterableAppendOnlySet for IterableAppendOnlySet.Data; /** @dev Maximum number of touched orders in auction (used in submitSolution) */ uint256 public constant MAX_TOUCHED_ORDERS = 30; /** @dev Fee charged for adding a token */ uint256 public constant FEE_FOR_LISTING_TOKEN_IN_OWL = 10 ether; /** @dev minimum allowed value (in WEI) of any prices or executed trade amounts */ uint128 public constant AMOUNT_MINIMUM = 10**4; /** @dev Numerator or denominator used in orders, which do not track its usedAmount*/ uint128 public constant UNLIMITED_ORDER_AMOUNT = uint128(-1); /** Corresponds to percentage that competing solution must improve on current * (p = IMPROVEMENT_DENOMINATOR + 1 / IMPROVEMENT_DENOMINATOR) */ uint256 public constant IMPROVEMENT_DENOMINATOR = 100; // 1% /** @dev A fixed integer used to evaluate fees as a fraction of trade execution 1/FEE_DENOMINATOR */ uint128 public constant FEE_DENOMINATOR = 1000; /** @dev The number of bytes a single auction element is serialized into */ uint128 public constant ENCODED_AUCTION_ELEMENT_WIDTH = 112; /** @dev maximum number of tokens that can be listed for exchange */ // solhint-disable-next-line var-name-mixedcase uint256 public MAX_TOKENS; /** @dev Current number of tokens listed/available for exchange */ uint16 public numTokens; /** @dev The feeToken of the exchange will be the OWL Token */ TokenOWL public feeToken; /** @dev mapping of type userAddress -> List[Order] where all the user's orders are stored */ mapping(address => Order[]) public orders; /** @dev mapping of type tokenId -> curentPrice of tokenId */ mapping(uint16 => uint128) public currentPrices; /** @dev Sufficient information for current winning auction solution */ SolutionData public latestSolution; // Iterable set of all users, required to collect auction information IterableAppendOnlySet.Data private allUsers; IdToAddressBiMap.Data private registeredTokens; struct Order { uint16 buyToken; uint16 sellToken; uint32 validFrom; // order is valid from auction collection period: validFrom inclusive uint32 validUntil; // order is valid till auction collection period: validUntil inclusive uint128 priceNumerator; uint128 priceDenominator; uint128 usedAmount; // remainingAmount = priceDenominator - usedAmount } struct TradeData { address owner; uint128 volume; uint16 orderId; } struct SolutionData { uint32 batchId; TradeData[] trades; uint16[] tokenIdsForPrice; address solutionSubmitter; uint256 feeReward; uint256 objectiveValue; } event OrderPlacement( address indexed owner, uint16 index, uint16 indexed buyToken, uint16 indexed sellToken, uint32 validFrom, uint32 validUntil, uint128 priceNumerator, uint128 priceDenominator ); event TokenListing(address token, uint16 id); /** @dev Event emitted when an order is cancelled but still valid in the batch that is * currently being solved. It remains in storage but will not be tradable in any future * batch to be solved. */ event OrderCancellation(address indexed owner, uint16 id); /** @dev Event emitted when an order is removed from storage. */ event OrderDeletion(address indexed owner, uint16 id); /** @dev Event emitted when a new trade is settled */ event Trade( address indexed owner, uint16 indexed orderId, uint16 indexed sellToken, // Solidity only supports three indexed arguments uint16 buyToken, uint128 executedSellAmount, uint128 executedBuyAmount ); /** @dev Event emitted when an already exectued trade gets reverted */ event TradeReversion( address indexed owner, uint16 indexed orderId, uint16 indexed sellToken, // Solidity only supports three indexed arguments uint16 buyToken, uint128 executedSellAmount, uint128 executedBuyAmount ); /** @dev Event emitted for each solution that is submitted */ event SolutionSubmission( address indexed submitter, uint256 utility, uint256 disregardedUtility, uint256 burntFees, uint256 lastAuctionBurntFees, uint128[] prices, uint16[] tokenIdsForPrice ); /** @dev Constructor determines exchange parameters * @param maxTokens The maximum number of tokens that can be listed. * @param _feeToken Address of ERC20 fee token. */ constructor(uint256 maxTokens, address _feeToken) public { // All solutions for the batches must have normalized prices. The following line sets the // price of OWL to 10**18 for all solutions and hence enforces a normalization. currentPrices[0] = 1 ether; MAX_TOKENS = maxTokens; feeToken = TokenOWL(_feeToken); // The burn functionallity of OWL requires an approval. // In the following line the approval is set for all future burn calls. feeToken.approve(address(this), uint256(-1)); addToken(_feeToken); // feeToken will always have the token index 0 } /** @dev Used to list a new token on the contract: Hence, making it available for exchange in an auction. * @param token ERC20 token to be listed. * * Requirements: * - `maxTokens` has not already been reached * - `token` has not already been added */ function addToken(address token) public { require(numTokens < MAX_TOKENS, "Max tokens reached"); if (numTokens > 0) { // Only charge fees for tokens other than the fee token itself feeToken.burnOWL(msg.sender, FEE_FOR_LISTING_TOKEN_IN_OWL); } require(IdToAddressBiMap.insert(registeredTokens, numTokens, token), "Token already registered"); emit TokenListing(token, numTokens); numTokens++; } /** @dev A user facing function used to place limit sell orders in auction with expiry defined by batchId * @param buyToken id of token to be bought * @param sellToken id of token to be sold * @param validUntil batchId representing order's expiry * @param buyAmount relative minimum amount of requested buy amount * @param sellAmount maximum amount of sell token to be exchanged * @return orderId defined as the index in user's order array * * Emits an {OrderPlacement} event with all relevant order details. */ function placeOrder(uint16 buyToken, uint16 sellToken, uint32 validUntil, uint128 buyAmount, uint128 sellAmount) public returns (uint256) { return placeOrderInternal(buyToken, sellToken, getCurrentBatchId(), validUntil, buyAmount, sellAmount); } /** @dev A user facing function used to place limit sell orders in auction with expiry defined by batchId * Note that parameters are passed as arrays and the indices correspond to each order. * @param buyTokens ids of tokens to be bought * @param sellTokens ids of tokens to be sold * @param validFroms batchIds representing order's validity start time * @param validUntils batchIds represnnting order's expiry * @param buyAmounts relative minimum amount of requested buy amounts * @param sellAmounts maximum amounts of sell token to be exchanged * @return `orderIds` an array of indices in which `msg.sender`'s orders are included * * Emits an {OrderPlacement} event with all relevant order details. */ function placeValidFromOrders( uint16[] memory buyTokens, uint16[] memory sellTokens, uint32[] memory validFroms, uint32[] memory validUntils, uint128[] memory buyAmounts, uint128[] memory sellAmounts ) public returns (uint16[] memory orderIds) { orderIds = new uint16[](buyTokens.length); for (uint256 i = 0; i < buyTokens.length; i++) { orderIds[i] = placeOrderInternal( buyTokens[i], sellTokens[i], validFroms[i], validUntils[i], buyAmounts[i], sellAmounts[i] ); } } /** @dev a user facing function used to cancel orders. If the order is valid for the batch that is currently * being solved, it sets order expiry to that batchId. Otherwise it removes it from storage. Can be called * multiple times (e.g. to eventually free storage once order is expired). * * @param orderIds referencing the indices of user's orders to be cancelled * * Emits an {OrderCancellation} or {OrderDeletion} with sender's address and orderId */ function cancelOrders(uint16[] memory orderIds) public { uint32 batchIdBeingSolved = getCurrentBatchId() - 1; for (uint16 i = 0; i < orderIds.length; i++) { if (!checkOrderValidity(orders[msg.sender][orderIds[i]], batchIdBeingSolved)) { delete orders[msg.sender][orderIds[i]]; emit OrderDeletion(msg.sender, orderIds[i]); } else { orders[msg.sender][orderIds[i]].validUntil = batchIdBeingSolved; emit OrderCancellation(msg.sender, orderIds[i]); } } } /** @dev A user facing wrapper to cancel and place new orders in the same transaction. * @param cancellations indices of orders to be cancelled * @param buyTokens ids of tokens to be bought in new orders * @param sellTokens ids of tokens to be sold in new orders * @param validFroms batchIds representing order's validity start time in new orders * @param validUntils batchIds represnnting order's expiry in new orders * @param buyAmounts relative minimum amount of requested buy amounts in new orders * @param sellAmounts maximum amounts of sell token to be exchanged in new orders * @return an array of indices in which `msg.sender`'s new orders are included * * Emits {OrderCancellation} events for all cancelled orders and {OrderPlacement} events with relevant new order details. */ function replaceOrders( uint16[] memory cancellations, uint16[] memory buyTokens, uint16[] memory sellTokens, uint32[] memory validFroms, uint32[] memory validUntils, uint128[] memory buyAmounts, uint128[] memory sellAmounts ) public returns (uint16[] memory) { cancelOrders(cancellations); return placeValidFromOrders(buyTokens, sellTokens, validFroms, validUntils, buyAmounts, sellAmounts); } /** @dev a solver facing function called for auction settlement * @param batchId index of auction solution is referring to * @param owners array of addresses corresponding to touched orders * @param orderIds array of order indices used in parallel with owners to identify touched order * @param buyVolumes executed buy amounts for each order identified by index of owner-orderId arrays * @param prices list of prices for touched tokens indexed by next parameter * @param tokenIdsForPrice price[i] is the price for the token with tokenID tokenIdsForPrice[i] * @return the computed objective value of the solution * * Requirements: * - Solutions for this `batchId` are currently being accepted. * - Claimed objetive value is a great enough improvement on the current winning solution * - Fee Token price is non-zero * - `tokenIdsForPrice` is sorted. * - Number of touched orders does not exceed `MAX_TOUCHED_ORDERS`. * - Each touched order is valid at current `batchId`. * - Each touched order's `executedSellAmount` does not exceed its remaining amount. * - Limit Price of each touched order is respected. * - Solution's objective evaluation must be positive. * * Sub Requirements: Those nested within other functions * - checkAndOverrideObjectiveValue; Objetive value is a great enough improvement on the current winning solution * - checkTokenConservation; for all, non-fee, tokens total amount sold == total amount bought */ function submitSolution( uint32 batchId, uint256 claimedObjectiveValue, address[] memory owners, uint16[] memory orderIds, uint128[] memory buyVolumes, uint128[] memory prices, uint16[] memory tokenIdsForPrice ) public returns (uint256) { require(acceptingSolutions(batchId), "Solutions are no longer accepted for this batch"); require( isObjectiveValueSufficientlyImproved(claimedObjectiveValue), "Claimed objective doesn't sufficiently improve current solution" ); require(verifyAmountThreshold(prices), "At least one price lower than AMOUNT_MINIMUM"); require(tokenIdsForPrice[0] != 0, "Fee token has fixed price!"); require(tokenIdsForPrice.checkPriceOrdering(), "prices are not ordered by tokenId"); require(owners.length <= MAX_TOUCHED_ORDERS, "Solution exceeds MAX_TOUCHED_ORDERS"); // Further assumptions are: owners.length == orderIds.length && owners.length == buyVolumes.length // && prices.length == tokenIdsForPrice.length // These assumptions are not checked explicitly, as violations of these constraints can not be used // to create a beneficial situation uint256 lastAuctionBurntFees = burnPreviousAuctionFees(); undoCurrentSolution(); updateCurrentPrices(prices, tokenIdsForPrice); delete latestSolution.trades; int256[] memory tokenConservation = TokenConservation.init(tokenIdsForPrice); uint256 utility = 0; for (uint256 i = 0; i < owners.length; i++) { Order memory order = orders[owners[i]][orderIds[i]]; require(checkOrderValidity(order, batchId), "Order is invalid"); (uint128 executedBuyAmount, uint128 executedSellAmount) = getTradedAmounts(buyVolumes[i], order); require(executedBuyAmount >= AMOUNT_MINIMUM, "buy amount less than AMOUNT_MINIMUM"); require(executedSellAmount >= AMOUNT_MINIMUM, "sell amount less than AMOUNT_MINIMUM"); tokenConservation.updateTokenConservation( order.buyToken, order.sellToken, tokenIdsForPrice, executedBuyAmount, executedSellAmount ); require(getRemainingAmount(order) >= executedSellAmount, "executedSellAmount bigger than specified in order"); // Ensure executed price is not lower than the order price: // executedSellAmount / executedBuyAmount <= order.priceDenominator / order.priceNumerator require( executedSellAmount.mul(order.priceNumerator) <= executedBuyAmount.mul(order.priceDenominator), "limit price not satisfied" ); // accumulate utility before updateRemainingOrder, but after limitPrice verified! utility = utility.add(evaluateUtility(executedBuyAmount, order)); updateRemainingOrder(owners[i], orderIds[i], executedSellAmount); addBalanceAndBlockWithdrawForThisBatch(owners[i], tokenIdToAddressMap(order.buyToken), executedBuyAmount); emit Trade(owners[i], orderIds[i], order.sellToken, order.buyToken, executedSellAmount, executedBuyAmount); } // Perform all subtractions after additions to avoid negative values for (uint256 i = 0; i < owners.length; i++) { Order memory order = orders[owners[i]][orderIds[i]]; (, uint128 executedSellAmount) = getTradedAmounts(buyVolumes[i], order); subtractBalance(owners[i], tokenIdToAddressMap(order.sellToken), executedSellAmount); } uint256 disregardedUtility = 0; for (uint256 i = 0; i < owners.length; i++) { disregardedUtility = disregardedUtility.add(evaluateDisregardedUtility(orders[owners[i]][orderIds[i]], owners[i])); } uint256 burntFees = uint256(tokenConservation.feeTokenImbalance()) / 2; // burntFees ensures direct trades (when available) yield better solutions than longer rings uint256 objectiveValue = utility.add(burntFees).sub(disregardedUtility); checkAndOverrideObjectiveValue(objectiveValue); grantRewardToSolutionSubmitter(burntFees); tokenConservation.checkTokenConservation(); documentTrades(batchId, owners, orderIds, buyVolumes, tokenIdsForPrice); emit SolutionSubmission( msg.sender, utility, disregardedUtility, burntFees, lastAuctionBurntFees, prices, tokenIdsForPrice ); return (objectiveValue); } /** * Public View Methods */ /** @dev View returning ID of listed tokens * @param addr address of listed token. * @return tokenId as stored within the contract. */ function tokenAddressToIdMap(address addr) public view returns (uint16) { return IdToAddressBiMap.getId(registeredTokens, addr); } /** @dev View returning address of listed token by ID * @param id tokenId as stored, via BiMap, within the contract. * @return address of (listed) token */ function tokenIdToAddressMap(uint16 id) public view returns (address) { return IdToAddressBiMap.getAddressAt(registeredTokens, id); } /** @dev View returning a bool attesting whether token was already added * @param addr address of the token to be checked * @return bool attesting whether token was already added */ function hasToken(address addr) public view returns (bool) { return IdToAddressBiMap.hasAddress(registeredTokens, addr); } /** @dev View returning all byte-encoded sell orders for specified user * @param user address of user whose orders are being queried * @param offset uint determining the starting orderIndex * @param pageSize uint determining the count of elements to be viewed * @return encoded bytes representing all orders */ function getEncodedUserOrdersPaginated(address user, uint16 offset, uint16 pageSize) public view returns (bytes memory elements) { for (uint16 i = offset; i < Math.min(orders[user].length, offset + pageSize); i++) { elements = elements.concat( encodeAuctionElement(user, getBalance(user, tokenIdToAddressMap(orders[user][i].sellToken)), orders[user][i]) ); } return elements; } /** @dev View returning all byte-encoded users in paginated form * @param previousPageUser address of last user received in last pages (address(0) for first page) * @param pageSize uint determining the count of users to be returned per page * @return encoded packed bytes of user addresses */ function getUsersPaginated(address previousPageUser, uint16 pageSize) public view returns (bytes memory users) { if (allUsers.size() == 0) { return users; } uint16 count = 0; address current = previousPageUser; if (current == address(0)) { current = allUsers.first(); users = users.concat(abi.encodePacked(current)); count++; } while (count < pageSize && current != allUsers.last) { current = allUsers.next(current); users = users.concat(abi.encodePacked(current)); count++; } return users; } /** @dev View returning all byte-encoded sell orders for specified user * @param user address of user whose orders are being queried * @return encoded bytes representing all orders */ function getEncodedUserOrders(address user) public view returns (bytes memory elements) { return getEncodedUserOrdersPaginated(user, 0, uint16(-1)); } /** @dev View returning byte-encoded sell orders in paginated form * @param previousPageUser address of last user received in the previous page (address(0) for first page) * @param previousPageUserOffset the number of orders received for the last user on the previous page (0 for first page). * @param pageSize uint determining the count of orders to be returned per page * @return encoded bytes representing a page of orders ordered by (user, index) */ function getEncodedUsersPaginated(address previousPageUser, uint16 previousPageUserOffset, uint16 pageSize) public view returns (bytes memory elements) { if (allUsers.size() == 0) { return elements; } uint16 currentOffset = previousPageUserOffset; address currentUser = previousPageUser; if (currentUser == address(0x0)) { currentUser = allUsers.first(); } while (elements.length / ENCODED_AUCTION_ELEMENT_WIDTH < pageSize) { elements = elements.concat( getEncodedUserOrdersPaginated( currentUser, currentOffset, pageSize - uint16(elements.length / ENCODED_AUCTION_ELEMENT_WIDTH) ) ); if (currentUser == allUsers.last) { return elements; } currentOffset = 0; currentUser = allUsers.next(currentUser); } } /** @dev View returning all byte-encoded sell orders * @return encoded bytes representing all orders ordered by (user, index) */ function getEncodedOrders() public view returns (bytes memory elements) { if (allUsers.size() > 0) { address user = allUsers.first(); bool stop = false; while (!stop) { elements = elements.concat(getEncodedUserOrders(user)); if (user == allUsers.last) { stop = true; } else { user = allUsers.next(user); } } } return elements; } function acceptingSolutions(uint32 batchId) public view returns (bool) { return batchId == getCurrentBatchId() - 1 && getSecondsRemainingInBatch() >= 1 minutes; } /** @dev gets the objective value of currently winning solution. * @return objective function evaluation of the currently winning solution, or zero if no solution proposed. */ function getCurrentObjectiveValue() public view returns (uint256) { if (latestSolution.batchId == getCurrentBatchId() - 1) { return latestSolution.objectiveValue; } else { return 0; } } /** * Private Functions */ function placeOrderInternal( uint16 buyToken, uint16 sellToken, uint32 validFrom, uint32 validUntil, uint128 buyAmount, uint128 sellAmount ) private returns (uint16) { require(IdToAddressBiMap.hasId(registeredTokens, buyToken), "Buy token must be listed"); require(IdToAddressBiMap.hasId(registeredTokens, sellToken), "Sell token must be listed"); require(buyToken != sellToken, "Exchange tokens not distinct"); require(validFrom >= getCurrentBatchId(), "Orders can't be placed in the past"); orders[msg.sender].push( Order({ buyToken: buyToken, sellToken: sellToken, validFrom: validFrom, validUntil: validUntil, priceNumerator: buyAmount, priceDenominator: sellAmount, usedAmount: 0 }) ); uint16 orderId = (orders[msg.sender].length - 1).toUint16(); emit OrderPlacement(msg.sender, orderId, buyToken, sellToken, validFrom, validUntil, buyAmount, sellAmount); allUsers.insert(msg.sender); return orderId; } /** @dev called at the end of submitSolution with a value of tokenConservation / 2 * @param feeReward amount to be rewarded to the solver */ function grantRewardToSolutionSubmitter(uint256 feeReward) private { latestSolution.feeReward = feeReward; addBalanceAndBlockWithdrawForThisBatch(msg.sender, tokenIdToAddressMap(0), feeReward); } /** @dev called during solution submission to burn fees from previous auction * @return amount of OWL burnt */ function burnPreviousAuctionFees() private returns (uint256) { if (!currentBatchHasSolution()) { feeToken.burnOWL(address(this), latestSolution.feeReward); return latestSolution.feeReward; } return 0; } /** @dev Called from within submitSolution to update the token prices. * @param prices list of prices for touched tokens only, first price is always fee token price * @param tokenIdsForPrice price[i] is the price for the token with tokenID tokenIdsForPrice[i] */ function updateCurrentPrices(uint128[] memory prices, uint16[] memory tokenIdsForPrice) private { for (uint256 i = 0; i < latestSolution.tokenIdsForPrice.length; i++) { currentPrices[latestSolution.tokenIdsForPrice[i]] = 0; } for (uint256 i = 0; i < tokenIdsForPrice.length; i++) { currentPrices[tokenIdsForPrice[i]] = prices[i]; } } /** @dev Updates an order's remaing requested sell amount upon (partial) execution of a standing order * @param owner order's corresponding user address * @param orderId index of order in list of owner's orders * @param executedAmount proportion of order's requested sellAmount that was filled. */ function updateRemainingOrder(address owner, uint16 orderId, uint128 executedAmount) private { if (isOrderWithLimitedAmount(orders[owner][orderId])) { orders[owner][orderId].usedAmount = orders[owner][orderId].usedAmount.add(executedAmount).toUint128(); } } /** @dev The inverse of updateRemainingOrder, called when reverting a solution in favour of a better one. * @param owner order's corresponding user address * @param orderId index of order in list of owner's orders * @param executedAmount proportion of order's requested sellAmount that was filled. */ function revertRemainingOrder(address owner, uint16 orderId, uint128 executedAmount) private { if (isOrderWithLimitedAmount(orders[owner][orderId])) { orders[owner][orderId].usedAmount = orders[owner][orderId].usedAmount.sub(executedAmount).toUint128(); } } /** @dev Checks whether an order is intended to track its usedAmount * @param order order under inspection * @return true if the given order does track its usedAmount */ function isOrderWithLimitedAmount(Order memory order) private pure returns (bool) { return order.priceNumerator != UNLIMITED_ORDER_AMOUNT && order.priceDenominator != UNLIMITED_ORDER_AMOUNT; } /** @dev This function writes solution information into contract storage * @param batchId index of referenced auction * @param owners array of addresses corresponding to touched orders * @param orderIds array of order indices used in parallel with owners to identify touched order * @param volumes executed buy amounts for each order identified by index of owner-orderId arrays * @param tokenIdsForPrice price[i] is the price for the token with tokenID tokenIdsForPrice[i] */ function documentTrades( uint32 batchId, address[] memory owners, uint16[] memory orderIds, uint128[] memory volumes, uint16[] memory tokenIdsForPrice ) private { latestSolution.batchId = batchId; for (uint256 i = 0; i < owners.length; i++) { latestSolution.trades.push(TradeData({owner: owners[i], orderId: orderIds[i], volume: volumes[i]})); } latestSolution.tokenIdsForPrice = tokenIdsForPrice; latestSolution.solutionSubmitter = msg.sender; } /** @dev reverts all relevant contract storage relating to an overwritten auction solution. */ function undoCurrentSolution() private { if (currentBatchHasSolution()) { for (uint256 i = 0; i < latestSolution.trades.length; i++) { address owner = latestSolution.trades[i].owner; uint16 orderId = latestSolution.trades[i].orderId; Order memory order = orders[owner][orderId]; (, uint128 sellAmount) = getTradedAmounts(latestSolution.trades[i].volume, order); addBalance(owner, tokenIdToAddressMap(order.sellToken), sellAmount); } for (uint256 i = 0; i < latestSolution.trades.length; i++) { address owner = latestSolution.trades[i].owner; uint16 orderId = latestSolution.trades[i].orderId; Order memory order = orders[owner][orderId]; (uint128 buyAmount, uint128 sellAmount) = getTradedAmounts(latestSolution.trades[i].volume, order); revertRemainingOrder(owner, orderId, sellAmount); subtractBalanceUnchecked(owner, tokenIdToAddressMap(order.buyToken), buyAmount); emit TradeReversion(owner, orderId, order.sellToken, order.buyToken, sellAmount, buyAmount); } // subtract granted fees: subtractBalanceUnchecked(latestSolution.solutionSubmitter, tokenIdToAddressMap(0), latestSolution.feeReward); } } /** @dev determines if value is better than currently and updates if it is. * @param newObjectiveValue proposed value to be updated if a great enough improvement on the current objective value */ function checkAndOverrideObjectiveValue(uint256 newObjectiveValue) private { require( isObjectiveValueSufficientlyImproved(newObjectiveValue), "New objective doesn't sufficiently improve current solution" ); latestSolution.objectiveValue = newObjectiveValue; } // Private view /** @dev Evaluates utility of executed trade * @param execBuy represents proportion of order executed (in terms of buy amount) * @param order the sell order whose utility is being evaluated * @return Utility = ((execBuy * order.sellAmt - execSell * order.buyAmt) * price.buyToken) / order.sellAmt */ function evaluateUtility(uint128 execBuy, Order memory order) private view returns (uint256) { // Utility = ((execBuy * order.sellAmt - execSell * order.buyAmt) * price.buyToken) / order.sellAmt uint256 execSellTimesBuy = getExecutedSellAmount(execBuy, currentPrices[order.buyToken], currentPrices[order.sellToken]) .mul(order.priceNumerator); uint256 roundedUtility = execBuy.sub(execSellTimesBuy.div(order.priceDenominator)).mul(currentPrices[order.buyToken]); uint256 utilityError = execSellTimesBuy.mod(order.priceDenominator).mul(currentPrices[order.buyToken]).div( order.priceDenominator ); return roundedUtility.sub(utilityError); } /** @dev computes a measure of how much of an order was disregarded (only valid when limit price is respected) * @param order the sell order whose disregarded utility is being evaluated * @param user address of order's owner * @return disregardedUtility of the order (after it has been applied) * Note that: * |disregardedUtility| = (limitTerm * leftoverSellAmount) / order.sellAmount * where limitTerm = price.SellToken * order.sellAmt - order.buyAmt * price.buyToken / (1 - phi) * and leftoverSellAmount = order.sellAmt - execSellAmt * Balances and orders have all been updated so: sellAmount - execSellAmt == remainingAmount(order). * For correctness, we take the minimum of this with the user's token balance. */ function evaluateDisregardedUtility(Order memory order, address user) private view returns (uint256) { uint256 leftoverSellAmount = Math.min(getRemainingAmount(order), getBalance(user, tokenIdToAddressMap(order.sellToken))); uint256 limitTermLeft = currentPrices[order.sellToken].mul(order.priceDenominator); uint256 limitTermRight = order.priceNumerator.mul(currentPrices[order.buyToken]).mul(FEE_DENOMINATOR).div( FEE_DENOMINATOR - 1 ); uint256 limitTerm = 0; if (limitTermLeft > limitTermRight) { limitTerm = limitTermLeft.sub(limitTermRight); } return leftoverSellAmount.mul(limitTerm).div(order.priceDenominator); } /** @dev Evaluates executedBuy amount based on prices and executedBuyAmout (fees included) * @param executedBuyAmount amount of buyToken executed for purchase in batch auction * @param buyTokenPrice uniform clearing price of buyToken * @param sellTokenPrice uniform clearing price of sellToken * @return executedSellAmount as expressed in Equation (2) * https://github.com/gnosis/dex-contracts/issues/173#issuecomment-526163117 * execSellAmount * p[sellToken] * (1 - phi) == execBuyAmount * p[buyToken] * where phi = 1/FEE_DENOMINATOR * Note that: 1 - phi = (FEE_DENOMINATOR - 1) / FEE_DENOMINATOR * And so, 1/(1-phi) = FEE_DENOMINATOR / (FEE_DENOMINATOR - 1) * execSellAmount = (execBuyAmount * p[buyToken]) / (p[sellToken] * (1 - phi)) * = (execBuyAmount * buyTokenPrice / sellTokenPrice) * FEE_DENOMINATOR / (FEE_DENOMINATOR - 1) * in order to minimize rounding errors, the order of operations is switched * = ((executedBuyAmount * buyTokenPrice) / (FEE_DENOMINATOR - 1)) * FEE_DENOMINATOR) / sellTokenPrice */ function getExecutedSellAmount(uint128 executedBuyAmount, uint128 buyTokenPrice, uint128 sellTokenPrice) private pure returns (uint128) { /* solium-disable indentation */ return uint256(executedBuyAmount) .mul(buyTokenPrice) .div(FEE_DENOMINATOR - 1) .mul(FEE_DENOMINATOR) .div(sellTokenPrice) .toUint128(); /* solium-enable indentation */ } /** @dev used to determine if solution if first provided in current batch * @return true if `latestSolution` is storing a solution for current batch, else false */ function currentBatchHasSolution() private view returns (bool) { return latestSolution.batchId == getCurrentBatchId() - 1; } // Private view /** @dev Compute trade execution based on executedBuyAmount and relevant token prices * @param executedBuyAmount executed buy amount * @param order contains relevant buy-sell token information * @return (executedBuyAmount, executedSellAmount) */ function getTradedAmounts(uint128 executedBuyAmount, Order memory order) private view returns (uint128, uint128) { uint128 executedSellAmount = getExecutedSellAmount( executedBuyAmount, currentPrices[order.buyToken], currentPrices[order.sellToken] ); return (executedBuyAmount, executedSellAmount); } /** @dev Checks that the proposed objective value is a significant enough improvement on the latest one * @param objectiveValue the proposed objective value to check * @return true if the objectiveValue is a significant enough improvement, false otherwise */ function isObjectiveValueSufficientlyImproved(uint256 objectiveValue) private view returns (bool) { return (objectiveValue.mul(IMPROVEMENT_DENOMINATOR) > getCurrentObjectiveValue().mul(IMPROVEMENT_DENOMINATOR + 1)); } // Private pure /** @dev used to determine if an order is valid for specific auction/batch * @param order object whose validity is in question * @param batchId auction index of validity * @return true if order is valid in auction batchId else false */ function checkOrderValidity(Order memory order, uint32 batchId) private pure returns (bool) { return order.validFrom <= batchId && order.validUntil >= batchId; } /** @dev computes the remaining sell amount for a given order * @param order the order for which remaining amount should be calculated * @return the remaining sell amount */ function getRemainingAmount(Order memory order) private pure returns (uint128) { return order.priceDenominator - order.usedAmount; } /** @dev called only by getEncodedOrders and used to pack auction info into bytes * @param user list of tokenIds * @param sellTokenBalance user's account balance of sell token * @param order a sell order * @return byte encoded, packed, concatenation of relevant order information */ function encodeAuctionElement(address user, uint256 sellTokenBalance, Order memory order) private pure returns (bytes memory element) { element = abi.encodePacked(user); element = element.concat(abi.encodePacked(sellTokenBalance)); element = element.concat(abi.encodePacked(order.buyToken)); element = element.concat(abi.encodePacked(order.sellToken)); element = element.concat(abi.encodePacked(order.validFrom)); element = element.concat(abi.encodePacked(order.validUntil)); element = element.concat(abi.encodePacked(order.priceNumerator)); element = element.concat(abi.encodePacked(order.priceDenominator)); element = element.concat(abi.encodePacked(getRemainingAmount(order))); return element; } /** @dev determines if value is better than currently and updates if it is. * @param amounts array of values to be verified with AMOUNT_MINIMUM */ function verifyAmountThreshold(uint128[] memory amounts) private pure returns (bool) { for (uint256 i = 0; i < amounts.length; i++) { if (amounts[i] < AMOUNT_MINIMUM) { return false; } } return true; } }
File 2 of 2: Api3Token
// File: @openzeppelin/contracts/GSN/Context.sol 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; } } // File: @openzeppelin/contracts/token/ERC20/IERC20.sol pragma solidity ^0.6.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } // File: @openzeppelin/contracts/math/SafeMath.sol pragma solidity ^0.6.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } // File: @openzeppelin/contracts/utils/Address.sol 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); } } } } // File: @openzeppelin/contracts/token/ERC20/ERC20.sol pragma solidity ^0.6.0; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { using SafeMath for uint256; using Address for address; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor (string memory name, string memory symbol) public { _name = name; _symbol = symbol; _decimals = 18; } /** * @dev Returns the name of the token. */ function name() public view returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}; * * Requirements: * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } } // File: @openzeppelin/contracts/access/Ownable.sol pragma solidity ^0.6.0; /** * @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; } } // File: contracts/interfaces/IApi3Token.sol pragma solidity 0.6.12; interface IApi3Token is IERC20 { event MinterStatusUpdated( address indexed minterAddress, bool minterStatus ); event BurnerStatusUpdated( address indexed burnerAddress, bool burnerStatus ); function updateMinterStatus( address minterAddress, bool minterStatus ) external; function updateBurnerStatus(bool burnerStatus) external; function mint( address account, uint256 amount ) external; function burn(uint256 amount) external; function getMinterStatus(address minterAddress) external view returns(bool minterStatus); function getBurnerStatus(address burnerAddress) external view returns(bool burnerStatus); } // File: contracts/Api3Token.sol pragma solidity 0.6.12; /// @title API3 token contract /// @notice The API3 token contract is owned by the API3 DAO, which can grant /// minting privileges to addresses. Any account is allowed to burn their /// tokens, but this functionality is put behind a barrier that requires the /// account to make a call to remove. contract Api3Token is ERC20, Ownable, IApi3Token { /// @dev If an address is authorized to mint tokens. /// Token minting authorization is granted by the token contract owner /// (i.e., the API3 DAO). mapping(address => bool) private isMinter; /// @dev If an address is authorized to burn tokens. /// Token burning authorization is granted by the address itself, i.e., /// anyone can declare themselves a token burner. mapping(address => bool) private isBurner; /// @param contractOwner Address that will receive the ownership of the /// token contract /// @param mintingDestination Address that the tokens will be minted to constructor( address contractOwner, address mintingDestination ) public ERC20("API3", "API3") { transferOwnership(contractOwner); // Initial supply is 100 million (100e6) // We are using ether because the token has 18 decimals like ETH _mint(mintingDestination, 100e6 ether); } /// @notice The OpenZeppelin renounceOwnership() implementation is /// overriden to prevent ownership from being renounced accidentally. function renounceOwnership() public override onlyOwner { revert("Ownership cannot be renounced"); } /// @notice Updates if an address is authorized to mint tokens /// @param minterAddress Address whose minter authorization status will be /// updated /// @param minterStatus Updated minter authorization status function updateMinterStatus( address minterAddress, bool minterStatus ) external override onlyOwner { require( isMinter[minterAddress] != minterStatus, "Input will not update state" ); isMinter[minterAddress] = minterStatus; emit MinterStatusUpdated(minterAddress, minterStatus); } /// @notice Updates if the caller is authorized to burn tokens /// @param burnerStatus Updated minter authorization status function updateBurnerStatus(bool burnerStatus) external override { require( isBurner[msg.sender] != burnerStatus, "Input will not update state" ); isBurner[msg.sender] = burnerStatus; emit BurnerStatusUpdated(msg.sender, burnerStatus); } /// @notice Mints tokens /// @param account Address that will receive the minted tokens /// @param amount Amount that will be minted function mint( address account, uint256 amount ) external override { require(isMinter[msg.sender], "Only minters are allowed to mint"); _mint(account, amount); } /// @notice Burns caller's tokens /// @param amount Amount that will be burned function burn(uint256 amount) external override { require(isBurner[msg.sender], "Only burners are allowed to burn"); _burn(msg.sender, amount); } /// @notice Returns if an address is authorized to mint tokens /// @param minterAddress Address whose minter authorization status will be /// returned /// @return minterStatus Minter authorization status function getMinterStatus(address minterAddress) external view override returns(bool minterStatus) { minterStatus = isMinter[minterAddress]; } /// @notice Returns if an address is authorized to burn tokens /// @param burnerAddress Address whose burner authorization status will be /// returned /// @return burnerStatus Burner authorization status function getBurnerStatus(address burnerAddress) external view override returns(bool burnerStatus) { burnerStatus = isBurner[burnerAddress]; } }