Skip to content

Instantly share code, notes, and snippets.

@koeppelmann
Created July 3, 2018 21:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save koeppelmann/3bd8d209fdbe6e0667c0aba2cf870984 to your computer and use it in GitHub Desktop.
Save koeppelmann/3bd8d209fdbe6e0667c0aba2cf870984 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.4.24+commit.e67f0147.js&optimize=false&gist=
pragma solidity ^0.4.21;
// File: contracts/Oracle/DSAuth.sol
contract DSAuthority {
function canCall(
address src, address dst, bytes4 sig
) public view returns (bool);
}
contract DSAuthEvents {
event LogSetAuthority (address indexed authority);
event LogSetOwner (address indexed owner);
}
contract DSAuth is DSAuthEvents {
DSAuthority public authority;
address public owner;
function DSAuth() public {
owner = msg.sender;
LogSetOwner(msg.sender);
}
function setOwner(address owner_)
public
auth
{
owner = owner_;
LogSetOwner(owner);
}
function setAuthority(DSAuthority authority_)
public
auth
{
authority = authority_;
LogSetAuthority(authority);
}
modifier auth {
require(isAuthorized(msg.sender, msg.sig));
_;
}
function isAuthorized(address src, bytes4 sig) internal view returns (bool) {
if (src == address(this)) {
return true;
} else if (src == owner) {
return true;
} else if (authority == DSAuthority(0)) {
return false;
} else {
return authority.canCall(src, this, sig);
}
}
}
// File: contracts/Oracle/DSMath.sol
contract DSMath {
/*
standard uint256 functions
*/
function add(uint256 x, uint256 y) constant internal returns (uint256 z) {
assert((z = x + y) >= x);
}
function sub(uint256 x, uint256 y) constant internal returns (uint256 z) {
assert((z = x - y) <= x);
}
function mul(uint256 x, uint256 y) constant internal returns (uint256 z) {
assert((z = x * y) >= x);
}
function div(uint256 x, uint256 y) constant internal returns (uint256 z) {
z = x / y;
}
function min(uint256 x, uint256 y) constant internal returns (uint256 z) {
return x <= y ? x : y;
}
function max(uint256 x, uint256 y) constant internal returns (uint256 z) {
return x >= y ? x : y;
}
/*
uint128 functions (h is for half)
*/
function hadd(uint128 x, uint128 y) constant internal returns (uint128 z) {
assert((z = x + y) >= x);
}
function hsub(uint128 x, uint128 y) constant internal returns (uint128 z) {
assert((z = x - y) <= x);
}
function hmul(uint128 x, uint128 y) constant internal returns (uint128 z) {
assert((z = x * y) >= x);
}
function hdiv(uint128 x, uint128 y) constant internal returns (uint128 z) {
z = x / y;
}
function hmin(uint128 x, uint128 y) constant internal returns (uint128 z) {
return x <= y ? x : y;
}
function hmax(uint128 x, uint128 y) constant internal returns (uint128 z) {
return x >= y ? x : y;
}
/*
int256 functions
*/
function imin(int256 x, int256 y) constant internal returns (int256 z) {
return x <= y ? x : y;
}
function imax(int256 x, int256 y) constant internal returns (int256 z) {
return x >= y ? x : y;
}
/*
WAD math
*/
uint128 constant WAD = 10 ** 18;
function wadd(uint128 x, uint128 y) constant internal returns (uint128) {
return hadd(x, y);
}
function wsub(uint128 x, uint128 y) constant internal returns (uint128) {
return hsub(x, y);
}
function wmul(uint128 x, uint128 y) constant internal returns (uint128 z) {
z = cast((uint256(x) * y + WAD / 2) / WAD);
}
function wdiv(uint128 x, uint128 y) constant internal returns (uint128 z) {
z = cast((uint256(x) * WAD + y / 2) / y);
}
function wmin(uint128 x, uint128 y) constant internal returns (uint128) {
return hmin(x, y);
}
function wmax(uint128 x, uint128 y) constant internal returns (uint128) {
return hmax(x, y);
}
/*
RAY math
*/
uint128 constant RAY = 10 ** 27;
function radd(uint128 x, uint128 y) constant internal returns (uint128) {
return hadd(x, y);
}
function rsub(uint128 x, uint128 y) constant internal returns (uint128) {
return hsub(x, y);
}
function rmul(uint128 x, uint128 y) constant internal returns (uint128 z) {
z = cast((uint256(x) * y + RAY / 2) / RAY);
}
function rdiv(uint128 x, uint128 y) constant internal returns (uint128 z) {
z = cast((uint256(x) * RAY + y / 2) / y);
}
function rpow(uint128 x, uint64 n) constant internal returns (uint128 z) {
// This famous algorithm is called "exponentiation by squaring"
// and calculates x^n with x as fixed-point and n as regular unsigned.
//
// It's O(log n), instead of O(n) for naive repeated multiplication.
//
// These facts are why it works:
//
// If n is even, then x^n = (x^2)^(n/2).
// If n is odd, then x^n = x * x^(n-1),
// and applying the equation for even x gives
// x^n = x * (x^2)^((n-1) / 2).
//
// Also, EVM division is flooring and
// floor[(n-1) / 2] = floor[n / 2].
z = n % 2 != 0 ? x : RAY;
for (n /= 2; n != 0; n /= 2) {
x = rmul(x, x);
if (n % 2 != 0) {
z = rmul(z, x);
}
}
}
function rmin(uint128 x, uint128 y) constant internal returns (uint128) {
return hmin(x, y);
}
function rmax(uint128 x, uint128 y) constant internal returns (uint128) {
return hmax(x, y);
}
function cast(uint256 x) constant internal returns (uint128 z) {
assert((z = uint128(x)) == x);
}
}
// File: contracts/Oracle/DSNote.sol
contract DSNote {
event LogNote(
bytes4 indexed sig,
address indexed guy,
bytes32 indexed foo,
bytes32 indexed bar,
uint wad,
bytes fax
) anonymous;
modifier note {
bytes32 foo;
bytes32 bar;
assembly {
foo := calldataload(4)
bar := calldataload(36)
}
LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data);
_;
}
}
// File: contracts/Oracle/DSThing.sol
contract DSThing is DSAuth, DSNote, DSMath {
}
// File: contracts/Oracle/DSValue.sol
contract DSValue is DSThing {
bool has;
bytes32 val;
function peek() constant returns (bytes32, bool) {
return (val,has);
}
function read() constant returns (bytes32) {
var (wut, has) = peek();
assert(has);
return wut;
}
function poke(bytes32 wut) note auth {
val = wut;
has = true;
}
function void() note auth { // unset the value
has = false;
}
}
// File: contracts/Oracle/Medianizer.sol
contract Medianizer is DSValue {
mapping (bytes12 => address) public values;
mapping (address => bytes12) public indexes;
bytes12 public next = 0x1;
uint96 public min = 0x1;
function set(address wat) auth {
bytes12 nextId = bytes12(uint96(next) + 1);
assert(nextId != 0x0);
set(next, wat);
next = nextId;
}
function set(bytes12 pos, address wat) note auth {
if (pos == 0x0) throw;
if (wat != 0 && indexes[wat] != 0) throw;
indexes[values[pos]] = 0; // Making sure to remove a possible existing address in that position
if (wat != 0) {
indexes[wat] = pos;
}
values[pos] = wat;
}
function setMin(uint96 min_) note auth {
if (min_ == 0x0) throw;
min = min_;
}
function setNext(bytes12 next_) note auth {
if (next_ == 0x0) throw;
next = next_;
}
function unset(bytes12 pos) {
set(pos, 0);
}
function unset(address wat) {
set(indexes[wat], 0);
}
function poke() {
poke(0);
}
function poke(bytes32) note {
(val, has) = compute();
}
function compute() constant returns (bytes32, bool) {
bytes32[] memory wuts = new bytes32[](uint96(next) - 1);
uint96 ctr = 0;
for (uint96 i = 1; i < uint96(next); i++) {
if (values[bytes12(i)] != 0) {
var (wut, wuz) = DSValue(values[bytes12(i)]).peek();
if (wuz) {
if (ctr == 0 || wut >= wuts[ctr - 1]) {
wuts[ctr] = wut;
} else {
uint96 j = 0;
while (wut >= wuts[j]) {
j++;
}
for (uint96 k = ctr; k > j; k--) {
wuts[k] = wuts[k - 1];
}
wuts[j] = wut;
}
ctr++;
}
}
}
if (ctr < min) return (val, false);
bytes32 value;
if (ctr % 2 == 0) {
uint128 val1 = uint128(wuts[(ctr / 2) - 1]);
uint128 val2 = uint128(wuts[ctr / 2]);
value = bytes32(wdiv(hadd(val1, val2), 2 ether));
} else {
value = wuts[(ctr - 1) / 2];
}
return (value, true);
}
}
// File: contracts/Oracle/PriceFeed.sol
/// price-feed.sol
// Copyright (C) 2017 DappHub, LLC
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND (express or implied).
contract PriceFeed is DSThing {
uint128 val;
uint32 public zzz;
function peek() public view
returns (bytes32, bool)
{
return (bytes32(val), now < zzz);
}
function read() public view
returns (bytes32)
{
assert(now < zzz);
return bytes32(val);
}
function post(uint128 val_, uint32 zzz_, address med_) public note auth
{
val = val_;
zzz = zzz_;
bool ret = med_.call(bytes4(keccak256("poke()")));
ret;
}
function void() public note auth
{
zzz = 0;
}
}
// File: contracts/Oracle/PriceOracleInterface.sol
/*
This contract is the interface between the MakerDAO priceFeed and our DX platform.
*/
contract PriceOracleInterface {
address public priceFeedSource;
address public owner;
bool public emergencyMode;
event NonValidPriceFeed(address priceFeedSource);
// Modifiers
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/// @dev constructor of the contract
/// @param _priceFeedSource address of price Feed Source -> should be maker feeds Medianizer contract
function PriceOracleInterface(
address _owner,
address _priceFeedSource
)
public
{
owner = _owner;
priceFeedSource = _priceFeedSource;
}
/// @dev gives the owner the possibility to put the Interface into an emergencyMode, which will
/// output always a price of 600 USD. This gives everyone time to set up a new pricefeed.
function raiseEmergency(bool _emergencyMode)
public
onlyOwner()
{
emergencyMode = _emergencyMode;
}
/// @dev updates the priceFeedSource
/// @param _owner address of owner
function updateCurator(
address _owner
)
public
onlyOwner()
{
owner = _owner;
}
/// @dev returns the USDETH price, ie gets the USD price from Maker feed with 18 digits, but last 18 digits are cut off
function getUSDETHPrice()
public
returns (uint256)
{
// if the contract is in the emergencyMode, because there is an issue with the oracle, we will simply return a price of 600 USD
if(emergencyMode){
return 600;
}
bytes32 price;
bool valid=true;
(price, valid) = Medianizer(priceFeedSource).peek();
if (!valid) {
NonValidPriceFeed(priceFeedSource);
}
// ensuring that there is no underflow or overflow possible,
// even if the price is compromised
uint priceUint = uint256(price)/(1 ether);
if (priceUint == 0) return 1;
if (priceUint > 1000000) return 1000000;
return priceUint;
}
}
// File: @gnosis.pm/util-contracts/contracts/Math.sol
/// @title Math library - Allows calculation of logarithmic and exponential functions
/// @author Alan Lu - <alan.lu@gnosis.pm>
/// @author Stefan George - <stefan@gnosis.pm>
library Math {
/*
* 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[] 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;
}
}
// File: @gnosis.pm/util-contracts/contracts/Proxy.sol
/// @title Proxied - indicates that a contract will be proxied. Also defines storage requirements for Proxy.
/// @author Alan Lu - <alan@gnosis.pm>
contract Proxied {
address public masterCopy;
}
/// @title Proxy - Generic proxy contract allows to execute all transactions applying the code of a master contract.
/// @author Stefan George - <stefan@gnosis.pm>
contract Proxy is Proxied {
/// @dev Constructor function sets address of master copy contract.
/// @param _masterCopy Master copy address.
function Proxy(address _masterCopy)
public
{
require(_masterCopy != 0);
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()) }
}
}
}
// File: @gnosis.pm/util-contracts/contracts/Token.sol
/// Implements ERC 20 Token standard: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
pragma solidity ^0.4.21;
/// @title Abstract token contract - Functions to be implemented by token contracts
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);
}
// File: @gnosis.pm/util-contracts/contracts/StandardToken.sol
contract StandardTokenData {
/*
* Storage
*/
mapping (address => uint) balances;
mapping (address => mapping (address => uint)) allowances;
uint totalTokens;
}
/// @title Standard token contract with overflow protection
contract StandardToken is Token, StandardTokenData {
using Math 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;
}
}
// File: contracts/TokenFRT.sol
/// @title Standard token contract with overflow protection
contract TokenFRT is StandardToken {
string public constant symbol = "MGN";
string public constant name = "Magnolia Token";
uint8 public constant decimals = 18;
struct unlockedToken {
uint amountUnlocked;
uint withdrawalTime;
}
/*
* Storage
*/
address public owner;
address public minter;
// user => unlockedToken
mapping (address => unlockedToken) public unlockedTokens;
// user => amount
mapping (address => uint) public lockedTokenBalances;
/*
* Public functions
*/
function TokenFRT(
address _owner
)
public
{
require(_owner != address(0));
owner = _owner;
}
// @dev allows to set the minter of Magnolia tokens once.
// @param _minter the minter of the Magnolia tokens, should be the DX-proxy
function updateMinter(
address _minter
)
public
{
require(msg.sender == owner);
require(_minter != address(0));
minter = _minter;
}
// @dev the intention is to set the owner as the DX-proxy, once it is deployed
// Then only an update of the DX-proxy contract after a 30 days delay could change the minter again.
function updateOwner(
address _owner
)
public
{
require(msg.sender == owner);
require(_owner != address(0));
owner = _owner;
}
function mintTokens(
address user,
uint amount
)
public
{
require(msg.sender == minter);
lockedTokenBalances[user] = add(lockedTokenBalances[user], amount);
totalTokens = add(totalTokens, amount);
}
/// @dev Lock Token
function lockTokens(
uint amount
)
public
returns (uint totalAmountLocked)
{
// Adjust amount by balance
amount = min(amount, balances[msg.sender]);
// Update state variables
balances[msg.sender] = sub(balances[msg.sender], amount);
lockedTokenBalances[msg.sender] = add(lockedTokenBalances[msg.sender], amount);
// Get return variable
totalAmountLocked = lockedTokenBalances[msg.sender];
}
function unlockTokens(
uint amount
)
public
returns (uint totalAmountUnlocked, uint withdrawalTime)
{
// Adjust amount by locked balances
amount = min(amount, lockedTokenBalances[msg.sender]);
if (amount > 0) {
// Update state variables
lockedTokenBalances[msg.sender] = sub(lockedTokenBalances[msg.sender], amount);
unlockedTokens[msg.sender].amountUnlocked = add(unlockedTokens[msg.sender].amountUnlocked, amount);
unlockedTokens[msg.sender].withdrawalTime = now + 24 hours;
}
// Get return variables
totalAmountUnlocked = unlockedTokens[msg.sender].amountUnlocked;
withdrawalTime = unlockedTokens[msg.sender].withdrawalTime;
}
function withdrawUnlockedTokens()
public
{
require(unlockedTokens[msg.sender].withdrawalTime < now);
balances[msg.sender] = add(balances[msg.sender], unlockedTokens[msg.sender].amountUnlocked);
unlockedTokens[msg.sender].amountUnlocked = 0;
}
function min(uint a, uint b)
public
pure
returns (uint)
{
if (a < b) {
return a;
} else {
return 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(uint a, uint b)
public
constant
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)
public
constant
returns (bool)
{
return a >= b;
}
/// @dev Returns sum if no overflow occurred
/// @param a First addend
/// @param b Second addend
/// @return Sum
function add(uint a, uint b)
public
constant
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)
public
constant
returns (uint)
{
require(safeToSub(a, b));
return a - b;
}
}
// File: @gnosis.pm/owl-token/contracts/TokenOWL.sol
contract TokenOWL is Proxied, StandardToken {
using Math 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);
_;
}
/// @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) != 0);
// 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) != 0);
require(now >= masterCopyCountdown.timeWhenAvailable);
// 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 != 0 && msg.sender == minter);
balances[to] = balances[to].add(amount);
totalTokens = totalTokens.add(amount);
emit Minted(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);
}
}
// File: contracts/DutchExchange.sol
/// @title Dutch Exchange - exchange token pairs with the clever mechanism of the dutch auction
/// @author Alex Herrmann - <alex@gnosis.pm>
/// @author Dominik Teiml - <dominik@gnosis.pm>
contract DutchExchange is Proxied {
// The price is a rational number, so we need a concept of a fraction
struct fraction {
uint num;
uint den;
}
uint constant WAITING_PERIOD_NEW_TOKEN_PAIR = 6 hours;
uint constant WAITING_PERIOD_NEW_AUCTION = 10 minutes;
uint constant WAITING_PERIOD_CHANGE_MASTERCOPY_OR_ORACLE = 30 days;
uint constant AUCTION_START_WAITING_FOR_FUNDING = 1;
address public newMasterCopy;
// Time when new masterCopy is updatabale
uint public masterCopyCountdown;
// > Storage
// auctioneer has the power to manage some variables
address public auctioneer;
// Ether ERC-20 token
address public ethToken;
// Price Oracle interface
PriceOracleInterface public ethUSDOracle;
// Price Oracle interface proposals during update process
PriceOracleInterface public newProposalEthUSDOracle;
uint public oracleInterfaceCountdown;
// Minimum required sell funding for adding a new token pair, in USD
uint public thresholdNewTokenPair;
// Minimum required sell funding for starting antoher auction, in USD
uint public thresholdNewAuction;
// Fee reduction token (magnolia, ERC-20 token)
TokenFRT public frtToken;
// Token for paying fees
TokenOWL public owlToken;
// mapping that stores the tokens, which are approved
// Token => approved
// Only tokens approved by auctioneer generate frtToken tokens
mapping (address => bool) public approvedTokens;
// For the following two mappings, there is one mapping for each token pair
// The order which the tokens should be called is smaller, larger
// These variables should never be called directly! They have getters below
// Token => Token => index
mapping (address => mapping (address => uint)) public latestAuctionIndices;
// Token => Token => time
mapping (address => mapping (address => uint)) public auctionStarts;
// Token => Token => auctionIndex => price
mapping (address => mapping (address => mapping (uint => fraction))) public closingPrices;
// Token => Token => amount
mapping (address => mapping (address => uint)) public sellVolumesCurrent;
// Token => Token => amount
mapping (address => mapping (address => uint)) public sellVolumesNext;
// Token => Token => amount
mapping (address => mapping (address => uint)) public buyVolumes;
// Token => user => amount
// balances stores a user's balance in the DutchX
mapping (address => mapping (address => uint)) public balances;
// Token => Token => auctionIndex => amount
mapping (address => mapping (address => mapping (uint => uint))) public extraTokens;
// Token => Token => auctionIndex => user => amount
mapping (address => mapping (address => mapping (uint => mapping (address => uint)))) public sellerBalances;
mapping (address => mapping (address => mapping (uint => mapping (address => uint)))) public buyerBalances;
mapping (address => mapping (address => mapping (uint => mapping (address => uint)))) public claimedAmounts;
// > Modifiers
modifier onlyAuctioneer() {
// Only allows auctioneer to proceed
// R1
require(msg.sender == auctioneer);
_;
}
/// @dev Constructor-Function creates exchange
/// @param _frtToken - address of frtToken ERC-20 token
/// @param _owlToken - address of owlToken ERC-20 token
/// @param _auctioneer - auctioneer for managing interfaces
/// @param _ethToken - address of ETH ERC-20 token
/// @param _ethUSDOracle - address of the oracle contract for fetching feeds
/// @param _thresholdNewTokenPair - Minimum required sell funding for adding a new token pair, in USD
function setupDutchExchange(
TokenFRT _frtToken,
TokenOWL _owlToken,
address _auctioneer,
address _ethToken,
PriceOracleInterface _ethUSDOracle,
uint _thresholdNewTokenPair,
uint _thresholdNewAuction
)
public
{
// Make sure contract hasn't been initialised
require(ethToken == 0);
// Validates inputs
require(address(_owlToken) != address(0));
require(address(_frtToken) != address(0));
require(_auctioneer != 0);
require(_ethToken != 0);
require(address(_ethUSDOracle) != address(0));
frtToken = _frtToken;
owlToken = _owlToken;
auctioneer = _auctioneer;
ethToken = _ethToken;
ethUSDOracle = _ethUSDOracle;
thresholdNewTokenPair = _thresholdNewTokenPair;
thresholdNewAuction = _thresholdNewAuction;
}
function updateAuctioneer(
address _auctioneer
)
public
onlyAuctioneer
{
require(_auctioneer != address(0));
auctioneer = _auctioneer;
}
function initiateEthUsdOracleUpdate(
PriceOracleInterface _ethUSDOracle
)
public
onlyAuctioneer
{
require(address(_ethUSDOracle) != address(0));
newProposalEthUSDOracle = _ethUSDOracle;
oracleInterfaceCountdown = add(now, WAITING_PERIOD_CHANGE_MASTERCOPY_OR_ORACLE);
NewOracleProposal(_ethUSDOracle);
}
function updateEthUSDOracle()
public
onlyAuctioneer
{
require(address(newProposalEthUSDOracle) != address(0));
require(oracleInterfaceCountdown < now);
ethUSDOracle = newProposalEthUSDOracle;
newProposalEthUSDOracle = PriceOracleInterface(0);
}
function updateThresholdNewTokenPair(
uint _thresholdNewTokenPair
)
public
onlyAuctioneer
{
thresholdNewTokenPair = _thresholdNewTokenPair;
}
function updateThresholdNewAuction(
uint _thresholdNewAuction
)
public
onlyAuctioneer
{
thresholdNewAuction = _thresholdNewAuction;
}
function updateApprovalOfToken(
address[] token,
bool approved
)
public
onlyAuctioneer
{
for(uint i = 0; i < token.length; i++) {
approvedTokens[token[i]] = approved;
Approval(token[i], approved);
}
}
function startMasterCopyCountdown (
address _masterCopy
)
public
onlyAuctioneer
{
require(_masterCopy != address(0));
// Update masterCopyCountdown
newMasterCopy = _masterCopy;
masterCopyCountdown = add(now, WAITING_PERIOD_CHANGE_MASTERCOPY_OR_ORACLE);
NewMasterCopyProposal(_masterCopy);
}
function updateMasterCopy()
public
onlyAuctioneer
{
require(newMasterCopy != address(0));
require(now >= masterCopyCountdown);
// Update masterCopy
masterCopy = newMasterCopy;
newMasterCopy = address(0);
}
/// @param initialClosingPriceNum initial price will be 2 * initialClosingPrice. This is its numerator
/// @param initialClosingPriceDen initial price will be 2 * initialClosingPrice. This is its denominator
function addTokenPair(
address token1,
address token2,
uint token1Funding,
uint token2Funding,
uint initialClosingPriceNum,
uint initialClosingPriceDen
)
public
{
// R1
require(token1 != token2);
// R2
require(initialClosingPriceNum != 0);
// R3
require(initialClosingPriceDen != 0);
// R4
require(getAuctionIndex(token1, token2) == 0);
// R5: to prevent overflow
require(initialClosingPriceNum < 10 ** 18);
// R6
require(initialClosingPriceDen < 10 ** 18);
setAuctionIndex(token1, token2);
token1Funding = min(token1Funding, balances[token1][msg.sender]);
token2Funding = min(token2Funding, balances[token2][msg.sender]);
// R7
require(token1Funding < 10 ** 30);
// R8
require(token2Funding < 10 ** 30);
uint fundedValueUSD;
uint ethUSDPrice = ethUSDOracle.getUSDETHPrice();
// Compute fundedValueUSD
address ethTokenMem = ethToken;
if (token1 == ethTokenMem) {
// C1
// MUL: 10^30 * 10^6 = 10^36
fundedValueUSD = mul(token1Funding, ethUSDPrice);
} else if (token2 == ethTokenMem) {
// C2
// MUL: 10^30 * 10^6 = 10^36
fundedValueUSD = mul(token2Funding, ethUSDPrice);
} else {
// C3: Neither token is ethToken
fundedValueUSD = calculateFundedValueTokenToken(token1, token2,
token1Funding, token2Funding, ethTokenMem, ethUSDPrice);
}
// R5
require(fundedValueUSD >= thresholdNewTokenPair);
// Save prices of opposite auctions
closingPrices[token1][token2][0] = fraction(initialClosingPriceNum, initialClosingPriceDen);
closingPrices[token2][token1][0] = fraction(initialClosingPriceDen, initialClosingPriceNum);
// Split into two fns because of 16 local-var cap
addTokenPairSecondPart(token1, token2, token1Funding, token2Funding);
}
function calculateFundedValueTokenToken(
address token1,
address token2,
uint token1Funding,
uint token2Funding,
address ethTokenMem,
uint ethUSDPrice
)
internal
view
returns (uint fundedValueUSD)
{
// We require there to exist ethToken-Token auctions
// R3.1
require(getAuctionIndex(token1, ethTokenMem) > 0);
// R3.2
require(getAuctionIndex(token2, ethTokenMem) > 0);
// Price of Token 1
uint priceToken1Num;
uint priceToken1Den;
(priceToken1Num, priceToken1Den) = getPriceOfTokenInLastAuction(token1);
// Price of Token 2
uint priceToken2Num;
uint priceToken2Den;
(priceToken2Num, priceToken2Den) = getPriceOfTokenInLastAuction(token2);
// Compute funded value in ethToken and USD
// 10^30 * 10^30 = 10^60
uint fundedValueETH = add(mul(token1Funding, priceToken1Num) / priceToken1Den,
token2Funding * priceToken2Num / priceToken2Den);
fundedValueUSD = mul(fundedValueETH, ethUSDPrice);
}
function addTokenPairSecondPart(
address token1,
address token2,
uint token1Funding,
uint token2Funding
)
internal
{
balances[token1][msg.sender] = sub(balances[token1][msg.sender], token1Funding);
balances[token2][msg.sender] = sub(balances[token2][msg.sender], token2Funding);
// Fee mechanism, fees are added to extraTokens
uint token1FundingAfterFee = settleFee(token1, token2, 1, token1Funding);
uint token2FundingAfterFee = settleFee(token2, token1, 1, token2Funding);
// Update other variables
sellVolumesCurrent[token1][token2] = token1FundingAfterFee;
sellVolumesCurrent[token2][token1] = token2FundingAfterFee;
sellerBalances[token1][token2][1][msg.sender] = token1FundingAfterFee;
sellerBalances[token2][token1][1][msg.sender] = token2FundingAfterFee;
setAuctionStart(token1, token2, WAITING_PERIOD_NEW_TOKEN_PAIR);
NewTokenPair(token1, token2);
}
function deposit(
address tokenAddress,
uint amount
)
public
returns (uint)
{
// R1
require(Token(tokenAddress).transferFrom(msg.sender, this, amount));
uint newBal = add(balances[tokenAddress][msg.sender], amount);
balances[tokenAddress][msg.sender] = newBal;
NewDeposit(tokenAddress, amount);
return newBal;
}
function withdraw(
address tokenAddress,
uint amount
)
public
returns (uint)
{
uint usersBalance = balances[tokenAddress][msg.sender];
amount = min(amount, usersBalance);
// R1
require(amount > 0);
// R2
require(Token(tokenAddress).transfer(msg.sender, amount));
uint newBal = sub(usersBalance, amount);
balances[tokenAddress][msg.sender] = newBal;
NewWithdrawal(tokenAddress, amount);
return newBal;
}
function postSellOrder(
address sellToken,
address buyToken,
uint auctionIndex,
uint amount
)
public
returns (uint, uint)
{
// Note: if a user specifies auctionIndex of 0, it
// means he is agnostic which auction his sell order goes into
amount = min(amount, balances[sellToken][msg.sender]);
// R1
require(amount > 0);
// R2
uint latestAuctionIndex = getAuctionIndex(sellToken, buyToken);
require(latestAuctionIndex > 0);
// R3
uint auctionStart = getAuctionStart(sellToken, buyToken);
if (auctionStart == AUCTION_START_WAITING_FOR_FUNDING || auctionStart > now) {
// C1: We are in the 10 minute buffer period
// OR waiting for an auction to receive sufficient sellVolume
// Auction has already cleared, and index has been incremented
// sell order must use that auction index
// R1.1
if (auctionIndex == 0) {
auctionIndex = latestAuctionIndex;
} else {
require(auctionIndex == latestAuctionIndex);
}
// R1.2
require(add(sellVolumesCurrent[sellToken][buyToken], amount) < 10 ** 30);
} else {
// C2
// R2.1: Sell orders must go to next auction
if (auctionIndex == 0) {
auctionIndex = latestAuctionIndex + 1;
} else {
require(auctionIndex == latestAuctionIndex + 1);
}
// R2.2
require(add(sellVolumesNext[sellToken][buyToken], amount) < 10 ** 30);
}
// Fee mechanism, fees are added to extraTokens
uint amountAfterFee = settleFee(sellToken, buyToken, auctionIndex, amount);
// Update variables
balances[sellToken][msg.sender] = sub(balances[sellToken][msg.sender], amount);
uint newSellerBal = add(sellerBalances[sellToken][buyToken][auctionIndex][msg.sender], amountAfterFee);
sellerBalances[sellToken][buyToken][auctionIndex][msg.sender] = newSellerBal;
if (auctionStart == AUCTION_START_WAITING_FOR_FUNDING || auctionStart > now) {
// C1
uint sellVolumeCurrent = sellVolumesCurrent[sellToken][buyToken];
sellVolumesCurrent[sellToken][buyToken] = add(sellVolumeCurrent, amountAfterFee);
} else {
// C2
uint sellVolumeNext = sellVolumesNext[sellToken][buyToken];
sellVolumesNext[sellToken][buyToken] = add(sellVolumeNext, amountAfterFee);
}
if (auctionStart == AUCTION_START_WAITING_FOR_FUNDING) {
scheduleNextAuction(sellToken, buyToken);
}
NewSellOrder(sellToken, buyToken, msg.sender, auctionIndex, amountAfterFee);
return (auctionIndex, newSellerBal);
}
function postBuyOrder(
address sellToken,
address buyToken,
uint auctionIndex,
uint amount
)
public
returns (uint)
{
// R1: auction must not have cleared
require(closingPrices[sellToken][buyToken][auctionIndex].den == 0);
uint auctionStart = getAuctionStart(sellToken, buyToken);
// R2
require(auctionStart <= now);
// R4
require(auctionIndex == getAuctionIndex(sellToken, buyToken));
// R5: auction must not be in waiting period
require(auctionStart > AUCTION_START_WAITING_FOR_FUNDING);
// R6: auction must be funded
require(sellVolumesCurrent[sellToken][buyToken] > 0);
uint buyVolume = buyVolumes[sellToken][buyToken];
amount = min(amount, balances[buyToken][msg.sender]);
// R7
require(add(buyVolume, amount) < 10 ** 30);
// Overbuy is when a part of a buy order clears an auction
// In that case we only process the part before the overbuy
// To calculate overbuy, we first get current price
uint sellVolume = sellVolumesCurrent[sellToken][buyToken];
uint num;
uint den;
(num, den) = getCurrentAuctionPrice(sellToken, buyToken, auctionIndex);
// 10^30 * 10^37 = 10^67
uint outstandingVolume = atleastZero(int(mul(sellVolume, num) / den - buyVolume));
uint amountAfterFee;
if (amount < outstandingVolume) {
if (amount > 0) {
amountAfterFee = settleFee(buyToken, sellToken, auctionIndex, amount);
}
} else {
amount = outstandingVolume;
amountAfterFee = outstandingVolume;
}
// Here we could also use outstandingVolume or amountAfterFee, it doesn't matter
if (amount > 0) {
// Update variables
balances[buyToken][msg.sender] = sub(balances[buyToken][msg.sender], amount);
uint newBuyerBal = add(buyerBalances[sellToken][buyToken][auctionIndex][msg.sender], amountAfterFee);
buyerBalances[sellToken][buyToken][auctionIndex][msg.sender] = newBuyerBal;
buyVolumes[sellToken][buyToken] = add(buyVolumes[sellToken][buyToken], amountAfterFee);
NewBuyOrder(sellToken, buyToken, msg.sender, auctionIndex, amountAfterFee);
}
// Checking for equality would suffice here. nevertheless:
if (amount >= outstandingVolume) {
// Clear auction
clearAuction(sellToken, buyToken, auctionIndex, sellVolume);
}
return (newBuyerBal);
}
function claimSellerFunds(
address sellToken,
address buyToken,
address user,
uint auctionIndex
)
public
// < (10^60, 10^61)
returns (uint returned, uint frtsIssued)
{
closeTheoreticalClosedAuction(sellToken, buyToken, auctionIndex);
uint sellerBalance = sellerBalances[sellToken][buyToken][auctionIndex][user];
// R1
require(sellerBalance > 0);
// Get closing price for said auction
fraction memory closingPrice = closingPrices[sellToken][buyToken][auctionIndex];
uint num = closingPrice.num;
uint den = closingPrice.den;
// R2: require auction to have cleared
require(den > 0);
// Calculate return
// < 10^30 * 10^30 = 10^60
returned = mul(sellerBalance, num) / den;
frtsIssued = issueFrts(sellToken, buyToken, returned, auctionIndex, sellerBalance, user);
// Claim tokens
sellerBalances[sellToken][buyToken][auctionIndex][user] = 0;
if (returned > 0) {
balances[buyToken][user] = add(balances[buyToken][user], returned);
}
NewSellerFundsClaim(sellToken, buyToken, user, auctionIndex, returned, frtsIssued);
}
function claimBuyerFunds(
address sellToken,
address buyToken,
address user,
uint auctionIndex
)
public
returns (uint returned, uint frtsIssued)
{
closeTheoreticalClosedAuction(sellToken, buyToken, auctionIndex);
uint num;
uint den;
(returned, num, den) = getUnclaimedBuyerFunds(sellToken, buyToken, user, auctionIndex);
if (closingPrices[sellToken][buyToken][auctionIndex].den == 0) {
// Auction is running
claimedAmounts[sellToken][buyToken][auctionIndex][user] = add(claimedAmounts[sellToken][buyToken][auctionIndex][user], returned);
} else {
// Auction has closed
// We DON'T want to check for returned > 0, because that would fail if a user claims
// intermediate funds & auction clears in same block (he/she would not be able to claim extraTokens)
// Assign extra sell tokens (this is possible only after auction has cleared,
// because buyVolume could still increase before that)
uint extraTokensTotal = extraTokens[sellToken][buyToken][auctionIndex];
uint buyerBalance = buyerBalances[sellToken][buyToken][auctionIndex][user];
// closingPrices.num represents buyVolume
// < 10^30 * 10^30 = 10^60
uint tokensExtra = mul(buyerBalance, extraTokensTotal) / closingPrices[sellToken][buyToken][auctionIndex].num;
returned = add(returned, tokensExtra);
frtsIssued = issueFrts(buyToken, sellToken, mul(buyerBalance, den) / num, auctionIndex, buyerBalance, user);
// Auction has closed
// Reset buyerBalances and claimedAmounts
buyerBalances[sellToken][buyToken][auctionIndex][user] = 0;
claimedAmounts[sellToken][buyToken][auctionIndex][user] = 0;
}
// Claim tokens
if (returned > 0) {
balances[sellToken][user] = add(balances[sellToken][user], returned);
}
NewBuyerFundsClaim(sellToken, buyToken, user, auctionIndex, returned, frtsIssued);
}
function issueFrts(
address primaryToken,
address secondaryToken,
uint x,
uint auctionIndex,
uint bal,
address user
)
internal
returns (uint frtsIssued)
{
if (approvedTokens[primaryToken] && approvedTokens[secondaryToken]) {
address ethTokenMem = ethToken;
// Get frts issued based on ETH price of returned tokens
if (primaryToken == ethTokenMem) {
frtsIssued = bal;
} else if (secondaryToken == ethTokenMem) {
// 10^30 * 10^39 = 10^66
frtsIssued = x;
} else {
// Neither token is ethToken, so we use getHhistoricalPriceOracle()
uint pastNum;
uint pastDen;
(pastNum, pastDen) = getPriceInPastAuction(primaryToken, ethTokenMem, auctionIndex - 1);
// 10^30 * 10^35 = 10^65
frtsIssued = mul(bal, pastNum) / pastDen;
}
if (frtsIssued > 0) {
// Issue frtToken
frtToken.mintTokens(user, frtsIssued);
}
}
}
//@dev allows to close possible theoretical closed markets
//@param sellToken sellToken of an auction
//@param buyToken buyToken of an auction
//@param index is the auctionIndex of the auction
function closeTheoreticalClosedAuction(
address sellToken,
address buyToken,
uint auctionIndex
)
public
{
if(auctionIndex == getAuctionIndex(buyToken, sellToken) && closingPrices[sellToken][buyToken][auctionIndex].num == 0) {
uint buyVolume = buyVolumes[sellToken][buyToken];
uint sellVolume = sellVolumesCurrent[sellToken][buyToken];
uint num;
uint den;
(num, den) = getCurrentAuctionPrice(sellToken, buyToken, auctionIndex);
// 10^30 * 10^37 = 10^67
uint outstandingVolume = atleastZero(int(mul(sellVolume, num) / den - buyVolume));
if(outstandingVolume == 0) {
postBuyOrder(sellToken, buyToken, auctionIndex, 0);
}
}
}
/// @dev Claim buyer funds for one auction
function getUnclaimedBuyerFunds(
address sellToken,
address buyToken,
address user,
uint auctionIndex
)
public
view
// < (10^67, 10^37)
returns (uint unclaimedBuyerFunds, uint num, uint den)
{
// R1: checks if particular auction has ever run
require(auctionIndex <= getAuctionIndex(sellToken, buyToken));
(num, den) = getCurrentAuctionPrice(sellToken, buyToken, auctionIndex);
if (num == 0) {
// This should rarely happen - as long as there is >= 1 buy order,
// auction will clear before price = 0. So this is just fail-safe
unclaimedBuyerFunds = 0;
} else {
uint buyerBalance = buyerBalances[sellToken][buyToken][auctionIndex][user];
// < 10^30 * 10^37 = 10^67
unclaimedBuyerFunds = atleastZero(int(
mul(buyerBalance, den) / num -
claimedAmounts[sellToken][buyToken][auctionIndex][user]
));
}
}
function settleFee(
address primaryToken,
address secondaryToken,
uint auctionIndex,
uint amount
)
internal
// < 10^30
returns (uint amountAfterFee)
{
uint feeNum;
uint feeDen;
(feeNum, feeDen) = getFeeRatio(msg.sender);
// 10^30 * 10^3 / 10^4 = 10^29
uint fee = mul(amount, feeNum) / feeDen;
if (fee > 0) {
fee = settleFeeSecondPart(primaryToken, fee);
uint usersExtraTokens = extraTokens[primaryToken][secondaryToken][auctionIndex + 1];
extraTokens[primaryToken][secondaryToken][auctionIndex + 1] = add(usersExtraTokens, fee);
Fee(primaryToken, secondaryToken, msg.sender, auctionIndex, fee);
}
amountAfterFee = sub(amount, fee);
}
function settleFeeSecondPart(
address primaryToken,
uint fee
)
internal
returns (uint newFee)
{
// Allow user to reduce up to half of the fee with owlToken
uint num;
uint den;
(num, den) = getPriceOfTokenInLastAuction(primaryToken);
// Convert fee to ETH, then USD
// 10^29 * 10^30 / 10^30 = 10^29
uint feeInETH = mul(fee, num) / den;
uint ethUSDPrice = ethUSDOracle.getUSDETHPrice();
// 10^29 * 10^6 = 10^35
// Uses 18 decimal places <> exactly as owlToken tokens: 10**18 owlToken == 1 USD
uint feeInUSD = mul(feeInETH, ethUSDPrice);
uint amountOfowlTokenBurned = min(owlToken.allowance(msg.sender, this), feeInUSD / 2);
amountOfowlTokenBurned = min(owlToken.balanceOf(msg.sender), amountOfowlTokenBurned);
if (amountOfowlTokenBurned > 0) {
owlToken.burnOWL(msg.sender, amountOfowlTokenBurned);
// Adjust fee
// 10^35 * 10^29 = 10^64
uint adjustment = mul(amountOfowlTokenBurned, fee) / feeInUSD;
newFee = sub(fee, adjustment);
} else {
newFee = fee;
}
}
function getFeeRatio(
address user
)
public
view
// feeRatio < 10^4
returns (uint num, uint den)
{
uint t = frtToken.totalSupply();
uint b = frtToken.lockedTokenBalances(user);
if (b * 100000 < t || t == 0) {
// 0.5%
num = 1;
den = 200;
} else if (b * 10000 < t) {
// 0.4%
num = 1;
den = 250;
} else if (b * 1000 < t) {
// 0.3%
num = 3;
den = 1000;
} else if (b * 100 < t) {
// 0.2%
num = 1;
den = 500;
} else if (b * 10 < t) {
// 0.1%
num = 1;
den = 1000;
} else {
// 0%
num = 0;
den = 1;
}
}
/// @dev clears an Auction
/// @param sellToken sellToken of the auction
/// @param buyToken buyToken of the auction
/// @param auctionIndex of the auction to be cleared.
function clearAuction(
address sellToken,
address buyToken,
uint auctionIndex,
uint sellVolume
)
internal
{
// Get variables
uint buyVolume = buyVolumes[sellToken][buyToken];
uint sellVolumeOpp = sellVolumesCurrent[buyToken][sellToken];
uint closingPriceOppDen = closingPrices[buyToken][sellToken][auctionIndex].den;
uint auctionStart = getAuctionStart(sellToken, buyToken);
// Update closing price
if (sellVolume > 0) {
closingPrices[sellToken][buyToken][auctionIndex] = fraction(buyVolume, sellVolume);
}
// if (opposite is 0 auction OR price = 0 OR opposite auction cleared)
// price = 0 happens if auction pair has been running for >= 24 hrs = 86400
if (sellVolumeOpp == 0 || now >= auctionStart + 86400 || closingPriceOppDen > 0) {
// Close auction pair
uint buyVolumeOpp = buyVolumes[buyToken][sellToken];
if (closingPriceOppDen == 0 && sellVolumeOpp > 0) {
// Save opposite price
closingPrices[buyToken][sellToken][auctionIndex] = fraction(buyVolumeOpp, sellVolumeOpp);
}
uint sellVolumeNext = sellVolumesNext[sellToken][buyToken];
uint sellVolumeNextOpp = sellVolumesNext[buyToken][sellToken];
// Update state variables for both auctions
sellVolumesCurrent[sellToken][buyToken] = sellVolumeNext;
if (sellVolumeNext > 0) {
sellVolumesNext[sellToken][buyToken] = 0;
}
if (buyVolume > 0) {
buyVolumes[sellToken][buyToken] = 0;
}
sellVolumesCurrent[buyToken][sellToken] = sellVolumeNextOpp;
if (sellVolumeNextOpp > 0) {
sellVolumesNext[buyToken][sellToken] = 0;
}
if (buyVolumeOpp > 0) {
buyVolumes[buyToken][sellToken] = 0;
}
// Increment auction index
setAuctionIndex(sellToken, buyToken);
// Check if next auction can be scheduled
scheduleNextAuction(sellToken, buyToken);
}
AuctionCleared(sellToken, buyToken, sellVolume, buyVolume, auctionIndex);
}
function scheduleNextAuction(
address sellToken,
address buyToken
)
internal
{
// Check if auctions received enough sell orders
uint ethUSDPrice = ethUSDOracle.getUSDETHPrice();
uint sellNum;
uint sellDen;
(sellNum, sellDen) = getPriceOfTokenInLastAuction(sellToken);
uint buyNum;
uint buyDen;
(buyNum, buyDen) = getPriceOfTokenInLastAuction(buyToken);
// We use current sell volume, because in clearAuction() we set
// sellVolumesCurrent = sellVolumesNext before calling this function
// (this is so that we don't need case work,
// since it might also be called from postSellOrder())
// < 10^30 * 10^31 * 10^6 = 10^67
uint sellVolume = mul(mul(sellVolumesCurrent[sellToken][buyToken], sellNum), ethUSDPrice) / sellDen;
uint sellVolumeOpp = mul(mul(sellVolumesCurrent[buyToken][sellToken], buyNum), ethUSDPrice) / buyDen;
if (sellVolume >= thresholdNewAuction || sellVolumeOpp >= thresholdNewAuction) {
// Schedule next auction
setAuctionStart(sellToken, buyToken, WAITING_PERIOD_NEW_AUCTION);
} else {
resetAuctionStart(sellToken, buyToken);
}
}
//@ dev returns price in units [token2]/[token1]
//@ param token1 first token for price calculation
//@ param token2 second token for price calculation
//@ param auctionIndex index for the auction to get the averaged price from
function getPriceInPastAuction(
address token1,
address token2,
uint auctionIndex
)
public
view
// price < 10^31
returns (uint num, uint den)
{
if (token1 == token2) {
// C1
num = 1;
den = 1;
} else {
// C2
// R2.1
require(auctionIndex >= 0);
// C3
// R3.1
require(auctionIndex <= getAuctionIndex(token1, token2));
// auction still running
uint i = 0;
bool correctPair = false;
fraction memory closingPriceToken1;
fraction memory closingPriceToken2;
while (!correctPair) {
closingPriceToken2 = closingPrices[token2][token1][auctionIndex - i];
closingPriceToken1 = closingPrices[token1][token2][auctionIndex - i];
if (closingPriceToken1.num > 0 && closingPriceToken1.den > 0 ||
closingPriceToken2.num > 0 && closingPriceToken2.den > 0)
{
correctPair = true;
}
i++;
}
// At this point at least one closing price is strictly positive
// If only one is positive, we want to output that
if (closingPriceToken1.num == 0 || closingPriceToken1.den == 0) {
num = closingPriceToken2.den;
den = closingPriceToken2.num;
} else if (closingPriceToken2.num == 0 || closingPriceToken2.den == 0) {
num = closingPriceToken1.num;
den = closingPriceToken1.den;
} else {
// If both prices are positive, output weighted average
num = closingPriceToken2.den + closingPriceToken1.num;
den = closingPriceToken2.num + closingPriceToken1.den;
}
}
}
/// @dev Gives best estimate for market price of a token in ETH of any price oracle on the Ethereum network
/// @param token address of ERC-20 token
/// @return Weighted average of closing prices of opposite Token-ethToken auctions, based on their sellVolume
function getPriceOfTokenInLastAuction(
address token
)
public
view
// price < 10^31
returns (uint num, uint den)
{
uint latestAuctionIndex = getAuctionIndex(token, ethToken);
// getPriceInPastAuction < 10^30
(num, den) = getPriceInPastAuction(token, ethToken, latestAuctionIndex - 1);
}
function getCurrentAuctionPrice(
address sellToken,
address buyToken,
uint auctionIndex
)
public
view
// price < 10^37
returns (uint num, uint den)
{
fraction memory closingPrice = closingPrices[sellToken][buyToken][auctionIndex];
if (closingPrice.den != 0) {
// Auction has closed
(num, den) = (closingPrice.num, closingPrice.den);
} else if (auctionIndex > getAuctionIndex(sellToken, buyToken)) {
(num, den) = (0, 0);
} else {
// Auction is running
uint pastNum;
uint pastDen;
(pastNum, pastDen) = getPriceInPastAuction(sellToken, buyToken, auctionIndex - 1);
// If we're calling the function into an unstarted auction,
// it will return the starting price of that auction
uint timeElapsed = atleastZero(int(now - getAuctionStart(sellToken, buyToken)));
// The numbers below are chosen such that
// P(0 hrs) = 2 * lastClosingPrice, P(6 hrs) = lastClosingPrice, P(>=24 hrs) = 0
// 10^5 * 10^31 = 10^36
num = atleastZero(int((86400 - timeElapsed) * pastNum));
// 10^6 * 10^31 = 10^37
den = mul((timeElapsed + 43200), pastDen);
if (mul(num, sellVolumesCurrent[sellToken][buyToken]) <= mul(den, buyVolumes[sellToken][buyToken])) {
num = buyVolumes[sellToken][buyToken];
den = sellVolumesCurrent[sellToken][buyToken];
}
}
}
function depositAndSell(
address sellToken,
address buyToken,
uint amount
)
external
returns (uint newBal, uint auctionIndex, uint newSellerBal)
{
newBal = deposit(sellToken, amount);
(auctionIndex, newSellerBal) = postSellOrder(sellToken, buyToken, 0, amount);
}
function claimAndWithdraw(
address sellToken,
address buyToken,
address user,
uint auctionIndex,
uint amount
)
external
returns (uint returned, uint frtsIssued, uint newBal)
{
(returned, frtsIssued) = claimSellerFunds(sellToken, buyToken, user, auctionIndex);
newBal = withdraw(buyToken, amount);
}
// > Helper fns
function getTokenOrder(
address token1,
address token2
)
public
pure
returns (address, address)
{
if (token2 < token1) {
(token1, token2) = (token2, token1);
}
return (token1, token2);
}
function setAuctionStart(
address token1,
address token2,
uint value
)
internal
{
(token1, token2) = getTokenOrder(token1, token2);
uint auctionStart = now + value;
uint auctionIndex = latestAuctionIndices[token1][token2];
auctionStarts[token1][token2] = auctionStart;
AuctionStartScheduled(token1, token2, auctionIndex, auctionStart);
}
function resetAuctionStart(
address token1,
address token2
)
internal
{
(token1, token2) = getTokenOrder(token1, token2);
if (auctionStarts[token1][token2] != AUCTION_START_WAITING_FOR_FUNDING) {
auctionStarts[token1][token2] = AUCTION_START_WAITING_FOR_FUNDING;
}
}
function getAuctionStart(
address token1,
address token2
)
public
view
returns (uint auctionStart)
{
(token1, token2) = getTokenOrder(token1, token2);
auctionStart = auctionStarts[token1][token2];
}
function setAuctionIndex(
address token1,
address token2
)
internal
{
(token1, token2) = getTokenOrder(token1, token2);
latestAuctionIndices[token1][token2] += 1;
}
function getAuctionIndex(
address token1,
address token2
)
public
view
returns (uint auctionIndex)
{
(token1, token2) = getTokenOrder(token1, token2);
auctionIndex = latestAuctionIndices[token1][token2];
}
// > Math fns
function min(uint a, uint b)
public
pure
returns (uint)
{
if (a < b) {
return a;
} else {
return b;
}
}
function atleastZero(int a)
public
pure
returns (uint)
{
if (a < 0) {
return 0;
} else {
return uint(a);
}
}
/// @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)
public
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)
public
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)
public
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)
public
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)
public
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)
public
pure
returns (uint)
{
require(safeToMul(a, b));
return a * b;
}
function getRunningTokenPairs(
address[] tokens
)
external
view
returns (address[] tokens1, address[] tokens2)
{
uint arrayLength;
for (uint k = 0; k < tokens.length - 1; k++) {
for (uint l = k + 1; l < tokens.length; l++) {
if (getAuctionIndex(tokens[k], tokens[l]) > 0) {
arrayLength++;
}
}
}
tokens1 = new address[](arrayLength);
tokens2 = new address[](arrayLength);
uint h;
for (uint i = 0; i < tokens.length - 1; i++) {
for (uint j = i + 1; j < tokens.length; j++) {
if (getAuctionIndex(tokens[i], tokens[j]) > 0) {
tokens1[h] = tokens[i];
tokens2[h] = tokens[j];
h++;
}
}
}
}
//@dev for quick overview of possible sellerBalances to calculate the possible withdraw tokens
//@param auctionSellToken is the sellToken defining an auctionPair
//@param auctionBuyToken is the buyToken defining an auctionPair
//@param user is the user who wants to his tokens
//@param lastNAuctions how many auctions will be checked. 0 means all
//@returns returns sellbal for all indices for all tokenpairs
function getIndicesWithClaimableTokensForSellers(
address auctionSellToken,
address auctionBuyToken,
address user,
uint lastNAuctions
)
external
view
returns(uint[] indices, uint[] usersBalances)
{
uint runningAuctionIndex = getAuctionIndex(auctionSellToken, auctionBuyToken);
uint arrayLength;
uint startingIndex = lastNAuctions == 0 ? 1 : runningAuctionIndex - lastNAuctions + 1;
for (uint j = startingIndex; j <= runningAuctionIndex; j++) {
if (sellerBalances[auctionSellToken][auctionBuyToken][j][user] > 0) {
arrayLength++;
}
}
indices = new uint[](arrayLength);
usersBalances = new uint[](arrayLength);
uint k;
for (uint i = startingIndex; i <= runningAuctionIndex; i++) {
if (sellerBalances[auctionSellToken][auctionBuyToken][i][user] > 0) {
indices[k] = i;
usersBalances[k] = sellerBalances[auctionSellToken][auctionBuyToken][i][user];
k++;
}
}
}
//@dev for quick overview of current sellerBalances for a user
//@param auctionSellTokens are the sellTokens defining an auctionPair
//@param auctionBuyTokens are the buyTokens defining an auctionPair
//@param user is the user who wants to his tokens
function getSellerBalancesOfCurrentAuctions(
address[] auctionSellTokens,
address[] auctionBuyTokens,
address user
)
external
view
returns (uint[])
{
uint length = auctionSellTokens.length;
uint length2 = auctionBuyTokens.length;
require(length == length2);
uint[] memory sellersBalances = new uint[](length);
for (uint i = 0; i < length; i++) {
uint runningAuctionIndex = getAuctionIndex(auctionSellTokens[i], auctionBuyTokens[i]);
sellersBalances[i] = sellerBalances[auctionSellTokens[i]][auctionBuyTokens[i]][runningAuctionIndex][user];
}
return sellersBalances;
}
//@dev for quick overview of possible buyerBalances to calculate the possible withdraw tokens
//@param auctionSellToken is the sellToken defining an auctionPair
//@param auctionBuyToken is the buyToken defining an auctionPair
//@param user is the user who wants to his tokens
//@param lastNAuctions how many auctions will be checked. 0 means all
//@returns returns sellbal for all indices for all tokenpairs
function getIndicesWithClaimableTokensForBuyers(
address auctionSellToken,
address auctionBuyToken,
address user,
uint lastNAuctions
)
external
view
returns(uint[] indices, uint[] usersBalances)
{
uint runningAuctionIndex = getAuctionIndex(auctionSellToken, auctionBuyToken);
uint arrayLength;
uint startingIndex = lastNAuctions == 0 ? 1 : runningAuctionIndex - lastNAuctions + 1;
for (uint j = startingIndex; j <= runningAuctionIndex; j++) {
if (buyerBalances[auctionSellToken][auctionBuyToken][j][user] > 0) {
arrayLength++;
}
}
indices = new uint[](arrayLength);
usersBalances = new uint[](arrayLength);
uint k;
for (uint i = startingIndex; i <= runningAuctionIndex; i++) {
if (buyerBalances[auctionSellToken][auctionBuyToken][i][user] > 0) {
indices[k] = i;
usersBalances[k] = buyerBalances[auctionSellToken][auctionBuyToken][i][user];
k++;
}
}
}
//@dev for quick overview of current sellerBalances for a user
//@param auctionSellTokens are the sellTokens defining an auctionPair
//@param auctionBuyTokens are the buyTokens defining an auctionPair
//@param user is the user who wants to his tokens
function getBuyerBalancesOfCurrentAuctions(
address[] auctionSellTokens,
address[] auctionBuyTokens,
address user
)
external
view
returns (uint[])
{
uint length = auctionSellTokens.length;
uint length2 = auctionBuyTokens.length;
require(length == length2);
uint[] memory buyersBalances = new uint[](length);
for (uint i = 0; i < length; i++) {
uint runningAuctionIndex = getAuctionIndex(auctionSellTokens[i], auctionBuyTokens[i]);
buyersBalances[i] = buyerBalances[auctionSellTokens[i]][auctionBuyTokens[i]][runningAuctionIndex][user];
}
return buyersBalances;
}
//@dev for quick overview of approved Tokens
//@param addressesToCheck are the ERC-20 token addresses to be checked whether they are approved
function getApprovedAddressesOfList(
address[] addressToCheck
)
external
view
returns (bool[])
{
uint length = addressToCheck.length;
bool[] memory isApproved = new bool[](length);
for (uint i = 0; i < length; i++) {
isApproved[i] = approvedTokens[addressToCheck[i]];
}
return isApproved;
}
//@dev for multiple withdraws
//@param auctionSellTokens are the sellTokens defining an auctionPair
//@param auctionBuyTokens are the buyTokens defining an auctionPair
//@param auctionIndices are the auction indices on which an token should be claimedAmounts
//@param user is the user who wants to his tokens
function claimTokensFromSeveralAuctionsAsSeller(
address[] auctionSellTokens,
address[] auctionBuyTokens,
uint[] auctionIndices,
address user
)
external
{
uint length = auctionSellTokens.length;
uint length2 = auctionBuyTokens.length;
require(length == length2);
uint length3 = auctionIndices.length;
require(length2 == length3);
for (uint i = 0; i < length; i++)
claimSellerFunds(auctionSellTokens[i], auctionBuyTokens[i], user, auctionIndices[i]);
}
//@dev for multiple withdraws
//@param auctionSellTokens are the sellTokens defining an auctionPair
//@param auctionBuyTokens are the buyTokens defining an auctionPair
//@param auctionIndices are the auction indices on which an token should be claimedAmounts
//@param user is the user who wants to his tokens
function claimTokensFromSeveralAuctionsAsBuyer(
address[] auctionSellTokens,
address[] auctionBuyTokens,
uint[] auctionIndices,
address user
)
external
{
uint length = auctionSellTokens.length;
uint length2 = auctionBuyTokens.length;
require(length == length2);
uint length3 = auctionIndices.length;
require(length2 == length3);
for (uint i = 0; i < length; i++)
claimBuyerFunds(auctionSellTokens[i], auctionBuyTokens[i], user, auctionIndices[i]);
}
function getMasterCopy()
external
view
returns (address)
{
return masterCopy;
}
// > Events
event NewDeposit(
address indexed token,
uint amount
);
event NewOracleProposal(
PriceOracleInterface priceOracleInterface
);
event NewMasterCopyProposal(
address newMasterCopy
);
event NewWithdrawal(
address indexed token,
uint amount
);
event NewSellOrder(
address indexed sellToken,
address indexed buyToken,
address indexed user,
uint auctionIndex,
uint amount
);
event NewBuyOrder(
address indexed sellToken,
address indexed buyToken,
address indexed user,
uint auctionIndex,
uint amount
);
event NewSellerFundsClaim(
address indexed sellToken,
address indexed buyToken,
address indexed user,
uint auctionIndex,
uint amount,
uint frtsIssued
);
event NewBuyerFundsClaim(
address indexed sellToken,
address indexed buyToken,
address indexed user,
uint auctionIndex,
uint amount,
uint frtsIssued
);
event NewTokenPair(
address indexed sellToken,
address indexed buyToken
);
event AuctionCleared(
address indexed sellToken,
address indexed buyToken,
uint sellVolume,
uint buyVolume,
uint indexed auctionIndex
);
event Approval(
address indexed token,
bool approved
);
event AuctionStartScheduled(
address indexed sellToken,
address indexed buyToken,
uint indexed auctionIndex,
uint auctionStart
);
event Fee(
address indexed primaryToken,
address indexed secondarToken,
address indexed user,
uint auctionIndex,
uint fee
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment