Created
January 8, 2020 10:58
-
-
Save travs/3c0356fb506ef90093c8333936f72b72 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/src/contracts/dependencies/Owned.sol b/src/contracts/dependencies/Owned.sol | |
deleted file mode 100644 | |
index 444b852a..00000000 | |
--- a/src/contracts/dependencies/Owned.sol | |
+++ /dev/null | |
@@ -1,24 +0,0 @@ | |
-pragma solidity ^0.4.25; | |
- | |
- | |
-contract Owned { | |
- | |
- address public owner; | |
- | |
- modifier onlyOwner { | |
- require(isOwner(msg.sender)); | |
- _; | |
- } | |
- | |
- function Owned() { | |
- owner = msg.sender; | |
- } | |
- | |
- function changeOwner(address _newOwner) public onlyOwner { | |
- owner = _newOwner; | |
- } | |
- | |
- function isOwner(address _address) public view returns (bool) { | |
- return _address == owner; | |
- } | |
-} | |
diff --git a/src/contracts/dependencies/exec.sol b/src/contracts/dependencies/exec.sol | |
deleted file mode 100644 | |
index 8435aecc..00000000 | |
--- a/src/contracts/dependencies/exec.sol | |
+++ /dev/null | |
@@ -1,58 +0,0 @@ | |
-// exec.sol - base contract used by anything that wants to do "untyped" calls | |
- | |
-// Copyright (C) 2017 DappHub, LLC | |
- | |
-// This program is free software: you can redistribute it and/or modify | |
-// it under the terms of the GNU General Public License as published by | |
-// the Free Software Foundation, either version 3 of the License, or | |
-// (at your option) any later version. | |
- | |
-// This program is distributed in the hope that it will be useful, | |
-// but WITHOUT ANY WARRANTY; without even the implied warranty of | |
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
-// GNU General Public License for more details. | |
- | |
-// You should have received a copy of the GNU General Public License | |
-// along with this program. If not, see <http://www.gnu.org/licenses/>. | |
- | |
-pragma solidity ^0.4.25; | |
- | |
-contract DSExec { | |
- function tryExec( address target, bytes calldata, uint value) | |
- internal | |
- returns (bool call_ret) | |
- { | |
- return target.call.value(value)(calldata); | |
- } | |
- function exec( address target, bytes calldata, uint value) | |
- internal | |
- { | |
- if(!tryExec(target, calldata, value)) { | |
- revert(); | |
- } | |
- } | |
- | |
- // Convenience aliases | |
- function exec( address t, bytes c ) | |
- internal | |
- { | |
- exec(t, c, 0); | |
- } | |
- function exec( address t, uint256 v ) | |
- internal | |
- { | |
- bytes memory c; exec(t, c, v); | |
- } | |
- function tryExec( address t, bytes c ) | |
- internal | |
- returns (bool) | |
- { | |
- return tryExec(t, c, 0); | |
- } | |
- function tryExec( address t, uint256 v ) | |
- internal | |
- returns (bool) | |
- { | |
- bytes memory c; return tryExec(t, c, v); | |
- } | |
-} | |
diff --git a/src/contracts/dependencies/group.sol b/src/contracts/dependencies/group.sol | |
deleted file mode 100644 | |
index 44700be2..00000000 | |
--- a/src/contracts/dependencies/group.sol | |
+++ /dev/null | |
@@ -1,176 +0,0 @@ | |
-/// group.sol -- simple m-of-n multisig implementation | |
- | |
-// Copyright (C) 2015, 2016 Ryan Casey <ryepdx@gmail.com> | |
-// Copyright (C) 2016, 2017 Daniel Brockman <daniel@brockman.se> | |
- | |
-// 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). | |
- | |
-pragma solidity ^0.4.25; | |
- | |
-import "exec.sol"; | |
-import "note.sol"; | |
- | |
-contract DSGroup is DSExec, DSNote { | |
- address[] public members; | |
- uint public quorum; | |
- uint public window; | |
- uint public actionCount; | |
- | |
- mapping (uint => Action) public actions; | |
- mapping (uint => mapping (address => bool)) public confirmedBy; | |
- mapping (address => bool) public isMember; | |
- | |
- // Legacy events | |
- event Proposed (uint id, bytes calldata); | |
- event Confirmed (uint id, address member); | |
- event Triggered (uint id); | |
- | |
- struct Action { | |
- address target; | |
- bytes calldata; | |
- uint value; | |
- | |
- uint confirmations; | |
- uint deadline; | |
- bool triggered; | |
- } | |
- | |
- function DSGroup( | |
- address[] members_, | |
- uint quorum_, | |
- uint window_ | |
- ) { | |
- members = members_; | |
- quorum = quorum_; | |
- window = window_; | |
- | |
- for (uint i = 0; i < members.length; i++) { | |
- isMember[members[i]] = true; | |
- } | |
- } | |
- | |
- function memberCount() constant returns (uint) { | |
- return members.length; | |
- } | |
- | |
- function target(uint id) constant returns (address) { | |
- return actions[id].target; | |
- } | |
- function calldata(uint id) constant returns (bytes) { | |
- return actions[id].calldata; | |
- } | |
- function value(uint id) constant returns (uint) { | |
- return actions[id].value; | |
- } | |
- | |
- function confirmations(uint id) constant returns (uint) { | |
- return actions[id].confirmations; | |
- } | |
- function deadline(uint id) constant returns (uint) { | |
- return actions[id].deadline; | |
- } | |
- function triggered(uint id) constant returns (bool) { | |
- return actions[id].triggered; | |
- } | |
- | |
- function confirmed(uint id) constant returns (bool) { | |
- return confirmations(id) >= quorum; | |
- } | |
- function expired(uint id) constant returns (bool) { | |
- return now > deadline(id); | |
- } | |
- | |
- function deposit() note payable { | |
- } | |
- | |
- function propose( | |
- address target, | |
- bytes calldata, | |
- uint value | |
- ) onlyMembers note returns (uint id) { | |
- id = ++actionCount; | |
- | |
- actions[id].target = target; | |
- actions[id].calldata = calldata; | |
- actions[id].value = value; | |
- actions[id].deadline = now + window; | |
- | |
- Proposed(id, calldata); | |
- } | |
- | |
- function confirm(uint id) onlyMembers onlyActive(id) note { | |
- assert(!confirmedBy[id][msg.sender]); | |
- | |
- confirmedBy[id][msg.sender] = true; | |
- actions[id].confirmations++; | |
- | |
- Confirmed(id, msg.sender); | |
- } | |
- | |
- function trigger(uint id) onlyMembers onlyActive(id) note { | |
- assert(confirmed(id)); | |
- | |
- actions[id].triggered = true; | |
- exec(actions[id].target, actions[id].calldata, actions[id].value); | |
- | |
- Triggered(id); | |
- } | |
- | |
- modifier onlyMembers { | |
- assert(isMember[msg.sender]); | |
- _; | |
- } | |
- | |
- modifier onlyActive(uint id) { | |
- assert(!expired(id)); | |
- assert(!triggered(id)); | |
- _; | |
- } | |
- | |
- //------------------------------------------------------------------ | |
- // Legacy functions | |
- //------------------------------------------------------------------ | |
- | |
- function getInfo() constant returns ( | |
- uint quorum_, | |
- uint memberCount, | |
- uint window_, | |
- uint actionCount_ | |
- ) { | |
- return (quorum, members.length, window, actionCount); | |
- } | |
- | |
- function getActionStatus(uint id) constant returns ( | |
- uint confirmations, | |
- uint deadline, | |
- bool triggered, | |
- address target, | |
- uint value | |
- ) { | |
- return ( | |
- actions[id].confirmations, | |
- actions[id].deadline, | |
- actions[id].triggered, | |
- actions[id].target, | |
- actions[id].value | |
- ); | |
- } | |
-} | |
- | |
-contract DSGroupFactory is DSNote { | |
- mapping (address => bool) public isGroup; | |
- | |
- function newGroup( | |
- address[] members, | |
- uint quorum, | |
- uint window | |
- ) note returns (DSGroup group) { | |
- group = new DSGroup(members, quorum, window); | |
- isGroup[group] = true; | |
- } | |
-} | |
diff --git a/src/contracts/dependencies/note.sol b/src/contracts/dependencies/note.sol | |
deleted file mode 100644 | |
index 74865f8a..00000000 | |
--- a/src/contracts/dependencies/note.sol | |
+++ /dev/null | |
@@ -1,41 +0,0 @@ | |
-/// note.sol -- the `note' modifier, for logging calls as events | |
- | |
-// This program is free software: you can redistribute it and/or modify | |
-// it under the terms of the GNU General Public License as published by | |
-// the Free Software Foundation, either version 3 of the License, or | |
-// (at your option) any later version. | |
- | |
-// This program is distributed in the hope that it will be useful, | |
-// but WITHOUT ANY WARRANTY; without even the implied warranty of | |
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
-// GNU General Public License for more details. | |
- | |
-// You should have received a copy of the GNU General Public License | |
-// along with this program. If not, see <http://www.gnu.org/licenses/>. | |
- | |
-pragma solidity ^0.4.25; | |
- | |
-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); | |
- | |
- _; | |
- } | |
-} | |
diff --git a/src/contracts/dependencies/thing.sol b/src/contracts/dependencies/thing.sol | |
deleted file mode 100644 | |
index 7aa97e01..00000000 | |
--- a/src/contracts/dependencies/thing.sol | |
+++ /dev/null | |
@@ -1,30 +0,0 @@ | |
-// thing.sol - `auth` with handy mixins. your things should be DSThings | |
- | |
-// Copyright (C) 2017 DappHub, LLC | |
- | |
-// This program is free software: you can redistribute it and/or modify | |
-// it under the terms of the GNU General Public License as published by | |
-// the Free Software Foundation, either version 3 of the License, or | |
-// (at your option) any later version. | |
- | |
-// This program is distributed in the hope that it will be useful, | |
-// but WITHOUT ANY WARRANTY; without even the implied warranty of | |
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
-// GNU General Public License for more details. | |
- | |
-// You should have received a copy of the GNU General Public License | |
-// along with this program. If not, see <http://www.gnu.org/licenses/>. | |
- | |
-pragma solidity ^0.4.25; | |
- | |
-import "auth.sol"; | |
-import "note.sol"; | |
-import "math.sol"; | |
- | |
-contract DSThing is DSAuth, DSNote, DSMath { | |
- | |
- function S(string s) internal pure returns (bytes4) { | |
- return bytes4(keccak256(s)); | |
- } | |
- | |
-} | |
diff --git a/src/contracts/exchanges/third-party/0x/Exchange.sol b/src/contracts/exchanges/third-party/0x/Exchange.sol | |
deleted file mode 100644 | |
index 77d7e750..00000000 | |
--- a/src/contracts/exchanges/third-party/0x/Exchange.sol | |
+++ /dev/null | |
@@ -1,3931 +0,0 @@ | |
-/* | |
- | |
- Copyright 2018 ZeroEx Intl. | |
- | |
- Licensed under the Apache License, Version 2.0 (the "License"); | |
- you may not use this file except in compliance with the License. | |
- You may obtain a copy of the License at | |
- | |
- http://www.apache.org/licenses/LICENSE-2.0 | |
- | |
- 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, either express or implied. | |
- See the License for the specific language governing permissions and | |
- limitations under the License. | |
- | |
-*/ | |
- | |
-pragma solidity ^0.4.21; | |
-pragma experimental ABIEncoderV2; | |
- | |
-import "StandardToken.sol"; | |
-import "LibOrder.sol"; | |
-import "Ownable.sol"; | |
- | |
-library LibBytes { | |
- | |
- using LibBytes for bytes; | |
- | |
- /// @dev Gets the memory address for a byte array. | |
- /// @param input Byte array to lookup. | |
- /// @return memoryAddress Memory address of byte array. This | |
- /// points to the header of the byte array which contains | |
- /// the length. | |
- function rawAddress(bytes memory input) | |
- internal | |
- pure | |
- returns (uint256 memoryAddress) | |
- { | |
- assembly { | |
- memoryAddress := input | |
- } | |
- return memoryAddress; | |
- } | |
- | |
- /// @dev Gets the memory address for the contents of a byte array. | |
- /// @param input Byte array to lookup. | |
- /// @return memoryAddress Memory address of the contents of the byte array. | |
- function contentAddress(bytes memory input) | |
- internal | |
- pure | |
- returns (uint256 memoryAddress) | |
- { | |
- assembly { | |
- memoryAddress := add(input, 32) | |
- } | |
- return memoryAddress; | |
- } | |
- | |
- /// @dev Copies `length` bytes from memory location `source` to `dest`. | |
- /// @param dest memory address to copy bytes to. | |
- /// @param source memory address to copy bytes from. | |
- /// @param length number of bytes to copy. | |
- function memCopy( | |
- uint256 dest, | |
- uint256 source, | |
- uint256 length | |
- ) | |
- internal | |
- pure | |
- { | |
- if (length < 32) { | |
- // Handle a partial word by reading destination and masking | |
- // off the bits we are interested in. | |
- // This correctly handles overlap, zero lengths and source == dest | |
- assembly { | |
- let mask := sub(exp(256, sub(32, length)), 1) | |
- let s := and(mload(source), not(mask)) | |
- let d := and(mload(dest), mask) | |
- mstore(dest, or(s, d)) | |
- } | |
- } else { | |
- // Skip the O(length) loop when source == dest. | |
- if (source == dest) { | |
- return; | |
- } | |
- | |
- // For large copies we copy whole words at a time. The final | |
- // word is aligned to the end of the range (instead of after the | |
- // previous) to handle partial words. So a copy will look like this: | |
- // | |
- // #### | |
- // #### | |
- // #### | |
- // #### | |
- // | |
- // We handle overlap in the source and destination range by | |
- // changing the copying direction. This prevents us from | |
- // overwriting parts of source that we still need to copy. | |
- // | |
- // This correctly handles source == dest | |
- // | |
- if (source > dest) { | |
- assembly { | |
- // We subtract 32 from `sEnd` and `dEnd` because it | |
- // is easier to compare with in the loop, and these | |
- // are also the addresses we need for copying the | |
- // last bytes. | |
- length := sub(length, 32) | |
- let sEnd := add(source, length) | |
- let dEnd := add(dest, length) | |
- | |
- // Remember the last 32 bytes of source | |
- // This needs to be done here and not after the loop | |
- // because we may have overwritten the last bytes in | |
- // source already due to overlap. | |
- let last := mload(sEnd) | |
- | |
- // Copy whole words front to back | |
- // Note: the first check is always true, | |
- // this could have been a do-while loop. | |
- // solhint-disable-next-line no-empty-blocks | |
- for {} lt(source, sEnd) {} { | |
- mstore(dest, mload(source)) | |
- source := add(source, 32) | |
- dest := add(dest, 32) | |
- } | |
- | |
- // Write the last 32 bytes | |
- mstore(dEnd, last) | |
- } | |
- } else { | |
- assembly { | |
- // We subtract 32 from `sEnd` and `dEnd` because those | |
- // are the starting points when copying a word at the end. | |
- length := sub(length, 32) | |
- let sEnd := add(source, length) | |
- let dEnd := add(dest, length) | |
- | |
- // Remember the first 32 bytes of source | |
- // This needs to be done here and not after the loop | |
- // because we may have overwritten the first bytes in | |
- // source already due to overlap. | |
- let first := mload(source) | |
- | |
- // Copy whole words back to front | |
- // We use a signed comparisson here to allow dEnd to become | |
- // negative (happens when source and dest < 32). Valid | |
- // addresses in local memory will never be larger than | |
- // 2**255, so they can be safely re-interpreted as signed. | |
- // Note: the first check is always true, | |
- // this could have been a do-while loop. | |
- // solhint-disable-next-line no-empty-blocks | |
- for {} slt(dest, dEnd) {} { | |
- mstore(dEnd, mload(sEnd)) | |
- sEnd := sub(sEnd, 32) | |
- dEnd := sub(dEnd, 32) | |
- } | |
- | |
- // Write the first 32 bytes | |
- mstore(dest, first) | |
- } | |
- } | |
- } | |
- } | |
- | |
- /// @dev Returns a slices from a byte array. | |
- /// @param b The byte array to take a slice from. | |
- /// @param from The starting index for the slice (inclusive). | |
- /// @param to The final index for the slice (exclusive). | |
- /// @return result The slice containing bytes at indices [from, to) | |
- function slice( | |
- bytes memory b, | |
- uint256 from, | |
- uint256 to | |
- ) | |
- internal | |
- pure | |
- returns (bytes memory result) | |
- { | |
- require( | |
- from <= to, | |
- "FROM_LESS_THAN_TO_REQUIRED" | |
- ); | |
- require( | |
- to < b.length, | |
- "TO_LESS_THAN_LENGTH_REQUIRED" | |
- ); | |
- | |
- // Create a new bytes structure and copy contents | |
- result = new bytes(to - from); | |
- memCopy( | |
- result.contentAddress(), | |
- b.contentAddress() + from, | |
- result.length | |
- ); | |
- return result; | |
- } | |
- | |
- /// @dev Returns a slice from a byte array without preserving the input. | |
- /// @param b The byte array to take a slice from. Will be destroyed in the process. | |
- /// @param from The starting index for the slice (inclusive). | |
- /// @param to The final index for the slice (exclusive). | |
- /// @return result The slice containing bytes at indices [from, to) | |
- /// @dev When `from == 0`, the original array will match the slice. In other cases its state will be corrupted. | |
- function sliceDestructive( | |
- bytes memory b, | |
- uint256 from, | |
- uint256 to | |
- ) | |
- internal | |
- pure | |
- returns (bytes memory result) | |
- { | |
- require( | |
- from <= to, | |
- "FROM_LESS_THAN_TO_REQUIRED" | |
- ); | |
- require( | |
- to < b.length, | |
- "TO_LESS_THAN_LENGTH_REQUIRED" | |
- ); | |
- | |
- // Create a new bytes structure around [from, to) in-place. | |
- assembly { | |
- result := add(b, from) | |
- mstore(result, sub(to, from)) | |
- } | |
- return result; | |
- } | |
- | |
- /// @dev Pops the last byte off of a byte array by modifying its length. | |
- /// @param b Byte array that will be modified. | |
- /// @return The byte that was popped off. | |
- function popLastByte(bytes memory b) | |
- internal | |
- pure | |
- returns (bytes1 result) | |
- { | |
- require( | |
- b.length > 0, | |
- "GREATER_THAN_ZERO_LENGTH_REQUIRED" | |
- ); | |
- | |
- // Store last byte. | |
- result = b[b.length - 1]; | |
- | |
- assembly { | |
- // Decrement length of byte array. | |
- let newLen := sub(mload(b), 1) | |
- mstore(b, newLen) | |
- } | |
- return result; | |
- } | |
- | |
- /// @dev Pops the last 20 bytes off of a byte array by modifying its length. | |
- /// @param b Byte array that will be modified. | |
- /// @return The 20 byte address that was popped off. | |
- function popLast20Bytes(bytes memory b) | |
- internal | |
- pure | |
- returns (address result) | |
- { | |
- require( | |
- b.length >= 20, | |
- "GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED" | |
- ); | |
- | |
- // Store last 20 bytes. | |
- result = readAddress(b, b.length - 20); | |
- | |
- assembly { | |
- // Subtract 20 from byte array length. | |
- let newLen := sub(mload(b), 20) | |
- mstore(b, newLen) | |
- } | |
- return result; | |
- } | |
- | |
- /// @dev Tests equality of two byte arrays. | |
- /// @param lhs First byte array to compare. | |
- /// @param rhs Second byte array to compare. | |
- /// @return True if arrays are the same. False otherwise. | |
- function equals( | |
- bytes memory lhs, | |
- bytes memory rhs | |
- ) | |
- internal | |
- pure | |
- returns (bool equal) | |
- { | |
- // Keccak gas cost is 30 + numWords * 6. This is a cheap way to compare. | |
- // We early exit on unequal lengths, but keccak would also correctly | |
- // handle this. | |
- return lhs.length == rhs.length && keccak256(lhs) == keccak256(rhs); | |
- } | |
- | |
- /// @dev Reads an address from a position in a byte array. | |
- /// @param b Byte array containing an address. | |
- /// @param index Index in byte array of address. | |
- /// @return address from byte array. | |
- function readAddress( | |
- bytes memory b, | |
- uint256 index | |
- ) | |
- internal | |
- pure | |
- returns (address result) | |
- { | |
- require( | |
- b.length >= index + 20, // 20 is length of address | |
- "GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED" | |
- ); | |
- | |
- // Add offset to index: | |
- // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index) | |
- // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index) | |
- index += 20; | |
- | |
- // Read address from array memory | |
- assembly { | |
- // 1. Add index to address of bytes array | |
- // 2. Load 32-byte word from memory | |
- // 3. Apply 20-byte mask to obtain address | |
- result := and(mload(add(b, index)), 0xffffffffffffffffffffffffffffffffffffffff) | |
- } | |
- return result; | |
- } | |
- | |
- /// @dev Writes an address into a specific position in a byte array. | |
- /// @param b Byte array to insert address into. | |
- /// @param index Index in byte array of address. | |
- /// @param input Address to put into byte array. | |
- function writeAddress( | |
- bytes memory b, | |
- uint256 index, | |
- address input | |
- ) | |
- internal | |
- pure | |
- { | |
- require( | |
- b.length >= index + 20, // 20 is length of address | |
- "GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED" | |
- ); | |
- | |
- // Add offset to index: | |
- // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index) | |
- // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index) | |
- index += 20; | |
- | |
- // Store address into array memory | |
- assembly { | |
- // The address occupies 20 bytes and mstore stores 32 bytes. | |
- // First fetch the 32-byte word where we'll be storing the address, then | |
- // apply a mask so we have only the bytes in the word that the address will not occupy. | |
- // Then combine these bytes with the address and store the 32 bytes back to memory with mstore. | |
- | |
- // 1. Add index to address of bytes array | |
- // 2. Load 32-byte word from memory | |
- // 3. Apply 12-byte mask to obtain extra bytes occupying word of memory where we'll store the address | |
- let neighbors := and( | |
- mload(add(b, index)), | |
- 0xffffffffffffffffffffffff0000000000000000000000000000000000000000 | |
- ) | |
- | |
- // Make sure input address is clean. | |
- // (Solidity does not guarantee this) | |
- input := and(input, 0xffffffffffffffffffffffffffffffffffffffff) | |
- | |
- // Store the neighbors and address into memory | |
- mstore(add(b, index), xor(input, neighbors)) | |
- } | |
- } | |
- | |
- /// @dev Reads a bytes32 value from a position in a byte array. | |
- /// @param b Byte array containing a bytes32 value. | |
- /// @param index Index in byte array of bytes32 value. | |
- /// @return bytes32 value from byte array. | |
- function readBytes32( | |
- bytes memory b, | |
- uint256 index | |
- ) | |
- internal | |
- pure | |
- returns (bytes32 result) | |
- { | |
- require( | |
- b.length >= index + 32, | |
- "GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED" | |
- ); | |
- | |
- // Arrays are prefixed by a 256 bit length parameter | |
- index += 32; | |
- | |
- // Read the bytes32 from array memory | |
- assembly { | |
- result := mload(add(b, index)) | |
- } | |
- return result; | |
- } | |
- | |
- /// @dev Writes a bytes32 into a specific position in a byte array. | |
- /// @param b Byte array to insert <input> into. | |
- /// @param index Index in byte array of <input>. | |
- /// @param input bytes32 to put into byte array. | |
- function writeBytes32( | |
- bytes memory b, | |
- uint256 index, | |
- bytes32 input | |
- ) | |
- internal | |
- pure | |
- { | |
- require( | |
- b.length >= index + 32, | |
- "GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED" | |
- ); | |
- | |
- // Arrays are prefixed by a 256 bit length parameter | |
- index += 32; | |
- | |
- // Read the bytes32 from array memory | |
- assembly { | |
- mstore(add(b, index), input) | |
- } | |
- } | |
- | |
- /// @dev Reads a uint256 value from a position in a byte array. | |
- /// @param b Byte array containing a uint256 value. | |
- /// @param index Index in byte array of uint256 value. | |
- /// @return uint256 value from byte array. | |
- function readUint256( | |
- bytes memory b, | |
- uint256 index | |
- ) | |
- internal | |
- pure | |
- returns (uint256 result) | |
- { | |
- result = uint256(readBytes32(b, index)); | |
- return result; | |
- } | |
- | |
- /// @dev Writes a uint256 into a specific position in a byte array. | |
- /// @param b Byte array to insert <input> into. | |
- /// @param index Index in byte array of <input>. | |
- /// @param input uint256 to put into byte array. | |
- function writeUint256( | |
- bytes memory b, | |
- uint256 index, | |
- uint256 input | |
- ) | |
- internal | |
- pure | |
- { | |
- writeBytes32(b, index, bytes32(input)); | |
- } | |
- | |
- /// @dev Reads an unpadded bytes4 value from a position in a byte array. | |
- /// @param b Byte array containing a bytes4 value. | |
- /// @param index Index in byte array of bytes4 value. | |
- /// @return bytes4 value from byte array. | |
- function readBytes4( | |
- bytes memory b, | |
- uint256 index | |
- ) | |
- internal | |
- pure | |
- returns (bytes4 result) | |
- { | |
- require( | |
- b.length >= index + 4, | |
- "GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED" | |
- ); | |
- | |
- // Arrays are prefixed by a 32 byte length field | |
- index += 32; | |
- | |
- // Read the bytes4 from array memory | |
- assembly { | |
- result := mload(add(b, index)) | |
- // Solidity does not require us to clean the trailing bytes. | |
- // We do it anyway | |
- result := and(result, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000) | |
- } | |
- return result; | |
- } | |
- | |
- /// @dev Reads nested bytes from a specific position. | |
- /// @dev NOTE: the returned value overlaps with the input value. | |
- /// Both should be treated as immutable. | |
- /// @param b Byte array containing nested bytes. | |
- /// @param index Index of nested bytes. | |
- /// @return result Nested bytes. | |
- function readBytesWithLength( | |
- bytes memory b, | |
- uint256 index | |
- ) | |
- internal | |
- pure | |
- returns (bytes memory result) | |
- { | |
- // Read length of nested bytes | |
- uint256 nestedBytesLength = readUint256(b, index); | |
- index += 32; | |
- | |
- // Assert length of <b> is valid, given | |
- // length of nested bytes | |
- require( | |
- b.length >= index + nestedBytesLength, | |
- "GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED" | |
- ); | |
- | |
- // Return a pointer to the byte array as it exists inside `b` | |
- assembly { | |
- result := add(b, index) | |
- } | |
- return result; | |
- } | |
- | |
- /// @dev Inserts bytes at a specific position in a byte array. | |
- /// @param b Byte array to insert <input> into. | |
- /// @param index Index in byte array of <input>. | |
- /// @param input bytes to insert. | |
- function writeBytesWithLength( | |
- bytes memory b, | |
- uint256 index, | |
- bytes memory input | |
- ) | |
- internal | |
- pure | |
- { | |
- // Assert length of <b> is valid, given | |
- // length of input | |
- require( | |
- b.length >= index + 32 + input.length, // 32 bytes to store length | |
- "GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED" | |
- ); | |
- | |
- // Copy <input> into <b> | |
- memCopy( | |
- b.contentAddress() + index, | |
- input.rawAddress(), // includes length of <input> | |
- input.length + 32 // +32 bytes to store <input> length | |
- ); | |
- } | |
- | |
- /// @dev Performs a deep copy of a byte array onto another byte array of greater than or equal length. | |
- /// @param dest Byte array that will be overwritten with source bytes. | |
- /// @param source Byte array to copy onto dest bytes. | |
- function deepCopyBytes( | |
- bytes memory dest, | |
- bytes memory source | |
- ) | |
- internal | |
- pure | |
- { | |
- uint256 sourceLen = source.length; | |
- // Dest length must be >= source length, or some bytes would not be copied. | |
- require( | |
- dest.length >= sourceLen, | |
- "GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED" | |
- ); | |
- memCopy( | |
- dest.contentAddress(), | |
- source.contentAddress(), | |
- sourceLen | |
- ); | |
- } | |
-} | |
- | |
-contract ReentrancyGuard { | |
- | |
- // Locked state of mutex | |
- bool private locked = false; | |
- | |
- /// @dev Functions with this modifer cannot be reentered. The mutex will be locked | |
- /// before function execution and unlocked after. | |
- modifier nonReentrant() { | |
- // Ensure mutex is unlocked | |
- require( | |
- !locked, | |
- "REENTRANCY_ILLEGAL" | |
- ); | |
- | |
- // Lock mutex before function call | |
- locked = true; | |
- | |
- // Perform function call | |
- _; | |
- | |
- // Unlock mutex after function call | |
- locked = false; | |
- } | |
-} | |
- | |
-contract SafeMathClone { | |
- | |
- function safeMul(uint256 a, uint256 b) | |
- internal | |
- pure | |
- returns (uint256) | |
- { | |
- if (a == 0) { | |
- return 0; | |
- } | |
- uint256 c = a * b; | |
- require( | |
- c / a == b, | |
- "UINT256_OVERFLOW" | |
- ); | |
- return c; | |
- } | |
- | |
- function safeDiv(uint256 a, uint256 b) | |
- internal | |
- pure | |
- returns (uint256) | |
- { | |
- uint256 c = a / b; | |
- return c; | |
- } | |
- | |
- function safeSub(uint256 a, uint256 b) | |
- internal | |
- pure | |
- returns (uint256) | |
- { | |
- require( | |
- b <= a, | |
- "UINT256_UNDERFLOW" | |
- ); | |
- return a - b; | |
- } | |
- | |
- function safeAdd(uint256 a, uint256 b) | |
- internal | |
- pure | |
- returns (uint256) | |
- { | |
- uint256 c = a + b; | |
- require( | |
- c >= a, | |
- "UINT256_OVERFLOW" | |
- ); | |
- return c; | |
- } | |
- | |
- function max64(uint64 a, uint64 b) | |
- internal | |
- pure | |
- returns (uint256) | |
- { | |
- return a >= b ? a : b; | |
- } | |
- | |
- function min64(uint64 a, uint64 b) | |
- internal | |
- pure | |
- returns (uint256) | |
- { | |
- return a < b ? a : b; | |
- } | |
- | |
- function max256(uint256 a, uint256 b) | |
- internal | |
- pure | |
- returns (uint256) | |
- { | |
- return a >= b ? a : b; | |
- } | |
- | |
- function min256(uint256 a, uint256 b) | |
- internal | |
- pure | |
- returns (uint256) | |
- { | |
- return a < b ? a : b; | |
- } | |
-} | |
- | |
-// solhint-disable max-line-length | |
-contract LibConstants { | |
- | |
- // Asset data for ZRX token. Used for fee transfers. | |
- | |
- // The proxyId for ZRX_ASSET_DATA is bytes4(keccak256("ERC20Token(address)")) = 0xf47261b0 | |
- | |
- // Kovan ZRX address is 0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570. | |
- // The ABI encoded proxyId and address is 0xf47261b00000000000000000000000006ff6c0ff1d68b964901f986d4c9fa3ac68346570 | |
- // bytes constant public ZRX_ASSET_DATA = "\xf4\x72\x61\xb0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x6f\xf6\xc0\xff\x1d\x68\xb9\x64\x90\x1f\x98\x6d\x4c\x9f\xa3\xac\x68\x34\x65\x70"; | |
- | |
- // Mainnet ZRX address is 0xe41d2489571d322189246dafa5ebde1f4699f498. | |
- // The ABI encoded proxyId and address is 0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498 | |
- bytes public ZRX_ASSET_DATA = "\xf4\x72\x61\xb0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe4\x1d\x24\x89\x57\x1d\x32\x21\x89\x24\x6d\xaf\xa5\xeb\xde\x1f\x46\x99\xf4\x98"; | |
- | |
- // For easier testing | |
- function changeZRXAssetData(bytes newData) public { | |
- ZRX_ASSET_DATA = newData; | |
- } | |
-} | |
-// solhint-enable max-line-length | |
- | |
-contract LibFillResults is | |
- SafeMathClone | |
-{ | |
- struct FillResults { | |
- uint256 makerAssetFilledAmount; // Total amount of makerAsset(s) filled. | |
- uint256 takerAssetFilledAmount; // Total amount of takerAsset(s) filled. | |
- uint256 makerFeePaid; // Total amount of ZRX paid by maker(s) to feeRecipient(s). | |
- uint256 takerFeePaid; // Total amount of ZRX paid by taker to feeRecipients(s). | |
- } | |
- | |
- struct MatchedFillResults { | |
- FillResults left; // Amounts filled and fees paid of left order. | |
- FillResults right; // Amounts filled and fees paid of right order. | |
- uint256 leftMakerAssetSpreadAmount; // Spread between price of left and right order, denominated in the left order's makerAsset, paid to taker. | |
- } | |
- | |
- /// @dev Adds properties of both FillResults instances. | |
- /// Modifies the first FillResults instance specified. | |
- /// @param totalFillResults Fill results instance that will be added onto. | |
- /// @param singleFillResults Fill results instance that will be added to totalFillResults. | |
- function addFillResults(FillResults memory totalFillResults, FillResults memory singleFillResults) | |
- internal | |
- pure | |
- { | |
- totalFillResults.makerAssetFilledAmount = safeAdd(totalFillResults.makerAssetFilledAmount, singleFillResults.makerAssetFilledAmount); | |
- totalFillResults.takerAssetFilledAmount = safeAdd(totalFillResults.takerAssetFilledAmount, singleFillResults.takerAssetFilledAmount); | |
- totalFillResults.makerFeePaid = safeAdd(totalFillResults.makerFeePaid, singleFillResults.makerFeePaid); | |
- totalFillResults.takerFeePaid = safeAdd(totalFillResults.takerFeePaid, singleFillResults.takerFeePaid); | |
- } | |
-} | |
- | |
-contract LibMath is | |
- SafeMathClone | |
-{ | |
- /// @dev Calculates partial value given a numerator and denominator rounded down. | |
- /// Reverts if rounding error is >= 0.1% | |
- /// @param numerator Numerator. | |
- /// @param denominator Denominator. | |
- /// @param target Value to calculate partial of. | |
- /// @return Partial value of target rounded down. | |
- function safeGetPartialAmountFloor( | |
- uint256 numerator, | |
- uint256 denominator, | |
- uint256 target | |
- ) | |
- internal | |
- pure | |
- returns (uint256 partialAmount) | |
- { | |
- require( | |
- denominator > 0, | |
- "DIVISION_BY_ZERO" | |
- ); | |
- | |
- require( | |
- !isRoundingErrorFloor( | |
- numerator, | |
- denominator, | |
- target | |
- ), | |
- "ROUNDING_ERROR" | |
- ); | |
- | |
- partialAmount = safeDiv( | |
- safeMul(numerator, target), | |
- denominator | |
- ); | |
- return partialAmount; | |
- } | |
- | |
- /// @dev Calculates partial value given a numerator and denominator rounded down. | |
- /// Reverts if rounding error is >= 0.1% | |
- /// @param numerator Numerator. | |
- /// @param denominator Denominator. | |
- /// @param target Value to calculate partial of. | |
- /// @return Partial value of target rounded up. | |
- function safeGetPartialAmountCeil( | |
- uint256 numerator, | |
- uint256 denominator, | |
- uint256 target | |
- ) | |
- internal | |
- pure | |
- returns (uint256 partialAmount) | |
- { | |
- require( | |
- denominator > 0, | |
- "DIVISION_BY_ZERO" | |
- ); | |
- | |
- require( | |
- !isRoundingErrorCeil( | |
- numerator, | |
- denominator, | |
- target | |
- ), | |
- "ROUNDING_ERROR" | |
- ); | |
- | |
- // safeDiv computes `floor(a / b)`. We use the identity (a, b integer): | |
- // ceil(a / b) = floor((a + b - 1) / b) | |
- // To implement `ceil(a / b)` using safeDiv. | |
- partialAmount = safeDiv( | |
- safeAdd( | |
- safeMul(numerator, target), | |
- safeSub(denominator, 1) | |
- ), | |
- denominator | |
- ); | |
- return partialAmount; | |
- } | |
- | |
- /// @dev Calculates partial value given a numerator and denominator rounded down. | |
- /// @param numerator Numerator. | |
- /// @param denominator Denominator. | |
- /// @param target Value to calculate partial of. | |
- /// @return Partial value of target rounded down. | |
- function getPartialAmountFloor( | |
- uint256 numerator, | |
- uint256 denominator, | |
- uint256 target | |
- ) | |
- internal | |
- pure | |
- returns (uint256 partialAmount) | |
- { | |
- require( | |
- denominator > 0, | |
- "DIVISION_BY_ZERO" | |
- ); | |
- | |
- partialAmount = safeDiv( | |
- safeMul(numerator, target), | |
- denominator | |
- ); | |
- return partialAmount; | |
- } | |
- | |
- /// @dev Calculates partial value given a numerator and denominator rounded down. | |
- /// @param numerator Numerator. | |
- /// @param denominator Denominator. | |
- /// @param target Value to calculate partial of. | |
- /// @return Partial value of target rounded up. | |
- function getPartialAmountCeil( | |
- uint256 numerator, | |
- uint256 denominator, | |
- uint256 target | |
- ) | |
- internal | |
- pure | |
- returns (uint256 partialAmount) | |
- { | |
- require( | |
- denominator > 0, | |
- "DIVISION_BY_ZERO" | |
- ); | |
- | |
- // safeDiv computes `floor(a / b)`. We use the identity (a, b integer): | |
- // ceil(a / b) = floor((a + b - 1) / b) | |
- // To implement `ceil(a / b)` using safeDiv. | |
- partialAmount = safeDiv( | |
- safeAdd( | |
- safeMul(numerator, target), | |
- safeSub(denominator, 1) | |
- ), | |
- denominator | |
- ); | |
- return partialAmount; | |
- } | |
- | |
- /// @dev Checks if rounding error >= 0.1% when rounding down. | |
- /// @param numerator Numerator. | |
- /// @param denominator Denominator. | |
- /// @param target Value to multiply with numerator/denominator. | |
- /// @return Rounding error is present. | |
- function isRoundingErrorFloor( | |
- uint256 numerator, | |
- uint256 denominator, | |
- uint256 target | |
- ) | |
- internal | |
- pure | |
- returns (bool isError) | |
- { | |
- require( | |
- denominator > 0, | |
- "DIVISION_BY_ZERO" | |
- ); | |
- | |
- // The absolute rounding error is the difference between the rounded | |
- // value and the ideal value. The relative rounding error is the | |
- // absolute rounding error divided by the absolute value of the | |
- // ideal value. This is undefined when the ideal value is zero. | |
- // | |
- // The ideal value is `numerator * target / denominator`. | |
- // Let's call `numerator * target % denominator` the remainder. | |
- // The absolute error is `remainder / denominator`. | |
- // | |
- // When the ideal value is zero, we require the absolute error to | |
- // be zero. Fortunately, this is always the case. The ideal value is | |
- // zero iff `numerator == 0` and/or `target == 0`. In this case the | |
- // remainder and absolute error are also zero. | |
- if (target == 0 || numerator == 0) { | |
- return false; | |
- } | |
- | |
- // Otherwise, we want the relative rounding error to be strictly | |
- // less than 0.1%. | |
- // The relative error is `remainder / (numerator * target)`. | |
- // We want the relative error less than 1 / 1000: | |
- // remainder / (numerator * denominator) < 1 / 1000 | |
- // or equivalently: | |
- // 1000 * remainder < numerator * target | |
- // so we have a rounding error iff: | |
- // 1000 * remainder >= numerator * target | |
- uint256 remainder = mulmod( | |
- target, | |
- numerator, | |
- denominator | |
- ); | |
- isError = safeMul(1000, remainder) >= safeMul(numerator, target); | |
- return isError; | |
- } | |
- | |
- /// @dev Checks if rounding error >= 0.1% when rounding up. | |
- /// @param numerator Numerator. | |
- /// @param denominator Denominator. | |
- /// @param target Value to multiply with numerator/denominator. | |
- /// @return Rounding error is present. | |
- function isRoundingErrorCeil( | |
- uint256 numerator, | |
- uint256 denominator, | |
- uint256 target | |
- ) | |
- internal | |
- pure | |
- returns (bool isError) | |
- { | |
- require( | |
- denominator > 0, | |
- "DIVISION_BY_ZERO" | |
- ); | |
- | |
- // See the comments in `isRoundingError`. | |
- if (target == 0 || numerator == 0) { | |
- // When either is zero, the ideal value and rounded value are zero | |
- // and there is no rounding error. (Although the relative error | |
- // is undefined.) | |
- return false; | |
- } | |
- // Compute remainder as before | |
- uint256 remainder = mulmod( | |
- target, | |
- numerator, | |
- denominator | |
- ); | |
- remainder = safeSub(denominator, remainder) % denominator; | |
- isError = safeMul(1000, remainder) >= safeMul(numerator, target); | |
- return isError; | |
- } | |
-} | |
- | |
-contract LibAbiEncoder { | |
- | |
- /// @dev ABI encodes calldata for `fillOrder`. | |
- /// @param order Order struct containing order specifications. | |
- /// @param takerAssetFillAmount Desired amount of takerAsset to sell. | |
- /// @param signature Proof that order has been created by maker. | |
- /// @return ABI encoded calldata for `fillOrder`. | |
- function abiEncodeFillOrder( | |
- LibOrder.Order memory order, | |
- uint256 takerAssetFillAmount, | |
- bytes memory signature | |
- ) | |
- internal | |
- pure | |
- returns (bytes memory fillOrderCalldata) | |
- { | |
- // We need to call MExchangeCore.fillOrder using a delegatecall in | |
- // assembly so that we can intercept a call that throws. For this, we | |
- // need the input encoded in memory in the Ethereum ABIv2 format [1]. | |
- | |
- // | Area | Offset | Length | Contents | | |
- // | -------- |--------|---------|-------------------------------------------- | | |
- // | Header | 0x00 | 4 | function selector | | |
- // | Params | | 3 * 32 | function parameters: | | |
- // | | 0x00 | | 1. offset to order (*) | | |
- // | | 0x20 | | 2. takerAssetFillAmount | | |
- // | | 0x40 | | 3. offset to signature (*) | | |
- // | Data | | 12 * 32 | order: | | |
- // | | 0x000 | | 1. senderAddress | | |
- // | | 0x020 | | 2. makerAddress | | |
- // | | 0x040 | | 3. takerAddress | | |
- // | | 0x060 | | 4. feeRecipientAddress | | |
- // | | 0x080 | | 5. makerAssetAmount | | |
- // | | 0x0A0 | | 6. takerAssetAmount | | |
- // | | 0x0C0 | | 7. makerFeeAmount | | |
- // | | 0x0E0 | | 8. takerFeeAmount | | |
- // | | 0x100 | | 9. expirationTimeSeconds | | |
- // | | 0x120 | | 10. salt | | |
- // | | 0x140 | | 11. Offset to makerAssetData (*) | | |
- // | | 0x160 | | 12. Offset to takerAssetData (*) | | |
- // | | 0x180 | 32 | makerAssetData Length | | |
- // | | 0x1A0 | ** | makerAssetData Contents | | |
- // | | 0x1C0 | 32 | takerAssetData Length | | |
- // | | 0x1E0 | ** | takerAssetData Contents | | |
- // | | 0x200 | 32 | signature Length | | |
- // | | 0x220 | ** | signature Contents | | |
- | |
- // * Offsets are calculated from the beginning of the current area: Header, Params, Data: | |
- // An offset stored in the Params area is calculated from the beginning of the Params section. | |
- // An offset stored in the Data area is calculated from the beginning of the Data section. | |
- | |
- // ** The length of dynamic array contents are stored in the field immediately preceeding the contents. | |
- | |
- // [1]: https://solidity.readthedocs.io/en/develop/abi-spec.html | |
- | |
- assembly { | |
- | |
- // Areas below may use the following variables: | |
- // 1. <area>Start -- Start of this area in memory | |
- // 2. <area>End -- End of this area in memory. This value may | |
- // be precomputed (before writing contents), | |
- // or it may be computed as contents are written. | |
- // 3. <area>Offset -- Current offset into area. If an area's End | |
- // is precomputed, this variable tracks the | |
- // offsets of contents as they are written. | |
- | |
- /////// Setup Header Area /////// | |
- // Load free memory pointer | |
- fillOrderCalldata := mload(0x40) | |
- // bytes4(keccak256("fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)")) | |
- // = 0xb4be83d5 | |
- // Leave 0x20 bytes to store the length | |
- mstore(add(fillOrderCalldata, 0x20), 0xb4be83d500000000000000000000000000000000000000000000000000000000) | |
- let headerAreaEnd := add(fillOrderCalldata, 0x24) | |
- | |
- /////// Setup Params Area /////// | |
- // This area is preallocated and written to later. | |
- // This is because we need to fill in offsets that have not yet been calculated. | |
- let paramsAreaStart := headerAreaEnd | |
- let paramsAreaEnd := add(paramsAreaStart, 0x60) | |
- let paramsAreaOffset := paramsAreaStart | |
- | |
- /////// Setup Data Area /////// | |
- let dataAreaStart := paramsAreaEnd | |
- let dataAreaEnd := dataAreaStart | |
- | |
- // Offset from the source data we're reading from | |
- let sourceOffset := order | |
- // arrayLenBytes and arrayLenWords track the length of a dynamically-allocated bytes array. | |
- let arrayLenBytes := 0 | |
- let arrayLenWords := 0 | |
- | |
- /////// Write order Struct /////// | |
- // Write memory location of Order, relative to the start of the | |
- // parameter list, then increment the paramsAreaOffset respectively. | |
- mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart)) | |
- paramsAreaOffset := add(paramsAreaOffset, 0x20) | |
- | |
- // Write values for each field in the order | |
- // It would be nice to use a loop, but we save on gas by writing | |
- // the stores sequentially. | |
- mstore(dataAreaEnd, mload(sourceOffset)) // makerAddress | |
- mstore(add(dataAreaEnd, 0x20), mload(add(sourceOffset, 0x20))) // takerAddress | |
- mstore(add(dataAreaEnd, 0x40), mload(add(sourceOffset, 0x40))) // feeRecipientAddress | |
- mstore(add(dataAreaEnd, 0x60), mload(add(sourceOffset, 0x60))) // senderAddress | |
- mstore(add(dataAreaEnd, 0x80), mload(add(sourceOffset, 0x80))) // makerAssetAmount | |
- mstore(add(dataAreaEnd, 0xA0), mload(add(sourceOffset, 0xA0))) // takerAssetAmount | |
- mstore(add(dataAreaEnd, 0xC0), mload(add(sourceOffset, 0xC0))) // makerFeeAmount | |
- mstore(add(dataAreaEnd, 0xE0), mload(add(sourceOffset, 0xE0))) // takerFeeAmount | |
- mstore(add(dataAreaEnd, 0x100), mload(add(sourceOffset, 0x100))) // expirationTimeSeconds | |
- mstore(add(dataAreaEnd, 0x120), mload(add(sourceOffset, 0x120))) // salt | |
- mstore(add(dataAreaEnd, 0x140), mload(add(sourceOffset, 0x140))) // Offset to makerAssetData | |
- mstore(add(dataAreaEnd, 0x160), mload(add(sourceOffset, 0x160))) // Offset to takerAssetData | |
- dataAreaEnd := add(dataAreaEnd, 0x180) | |
- sourceOffset := add(sourceOffset, 0x180) | |
- | |
- // Write offset to <order.makerAssetData> | |
- mstore(add(dataAreaStart, mul(10, 0x20)), sub(dataAreaEnd, dataAreaStart)) | |
- | |
- // Calculate length of <order.makerAssetData> | |
- sourceOffset := mload(add(order, 0x140)) // makerAssetData | |
- arrayLenBytes := mload(sourceOffset) | |
- sourceOffset := add(sourceOffset, 0x20) | |
- arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20) | |
- | |
- // Write length of <order.makerAssetData> | |
- mstore(dataAreaEnd, arrayLenBytes) | |
- dataAreaEnd := add(dataAreaEnd, 0x20) | |
- | |
- // Write contents of <order.makerAssetData> | |
- for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} { | |
- mstore(dataAreaEnd, mload(sourceOffset)) | |
- dataAreaEnd := add(dataAreaEnd, 0x20) | |
- sourceOffset := add(sourceOffset, 0x20) | |
- } | |
- | |
- // Write offset to <order.takerAssetData> | |
- mstore(add(dataAreaStart, mul(11, 0x20)), sub(dataAreaEnd, dataAreaStart)) | |
- | |
- // Calculate length of <order.takerAssetData> | |
- sourceOffset := mload(add(order, 0x160)) // takerAssetData | |
- arrayLenBytes := mload(sourceOffset) | |
- sourceOffset := add(sourceOffset, 0x20) | |
- arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20) | |
- | |
- // Write length of <order.takerAssetData> | |
- mstore(dataAreaEnd, arrayLenBytes) | |
- dataAreaEnd := add(dataAreaEnd, 0x20) | |
- | |
- // Write contents of <order.takerAssetData> | |
- for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} { | |
- mstore(dataAreaEnd, mload(sourceOffset)) | |
- dataAreaEnd := add(dataAreaEnd, 0x20) | |
- sourceOffset := add(sourceOffset, 0x20) | |
- } | |
- | |
- /////// Write takerAssetFillAmount /////// | |
- mstore(paramsAreaOffset, takerAssetFillAmount) | |
- paramsAreaOffset := add(paramsAreaOffset, 0x20) | |
- | |
- /////// Write signature /////// | |
- // Write offset to paramsArea | |
- mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart)) | |
- | |
- // Calculate length of signature | |
- sourceOffset := signature | |
- arrayLenBytes := mload(sourceOffset) | |
- sourceOffset := add(sourceOffset, 0x20) | |
- arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20) | |
- | |
- // Write length of signature | |
- mstore(dataAreaEnd, arrayLenBytes) | |
- dataAreaEnd := add(dataAreaEnd, 0x20) | |
- | |
- // Write contents of signature | |
- for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} { | |
- mstore(dataAreaEnd, mload(sourceOffset)) | |
- dataAreaEnd := add(dataAreaEnd, 0x20) | |
- sourceOffset := add(sourceOffset, 0x20) | |
- } | |
- | |
- // Set length of calldata | |
- mstore(fillOrderCalldata, sub(dataAreaEnd, add(fillOrderCalldata, 0x20))) | |
- | |
- // Increment free memory pointer | |
- mstore(0x40, dataAreaEnd) | |
- } | |
- | |
- return fillOrderCalldata; | |
- } | |
-} | |
- | |
-contract IAuthorizable is | |
- IOwnable | |
-{ | |
- /// @dev Authorizes an address. | |
- /// @param target Address to authorize. | |
- function addAuthorizedAddress(address target) | |
- external; | |
- | |
- /// @dev Removes authorizion of an address. | |
- /// @param target Address to remove authorization from. | |
- function removeAuthorizedAddress(address target) | |
- external; | |
- | |
- /// @dev Removes authorizion of an address. | |
- /// @param target Address to remove authorization from. | |
- /// @param index Index of target in authorities array. | |
- function removeAuthorizedAddressAtIndex( | |
- address target, | |
- uint256 index | |
- ) | |
- external; | |
- | |
- /// @dev Gets all authorized addresses. | |
- /// @return Array of authorized addresses. | |
- function getAuthorizedAddresses() | |
- external | |
- view | |
- returns (address[] memory); | |
-} | |
- | |
-contract IAssetProxy is | |
- IAuthorizable | |
-{ | |
- /// @dev Transfers assets. Either succeeds or throws. | |
- /// @param assetData Byte array encoded for the respective asset proxy. | |
- /// @param from Address to transfer asset from. | |
- /// @param to Address to transfer asset to. | |
- /// @param amount Amount of asset to transfer. | |
- function transferFrom( | |
- bytes assetData, | |
- address from, | |
- address to, | |
- uint256 amount | |
- ) | |
- external; | |
- | |
- /// @dev Gets the proxy id associated with the proxy address. | |
- /// @return Proxy id. | |
- function getProxyId() | |
- external | |
- pure | |
- returns (bytes4); | |
-} | |
- | |
-contract IValidator { | |
- | |
- /// @dev Verifies that a signature is valid. | |
- /// @param hash Message hash that is signed. | |
- /// @param signerAddress Address that should have signed the given hash. | |
- /// @param signature Proof of signing. | |
- /// @return Validity of order signature. | |
- function isValidSignature( | |
- bytes32 hash, | |
- address signerAddress, | |
- bytes signature | |
- ) | |
- external | |
- view | |
- returns (bool isValid); | |
-} | |
- | |
-contract IWallet { | |
- | |
- /// @dev Verifies that a signature is valid. | |
- /// @param hash Message hash that is signed. | |
- /// @param signature Proof of signing. | |
- /// @return Validity of order signature. | |
- function isValidSignature( | |
- bytes32 hash, | |
- bytes signature | |
- ) | |
- external | |
- view | |
- returns (bool isValid); | |
-} | |
- | |
-contract IExchangeCore { | |
- | |
- /// @dev Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch | |
- /// and senderAddress equal to msg.sender (or null address if msg.sender == makerAddress). | |
- /// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled. | |
- function cancelOrdersUpTo(uint256 targetOrderEpoch) | |
- external; | |
- | |
- /// @dev Fills the input order. | |
- /// @param order Order struct containing order specifications. | |
- /// @param takerAssetFillAmount Desired amount of takerAsset to sell. | |
- /// @param signature Proof that order has been created by maker. | |
- /// @return Amounts filled and fees paid by maker and taker. | |
- function fillOrder( | |
- LibOrder.Order memory order, | |
- uint256 takerAssetFillAmount, | |
- bytes memory signature | |
- ) | |
- public | |
- returns (LibFillResults.FillResults memory fillResults); | |
- | |
- /// @dev After calling, the order can not be filled anymore. | |
- /// @param order Order struct containing order specifications. | |
- function cancelOrder(LibOrder.Order memory order) | |
- public; | |
- | |
- /// @dev Gets information about an order: status, hash, and amount filled. | |
- /// @param order Order to gather information on. | |
- /// @return OrderInfo Information about the order and its state. | |
- /// See LibOrder.OrderInfo for a complete description. | |
- function getOrderInfo(LibOrder.Order memory order) | |
- public | |
- view | |
- returns (LibOrder.OrderInfo memory orderInfo); | |
-} | |
- | |
-contract IAssetProxyDispatcher { | |
- | |
- /// @dev Registers an asset proxy to its asset proxy id. | |
- /// Once an asset proxy is registered, it cannot be unregistered. | |
- /// @param assetProxy Address of new asset proxy to register. | |
- function registerAssetProxy(address assetProxy) | |
- external; | |
- | |
- /// @dev Gets an asset proxy. | |
- /// @param assetProxyId Id of the asset proxy. | |
- /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered. | |
- function getAssetProxy(bytes4 assetProxyId) | |
- external | |
- view | |
- returns (address); | |
-} | |
- | |
-contract IMatchOrders { | |
- | |
- /// @dev Match two complementary orders that have a profitable spread. | |
- /// Each order is filled at their respective price point. However, the calculations are | |
- /// carried out as though the orders are both being filled at the right order's price point. | |
- /// The profit made by the left order goes to the taker (who matched the two orders). | |
- /// @param leftOrder First order to match. | |
- /// @param rightOrder Second order to match. | |
- /// @param leftSignature Proof that order was created by the left maker. | |
- /// @param rightSignature Proof that order was created by the right maker. | |
- /// @return matchedFillResults Amounts filled and fees paid by maker and taker of matched orders. | |
- function matchOrders( | |
- LibOrder.Order memory leftOrder, | |
- LibOrder.Order memory rightOrder, | |
- bytes memory leftSignature, | |
- bytes memory rightSignature | |
- ) | |
- public | |
- returns (LibFillResults.MatchedFillResults memory matchedFillResults); | |
-} | |
- | |
-contract ISignatureValidator { | |
- | |
- /// @dev Approves a hash on-chain using any valid signature type. | |
- /// After presigning a hash, the preSign signature type will become valid for that hash and signer. | |
- /// @param signerAddress Address that should have signed the given hash. | |
- /// @param signature Proof that the hash has been signed by signer. | |
- function preSign( | |
- bytes32 hash, | |
- address signerAddress, | |
- bytes signature | |
- ) | |
- external; | |
- | |
- /// @dev Approves/unnapproves a Validator contract to verify signatures on signer's behalf. | |
- /// @param validatorAddress Address of Validator contract. | |
- /// @param approval Approval or disapproval of Validator contract. | |
- function setSignatureValidatorApproval( | |
- address validatorAddress, | |
- bool approval | |
- ) | |
- external; | |
- | |
- /// @dev Verifies that a signature is valid. | |
- /// @param hash Message hash that is signed. | |
- /// @param signerAddress Address of signer. | |
- /// @param signature Proof of signing. | |
- /// @return Validity of order signature. | |
- function isValidSignature( | |
- bytes32 hash, | |
- address signerAddress, | |
- bytes memory signature | |
- ) | |
- public | |
- view | |
- returns (bool isValid); | |
-} | |
- | |
-contract ITransactions { | |
- | |
- /// @dev Executes an exchange method call in the context of signer. | |
- /// @param salt Arbitrary number to ensure uniqueness of transaction hash. | |
- /// @param signerAddress Address of transaction signer. | |
- /// @param data AbiV2 encoded calldata. | |
- /// @param signature Proof of signer transaction by signer. | |
- function executeTransaction( | |
- uint256 salt, | |
- address signerAddress, | |
- bytes data, | |
- bytes signature | |
- ) | |
- external; | |
-} | |
- | |
-contract IWrapperFunctions { | |
- | |
- /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled. | |
- /// @param order LibOrder.Order struct containing order specifications. | |
- /// @param takerAssetFillAmount Desired amount of takerAsset to sell. | |
- /// @param signature Proof that order has been created by maker. | |
- function fillOrKillOrder( | |
- LibOrder.Order memory order, | |
- uint256 takerAssetFillAmount, | |
- bytes memory signature | |
- ) | |
- public | |
- returns (LibFillResults.FillResults memory fillResults); | |
- | |
- /// @dev Fills an order with specified parameters and ECDSA signature. | |
- /// Returns false if the transaction would otherwise revert. | |
- /// @param order LibOrder.Order struct containing order specifications. | |
- /// @param takerAssetFillAmount Desired amount of takerAsset to sell. | |
- /// @param signature Proof that order has been created by maker. | |
- /// @return Amounts filled and fees paid by maker and taker. | |
- function fillOrderNoThrow( | |
- LibOrder.Order memory order, | |
- uint256 takerAssetFillAmount, | |
- bytes memory signature | |
- ) | |
- public | |
- returns (LibFillResults.FillResults memory fillResults); | |
- | |
- /// @dev Synchronously executes multiple calls of fillOrder. | |
- /// @param orders Array of order specifications. | |
- /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders. | |
- /// @param signatures Proofs that orders have been created by makers. | |
- /// @return Amounts filled and fees paid by makers and taker. | |
- function batchFillOrders( | |
- LibOrder.Order[] memory orders, | |
- uint256[] memory takerAssetFillAmounts, | |
- bytes[] memory signatures | |
- ) | |
- public | |
- returns (LibFillResults.FillResults memory totalFillResults); | |
- | |
- /// @dev Synchronously executes multiple calls of fillOrKill. | |
- /// @param orders Array of order specifications. | |
- /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders. | |
- /// @param signatures Proofs that orders have been created by makers. | |
- /// @return Amounts filled and fees paid by makers and taker. | |
- function batchFillOrKillOrders( | |
- LibOrder.Order[] memory orders, | |
- uint256[] memory takerAssetFillAmounts, | |
- bytes[] memory signatures | |
- ) | |
- public | |
- returns (LibFillResults.FillResults memory totalFillResults); | |
- | |
- /// @dev Fills an order with specified parameters and ECDSA signature. | |
- /// Returns false if the transaction would otherwise revert. | |
- /// @param orders Array of order specifications. | |
- /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders. | |
- /// @param signatures Proofs that orders have been created by makers. | |
- /// @return Amounts filled and fees paid by makers and taker. | |
- function batchFillOrdersNoThrow( | |
- LibOrder.Order[] memory orders, | |
- uint256[] memory takerAssetFillAmounts, | |
- bytes[] memory signatures | |
- ) | |
- public | |
- returns (LibFillResults.FillResults memory totalFillResults); | |
- | |
- /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker. | |
- /// @param orders Array of order specifications. | |
- /// @param takerAssetFillAmount Desired amount of takerAsset to sell. | |
- /// @param signatures Proofs that orders have been created by makers. | |
- /// @return Amounts filled and fees paid by makers and taker. | |
- function marketSellOrders( | |
- LibOrder.Order[] memory orders, | |
- uint256 takerAssetFillAmount, | |
- bytes[] memory signatures | |
- ) | |
- public | |
- returns (LibFillResults.FillResults memory totalFillResults); | |
- | |
- /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker. | |
- /// Returns false if the transaction would otherwise revert. | |
- /// @param orders Array of order specifications. | |
- /// @param takerAssetFillAmount Desired amount of takerAsset to sell. | |
- /// @param signatures Proofs that orders have been signed by makers. | |
- /// @return Amounts filled and fees paid by makers and taker. | |
- function marketSellOrdersNoThrow( | |
- LibOrder.Order[] memory orders, | |
- uint256 takerAssetFillAmount, | |
- bytes[] memory signatures | |
- ) | |
- public | |
- returns (LibFillResults.FillResults memory totalFillResults); | |
- | |
- /// @dev Synchronously executes multiple calls of fillOrder until total amount of makerAsset is bought by taker. | |
- /// @param orders Array of order specifications. | |
- /// @param makerAssetFillAmount Desired amount of makerAsset to buy. | |
- /// @param signatures Proofs that orders have been signed by makers. | |
- /// @return Amounts filled and fees paid by makers and taker. | |
- function marketBuyOrders( | |
- LibOrder.Order[] memory orders, | |
- uint256 makerAssetFillAmount, | |
- bytes[] memory signatures | |
- ) | |
- public | |
- returns (LibFillResults.FillResults memory totalFillResults); | |
- | |
- /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker. | |
- /// Returns false if the transaction would otherwise revert. | |
- /// @param orders Array of order specifications. | |
- /// @param makerAssetFillAmount Desired amount of makerAsset to buy. | |
- /// @param signatures Proofs that orders have been signed by makers. | |
- /// @return Amounts filled and fees paid by makers and taker. | |
- function marketBuyOrdersNoThrow( | |
- LibOrder.Order[] memory orders, | |
- uint256 makerAssetFillAmount, | |
- bytes[] memory signatures | |
- ) | |
- public | |
- returns (LibFillResults.FillResults memory totalFillResults); | |
- | |
- /// @dev Synchronously cancels multiple orders in a single transaction. | |
- /// @param orders Array of order specifications. | |
- function batchCancelOrders(LibOrder.Order[] memory orders) | |
- public; | |
- | |
- /// @dev Fetches information for all passed in orders | |
- /// @param orders Array of order specifications. | |
- /// @return Array of OrderInfo instances that correspond to each order. | |
- function getOrdersInfo(LibOrder.Order[] memory orders) | |
- public | |
- view | |
- returns (LibOrder.OrderInfo[] memory); | |
-} | |
- | |
-// solhint-disable no-empty-blocks | |
-contract IExchange is | |
- IExchangeCore, | |
- IMatchOrders, | |
- ISignatureValidator, | |
- ITransactions, | |
- IAssetProxyDispatcher, | |
- IWrapperFunctions | |
-{} | |
- | |
-contract MExchangeCore is | |
- IExchangeCore | |
-{ | |
- // Fill event is emitted whenever an order is filled. | |
- event Fill( | |
- address indexed makerAddress, // Address that created the order. | |
- address indexed feeRecipientAddress, // Address that received fees. | |
- address takerAddress, // Address that filled the order. | |
- address senderAddress, // Address that called the Exchange contract (msg.sender). | |
- uint256 makerAssetFilledAmount, // Amount of makerAsset sold by maker and bought by taker. | |
- uint256 takerAssetFilledAmount, // Amount of takerAsset sold by taker and bought by maker. | |
- uint256 makerFeePaid, // Amount of ZRX paid to feeRecipient by maker. | |
- uint256 takerFeePaid, // Amount of ZRX paid to feeRecipient by taker. | |
- bytes32 indexed orderHash, // EIP712 hash of order (see LibOrder.getOrderHash). | |
- bytes makerAssetData, // Encoded data specific to makerAsset. | |
- bytes takerAssetData // Encoded data specific to takerAsset. | |
- ); | |
- | |
- // Cancel event is emitted whenever an individual order is cancelled. | |
- event Cancel( | |
- address indexed makerAddress, // Address that created the order. | |
- address indexed feeRecipientAddress, // Address that would have recieved fees if order was filled. | |
- address senderAddress, // Address that called the Exchange contract (msg.sender). | |
- bytes32 indexed orderHash, // EIP712 hash of order (see LibOrder.getOrderHash). | |
- bytes makerAssetData, // Encoded data specific to makerAsset. | |
- bytes takerAssetData // Encoded data specific to takerAsset. | |
- ); | |
- | |
- // CancelUpTo event is emitted whenever `cancelOrdersUpTo` is executed succesfully. | |
- event CancelUpTo( | |
- address indexed makerAddress, // Orders cancelled must have been created by this address. | |
- address indexed senderAddress, // Orders cancelled must have a `senderAddress` equal to this address. | |
- uint256 orderEpoch // Orders with specified makerAddress and senderAddress with a salt less than this value are considered cancelled. | |
- ); | |
- | |
- /// @dev Fills the input order. | |
- /// @param order Order struct containing order specifications. | |
- /// @param takerAssetFillAmount Desired amount of takerAsset to sell. | |
- /// @param signature Proof that order has been created by maker. | |
- /// @return Amounts filled and fees paid by maker and taker. | |
- function fillOrderInternal( | |
- LibOrder.Order memory order, | |
- uint256 takerAssetFillAmount, | |
- bytes memory signature | |
- ) | |
- internal | |
- returns (LibFillResults.FillResults memory fillResults); | |
- | |
- /// @dev After calling, the order can not be filled anymore. | |
- /// @param order Order struct containing order specifications. | |
- function cancelOrderInternal(LibOrder.Order memory order) | |
- internal; | |
- | |
- /// @dev Updates state with results of a fill order. | |
- /// @param order that was filled. | |
- /// @param takerAddress Address of taker who filled the order. | |
- /// @param orderTakerAssetFilledAmount Amount of order already filled. | |
- /// @return fillResults Amounts filled and fees paid by maker and taker. | |
- function updateFilledState( | |
- LibOrder.Order memory order, | |
- address takerAddress, | |
- bytes32 orderHash, | |
- uint256 orderTakerAssetFilledAmount, | |
- LibFillResults.FillResults memory fillResults | |
- ) | |
- internal; | |
- | |
- /// @dev Updates state with results of cancelling an order. | |
- /// State is only updated if the order is currently fillable. | |
- /// Otherwise, updating state would have no effect. | |
- /// @param order that was cancelled. | |
- /// @param orderHash Hash of order that was cancelled. | |
- function updateCancelledState( | |
- LibOrder.Order memory order, | |
- bytes32 orderHash | |
- ) | |
- internal; | |
- | |
- /// @dev Validates context for fillOrder. Succeeds or throws. | |
- /// @param order to be filled. | |
- /// @param orderInfo OrderStatus, orderHash, and amount already filled of order. | |
- /// @param takerAddress Address of order taker. | |
- /// @param signature Proof that the orders was created by its maker. | |
- function assertFillableOrder( | |
- LibOrder.Order memory order, | |
- LibOrder.OrderInfo memory orderInfo, | |
- address takerAddress, | |
- bytes memory signature | |
- ) | |
- internal | |
- view; | |
- | |
- /// @dev Validates context for fillOrder. Succeeds or throws. | |
- /// @param order to be filled. | |
- /// @param orderInfo Status, orderHash, and amount already filled of order. | |
- /// @param takerAssetFillAmount Desired amount of order to fill by taker. | |
- /// @param takerAssetFilledAmount Amount of takerAsset that will be filled. | |
- /// @param makerAssetFilledAmount Amount of makerAsset that will be transfered. | |
- function assertValidFill( | |
- LibOrder.Order memory order, | |
- LibOrder.OrderInfo memory orderInfo, | |
- uint256 takerAssetFillAmount, | |
- uint256 takerAssetFilledAmount, | |
- uint256 makerAssetFilledAmount | |
- ) | |
- internal | |
- view; | |
- | |
- /// @dev Validates context for cancelOrder. Succeeds or throws. | |
- /// @param order to be cancelled. | |
- /// @param orderInfo OrderStatus, orderHash, and amount already filled of order. | |
- function assertValidCancel( | |
- LibOrder.Order memory order, | |
- LibOrder.OrderInfo memory orderInfo | |
- ) | |
- internal | |
- view; | |
- | |
- /// @dev Calculates amounts filled and fees paid by maker and taker. | |
- /// @param order to be filled. | |
- /// @param takerAssetFilledAmount Amount of takerAsset that will be filled. | |
- /// @return fillResults Amounts filled and fees paid by maker and taker. | |
- function calculateFillResults( | |
- LibOrder.Order memory order, | |
- uint256 takerAssetFilledAmount | |
- ) | |
- internal | |
- pure | |
- returns (LibFillResults.FillResults memory fillResults); | |
- | |
-} | |
- | |
-contract MAssetProxyDispatcher is | |
- IAssetProxyDispatcher | |
-{ | |
- // Logs registration of new asset proxy | |
- event AssetProxyRegistered( | |
- bytes4 id, // Id of new registered AssetProxy. | |
- address assetProxy // Address of new registered AssetProxy. | |
- ); | |
- | |
- /// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws. | |
- /// @param assetData Byte array encoded for the asset. | |
- /// @param from Address to transfer token from. | |
- /// @param to Address to transfer token to. | |
- /// @param amount Amount of token to transfer. | |
- function dispatchTransferFrom( | |
- bytes memory assetData, | |
- address from, | |
- address to, | |
- uint256 amount | |
- ) | |
- internal; | |
-} | |
- | |
-contract MMatchOrders is | |
- IMatchOrders | |
-{ | |
- /// @dev Validates context for matchOrders. Succeeds or throws. | |
- /// @param leftOrder First order to match. | |
- /// @param rightOrder Second order to match. | |
- function assertValidMatch( | |
- LibOrder.Order memory leftOrder, | |
- LibOrder.Order memory rightOrder | |
- ) | |
- internal | |
- pure; | |
- | |
- /// @dev Calculates fill amounts for the matched orders. | |
- /// Each order is filled at their respective price point. However, the calculations are | |
- /// carried out as though the orders are both being filled at the right order's price point. | |
- /// The profit made by the leftOrder order goes to the taker (who matched the two orders). | |
- /// @param leftOrder First order to match. | |
- /// @param rightOrder Second order to match. | |
- /// @param leftOrderTakerAssetFilledAmount Amount of left order already filled. | |
- /// @param rightOrderTakerAssetFilledAmount Amount of right order already filled. | |
- /// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders. | |
- function calculateMatchedFillResults( | |
- LibOrder.Order memory leftOrder, | |
- LibOrder.Order memory rightOrder, | |
- uint256 leftOrderTakerAssetFilledAmount, | |
- uint256 rightOrderTakerAssetFilledAmount | |
- ) | |
- internal | |
- pure | |
- returns (LibFillResults.MatchedFillResults memory matchedFillResults); | |
- | |
-} | |
- | |
-contract MSignatureValidator is | |
- ISignatureValidator | |
-{ | |
- event SignatureValidatorApproval( | |
- address indexed signerAddress, // Address that approves or disapproves a contract to verify signatures. | |
- address indexed validatorAddress, // Address of signature validator contract. | |
- bool approved // Approval or disapproval of validator contract. | |
- ); | |
- | |
- // Allowed signature types. | |
- enum SignatureType { | |
- Illegal, // 0x00, default value | |
- Invalid, // 0x01 | |
- EIP712, // 0x02 | |
- EthSign, // 0x03 | |
- Wallet, // 0x04 | |
- Validator, // 0x05 | |
- PreSigned, // 0x06 | |
- NSignatureTypes // 0x07, number of signature types. Always leave at end. | |
- } | |
- | |
- /// @dev Verifies signature using logic defined by Wallet contract. | |
- /// @param hash Any 32 byte hash. | |
- /// @param walletAddress Address that should have signed the given hash | |
- /// and defines its own signature verification method. | |
- /// @param signature Proof that the hash has been signed by signer. | |
- /// @return True if the address recovered from the provided signature matches the input signer address. | |
- function isValidWalletSignature( | |
- bytes32 hash, | |
- address walletAddress, | |
- bytes signature | |
- ) | |
- internal | |
- view | |
- returns (bool isValid); | |
- | |
- /// @dev Verifies signature using logic defined by Validator contract. | |
- /// @param validatorAddress Address of validator contract. | |
- /// @param hash Any 32 byte hash. | |
- /// @param signerAddress Address that should have signed the given hash. | |
- /// @param signature Proof that the hash has been signed by signer. | |
- /// @return True if the address recovered from the provided signature matches the input signer address. | |
- function isValidValidatorSignature( | |
- address validatorAddress, | |
- bytes32 hash, | |
- address signerAddress, | |
- bytes signature | |
- ) | |
- internal | |
- view | |
- returns (bool isValid); | |
-} | |
- | |
-contract MTransactions is | |
- ITransactions | |
-{ | |
- // Hash for the EIP712 ZeroEx Transaction Schema | |
- bytes32 constant internal EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = keccak256(abi.encodePacked( | |
- "ZeroExTransaction(", | |
- "uint256 salt,", | |
- "address signerAddress,", | |
- "bytes data", | |
- ")" | |
- )); | |
- | |
- /// @dev Calculates EIP712 hash of the Transaction. | |
- /// @param salt Arbitrary number to ensure uniqueness of transaction hash. | |
- /// @param signerAddress Address of transaction signer. | |
- /// @param data AbiV2 encoded calldata. | |
- /// @return EIP712 hash of the Transaction. | |
- function hashZeroExTransaction( | |
- uint256 salt, | |
- address signerAddress, | |
- bytes memory data | |
- ) | |
- internal | |
- pure | |
- returns (bytes32 result); | |
- | |
- /// @dev The current function will be called in the context of this address (either 0x transaction signer or `msg.sender`). | |
- /// If calling a fill function, this address will represent the taker. | |
- /// If calling a cancel function, this address will represent the maker. | |
- /// @return Signer of 0x transaction if entry point is `executeTransaction`. | |
- /// `msg.sender` if entry point is any other function. | |
- function getCurrentContextAddress() | |
- internal | |
- view | |
- returns (address); | |
-} | |
- | |
-contract MWrapperFunctions is | |
- IWrapperFunctions | |
-{ | |
- /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled. | |
- /// @param order LibOrder.Order struct containing order specifications. | |
- /// @param takerAssetFillAmount Desired amount of takerAsset to sell. | |
- /// @param signature Proof that order has been created by maker. | |
- function fillOrKillOrderInternal( | |
- LibOrder.Order memory order, | |
- uint256 takerAssetFillAmount, | |
- bytes memory signature | |
- ) | |
- internal | |
- returns (LibFillResults.FillResults memory fillResults); | |
-} | |
- | |
-contract MixinExchangeCore is | |
- ReentrancyGuard, | |
- LibConstants, | |
- LibMath, | |
- LibOrder, | |
- LibFillResults, | |
- MAssetProxyDispatcher, | |
- MExchangeCore, | |
- MSignatureValidator, | |
- MTransactions | |
-{ | |
- // Mapping of orderHash => amount of takerAsset already bought by maker | |
- mapping (bytes32 => uint256) public filled; | |
- | |
- // Mapping of orderHash => cancelled | |
- mapping (bytes32 => bool) public cancelled; | |
- | |
- // Mapping of makerAddress => senderAddress => lowest salt an order can have in order to be fillable | |
- // Orders with specified senderAddress and with a salt less than their epoch are considered cancelled | |
- mapping (address => mapping (address => uint256)) public orderEpoch; | |
- | |
- /// @dev Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch | |
- /// and senderAddress equal to msg.sender (or null address if msg.sender == makerAddress). | |
- /// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled. | |
- function cancelOrdersUpTo(uint256 targetOrderEpoch) | |
- external | |
- nonReentrant | |
- { | |
- address makerAddress = getCurrentContextAddress(); | |
- // If this function is called via `executeTransaction`, we only update the orderEpoch for the makerAddress/msg.sender combination. | |
- // This allows external filter contracts to add rules to how orders are cancelled via this function. | |
- address senderAddress = makerAddress == msg.sender ? address(0) : msg.sender; | |
- | |
- // orderEpoch is initialized to 0, so to cancelUpTo we need salt + 1 | |
- uint256 newOrderEpoch = targetOrderEpoch + 1; | |
- uint256 oldOrderEpoch = orderEpoch[makerAddress][senderAddress]; | |
- | |
- // Ensure orderEpoch is monotonically increasing | |
- require( | |
- newOrderEpoch > oldOrderEpoch, | |
- "INVALID_NEW_ORDER_EPOCH" | |
- ); | |
- | |
- // Update orderEpoch | |
- orderEpoch[makerAddress][senderAddress] = newOrderEpoch; | |
- emit CancelUpTo( | |
- makerAddress, | |
- senderAddress, | |
- newOrderEpoch | |
- ); | |
- } | |
- | |
- /// @dev Fills the input order. | |
- /// @param order Order struct containing order specifications. | |
- /// @param takerAssetFillAmount Desired amount of takerAsset to sell. | |
- /// @param signature Proof that order has been created by maker. | |
- /// @return Amounts filled and fees paid by maker and taker. | |
- function fillOrder( | |
- Order memory order, | |
- uint256 takerAssetFillAmount, | |
- bytes memory signature | |
- ) | |
- public | |
- nonReentrant | |
- returns (FillResults memory fillResults) | |
- { | |
- fillResults = fillOrderInternal( | |
- order, | |
- takerAssetFillAmount, | |
- signature | |
- ); | |
- return fillResults; | |
- } | |
- | |
- /// @dev After calling, the order can not be filled anymore. | |
- /// Throws if order is invalid or sender does not have permission to cancel. | |
- /// @param order Order to cancel. Order must be OrderStatus.FILLABLE. | |
- function cancelOrder(Order memory order) | |
- public | |
- nonReentrant | |
- { | |
- cancelOrderInternal(order); | |
- } | |
- | |
- /// @dev Gets information about an order: status, hash, and amount filled. | |
- /// @param order Order to gather information on. | |
- /// @return OrderInfo Information about the order and its state. | |
- /// See LibOrder.OrderInfo for a complete description. | |
- function getOrderInfo(Order memory order) | |
- public | |
- view | |
- returns (OrderInfo memory orderInfo) | |
- { | |
- // Compute the order hash | |
- orderInfo.orderHash = getOrderHash(order); | |
- | |
- // Fetch filled amount | |
- orderInfo.orderTakerAssetFilledAmount = filled[orderInfo.orderHash]; | |
- | |
- // If order.makerAssetAmount is zero, we also reject the order. | |
- // While the Exchange contract handles them correctly, they create | |
- // edge cases in the supporting infrastructure because they have | |
- // an 'infinite' price when computed by a simple division. | |
- if (order.makerAssetAmount == 0) { | |
- orderInfo.orderStatus = uint8(OrderStatus.INVALID_MAKER_ASSET_AMOUNT); | |
- return orderInfo; | |
- } | |
- | |
- // If order.takerAssetAmount is zero, then the order will always | |
- // be considered filled because 0 == takerAssetAmount == orderTakerAssetFilledAmount | |
- // Instead of distinguishing between unfilled and filled zero taker | |
- // amount orders, we choose not to support them. | |
- if (order.takerAssetAmount == 0) { | |
- orderInfo.orderStatus = uint8(OrderStatus.INVALID_TAKER_ASSET_AMOUNT); | |
- return orderInfo; | |
- } | |
- | |
- // Validate order availability | |
- if (orderInfo.orderTakerAssetFilledAmount >= order.takerAssetAmount) { | |
- orderInfo.orderStatus = uint8(OrderStatus.FULLY_FILLED); | |
- return orderInfo; | |
- } | |
- | |
- // Validate order expiration | |
- // solhint-disable-next-line not-rely-on-time | |
- if (block.timestamp >= order.expirationTimeSeconds) { | |
- orderInfo.orderStatus = uint8(OrderStatus.EXPIRED); | |
- return orderInfo; | |
- } | |
- | |
- // Check if order has been cancelled | |
- if (cancelled[orderInfo.orderHash]) { | |
- orderInfo.orderStatus = uint8(OrderStatus.CANCELLED); | |
- return orderInfo; | |
- } | |
- if (orderEpoch[order.makerAddress][order.senderAddress] > order.salt) { | |
- orderInfo.orderStatus = uint8(OrderStatus.CANCELLED); | |
- return orderInfo; | |
- } | |
- | |
- // All other statuses are ruled out: order is Fillable | |
- orderInfo.orderStatus = uint8(OrderStatus.FILLABLE); | |
- return orderInfo; | |
- } | |
- | |
- /// @dev Fills the input order. | |
- /// @param order Order struct containing order specifications. | |
- /// @param takerAssetFillAmount Desired amount of takerAsset to sell. | |
- /// @param signature Proof that order has been created by maker. | |
- /// @return Amounts filled and fees paid by maker and taker. | |
- function fillOrderInternal( | |
- Order memory order, | |
- uint256 takerAssetFillAmount, | |
- bytes memory signature | |
- ) | |
- internal | |
- returns (FillResults memory fillResults) | |
- { | |
- // Fetch order info | |
- OrderInfo memory orderInfo = getOrderInfo(order); | |
- | |
- // Fetch taker address | |
- address takerAddress = getCurrentContextAddress(); | |
- | |
- // Assert that the order is fillable by taker | |
- assertFillableOrder( | |
- order, | |
- orderInfo, | |
- takerAddress, | |
- signature | |
- ); | |
- | |
- // Get amount of takerAsset to fill | |
- uint256 remainingTakerAssetAmount = safeSub(order.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount); | |
- uint256 takerAssetFilledAmount = min256(takerAssetFillAmount, remainingTakerAssetAmount); | |
- | |
- // Validate context | |
- assertValidFill( | |
- order, | |
- orderInfo, | |
- takerAssetFillAmount, | |
- takerAssetFilledAmount, | |
- fillResults.makerAssetFilledAmount | |
- ); | |
- | |
- // Compute proportional fill amounts | |
- fillResults = calculateFillResults(order, takerAssetFilledAmount); | |
- | |
- // Update exchange internal state | |
- updateFilledState( | |
- order, | |
- takerAddress, | |
- orderInfo.orderHash, | |
- orderInfo.orderTakerAssetFilledAmount, | |
- fillResults | |
- ); | |
- | |
- // Settle order | |
- settleOrder( | |
- order, | |
- takerAddress, | |
- fillResults | |
- ); | |
- | |
- return fillResults; | |
- } | |
- | |
- /// @dev After calling, the order can not be filled anymore. | |
- /// Throws if order is invalid or sender does not have permission to cancel. | |
- /// @param order Order to cancel. Order must be OrderStatus.FILLABLE. | |
- function cancelOrderInternal(Order memory order) | |
- internal | |
- { | |
- // Fetch current order status | |
- OrderInfo memory orderInfo = getOrderInfo(order); | |
- | |
- // Validate context | |
- assertValidCancel(order, orderInfo); | |
- | |
- // Perform cancel | |
- updateCancelledState(order, orderInfo.orderHash); | |
- } | |
- | |
- /// @dev Updates state with results of a fill order. | |
- /// @param order that was filled. | |
- /// @param takerAddress Address of taker who filled the order. | |
- /// @param orderTakerAssetFilledAmount Amount of order already filled. | |
- function updateFilledState( | |
- Order memory order, | |
- address takerAddress, | |
- bytes32 orderHash, | |
- uint256 orderTakerAssetFilledAmount, | |
- FillResults memory fillResults | |
- ) | |
- internal | |
- { | |
- // Update state | |
- filled[orderHash] = safeAdd(orderTakerAssetFilledAmount, fillResults.takerAssetFilledAmount); | |
- | |
- // Log order | |
- emit Fill( | |
- order.makerAddress, | |
- order.feeRecipientAddress, | |
- takerAddress, | |
- msg.sender, | |
- fillResults.makerAssetFilledAmount, | |
- fillResults.takerAssetFilledAmount, | |
- fillResults.makerFeePaid, | |
- fillResults.takerFeePaid, | |
- orderHash, | |
- order.makerAssetData, | |
- order.takerAssetData | |
- ); | |
- } | |
- | |
- /// @dev Updates state with results of cancelling an order. | |
- /// State is only updated if the order is currently fillable. | |
- /// Otherwise, updating state would have no effect. | |
- /// @param order that was cancelled. | |
- /// @param orderHash Hash of order that was cancelled. | |
- function updateCancelledState( | |
- Order memory order, | |
- bytes32 orderHash | |
- ) | |
- internal | |
- { | |
- // Perform cancel | |
- cancelled[orderHash] = true; | |
- | |
- // Log cancel | |
- emit Cancel( | |
- order.makerAddress, | |
- order.feeRecipientAddress, | |
- msg.sender, | |
- orderHash, | |
- order.makerAssetData, | |
- order.takerAssetData | |
- ); | |
- } | |
- | |
- /// @dev Validates context for fillOrder. Succeeds or throws. | |
- /// @param order to be filled. | |
- /// @param orderInfo OrderStatus, orderHash, and amount already filled of order. | |
- /// @param takerAddress Address of order taker. | |
- /// @param signature Proof that the orders was created by its maker. | |
- function assertFillableOrder( | |
- Order memory order, | |
- OrderInfo memory orderInfo, | |
- address takerAddress, | |
- bytes memory signature | |
- ) | |
- internal | |
- view | |
- { | |
- // An order can only be filled if its status is FILLABLE. | |
- require( | |
- orderInfo.orderStatus == uint8(OrderStatus.FILLABLE), | |
- "ORDER_UNFILLABLE" | |
- ); | |
- | |
- // Validate sender is allowed to fill this order | |
- if (order.senderAddress != address(0)) { | |
- require( | |
- order.senderAddress == msg.sender, | |
- "INVALID_SENDER" | |
- ); | |
- } | |
- | |
- // Validate taker is allowed to fill this order | |
- if (order.takerAddress != address(0)) { | |
- require( | |
- order.takerAddress == takerAddress, | |
- "INVALID_TAKER" | |
- ); | |
- } | |
- | |
- // Validate Maker signature (check only if first time seen) | |
- if (orderInfo.orderTakerAssetFilledAmount == 0) { | |
- require( | |
- isValidSignature( | |
- orderInfo.orderHash, | |
- order.makerAddress, | |
- signature | |
- ), | |
- "INVALID_ORDER_SIGNATURE" | |
- ); | |
- } | |
- } | |
- | |
- /// @dev Validates context for fillOrder. Succeeds or throws. | |
- /// @param order to be filled. | |
- /// @param orderInfo OrderStatus, orderHash, and amount already filled of order. | |
- /// @param takerAssetFillAmount Desired amount of order to fill by taker. | |
- /// @param takerAssetFilledAmount Amount of takerAsset that will be filled. | |
- /// @param makerAssetFilledAmount Amount of makerAsset that will be transfered. | |
- function assertValidFill( | |
- Order memory order, | |
- OrderInfo memory orderInfo, | |
- uint256 takerAssetFillAmount, // TODO: use FillResults | |
- uint256 takerAssetFilledAmount, | |
- uint256 makerAssetFilledAmount | |
- ) | |
- internal | |
- view | |
- { | |
- // Revert if fill amount is invalid | |
- // TODO: reconsider necessity for v2.1 | |
- require( | |
- takerAssetFillAmount != 0, | |
- "INVALID_TAKER_AMOUNT" | |
- ); | |
- | |
- // Make sure taker does not pay more than desired amount | |
- // NOTE: This assertion should never fail, it is here | |
- // as an extra defence against potential bugs. | |
- require( | |
- takerAssetFilledAmount <= takerAssetFillAmount, | |
- "TAKER_OVERPAY" | |
- ); | |
- | |
- // Make sure order is not overfilled | |
- // NOTE: This assertion should never fail, it is here | |
- // as an extra defence against potential bugs. | |
- require( | |
- safeAdd(orderInfo.orderTakerAssetFilledAmount, takerAssetFilledAmount) <= order.takerAssetAmount, | |
- "ORDER_OVERFILL" | |
- ); | |
- | |
- // Make sure order is filled at acceptable price. | |
- // The order has an implied price from the makers perspective: | |
- // order price = order.makerAssetAmount / order.takerAssetAmount | |
- // i.e. the number of makerAsset maker is paying per takerAsset. The | |
- // maker is guaranteed to get this price or a better (lower) one. The | |
- // actual price maker is getting in this fill is: | |
- // fill price = makerAssetFilledAmount / takerAssetFilledAmount | |
- // We need `fill price <= order price` for the fill to be fair to maker. | |
- // This amounts to: | |
- // makerAssetFilledAmount order.makerAssetAmount | |
- // ------------------------ <= ----------------------- | |
- // takerAssetFilledAmount order.takerAssetAmount | |
- // or, equivalently: | |
- // makerAssetFilledAmount * order.takerAssetAmount <= | |
- // order.makerAssetAmount * takerAssetFilledAmount | |
- // NOTE: This assertion should never fail, it is here | |
- // as an extra defence against potential bugs. | |
- require( | |
- safeMul(makerAssetFilledAmount, order.takerAssetAmount) | |
- <= | |
- safeMul(order.makerAssetAmount, takerAssetFilledAmount), | |
- "INVALID_FILL_PRICE" | |
- ); | |
- } | |
- | |
- /// @dev Validates context for cancelOrder. Succeeds or throws. | |
- /// @param order to be cancelled. | |
- /// @param orderInfo OrderStatus, orderHash, and amount already filled of order. | |
- function assertValidCancel( | |
- Order memory order, | |
- OrderInfo memory orderInfo | |
- ) | |
- internal | |
- view | |
- { | |
- // Ensure order is valid | |
- // An order can only be cancelled if its status is FILLABLE. | |
- require( | |
- orderInfo.orderStatus == uint8(OrderStatus.FILLABLE), | |
- "ORDER_UNFILLABLE" | |
- ); | |
- | |
- // Validate sender is allowed to cancel this order | |
- if (order.senderAddress != address(0)) { | |
- require( | |
- order.senderAddress == msg.sender, | |
- "INVALID_SENDER" | |
- ); | |
- } | |
- | |
- // Validate transaction signed by maker | |
- address makerAddress = getCurrentContextAddress(); | |
- require( | |
- order.makerAddress == makerAddress, | |
- "INVALID_MAKER" | |
- ); | |
- } | |
- | |
- /// @dev Calculates amounts filled and fees paid by maker and taker. | |
- /// @param order to be filled. | |
- /// @param takerAssetFilledAmount Amount of takerAsset that will be filled. | |
- /// @return fillResults Amounts filled and fees paid by maker and taker. | |
- function calculateFillResults( | |
- Order memory order, | |
- uint256 takerAssetFilledAmount | |
- ) | |
- internal | |
- pure | |
- returns (FillResults memory fillResults) | |
- { | |
- // Compute proportional transfer amounts | |
- fillResults.takerAssetFilledAmount = takerAssetFilledAmount; | |
- fillResults.makerAssetFilledAmount = safeGetPartialAmountFloor( | |
- takerAssetFilledAmount, | |
- order.takerAssetAmount, | |
- order.makerAssetAmount | |
- ); | |
- fillResults.makerFeePaid = safeGetPartialAmountFloor( | |
- fillResults.makerAssetFilledAmount, | |
- order.makerAssetAmount, | |
- order.makerFee | |
- ); | |
- fillResults.takerFeePaid = safeGetPartialAmountFloor( | |
- takerAssetFilledAmount, | |
- order.takerAssetAmount, | |
- order.takerFee | |
- ); | |
- | |
- return fillResults; | |
- } | |
- | |
- /// @dev Settles an order by transferring assets between counterparties. | |
- /// @param order Order struct containing order specifications. | |
- /// @param takerAddress Address selling takerAsset and buying makerAsset. | |
- /// @param fillResults Amounts to be filled and fees paid by maker and taker. | |
- function settleOrder( | |
- LibOrder.Order memory order, | |
- address takerAddress, | |
- LibFillResults.FillResults memory fillResults | |
- ) | |
- private | |
- { | |
- bytes memory zrxAssetData = ZRX_ASSET_DATA; | |
- dispatchTransferFrom( | |
- order.makerAssetData, | |
- order.makerAddress, | |
- takerAddress, | |
- fillResults.makerAssetFilledAmount | |
- ); | |
- dispatchTransferFrom( | |
- order.takerAssetData, | |
- takerAddress, | |
- order.makerAddress, | |
- fillResults.takerAssetFilledAmount | |
- ); | |
- dispatchTransferFrom( | |
- zrxAssetData, | |
- order.makerAddress, | |
- order.feeRecipientAddress, | |
- fillResults.makerFeePaid | |
- ); | |
- dispatchTransferFrom( | |
- zrxAssetData, | |
- takerAddress, | |
- order.feeRecipientAddress, | |
- fillResults.takerFeePaid | |
- ); | |
- } | |
-} | |
- | |
-contract MixinSignatureValidator is | |
- ReentrancyGuard, | |
- MSignatureValidator, | |
- MTransactions | |
-{ | |
- using LibBytes for bytes; | |
- | |
- // Mapping of hash => signer => signed | |
- mapping (bytes32 => mapping (address => bool)) public preSigned; | |
- | |
- // Mapping of signer => validator => approved | |
- mapping (address => mapping (address => bool)) public allowedValidators; | |
- | |
- /// @dev Approves a hash on-chain using any valid signature type. | |
- /// After presigning a hash, the preSign signature type will become valid for that hash and signer. | |
- /// @param signerAddress Address that should have signed the given hash. | |
- /// @param signature Proof that the hash has been signed by signer. | |
- function preSign( | |
- bytes32 hash, | |
- address signerAddress, | |
- bytes signature | |
- ) | |
- external | |
- { | |
- if (signerAddress != msg.sender) { | |
- require( | |
- isValidSignature( | |
- hash, | |
- signerAddress, | |
- signature | |
- ), | |
- "INVALID_SIGNATURE" | |
- ); | |
- } | |
- preSigned[hash][signerAddress] = true; | |
- } | |
- | |
- /// @dev Approves/unnapproves a Validator contract to verify signatures on signer's behalf. | |
- /// @param validatorAddress Address of Validator contract. | |
- /// @param approval Approval or disapproval of Validator contract. | |
- function setSignatureValidatorApproval( | |
- address validatorAddress, | |
- bool approval | |
- ) | |
- external | |
- nonReentrant | |
- { | |
- address signerAddress = getCurrentContextAddress(); | |
- allowedValidators[signerAddress][validatorAddress] = approval; | |
- emit SignatureValidatorApproval( | |
- signerAddress, | |
- validatorAddress, | |
- approval | |
- ); | |
- } | |
- | |
- /// @dev Verifies that a hash has been signed by the given signer. | |
- /// @param hash Any 32 byte hash. | |
- /// @param signerAddress Address that should have signed the given hash. | |
- /// @param signature Proof that the hash has been signed by signer. | |
- /// @return True if the address recovered from the provided signature matches the input signer address. | |
- function isValidSignature( | |
- bytes32 hash, | |
- address signerAddress, | |
- bytes memory signature | |
- ) | |
- public | |
- view | |
- returns (bool isValid) | |
- { | |
- require( | |
- signature.length > 0, | |
- "LENGTH_GREATER_THAN_0_REQUIRED" | |
- ); | |
- | |
- // Pop last byte off of signature byte array. | |
- uint8 signatureTypeRaw = uint8(signature.popLastByte()); | |
- | |
- // Ensure signature is supported | |
- require( | |
- signatureTypeRaw < uint8(SignatureType.NSignatureTypes), | |
- "SIGNATURE_UNSUPPORTED" | |
- ); | |
- | |
- SignatureType signatureType = SignatureType(signatureTypeRaw); | |
- | |
- // Variables are not scoped in Solidity. | |
- uint8 v; | |
- bytes32 r; | |
- bytes32 s; | |
- address recovered; | |
- | |
- // Always illegal signature. | |
- // This is always an implicit option since a signer can create a | |
- // signature array with invalid type or length. We may as well make | |
- // it an explicit option. This aids testing and analysis. It is | |
- // also the initialization value for the enum type. | |
- if (signatureType == SignatureType.Illegal) { | |
- revert("SIGNATURE_ILLEGAL"); | |
- | |
- // Always invalid signature. | |
- // Like Illegal, this is always implicitly available and therefore | |
- // offered explicitly. It can be implicitly created by providing | |
- // a correctly formatted but incorrect signature. | |
- } else if (signatureType == SignatureType.Invalid) { | |
- require( | |
- signature.length == 0, | |
- "LENGTH_0_REQUIRED" | |
- ); | |
- isValid = false; | |
- return isValid; | |
- | |
- // Signature using EIP712 | |
- } else if (signatureType == SignatureType.EIP712) { | |
- require( | |
- signature.length == 65, | |
- "LENGTH_65_REQUIRED" | |
- ); | |
- v = uint8(signature[0]); | |
- r = signature.readBytes32(1); | |
- s = signature.readBytes32(33); | |
- recovered = ecrecover( | |
- hash, | |
- v, | |
- r, | |
- s | |
- ); | |
- isValid = signerAddress == recovered; | |
- return isValid; | |
- | |
- // Signed using web3.eth_sign | |
- } else if (signatureType == SignatureType.EthSign) { | |
- require( | |
- signature.length == 65, | |
- "LENGTH_65_REQUIRED" | |
- ); | |
- v = uint8(signature[0]); | |
- r = signature.readBytes32(1); | |
- s = signature.readBytes32(33); | |
- recovered = ecrecover( | |
- keccak256(abi.encodePacked( | |
- "\x19Ethereum Signed Message:\n32", | |
- hash | |
- )), | |
- v, | |
- r, | |
- s | |
- ); | |
- isValid = signerAddress == recovered; | |
- return isValid; | |
- | |
- // Signature verified by wallet contract. | |
- // If used with an order, the maker of the order is the wallet contract. | |
- } else if (signatureType == SignatureType.Wallet) { | |
- isValid = isValidWalletSignature( | |
- hash, | |
- signerAddress, | |
- signature | |
- ); | |
- return isValid; | |
- | |
- // Signature verified by validator contract. | |
- // If used with an order, the maker of the order can still be an EOA. | |
- // A signature using this type should be encoded as: | |
- // | Offset | Length | Contents | | |
- // | 0x00 | x | Signature to validate | | |
- // | 0x00 + x | 20 | Address of validator contract | | |
- // | 0x14 + x | 1 | Signature type is always "\x06" | | |
- } else if (signatureType == SignatureType.Validator) { | |
- // Pop last 20 bytes off of signature byte array. | |
- address validatorAddress = signature.popLast20Bytes(); | |
- | |
- // Ensure signer has approved validator. | |
- if (!allowedValidators[signerAddress][validatorAddress]) { | |
- return false; | |
- } | |
- isValid = isValidValidatorSignature( | |
- validatorAddress, | |
- hash, | |
- signerAddress, | |
- signature | |
- ); | |
- return isValid; | |
- | |
- // Signer signed hash previously using the preSign function. | |
- } else if (signatureType == SignatureType.PreSigned) { | |
- isValid = preSigned[hash][signerAddress]; | |
- return isValid; | |
- } | |
- | |
- // Anything else is illegal (We do not return false because | |
- // the signature may actually be valid, just not in a format | |
- // that we currently support. In this case returning false | |
- // may lead the caller to incorrectly believe that the | |
- // signature was invalid.) | |
- revert("SIGNATURE_UNSUPPORTED"); | |
- } | |
- | |
- /// @dev Verifies signature using logic defined by Wallet contract. | |
- /// @param hash Any 32 byte hash. | |
- /// @param walletAddress Address that should have signed the given hash | |
- /// and defines its own signature verification method. | |
- /// @param signature Proof that the hash has been signed by signer. | |
- /// @return True if signature is valid for given wallet.. | |
- function isValidWalletSignature( | |
- bytes32 hash, | |
- address walletAddress, | |
- bytes signature | |
- ) | |
- internal | |
- view | |
- returns (bool isValid) | |
- { | |
- bytes memory calldata = abi.encodeWithSelector( | |
- IWallet(walletAddress).isValidSignature.selector, | |
- hash, | |
- signature | |
- ); | |
- assembly { | |
- let cdStart := add(calldata, 32) | |
- let success := staticcall( | |
- gas, // forward all gas | |
- walletAddress, // address of Wallet contract | |
- cdStart, // pointer to start of input | |
- mload(calldata), // length of input | |
- cdStart, // write output over input | |
- 32 // output size is 32 bytes | |
- ) | |
- | |
- switch success | |
- case 0 { | |
- // Revert with `Error("WALLET_ERROR")` | |
- mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) | |
- mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) | |
- mstore(64, 0x0000000c57414c4c45545f4552524f5200000000000000000000000000000000) | |
- mstore(96, 0) | |
- revert(0, 100) | |
- } | |
- case 1 { | |
- // Signature is valid if call did not revert and returned true | |
- isValid := mload(cdStart) | |
- } | |
- } | |
- return isValid; | |
- } | |
- | |
- /// @dev Verifies signature using logic defined by Validator contract. | |
- /// @param validatorAddress Address of validator contract. | |
- /// @param hash Any 32 byte hash. | |
- /// @param signerAddress Address that should have signed the given hash. | |
- /// @param signature Proof that the hash has been signed by signer. | |
- /// @return True if the address recovered from the provided signature matches the input signer address. | |
- function isValidValidatorSignature( | |
- address validatorAddress, | |
- bytes32 hash, | |
- address signerAddress, | |
- bytes signature | |
- ) | |
- internal | |
- view | |
- returns (bool isValid) | |
- { | |
- bytes memory calldata = abi.encodeWithSelector( | |
- IValidator(signerAddress).isValidSignature.selector, | |
- hash, | |
- signerAddress, | |
- signature | |
- ); | |
- assembly { | |
- let cdStart := add(calldata, 32) | |
- let success := staticcall( | |
- gas, // forward all gas | |
- validatorAddress, // address of Validator contract | |
- cdStart, // pointer to start of input | |
- mload(calldata), // length of input | |
- cdStart, // write output over input | |
- 32 // output size is 32 bytes | |
- ) | |
- | |
- switch success | |
- case 0 { | |
- // Revert with `Error("VALIDATOR_ERROR")` | |
- mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) | |
- mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) | |
- mstore(64, 0x0000000f56414c494441544f525f4552524f5200000000000000000000000000) | |
- mstore(96, 0) | |
- revert(0, 100) | |
- } | |
- case 1 { | |
- // Signature is valid if call did not revert and returned true | |
- isValid := mload(cdStart) | |
- } | |
- } | |
- return isValid; | |
- } | |
-} | |
- | |
-contract MixinWrapperFunctions is | |
- ReentrancyGuard, | |
- LibMath, | |
- LibFillResults, | |
- LibAbiEncoder, | |
- MExchangeCore, | |
- MWrapperFunctions | |
-{ | |
- /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled. | |
- /// @param order Order struct containing order specifications. | |
- /// @param takerAssetFillAmount Desired amount of takerAsset to sell. | |
- /// @param signature Proof that order has been created by maker. | |
- function fillOrKillOrder( | |
- LibOrder.Order memory order, | |
- uint256 takerAssetFillAmount, | |
- bytes memory signature | |
- ) | |
- public | |
- nonReentrant | |
- returns (FillResults memory fillResults) | |
- { | |
- fillResults = fillOrKillOrderInternal( | |
- order, | |
- takerAssetFillAmount, | |
- signature | |
- ); | |
- return fillResults; | |
- } | |
- | |
- /// @dev Fills the input order. | |
- /// Returns false if the transaction would otherwise revert. | |
- /// @param order Order struct containing order specifications. | |
- /// @param takerAssetFillAmount Desired amount of takerAsset to sell. | |
- /// @param signature Proof that order has been created by maker. | |
- /// @return Amounts filled and fees paid by maker and taker. | |
- function fillOrderNoThrow( | |
- LibOrder.Order memory order, | |
- uint256 takerAssetFillAmount, | |
- bytes memory signature | |
- ) | |
- public | |
- returns (FillResults memory fillResults) | |
- { | |
- // ABI encode calldata for `fillOrder` | |
- bytes memory fillOrderCalldata = abiEncodeFillOrder( | |
- order, | |
- takerAssetFillAmount, | |
- signature | |
- ); | |
- | |
- // Delegate to `fillOrder` and handle any exceptions gracefully | |
- assembly { | |
- let success := delegatecall( | |
- gas, // forward all gas | |
- address, // call address of this contract | |
- add(fillOrderCalldata, 32), // pointer to start of input (skip array length in first 32 bytes) | |
- mload(fillOrderCalldata), // length of input | |
- fillOrderCalldata, // write output over input | |
- 128 // output size is 128 bytes | |
- ) | |
- if success { | |
- mstore(fillResults, mload(fillOrderCalldata)) | |
- mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32))) | |
- mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64))) | |
- mstore(add(fillResults, 96), mload(add(fillOrderCalldata, 96))) | |
- } | |
- } | |
- // fillResults values will be 0 by default if call was unsuccessful | |
- return fillResults; | |
- } | |
- | |
- /// @dev Synchronously executes multiple calls of fillOrder. | |
- /// @param orders Array of order specifications. | |
- /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders. | |
- /// @param signatures Proofs that orders have been created by makers. | |
- /// @return Amounts filled and fees paid by makers and taker. | |
- /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets. | |
- function batchFillOrders( | |
- LibOrder.Order[] memory orders, | |
- uint256[] memory takerAssetFillAmounts, | |
- bytes[] memory signatures | |
- ) | |
- public | |
- nonReentrant | |
- returns (FillResults memory totalFillResults) | |
- { | |
- uint256 ordersLength = orders.length; | |
- for (uint256 i = 0; i != ordersLength; i++) { | |
- FillResults memory singleFillResults = fillOrderInternal( | |
- orders[i], | |
- takerAssetFillAmounts[i], | |
- signatures[i] | |
- ); | |
- addFillResults(totalFillResults, singleFillResults); | |
- } | |
- return totalFillResults; | |
- } | |
- | |
- /// @dev Synchronously executes multiple calls of fillOrKill. | |
- /// @param orders Array of order specifications. | |
- /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders. | |
- /// @param signatures Proofs that orders have been created by makers. | |
- /// @return Amounts filled and fees paid by makers and taker. | |
- /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets. | |
- function batchFillOrKillOrders( | |
- LibOrder.Order[] memory orders, | |
- uint256[] memory takerAssetFillAmounts, | |
- bytes[] memory signatures | |
- ) | |
- public | |
- nonReentrant | |
- returns (FillResults memory totalFillResults) | |
- { | |
- uint256 ordersLength = orders.length; | |
- for (uint256 i = 0; i != ordersLength; i++) { | |
- FillResults memory singleFillResults = fillOrKillOrderInternal( | |
- orders[i], | |
- takerAssetFillAmounts[i], | |
- signatures[i] | |
- ); | |
- addFillResults(totalFillResults, singleFillResults); | |
- } | |
- return totalFillResults; | |
- } | |
- | |
- /// @dev Fills an order with specified parameters and ECDSA signature. | |
- /// Returns false if the transaction would otherwise revert. | |
- /// @param orders Array of order specifications. | |
- /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders. | |
- /// @param signatures Proofs that orders have been created by makers. | |
- /// @return Amounts filled and fees paid by makers and taker. | |
- /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets. | |
- function batchFillOrdersNoThrow( | |
- LibOrder.Order[] memory orders, | |
- uint256[] memory takerAssetFillAmounts, | |
- bytes[] memory signatures | |
- ) | |
- public | |
- returns (FillResults memory totalFillResults) | |
- { | |
- uint256 ordersLength = orders.length; | |
- for (uint256 i = 0; i != ordersLength; i++) { | |
- FillResults memory singleFillResults = fillOrderNoThrow( | |
- orders[i], | |
- takerAssetFillAmounts[i], | |
- signatures[i] | |
- ); | |
- addFillResults(totalFillResults, singleFillResults); | |
- } | |
- return totalFillResults; | |
- } | |
- | |
- /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker. | |
- /// @param orders Array of order specifications. | |
- /// @param takerAssetFillAmount Desired amount of takerAsset to sell. | |
- /// @param signatures Proofs that orders have been created by makers. | |
- /// @return Amounts filled and fees paid by makers and taker. | |
- function marketSellOrders( | |
- LibOrder.Order[] memory orders, | |
- uint256 takerAssetFillAmount, | |
- bytes[] memory signatures | |
- ) | |
- public | |
- nonReentrant | |
- returns (FillResults memory totalFillResults) | |
- { | |
- bytes memory takerAssetData = orders[0].takerAssetData; | |
- | |
- uint256 ordersLength = orders.length; | |
- for (uint256 i = 0; i != ordersLength; i++) { | |
- | |
- // We assume that asset being sold by taker is the same for each order. | |
- // Rather than passing this in as calldata, we use the takerAssetData from the first order in all later orders. | |
- orders[i].takerAssetData = takerAssetData; | |
- | |
- // Calculate the remaining amount of takerAsset to sell | |
- uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount); | |
- | |
- // Attempt to sell the remaining amount of takerAsset | |
- FillResults memory singleFillResults = fillOrderInternal( | |
- orders[i], | |
- remainingTakerAssetFillAmount, | |
- signatures[i] | |
- ); | |
- | |
- // Update amounts filled and fees paid by maker and taker | |
- addFillResults(totalFillResults, singleFillResults); | |
- | |
- // Stop execution if the entire amount of takerAsset has been sold | |
- if (totalFillResults.takerAssetFilledAmount >= takerAssetFillAmount) { | |
- break; | |
- } | |
- } | |
- return totalFillResults; | |
- } | |
- | |
- /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker. | |
- /// Returns false if the transaction would otherwise revert. | |
- /// @param orders Array of order specifications. | |
- /// @param takerAssetFillAmount Desired amount of takerAsset to sell. | |
- /// @param signatures Proofs that orders have been signed by makers. | |
- /// @return Amounts filled and fees paid by makers and taker. | |
- function marketSellOrdersNoThrow( | |
- LibOrder.Order[] memory orders, | |
- uint256 takerAssetFillAmount, | |
- bytes[] memory signatures | |
- ) | |
- public | |
- returns (FillResults memory totalFillResults) | |
- { | |
- bytes memory takerAssetData = orders[0].takerAssetData; | |
- | |
- uint256 ordersLength = orders.length; | |
- for (uint256 i = 0; i != ordersLength; i++) { | |
- | |
- // We assume that asset being sold by taker is the same for each order. | |
- // Rather than passing this in as calldata, we use the takerAssetData from the first order in all later orders. | |
- orders[i].takerAssetData = takerAssetData; | |
- | |
- // Calculate the remaining amount of takerAsset to sell | |
- uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount); | |
- | |
- // Attempt to sell the remaining amount of takerAsset | |
- FillResults memory singleFillResults = fillOrderNoThrow( | |
- orders[i], | |
- remainingTakerAssetFillAmount, | |
- signatures[i] | |
- ); | |
- | |
- // Update amounts filled and fees paid by maker and taker | |
- addFillResults(totalFillResults, singleFillResults); | |
- | |
- // Stop execution if the entire amount of takerAsset has been sold | |
- if (totalFillResults.takerAssetFilledAmount >= takerAssetFillAmount) { | |
- break; | |
- } | |
- } | |
- return totalFillResults; | |
- } | |
- | |
- /// @dev Synchronously executes multiple calls of fillOrder until total amount of makerAsset is bought by taker. | |
- /// @param orders Array of order specifications. | |
- /// @param makerAssetFillAmount Desired amount of makerAsset to buy. | |
- /// @param signatures Proofs that orders have been signed by makers. | |
- /// @return Amounts filled and fees paid by makers and taker. | |
- function marketBuyOrders( | |
- LibOrder.Order[] memory orders, | |
- uint256 makerAssetFillAmount, | |
- bytes[] memory signatures | |
- ) | |
- public | |
- nonReentrant | |
- returns (FillResults memory totalFillResults) | |
- { | |
- bytes memory makerAssetData = orders[0].makerAssetData; | |
- | |
- uint256 ordersLength = orders.length; | |
- for (uint256 i = 0; i != ordersLength; i++) { | |
- | |
- // We assume that asset being bought by taker is the same for each order. | |
- // Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders. | |
- orders[i].makerAssetData = makerAssetData; | |
- | |
- // Calculate the remaining amount of makerAsset to buy | |
- uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount); | |
- | |
- // Convert the remaining amount of makerAsset to buy into remaining amount | |
- // of takerAsset to sell, assuming entire amount can be sold in the current order | |
- uint256 remainingTakerAssetFillAmount = getPartialAmountFloor( | |
- orders[i].takerAssetAmount, | |
- orders[i].makerAssetAmount, | |
- remainingMakerAssetFillAmount | |
- ); | |
- | |
- // Attempt to sell the remaining amount of takerAsset | |
- FillResults memory singleFillResults = fillOrderInternal( | |
- orders[i], | |
- remainingTakerAssetFillAmount, | |
- signatures[i] | |
- ); | |
- | |
- // Update amounts filled and fees paid by maker and taker | |
- addFillResults(totalFillResults, singleFillResults); | |
- | |
- // Stop execution if the entire amount of makerAsset has been bought | |
- if (totalFillResults.makerAssetFilledAmount >= makerAssetFillAmount) { | |
- break; | |
- } | |
- } | |
- return totalFillResults; | |
- } | |
- | |
- /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker. | |
- /// Returns false if the transaction would otherwise revert. | |
- /// @param orders Array of order specifications. | |
- /// @param makerAssetFillAmount Desired amount of makerAsset to buy. | |
- /// @param signatures Proofs that orders have been signed by makers. | |
- /// @return Amounts filled and fees paid by makers and taker. | |
- function marketBuyOrdersNoThrow( | |
- LibOrder.Order[] memory orders, | |
- uint256 makerAssetFillAmount, | |
- bytes[] memory signatures | |
- ) | |
- public | |
- returns (FillResults memory totalFillResults) | |
- { | |
- bytes memory makerAssetData = orders[0].makerAssetData; | |
- | |
- uint256 ordersLength = orders.length; | |
- for (uint256 i = 0; i != ordersLength; i++) { | |
- | |
- // We assume that asset being bought by taker is the same for each order. | |
- // Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders. | |
- orders[i].makerAssetData = makerAssetData; | |
- | |
- // Calculate the remaining amount of makerAsset to buy | |
- uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount); | |
- | |
- // Convert the remaining amount of makerAsset to buy into remaining amount | |
- // of takerAsset to sell, assuming entire amount can be sold in the current order | |
- uint256 remainingTakerAssetFillAmount = getPartialAmountFloor( | |
- orders[i].takerAssetAmount, | |
- orders[i].makerAssetAmount, | |
- remainingMakerAssetFillAmount | |
- ); | |
- | |
- // Attempt to sell the remaining amount of takerAsset | |
- FillResults memory singleFillResults = fillOrderNoThrow( | |
- orders[i], | |
- remainingTakerAssetFillAmount, | |
- signatures[i] | |
- ); | |
- | |
- // Update amounts filled and fees paid by maker and taker | |
- addFillResults(totalFillResults, singleFillResults); | |
- | |
- // Stop execution if the entire amount of makerAsset has been bought | |
- if (totalFillResults.makerAssetFilledAmount >= makerAssetFillAmount) { | |
- break; | |
- } | |
- } | |
- return totalFillResults; | |
- } | |
- | |
- /// @dev Synchronously cancels multiple orders in a single transaction. | |
- /// @param orders Array of order specifications. | |
- function batchCancelOrders(LibOrder.Order[] memory orders) | |
- public | |
- nonReentrant | |
- { | |
- uint256 ordersLength = orders.length; | |
- for (uint256 i = 0; i != ordersLength; i++) { | |
- cancelOrderInternal(orders[i]); | |
- } | |
- } | |
- | |
- /// @dev Fetches information for all passed in orders. | |
- /// @param orders Array of order specifications. | |
- /// @return Array of OrderInfo instances that correspond to each order. | |
- function getOrdersInfo(LibOrder.Order[] memory orders) | |
- public | |
- view | |
- returns (LibOrder.OrderInfo[] memory) | |
- { | |
- uint256 ordersLength = orders.length; | |
- LibOrder.OrderInfo[] memory ordersInfo = new LibOrder.OrderInfo[](ordersLength); | |
- for (uint256 i = 0; i != ordersLength; i++) { | |
- ordersInfo[i] = getOrderInfo(orders[i]); | |
- } | |
- return ordersInfo; | |
- } | |
- | |
- /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled. | |
- /// @param order Order struct containing order specifications. | |
- /// @param takerAssetFillAmount Desired amount of takerAsset to sell. | |
- /// @param signature Proof that order has been created by maker. | |
- function fillOrKillOrderInternal( | |
- LibOrder.Order memory order, | |
- uint256 takerAssetFillAmount, | |
- bytes memory signature | |
- ) | |
- internal | |
- returns (FillResults memory fillResults) | |
- { | |
- fillResults = fillOrderInternal( | |
- order, | |
- takerAssetFillAmount, | |
- signature | |
- ); | |
- require( | |
- fillResults.takerAssetFilledAmount == takerAssetFillAmount, | |
- "COMPLETE_FILL_FAILED" | |
- ); | |
- return fillResults; | |
- } | |
-} | |
- | |
-contract MixinTransactions is | |
- LibEIP712, | |
- MSignatureValidator, | |
- MTransactions | |
-{ | |
- // Mapping of transaction hash => executed | |
- // This prevents transactions from being executed more than once. | |
- mapping (bytes32 => bool) public transactions; | |
- | |
- // Address of current transaction signer | |
- address public currentContextAddress; | |
- | |
- /// @dev Executes an exchange method call in the context of signer. | |
- /// @param salt Arbitrary number to ensure uniqueness of transaction hash. | |
- /// @param signerAddress Address of transaction signer. | |
- /// @param data AbiV2 encoded calldata. | |
- /// @param signature Proof of signer transaction by signer. | |
- function executeTransaction( | |
- uint256 salt, | |
- address signerAddress, | |
- bytes data, | |
- bytes signature | |
- ) | |
- external | |
- { | |
- // Prevent reentrancy | |
- require( | |
- currentContextAddress == address(0), | |
- "REENTRANCY_ILLEGAL" | |
- ); | |
- | |
- bytes32 transactionHash = hashEIP712Message(hashZeroExTransaction( | |
- salt, | |
- signerAddress, | |
- data | |
- )); | |
- | |
- // Validate transaction has not been executed | |
- require( | |
- !transactions[transactionHash], | |
- "INVALID_TX_HASH" | |
- ); | |
- | |
- // Transaction always valid if signer is sender of transaction | |
- if (signerAddress != msg.sender) { | |
- // Validate signature | |
- require( | |
- isValidSignature( | |
- transactionHash, | |
- signerAddress, | |
- signature | |
- ), | |
- "INVALID_TX_SIGNATURE" | |
- ); | |
- | |
- // Set the current transaction signer | |
- currentContextAddress = signerAddress; | |
- } | |
- | |
- // Execute transaction | |
- transactions[transactionHash] = true; | |
- require( | |
- address(this).delegatecall(data), | |
- "FAILED_EXECUTION" | |
- ); | |
- | |
- // Reset current transaction signer if it was previously updated | |
- if (signerAddress != msg.sender) { | |
- currentContextAddress = address(0); | |
- } | |
- } | |
- | |
- /// @dev Calculates EIP712 hash of the Transaction. | |
- /// @param salt Arbitrary number to ensure uniqueness of transaction hash. | |
- /// @param signerAddress Address of transaction signer. | |
- /// @param data AbiV2 encoded calldata. | |
- /// @return EIP712 hash of the Transaction. | |
- function hashZeroExTransaction( | |
- uint256 salt, | |
- address signerAddress, | |
- bytes memory data | |
- ) | |
- internal | |
- pure | |
- returns (bytes32 result) | |
- { | |
- bytes32 schemaHash = EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH; | |
- bytes32 dataHash = keccak256(data); | |
- | |
- // Assembly for more efficiently computing: | |
- // keccak256(abi.encodePacked( | |
- // EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH, | |
- // salt, | |
- // bytes32(signerAddress), | |
- // keccak256(data) | |
- // )); | |
- | |
- assembly { | |
- // Load free memory pointer | |
- let memPtr := mload(64) | |
- | |
- mstore(memPtr, schemaHash) // hash of schema | |
- mstore(add(memPtr, 32), salt) // salt | |
- mstore(add(memPtr, 64), and(signerAddress, 0xffffffffffffffffffffffffffffffffffffffff)) // signerAddress | |
- mstore(add(memPtr, 96), dataHash) // hash of data | |
- | |
- // Compute hash | |
- result := keccak256(memPtr, 128) | |
- } | |
- return result; | |
- } | |
- | |
- /// @dev The current function will be called in the context of this address (either 0x transaction signer or `msg.sender`). | |
- /// If calling a fill function, this address will represent the taker. | |
- /// If calling a cancel function, this address will represent the maker. | |
- /// @return Signer of 0x transaction if entry point is `executeTransaction`. | |
- /// `msg.sender` if entry point is any other function. | |
- function getCurrentContextAddress() | |
- internal | |
- view | |
- returns (address) | |
- { | |
- address currentContextAddress_ = currentContextAddress; | |
- address contextAddress = currentContextAddress_ == address(0) ? msg.sender : currentContextAddress_; | |
- return contextAddress; | |
- } | |
-} | |
- | |
-contract MixinAssetProxyDispatcher is | |
- Ownable, | |
- MAssetProxyDispatcher | |
-{ | |
- // Mapping from Asset Proxy Id's to their respective Asset Proxy | |
- mapping (bytes4 => IAssetProxy) public assetProxies; | |
- | |
- /// @dev Registers an asset proxy to its asset proxy id. | |
- /// Once an asset proxy is registered, it cannot be unregistered. | |
- /// @param assetProxy Address of new asset proxy to register. | |
- function registerAssetProxy(address assetProxy) | |
- external | |
- onlyOwner | |
- { | |
- IAssetProxy assetProxyContract = IAssetProxy(assetProxy); | |
- | |
- // Ensure that no asset proxy exists with current id. | |
- bytes4 assetProxyId = assetProxyContract.getProxyId(); | |
- address currentAssetProxy = assetProxies[assetProxyId]; | |
- require( | |
- currentAssetProxy == address(0), | |
- "ASSET_PROXY_ALREADY_EXISTS" | |
- ); | |
- | |
- // Add asset proxy and log registration. | |
- assetProxies[assetProxyId] = assetProxyContract; | |
- emit AssetProxyRegistered( | |
- assetProxyId, | |
- assetProxy | |
- ); | |
- } | |
- | |
- /// @dev Gets an asset proxy. | |
- /// @param assetProxyId Id of the asset proxy. | |
- /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered. | |
- function getAssetProxy(bytes4 assetProxyId) | |
- external | |
- view | |
- returns (address) | |
- { | |
- return assetProxies[assetProxyId]; | |
- } | |
- | |
- /// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws. | |
- /// @param assetData Byte array encoded for the asset. | |
- /// @param from Address to transfer token from. | |
- /// @param to Address to transfer token to. | |
- /// @param amount Amount of token to transfer. | |
- function dispatchTransferFrom( | |
- bytes memory assetData, | |
- address from, | |
- address to, | |
- uint256 amount | |
- ) | |
- internal | |
- { | |
- // Do nothing if no amount should be transferred. | |
- if (amount > 0 && from != to) { | |
- // Ensure assetData length is valid | |
- require( | |
- assetData.length > 3, | |
- "LENGTH_GREATER_THAN_3_REQUIRED" | |
- ); | |
- | |
- // Lookup assetProxy. We do not use `LibBytes.readBytes4` for gas efficiency reasons. | |
- bytes4 assetProxyId; | |
- assembly { | |
- assetProxyId := and(mload( | |
- add(assetData, 32)), | |
- 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 | |
- ) | |
- } | |
- address assetProxy = assetProxies[assetProxyId]; | |
- | |
- // Ensure that assetProxy exists | |
- require( | |
- assetProxy != address(0), | |
- "ASSET_PROXY_DOES_NOT_EXIST" | |
- ); | |
- | |
- // We construct calldata for the `assetProxy.transferFrom` ABI. | |
- // The layout of this calldata is in the table below. | |
- // | |
- // | Area | Offset | Length | Contents | | |
- // | -------- |--------|---------|-------------------------------------------- | | |
- // | Header | 0 | 4 | function selector | | |
- // | Params | | 4 * 32 | function parameters: | | |
- // | | 4 | | 1. offset to assetData (*) | | |
- // | | 36 | | 2. from | | |
- // | | 68 | | 3. to | | |
- // | | 100 | | 4. amount | | |
- // | Data | | | assetData: | | |
- // | | 132 | 32 | assetData Length | | |
- // | | 164 | ** | assetData Contents | | |
- | |
- assembly { | |
- /////// Setup State /////// | |
- // `cdStart` is the start of the calldata for `assetProxy.transferFrom` (equal to free memory ptr). | |
- let cdStart := mload(64) | |
- // `dataAreaLength` is the total number of words needed to store `assetData` | |
- // As-per the ABI spec, this value is padded up to the nearest multiple of 32, | |
- // and includes 32-bytes for length. | |
- let dataAreaLength := and(add(mload(assetData), 63), 0xFFFFFFFFFFFE0) | |
- // `cdEnd` is the end of the calldata for `assetProxy.transferFrom`. | |
- let cdEnd := add(cdStart, add(132, dataAreaLength)) | |
- | |
- | |
- /////// Setup Header Area /////// | |
- // This area holds the 4-byte `transferFromSelector`. | |
- // bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4 | |
- mstore(cdStart, 0xa85e59e400000000000000000000000000000000000000000000000000000000) | |
- | |
- /////// Setup Params Area /////// | |
- // Each parameter is padded to 32-bytes. The entire Params Area is 128 bytes. | |
- // Notes: | |
- // 1. The offset to `assetData` is the length of the Params Area (128 bytes). | |
- // 2. A 20-byte mask is applied to addresses to zero-out the unused bytes. | |
- mstore(add(cdStart, 4), 128) | |
- mstore(add(cdStart, 36), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) | |
- mstore(add(cdStart, 68), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) | |
- mstore(add(cdStart, 100), amount) | |
- | |
- /////// Setup Data Area /////// | |
- // This area holds `assetData`. | |
- let dataArea := add(cdStart, 132) | |
- // solhint-disable-next-line no-empty-blocks | |
- for {} lt(dataArea, cdEnd) {} { | |
- mstore(dataArea, mload(assetData)) | |
- dataArea := add(dataArea, 32) | |
- assetData := add(assetData, 32) | |
- } | |
- | |
- /////// Call `assetProxy.transferFrom` using the constructed calldata /////// | |
- let success := call( | |
- gas, // forward all gas | |
- assetProxy, // call address of asset proxy | |
- 0, // don't send any ETH | |
- cdStart, // pointer to start of input | |
- sub(cdEnd, cdStart), // length of input | |
- cdStart, // write output over input | |
- 512 // reserve 512 bytes for output | |
- ) | |
- if iszero(success) { | |
- revert(cdStart, returndatasize()) | |
- } | |
- } | |
- } | |
- } | |
-} | |
- | |
-contract MixinMatchOrders is | |
- ReentrancyGuard, | |
- LibConstants, | |
- LibMath, | |
- MAssetProxyDispatcher, | |
- MExchangeCore, | |
- MMatchOrders, | |
- MTransactions | |
-{ | |
- /// @dev Match two complementary orders that have a profitable spread. | |
- /// Each order is filled at their respective price point. However, the calculations are | |
- /// carried out as though the orders are both being filled at the right order's price point. | |
- /// The profit made by the left order goes to the taker (who matched the two orders). | |
- /// @param leftOrder First order to match. | |
- /// @param rightOrder Second order to match. | |
- /// @param leftSignature Proof that order was created by the left maker. | |
- /// @param rightSignature Proof that order was created by the right maker. | |
- /// @return matchedFillResults Amounts filled and fees paid by maker and taker of matched orders. | |
- function matchOrders( | |
- LibOrder.Order memory leftOrder, | |
- LibOrder.Order memory rightOrder, | |
- bytes memory leftSignature, | |
- bytes memory rightSignature | |
- ) | |
- public | |
- nonReentrant | |
- returns (LibFillResults.MatchedFillResults memory matchedFillResults) | |
- { | |
- // We assume that rightOrder.takerAssetData == leftOrder.makerAssetData and rightOrder.makerAssetData == leftOrder.takerAssetData. | |
- // If this assumption isn't true, the match will fail at signature validation. | |
- rightOrder.makerAssetData = leftOrder.takerAssetData; | |
- rightOrder.takerAssetData = leftOrder.makerAssetData; | |
- | |
- // Get left & right order info | |
- LibOrder.OrderInfo memory leftOrderInfo = getOrderInfo(leftOrder); | |
- LibOrder.OrderInfo memory rightOrderInfo = getOrderInfo(rightOrder); | |
- | |
- // Fetch taker address | |
- address takerAddress = getCurrentContextAddress(); | |
- | |
- // Either our context is valid or we revert | |
- assertFillableOrder( | |
- leftOrder, | |
- leftOrderInfo, | |
- takerAddress, | |
- leftSignature | |
- ); | |
- assertFillableOrder( | |
- rightOrder, | |
- rightOrderInfo, | |
- takerAddress, | |
- rightSignature | |
- ); | |
- assertValidMatch(leftOrder, rightOrder); | |
- | |
- // Compute proportional fill amounts | |
- matchedFillResults = calculateMatchedFillResults( | |
- leftOrder, | |
- rightOrder, | |
- leftOrderInfo.orderTakerAssetFilledAmount, | |
- rightOrderInfo.orderTakerAssetFilledAmount | |
- ); | |
- | |
- // Validate fill contexts | |
- assertValidFill( | |
- leftOrder, | |
- leftOrderInfo, | |
- matchedFillResults.left.takerAssetFilledAmount, | |
- matchedFillResults.left.takerAssetFilledAmount, | |
- matchedFillResults.left.makerAssetFilledAmount | |
- ); | |
- assertValidFill( | |
- rightOrder, | |
- rightOrderInfo, | |
- matchedFillResults.right.takerAssetFilledAmount, | |
- matchedFillResults.right.takerAssetFilledAmount, | |
- matchedFillResults.right.makerAssetFilledAmount | |
- ); | |
- | |
- // Update exchange state | |
- updateFilledState( | |
- leftOrder, | |
- takerAddress, | |
- leftOrderInfo.orderHash, | |
- leftOrderInfo.orderTakerAssetFilledAmount, | |
- matchedFillResults.left | |
- ); | |
- updateFilledState( | |
- rightOrder, | |
- takerAddress, | |
- rightOrderInfo.orderHash, | |
- rightOrderInfo.orderTakerAssetFilledAmount, | |
- matchedFillResults.right | |
- ); | |
- | |
- // Settle matched orders. Succeeds or throws. | |
- settleMatchedOrders( | |
- leftOrder, | |
- rightOrder, | |
- takerAddress, | |
- matchedFillResults | |
- ); | |
- | |
- return matchedFillResults; | |
- } | |
- | |
- /// @dev Validates context for matchOrders. Succeeds or throws. | |
- /// @param leftOrder First order to match. | |
- /// @param rightOrder Second order to match. | |
- function assertValidMatch( | |
- LibOrder.Order memory leftOrder, | |
- LibOrder.Order memory rightOrder | |
- ) | |
- internal | |
- pure | |
- { | |
- // Make sure there is a profitable spread. | |
- // There is a profitable spread iff the cost per unit bought (OrderA.MakerAmount/OrderA.TakerAmount) for each order is greater | |
- // than the profit per unit sold of the matched order (OrderB.TakerAmount/OrderB.MakerAmount). | |
- // This is satisfied by the equations below: | |
- // <leftOrder.makerAssetAmount> / <leftOrder.takerAssetAmount> >= <rightOrder.takerAssetAmount> / <rightOrder.makerAssetAmount> | |
- // AND | |
- // <rightOrder.makerAssetAmount> / <rightOrder.takerAssetAmount> >= <leftOrder.takerAssetAmount> / <leftOrder.makerAssetAmount> | |
- // These equations can be combined to get the following: | |
- require( | |
- safeMul(leftOrder.makerAssetAmount, rightOrder.makerAssetAmount) >= | |
- safeMul(leftOrder.takerAssetAmount, rightOrder.takerAssetAmount), | |
- "NEGATIVE_SPREAD_REQUIRED" | |
- ); | |
- } | |
- | |
- /// @dev Calculates fill amounts for the matched orders. | |
- /// Each order is filled at their respective price point. However, the calculations are | |
- /// carried out as though the orders are both being filled at the right order's price point. | |
- /// The profit made by the leftOrder order goes to the taker (who matched the two orders). | |
- /// @param leftOrder First order to match. | |
- /// @param rightOrder Second order to match. | |
- /// @param leftOrderTakerAssetFilledAmount Amount of left order already filled. | |
- /// @param rightOrderTakerAssetFilledAmount Amount of right order already filled. | |
- /// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders. | |
- function calculateMatchedFillResults( | |
- LibOrder.Order memory leftOrder, | |
- LibOrder.Order memory rightOrder, | |
- uint256 leftOrderTakerAssetFilledAmount, | |
- uint256 rightOrderTakerAssetFilledAmount | |
- ) | |
- internal | |
- pure | |
- returns (LibFillResults.MatchedFillResults memory matchedFillResults) | |
- { | |
- // Derive maker asset amounts for left & right orders, given store taker assert amounts | |
- uint256 leftTakerAssetAmountRemaining = safeSub(leftOrder.takerAssetAmount, leftOrderTakerAssetFilledAmount); | |
- uint256 leftMakerAssetAmountRemaining = safeGetPartialAmountFloor( | |
- leftOrder.makerAssetAmount, | |
- leftOrder.takerAssetAmount, | |
- leftTakerAssetAmountRemaining | |
- ); | |
- uint256 rightTakerAssetAmountRemaining = safeSub(rightOrder.takerAssetAmount, rightOrderTakerAssetFilledAmount); | |
- uint256 rightMakerAssetAmountRemaining = safeGetPartialAmountFloor( | |
- rightOrder.makerAssetAmount, | |
- rightOrder.takerAssetAmount, | |
- rightTakerAssetAmountRemaining | |
- ); | |
- | |
- // Calculate fill results for maker and taker assets: at least one order will be fully filled. | |
- // The maximum amount the left maker can buy is `leftTakerAssetAmountRemaining` | |
- // The maximum amount the right maker can sell is `rightMakerAssetAmountRemaining` | |
- // We have two distinct cases for calculating the fill results: | |
- // Case 1. | |
- // If the left maker can buy more than the right maker can sell, then only the right order is fully filled. | |
- // If the left maker can buy exactly what the right maker can sell, then both orders are fully filled. | |
- // Case 2. | |
- // If the left maker cannot buy more than the right maker can sell, then only the left order is fully filled. | |
- if (leftTakerAssetAmountRemaining >= rightMakerAssetAmountRemaining) { | |
- // Case 1: Right order is fully filled | |
- matchedFillResults.right.makerAssetFilledAmount = rightMakerAssetAmountRemaining; | |
- matchedFillResults.right.takerAssetFilledAmount = rightTakerAssetAmountRemaining; | |
- matchedFillResults.left.takerAssetFilledAmount = matchedFillResults.right.makerAssetFilledAmount; | |
- // Round down to ensure the maker's exchange rate does not exceed the price specified by the order. | |
- // We favor the maker when the exchange rate must be rounded. | |
- matchedFillResults.left.makerAssetFilledAmount = safeGetPartialAmountFloor( | |
- leftOrder.makerAssetAmount, | |
- leftOrder.takerAssetAmount, | |
- matchedFillResults.left.takerAssetFilledAmount | |
- ); | |
- } else { | |
- // Case 2: Left order is fully filled | |
- matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining; | |
- matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining; | |
- matchedFillResults.right.makerAssetFilledAmount = matchedFillResults.left.takerAssetFilledAmount; | |
- // Round up to ensure the maker's exchange rate does not exceed the price specified by the order. | |
- // We favor the maker when the exchange rate must be rounded. | |
- matchedFillResults.right.takerAssetFilledAmount = safeGetPartialAmountCeil( | |
- rightOrder.takerAssetAmount, | |
- rightOrder.makerAssetAmount, | |
- matchedFillResults.right.makerAssetFilledAmount | |
- ); | |
- } | |
- | |
- // Calculate amount given to taker | |
- matchedFillResults.leftMakerAssetSpreadAmount = safeSub( | |
- matchedFillResults.left.makerAssetFilledAmount, | |
- matchedFillResults.right.takerAssetFilledAmount | |
- ); | |
- | |
- // Compute fees for left order | |
- matchedFillResults.left.makerFeePaid = safeGetPartialAmountFloor( | |
- matchedFillResults.left.makerAssetFilledAmount, | |
- leftOrder.makerAssetAmount, | |
- leftOrder.makerFee | |
- ); | |
- matchedFillResults.left.takerFeePaid = safeGetPartialAmountFloor( | |
- matchedFillResults.left.takerAssetFilledAmount, | |
- leftOrder.takerAssetAmount, | |
- leftOrder.takerFee | |
- ); | |
- | |
- // Compute fees for right order | |
- matchedFillResults.right.makerFeePaid = safeGetPartialAmountFloor( | |
- matchedFillResults.right.makerAssetFilledAmount, | |
- rightOrder.makerAssetAmount, | |
- rightOrder.makerFee | |
- ); | |
- matchedFillResults.right.takerFeePaid = safeGetPartialAmountFloor( | |
- matchedFillResults.right.takerAssetFilledAmount, | |
- rightOrder.takerAssetAmount, | |
- rightOrder.takerFee | |
- ); | |
- | |
- // Return fill results | |
- return matchedFillResults; | |
- } | |
- | |
- /// @dev Settles matched order by transferring appropriate funds between order makers, taker, and fee recipient. | |
- /// @param leftOrder First matched order. | |
- /// @param rightOrder Second matched order. | |
- /// @param takerAddress Address that matched the orders. The taker receives the spread between orders as profit. | |
- /// @param matchedFillResults Struct holding amounts to transfer between makers, taker, and fee recipients. | |
- function settleMatchedOrders( | |
- LibOrder.Order memory leftOrder, | |
- LibOrder.Order memory rightOrder, | |
- address takerAddress, | |
- LibFillResults.MatchedFillResults memory matchedFillResults | |
- ) | |
- private | |
- { | |
- bytes memory zrxAssetData = ZRX_ASSET_DATA; | |
- // Order makers and taker | |
- dispatchTransferFrom( | |
- leftOrder.makerAssetData, | |
- leftOrder.makerAddress, | |
- rightOrder.makerAddress, | |
- matchedFillResults.right.takerAssetFilledAmount | |
- ); | |
- dispatchTransferFrom( | |
- rightOrder.makerAssetData, | |
- rightOrder.makerAddress, | |
- leftOrder.makerAddress, | |
- matchedFillResults.left.takerAssetFilledAmount | |
- ); | |
- dispatchTransferFrom( | |
- leftOrder.makerAssetData, | |
- leftOrder.makerAddress, | |
- takerAddress, | |
- matchedFillResults.leftMakerAssetSpreadAmount | |
- ); | |
- | |
- // Maker fees | |
- dispatchTransferFrom( | |
- zrxAssetData, | |
- leftOrder.makerAddress, | |
- leftOrder.feeRecipientAddress, | |
- matchedFillResults.left.makerFeePaid | |
- ); | |
- dispatchTransferFrom( | |
- zrxAssetData, | |
- rightOrder.makerAddress, | |
- rightOrder.feeRecipientAddress, | |
- matchedFillResults.right.makerFeePaid | |
- ); | |
- | |
- // Taker fees | |
- if (leftOrder.feeRecipientAddress == rightOrder.feeRecipientAddress) { | |
- dispatchTransferFrom( | |
- zrxAssetData, | |
- takerAddress, | |
- leftOrder.feeRecipientAddress, | |
- safeAdd( | |
- matchedFillResults.left.takerFeePaid, | |
- matchedFillResults.right.takerFeePaid | |
- ) | |
- ); | |
- } else { | |
- dispatchTransferFrom( | |
- zrxAssetData, | |
- takerAddress, | |
- leftOrder.feeRecipientAddress, | |
- matchedFillResults.left.takerFeePaid | |
- ); | |
- dispatchTransferFrom( | |
- zrxAssetData, | |
- takerAddress, | |
- rightOrder.feeRecipientAddress, | |
- matchedFillResults.right.takerFeePaid | |
- ); | |
- } | |
- } | |
-} | |
- | |
-// solhint-disable no-empty-blocks | |
-contract Exchange is | |
- MixinExchangeCore, | |
- MixinMatchOrders, | |
- MixinSignatureValidator, | |
- MixinTransactions, | |
- MixinAssetProxyDispatcher, | |
- MixinWrapperFunctions | |
-{ | |
- string constant public VERSION = "2.0.0"; | |
- | |
- // Mixins are instantiated in the order they are inherited | |
- constructor () | |
- public | |
- MixinExchangeCore() | |
- MixinMatchOrders() | |
- MixinSignatureValidator() | |
- MixinTransactions() | |
- MixinAssetProxyDispatcher() | |
- MixinWrapperFunctions() | |
- {} | |
-} | |
- | |
-contract MAuthorizable is | |
- IAuthorizable | |
-{ | |
- // Event logged when a new address is authorized. | |
- event AuthorizedAddressAdded( | |
- address indexed target, | |
- address indexed caller | |
- ); | |
- | |
- // Event logged when a currently authorized address is unauthorized. | |
- event AuthorizedAddressRemoved( | |
- address indexed target, | |
- address indexed caller | |
- ); | |
- | |
- /// @dev Only authorized addresses can invoke functions with this modifier. | |
- modifier onlyAuthorized { revert(); _; } | |
-} | |
- | |
-contract MixinAuthorizable is | |
- Ownable, | |
- MAuthorizable | |
-{ | |
- /// @dev Only authorized addresses can invoke functions with this modifier. | |
- modifier onlyAuthorized { | |
- require( | |
- authorized[msg.sender], | |
- "SENDER_NOT_AUTHORIZED" | |
- ); | |
- _; | |
- } | |
- | |
- mapping (address => bool) public authorized; | |
- address[] public authorities; | |
- | |
- /// @dev Authorizes an address. | |
- /// @param target Address to authorize. | |
- function addAuthorizedAddress(address target) | |
- external | |
- onlyOwner | |
- { | |
- require( | |
- !authorized[target], | |
- "TARGET_ALREADY_AUTHORIZED" | |
- ); | |
- | |
- authorized[target] = true; | |
- authorities.push(target); | |
- emit AuthorizedAddressAdded(target, msg.sender); | |
- } | |
- | |
- /// @dev Removes authorizion of an address. | |
- /// @param target Address to remove authorization from. | |
- function removeAuthorizedAddress(address target) | |
- external | |
- onlyOwner | |
- { | |
- require( | |
- authorized[target], | |
- "TARGET_NOT_AUTHORIZED" | |
- ); | |
- | |
- delete authorized[target]; | |
- for (uint256 i = 0; i < authorities.length; i++) { | |
- if (authorities[i] == target) { | |
- authorities[i] = authorities[authorities.length - 1]; | |
- authorities.length -= 1; | |
- break; | |
- } | |
- } | |
- emit AuthorizedAddressRemoved(target, msg.sender); | |
- } | |
- | |
- /// @dev Removes authorizion of an address. | |
- /// @param target Address to remove authorization from. | |
- /// @param index Index of target in authorities array. | |
- function removeAuthorizedAddressAtIndex( | |
- address target, | |
- uint256 index | |
- ) | |
- external | |
- onlyOwner | |
- { | |
- require( | |
- authorized[target], | |
- "TARGET_NOT_AUTHORIZED" | |
- ); | |
- require( | |
- index < authorities.length, | |
- "INDEX_OUT_OF_BOUNDS" | |
- ); | |
- require( | |
- authorities[index] == target, | |
- "AUTHORIZED_ADDRESS_MISMATCH" | |
- ); | |
- | |
- delete authorized[target]; | |
- authorities[index] = authorities[authorities.length - 1]; | |
- authorities.length -= 1; | |
- emit AuthorizedAddressRemoved(target, msg.sender); | |
- } | |
- | |
- /// @dev Gets all authorized addresses. | |
- /// @return Array of authorized addresses. | |
- function getAuthorizedAddresses() | |
- external | |
- view | |
- returns (address[] memory) | |
- { | |
- return authorities; | |
- } | |
-} | |
- | |
-contract ERC20Proxy is | |
- MixinAuthorizable | |
-{ | |
- // Id of this proxy. | |
- bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC20Token(address)")); | |
- | |
- // solhint-disable-next-line payable-fallback | |
- function () | |
- external | |
- { | |
- assembly { | |
- // The first 4 bytes of calldata holds the function selector | |
- let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000) | |
- | |
- // `transferFrom` will be called with the following parameters: | |
- // assetData Encoded byte array. | |
- // from Address to transfer asset from. | |
- // to Address to transfer asset to. | |
- // amount Amount of asset to transfer. | |
- // bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4 | |
- if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) { | |
- | |
- // To lookup a value in a mapping, we load from the storage location keccak256(k, p), | |
- // where k is the key left padded to 32 bytes and p is the storage slot | |
- let start := mload(64) | |
- mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff)) | |
- mstore(add(start, 32), authorized_slot) | |
- | |
- // Revert if authorized[msg.sender] == false | |
- if iszero(sload(keccak256(start, 64))) { | |
- // Revert with `Error("SENDER_NOT_AUTHORIZED")` | |
- mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) | |
- mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) | |
- mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000) | |
- mstore(96, 0) | |
- revert(0, 100) | |
- } | |
- | |
- // `transferFrom`. | |
- // The function is marked `external`, so no abi decodeding is done for | |
- // us. Instead, we expect the `calldata` memory to contain the | |
- // following: | |
- // | |
- // | Area | Offset | Length | Contents | | |
- // |----------|--------|---------|-------------------------------------| | |
- // | Header | 0 | 4 | function selector | | |
- // | Params | | 4 * 32 | function parameters: | | |
- // | | 4 | | 1. offset to assetData (*) | | |
- // | | 36 | | 2. from | | |
- // | | 68 | | 3. to | | |
- // | | 100 | | 4. amount | | |
- // | Data | | | assetData: | | |
- // | | 132 | 32 | assetData Length | | |
- // | | 164 | ** | assetData Contents | | |
- // | |
- // (*): offset is computed from start of function parameters, so offset | |
- // by an additional 4 bytes in the calldata. | |
- // | |
- // (**): see table below to compute length of assetData Contents | |
- // | |
- // WARNING: The ABIv2 specification allows additional padding between | |
- // the Params and Data section. This will result in a larger | |
- // offset to assetData. | |
- | |
- // Asset data itself is encoded as follows: | |
- // | |
- // | Area | Offset | Length | Contents | | |
- // |----------|--------|---------|-------------------------------------| | |
- // | Header | 0 | 4 | function selector | | |
- // | Params | | 1 * 32 | function parameters: | | |
- // | | 4 | 12 + 20 | 1. token address | | |
- | |
- // We construct calldata for the `token.transferFrom` ABI. | |
- // The layout of this calldata is in the table below. | |
- // | |
- // | Area | Offset | Length | Contents | | |
- // |----------|--------|---------|-------------------------------------| | |
- // | Header | 0 | 4 | function selector | | |
- // | Params | | 3 * 32 | function parameters: | | |
- // | | 4 | | 1. from | | |
- // | | 36 | | 2. to | | |
- // | | 68 | | 3. amount | | |
- | |
- /////// Read token address from calldata /////// | |
- // * The token address is stored in `assetData`. | |
- // | |
- // * The "offset to assetData" is stored at offset 4 in the calldata (table 1). | |
- // [assetDataOffsetFromParams = calldataload(4)] | |
- // | |
- // * Notes that the "offset to assetData" is relative to the "Params" area of calldata; | |
- // add 4 bytes to account for the length of the "Header" area (table 1). | |
- // [assetDataOffsetFromHeader = assetDataOffsetFromParams + 4] | |
- // | |
- // * The "token address" is offset 32+4=36 bytes into "assetData" (tables 1 & 2). | |
- // [tokenOffset = assetDataOffsetFromHeader + 36 = calldataload(4) + 4 + 36] | |
- let token := calldataload(add(calldataload(4), 40)) | |
- | |
- /////// Setup Header Area /////// | |
- // This area holds the 4-byte `transferFrom` selector. | |
- // Any trailing data in transferFromSelector will be | |
- // overwritten in the next `mstore` call. | |
- mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000) | |
- | |
- /////// Setup Params Area /////// | |
- // We copy the fields `from`, `to` and `amount` in bulk | |
- // from our own calldata to the new calldata. | |
- calldatacopy(4, 36, 96) | |
- | |
- /////// Call `token.transferFrom` using the calldata /////// | |
- let success := call( | |
- gas, // forward all gas | |
- token, // call address of token contract | |
- 0, // don't send any ETH | |
- 0, // pointer to start of input | |
- 100, // length of input | |
- 0, // write output over input | |
- 32 // output size should be 32 bytes | |
- ) | |
- | |
- /////// Check return data. /////// | |
- // If there is no return data, we assume the token incorrectly | |
- // does not return a bool. In this case we expect it to revert | |
- // on failure, which was handled above. | |
- // If the token does return data, we require that it is a single | |
- // nonzero 32 bytes value. | |
- // So the transfer succeeded if the call succeeded and either | |
- // returned nothing, or returned a non-zero 32 byte value. | |
- success := and(success, or( | |
- iszero(returndatasize), | |
- and( | |
- eq(returndatasize, 32), | |
- gt(mload(0), 0) | |
- ) | |
- )) | |
- if success { | |
- return(0, 0) | |
- } | |
- | |
- // Revert with `Error("TRANSFER_FAILED")` | |
- mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) | |
- mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) | |
- mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000) | |
- mstore(96, 0) | |
- revert(0, 100) | |
- } | |
- | |
- // Revert if undefined function is called | |
- revert(0, 0) | |
- } | |
- } | |
- | |
- /// @dev Gets the proxy id associated with the proxy address. | |
- /// @return Proxy id. | |
- function getProxyId() | |
- external | |
- pure | |
- returns (bytes4) | |
- { | |
- return PROXY_ID; | |
- } | |
-} | |
- | |
-contract UnlimitedAllowanceToken is StandardToken { | |
- | |
- uint constant MAX_UINT = 2**256 - 1; | |
- | |
- /// @dev ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance. | |
- /// @param _from Address to transfer from. | |
- /// @param _to Address to transfer to. | |
- /// @param _value Amount to transfer. | |
- /// @return Success of transfer. | |
- function transferFrom(address _from, address _to, uint _value) | |
- public | |
- returns (bool) | |
- { | |
- uint allowance = allowed[_from][msg.sender]; | |
- if (balances[_from] >= _value | |
- && allowance >= _value | |
- && balances[_to] + _value >= balances[_to] | |
- ) { | |
- balances[_to] += _value; | |
- balances[_from] -= _value; | |
- if (allowance < MAX_UINT) { | |
- allowed[_from][msg.sender] -= _value; | |
- } | |
- Transfer(_from, _to, _value); | |
- return true; | |
- } else { | |
- return false; | |
- } | |
- } | |
-} | |
- | |
-contract ZRXToken is UnlimitedAllowanceToken { | |
- | |
- uint8 constant public decimals = 18; | |
- uint public totalSupply = 10**27; // 1 billion tokens, 18 decimal places | |
- string constant public name = "0x Protocol Token"; | |
- string constant public symbol = "ZRX"; | |
- | |
- function ZRXToken() { | |
- balances[msg.sender] = totalSupply; | |
- } | |
-} | |
diff --git a/src/contracts/exchanges/third-party/0x/LibOrder.sol b/src/contracts/exchanges/third-party/0x/LibOrder.sol | |
deleted file mode 100644 | |
index edb620f6..00000000 | |
--- a/src/contracts/exchanges/third-party/0x/LibOrder.sol | |
+++ /dev/null | |
@@ -1,191 +0,0 @@ | |
-pragma solidity ^0.4.21; | |
- | |
-contract LibEIP712 { | |
- | |
- // EIP191 header for EIP712 prefix | |
- string constant internal EIP191_HEADER = "\x19\x01"; | |
- | |
- // EIP712 Domain Name value | |
- string constant internal EIP712_DOMAIN_NAME = "0x Protocol"; | |
- | |
- // EIP712 Domain Version value | |
- string constant internal EIP712_DOMAIN_VERSION = "2"; | |
- | |
- // Hash of the EIP712 Domain Separator Schema | |
- bytes32 constant internal EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked( | |
- "EIP712Domain(", | |
- "string name,", | |
- "string version,", | |
- "address verifyingContract", | |
- ")" | |
- )); | |
- | |
- // Hash of the EIP712 Domain Separator data | |
- // solhint-disable-next-line var-name-mixedcase | |
- bytes32 public EIP712_DOMAIN_HASH; | |
- | |
- constructor () | |
- public | |
- { | |
- EIP712_DOMAIN_HASH = keccak256(abi.encodePacked( | |
- EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH, | |
- keccak256(bytes(EIP712_DOMAIN_NAME)), | |
- keccak256(bytes(EIP712_DOMAIN_VERSION)), | |
- bytes32(address(this)) | |
- )); | |
- } | |
- | |
- /// @dev Calculates EIP712 encoding for a hash struct in this EIP712 Domain. | |
- /// @param hashStruct The EIP712 hash struct. | |
- /// @return EIP712 hash applied to this EIP712 Domain. | |
- function hashEIP712Message(bytes32 hashStruct) | |
- internal | |
- view | |
- returns (bytes32 result) | |
- { | |
- bytes32 eip712DomainHash = EIP712_DOMAIN_HASH; | |
- | |
- // Assembly for more efficient computing: | |
- // keccak256(abi.encodePacked( | |
- // EIP191_HEADER, | |
- // EIP712_DOMAIN_HASH, | |
- // hashStruct | |
- // )); | |
- | |
- assembly { | |
- // Load free memory pointer | |
- let memPtr := mload(64) | |
- | |
- mstore(memPtr, 0x1901000000000000000000000000000000000000000000000000000000000000) // EIP191 header | |
- mstore(add(memPtr, 2), eip712DomainHash) // EIP712 domain hash | |
- mstore(add(memPtr, 34), hashStruct) // Hash of struct | |
- | |
- // Compute hash | |
- result := keccak256(memPtr, 66) | |
- } | |
- return result; | |
- } | |
-} | |
- | |
-contract LibOrder is | |
- LibEIP712 | |
-{ | |
- // Hash for the EIP712 Order Schema | |
- bytes32 constant internal EIP712_ORDER_SCHEMA_HASH = keccak256(abi.encodePacked( | |
- "Order(", | |
- "address makerAddress,", | |
- "address takerAddress,", | |
- "address feeRecipientAddress,", | |
- "address senderAddress,", | |
- "uint256 makerAssetAmount,", | |
- "uint256 takerAssetAmount,", | |
- "uint256 makerFee,", | |
- "uint256 takerFee,", | |
- "uint256 expirationTimeSeconds,", | |
- "uint256 salt,", | |
- "bytes makerAssetData,", | |
- "bytes takerAssetData", | |
- ")" | |
- )); | |
- | |
- // A valid order remains fillable until it is expired, fully filled, or cancelled. | |
- // An order's state is unaffected by external factors, like account balances. | |
- enum OrderStatus { | |
- INVALID, // Default value | |
- INVALID_MAKER_ASSET_AMOUNT, // Order does not have a valid maker asset amount | |
- INVALID_TAKER_ASSET_AMOUNT, // Order does not have a valid taker asset amount | |
- FILLABLE, // Order is fillable | |
- EXPIRED, // Order has already expired | |
- FULLY_FILLED, // Order is fully filled | |
- CANCELLED // Order has been cancelled | |
- } | |
- | |
- // solhint-disable max-line-length | |
- struct Order { | |
- address makerAddress; // Address that created the order. | |
- address takerAddress; // Address that is allowed to fill the order. If set to 0, any address is allowed to fill the order. | |
- address feeRecipientAddress; // Address that will recieve fees when order is filled. | |
- address senderAddress; // Address that is allowed to call Exchange contract methods that affect this order. If set to 0, any address is allowed to call these methods. | |
- uint256 makerAssetAmount; // Amount of makerAsset being offered by maker. Must be greater than 0. | |
- uint256 takerAssetAmount; // Amount of takerAsset being bid on by maker. Must be greater than 0. | |
- uint256 makerFee; // Amount of ZRX paid to feeRecipient by maker when order is filled. If set to 0, no transfer of ZRX from maker to feeRecipient will be attempted. | |
- uint256 takerFee; // Amount of ZRX paid to feeRecipient by taker when order is filled. If set to 0, no transfer of ZRX from taker to feeRecipient will be attempted. | |
- uint256 expirationTimeSeconds; // Timestamp in seconds at which order expires. | |
- uint256 salt; // Arbitrary number to facilitate uniqueness of the order's hash. | |
- bytes makerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring makerAsset. The last byte references the id of this proxy. | |
- bytes takerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset. The last byte references the id of this proxy. | |
- } | |
- // solhint-enable max-line-length | |
- | |
- struct OrderInfo { | |
- uint8 orderStatus; // Status that describes order's validity and fillability. | |
- bytes32 orderHash; // EIP712 hash of the order (see LibOrder.getOrderHash). | |
- uint256 orderTakerAssetFilledAmount; // Amount of order that has already been filled. | |
- } | |
- | |
- /// @dev Calculates Keccak-256 hash of the order. | |
- /// @param order The order structure. | |
- /// @return Keccak-256 EIP712 hash of the order. | |
- function getOrderHash(Order memory order) | |
- internal | |
- view | |
- returns (bytes32 orderHash) | |
- { | |
- orderHash = hashEIP712Message(hashOrder(order)); | |
- return orderHash; | |
- } | |
- | |
- /// @dev Calculates EIP712 hash of the order. | |
- /// @param order The order structure. | |
- /// @return EIP712 hash of the order. | |
- function hashOrder(Order memory order) | |
- internal | |
- pure | |
- returns (bytes32 result) | |
- { | |
- bytes32 schemaHash = EIP712_ORDER_SCHEMA_HASH; | |
- bytes32 makerAssetDataHash = keccak256(order.makerAssetData); | |
- bytes32 takerAssetDataHash = keccak256(order.takerAssetData); | |
- | |
- // Assembly for more efficiently computing: | |
- // keccak256(abi.encodePacked( | |
- // EIP712_ORDER_SCHEMA_HASH, | |
- // bytes32(order.makerAddress), | |
- // bytes32(order.takerAddress), | |
- // bytes32(order.feeRecipientAddress), | |
- // bytes32(order.senderAddress), | |
- // order.makerAssetAmount, | |
- // order.takerAssetAmount, | |
- // order.makerFee, | |
- // order.takerFee, | |
- // order.expirationTimeSeconds, | |
- // order.salt, | |
- // keccak256(order.makerAssetData), | |
- // keccak256(order.takerAssetData) | |
- // )); | |
- | |
- assembly { | |
- // Calculate memory addresses that will be swapped out before hashing | |
- let pos1 := sub(order, 32) | |
- let pos2 := add(order, 320) | |
- let pos3 := add(order, 352) | |
- | |
- // Backup | |
- let temp1 := mload(pos1) | |
- let temp2 := mload(pos2) | |
- let temp3 := mload(pos3) | |
- | |
- // Hash in place | |
- mstore(pos1, schemaHash) | |
- mstore(pos2, makerAssetDataHash) | |
- mstore(pos3, takerAssetDataHash) | |
- result := keccak256(pos1, 416) | |
- | |
- // Restore | |
- mstore(pos1, temp1) | |
- mstore(pos2, temp2) | |
- mstore(pos3, temp3) | |
- } | |
- return result; | |
- } | |
-} | |
\ No newline at end of file | |
diff --git a/src/contracts/exchanges/third-party/0x/Ownable.sol b/src/contracts/exchanges/third-party/0x/Ownable.sol | |
deleted file mode 100644 | |
index 16973c2a..00000000 | |
--- a/src/contracts/exchanges/third-party/0x/Ownable.sol | |
+++ /dev/null | |
@@ -1,36 +0,0 @@ | |
-pragma solidity ^0.4.21; | |
- | |
-contract IOwnable { | |
- | |
- function transferOwnership(address newOwner) | |
- public; | |
-} | |
- | |
-contract Ownable is | |
- IOwnable | |
-{ | |
- address public owner; | |
- | |
- constructor () | |
- public | |
- { | |
- owner = msg.sender; | |
- } | |
- | |
- modifier onlyOwner() { | |
- require( | |
- msg.sender == owner, | |
- "ONLY_CONTRACT_OWNER" | |
- ); | |
- _; | |
- } | |
- | |
- function transferOwnership(address newOwner) | |
- public | |
- onlyOwner | |
- { | |
- if (newOwner != address(0)) { | |
- owner = newOwner; | |
- } | |
- } | |
-} | |
diff --git a/src/contracts/exchanges/third-party/ethfinex/WrapperDependencies.sol b/src/contracts/exchanges/third-party/ethfinex/WrapperDependencies.sol | |
deleted file mode 100644 | |
index c18f3023..00000000 | |
--- a/src/contracts/exchanges/third-party/ethfinex/WrapperDependencies.sol | |
+++ /dev/null | |
@@ -1,89 +0,0 @@ | |
-pragma solidity ^0.4.21; | |
- | |
-import "SafeMath.sol"; | |
- | |
-/** | |
- * @title ERC20Basic | |
- * @dev Simpler version of ERC20 interface | |
- * @dev see https://github.com/ethereum/EIPs/issues/179 | |
- */ | |
-contract ERC20Basic { | |
- function totalSupply() public view returns (uint256); | |
- function balanceOf(address who) public view returns (uint256); | |
- function transfer(address to, uint256 value) public returns (bool); | |
- event Transfer(address indexed from, address indexed to, uint256 value); | |
-} | |
- | |
-contract ERC20Extended is ERC20Basic { | |
- function allowance(address owner, address spender) public view returns (uint256); | |
- function transferFrom(address from, address to, uint256 value) public returns (bool); | |
- function approve(address spender, uint256 value) public returns (bool); | |
- event Approval(address indexed owner, address indexed spender, uint256 value); | |
-} | |
- | |
-/**ERC20OldBasic.sol | |
- * @title ERC20Basic | |
- * @dev Simpler version of ERC20 interface | |
- */ | |
-contract ERC20OldBasic { | |
- function totalSupply() public view returns (uint256); | |
- function balanceOf(address who) public view returns (uint256); | |
- function transfer(address to, uint256 value) public; | |
- event Transfer(address indexed from, address indexed to, uint256 value); | |
-} | |
- | |
-/** | |
- * @title ERC20 interface | |
- * @dev see https://github.com/ethereum/EIPs/issues/20 | |
- */ | |
-contract ERC20Old is ERC20OldBasic { | |
- function allowance(address owner, address spender) public view returns (uint256); | |
- function transferFrom(address from, address to, uint256 value) public; | |
- function approve(address spender, uint256 value) public returns (bool); | |
- event Approval(address indexed owner, address indexed spender, uint256 value); | |
-} | |
- | |
-/** | |
- * @title Basic token | |
- * @dev Basic version of StandardToken, with no allowances. | |
- */ | |
-contract BasicToken is ERC20Basic { | |
- using SafeMath for uint256; | |
- | |
- mapping(address => uint256) balances; | |
- | |
- uint256 totalSupply_; | |
- | |
- /** | |
- * @dev total number of tokens in existence | |
- */ | |
- function totalSupply() public view returns (uint256) { | |
- return totalSupply_; | |
- } | |
- | |
- /** | |
- * @dev transfer token for a specified address | |
- * @param _to The address to transfer to. | |
- * @param _value The amount to be transferred. | |
- */ | |
- function transfer(address _to, uint256 _value) public returns (bool) { | |
- require(_to != address(0)); | |
- require(_value <= balances[msg.sender]); | |
- | |
- // SafeMath.sub will throw if there is not enough balance. | |
- balances[msg.sender] = balances[msg.sender].sub(_value); | |
- balances[_to] = balances[_to].add(_value); | |
- Transfer(msg.sender, _to, _value); | |
- return true; | |
- } | |
- | |
- /** | |
- * @dev Gets the balance of the specified address. | |
- * @param _owner The address to query the the balance of. | |
- * @return An uint256 representing the amount owned by the passed address. | |
- */ | |
- function balanceOf(address _owner) public view returns (uint256 balance) { | |
- return balances[_owner]; | |
- } | |
- | |
-} | |
\ No newline at end of file | |
diff --git a/src/contracts/exchanges/third-party/ethfinex/WrapperLock.sol b/src/contracts/exchanges/third-party/ethfinex/WrapperLock.sol | |
deleted file mode 100644 | |
index 95caa1da..00000000 | |
--- a/src/contracts/exchanges/third-party/ethfinex/WrapperLock.sol | |
+++ /dev/null | |
@@ -1,165 +0,0 @@ | |
-pragma solidity ^0.4.21; | |
- | |
-import "WrapperDependencies.sol"; | |
-import "SafeMath.sol"; | |
-import "Ownable.sol"; | |
- | |
-/* | |
- | |
- Copyright Ethfinex Inc 2018 | |
- | |
- Licensed under the Apache License, Version 2.0 | |
- http://www.apache.org/licenses/LICENSE-2.0 | |
- | |
-*/ | |
- | |
-contract WrapperLock is BasicToken, Ownable { | |
- using SafeMath for uint256; | |
- | |
- address public TRANSFER_PROXY_VEFX; | |
- address public TRANSFER_PROXY_V2; | |
- mapping (address => bool) public isSigner; | |
- | |
- bool public erc20old; | |
- string public name; | |
- string public symbol; | |
- uint public decimals; | |
- address public originalToken; | |
- | |
- mapping (address => uint256) public depositLock; | |
- mapping (address => uint256) public balances; | |
- | |
- function WrapperLock( | |
- address _originalToken, | |
- string _name, | |
- string _symbol, | |
- uint _decimals, | |
- bool _erc20old, | |
- address _proxyEfx, | |
- address _proxyV2 | |
- ) | |
- Ownable() | |
- { | |
- originalToken = _originalToken; | |
- name = _name; | |
- symbol = _symbol; | |
- decimals = _decimals; | |
- isSigner[msg.sender] = true; | |
- erc20old = _erc20old; | |
- TRANSFER_PROXY_VEFX = _proxyEfx; | |
- TRANSFER_PROXY_V2 = _proxyV2; | |
- } | |
- | |
- function deposit(uint _value, uint _forTime) public returns (bool success) { | |
- require(_forTime >= 1); | |
- require(now + _forTime * 1 hours >= depositLock[msg.sender]); | |
- if (erc20old) { | |
- ERC20Old(originalToken).transferFrom(msg.sender, address(this), _value); | |
- } else { | |
- require(ERC20Extended(originalToken).transferFrom(msg.sender, address(this), _value)); | |
- } | |
- balances[msg.sender] = balances[msg.sender].add(_value); | |
- totalSupply_ = totalSupply_.add(_value); | |
- depositLock[msg.sender] = now + _forTime * 1 hours; | |
- return true; | |
- } | |
- | |
- function withdraw( | |
- uint _value, | |
- uint8 v, | |
- bytes32 r, | |
- bytes32 s, | |
- uint signatureValidUntilBlock | |
- ) | |
- public | |
- returns | |
- (bool success) | |
- { | |
- require(balanceOf(msg.sender) >= _value); | |
- if (now <= depositLock[msg.sender]) { | |
- require(block.number < signatureValidUntilBlock); | |
- require(isValidSignature(keccak256(msg.sender, address(this), signatureValidUntilBlock), v, r, s)); | |
- } | |
- balances[msg.sender] = balances[msg.sender].sub(_value); | |
- totalSupply_ = totalSupply_.sub(_value); | |
- depositLock[msg.sender] = 0; | |
- if (erc20old) { | |
- ERC20Old(originalToken).transfer(msg.sender, _value); | |
- } else { | |
- require(ERC20Extended(originalToken).transfer(msg.sender, _value)); | |
- } | |
- return true; | |
- } | |
- | |
- function withdrawBalanceDifference() public onlyOwner returns (bool success) { | |
- require(ERC20Extended(originalToken).balanceOf(address(this)).sub(totalSupply_) > 0); | |
- if (erc20old) { | |
- ERC20Old(originalToken).transfer(msg.sender, ERC20Extended(originalToken).balanceOf(address(this)).sub(totalSupply_)); | |
- } else { | |
- require(ERC20Extended(originalToken).transfer(msg.sender, ERC20Extended(originalToken).balanceOf(address(this)).sub(totalSupply_))); | |
- } | |
- return true; | |
- } | |
- | |
- function withdrawDifferentToken(address _differentToken, bool _erc20old) public onlyOwner returns (bool) { | |
- require(_differentToken != originalToken); | |
- require(ERC20Extended(_differentToken).balanceOf(address(this)) > 0); | |
- if (_erc20old) { | |
- ERC20Old(_differentToken).transfer(msg.sender, ERC20Extended(_differentToken).balanceOf(address(this))); | |
- } else { | |
- require(ERC20Extended(_differentToken).transfer(msg.sender, ERC20Extended(_differentToken).balanceOf(address(this)))); | |
- } | |
- return true; | |
- } | |
- | |
- function transfer(address _to, uint256 _value) public returns (bool) { | |
- return false; | |
- } | |
- | |
- function transferFrom(address _from, address _to, uint _value) public { | |
- require(isSigner[_to] || isSigner[_from]); | |
- assert(msg.sender == TRANSFER_PROXY_VEFX || msg.sender == TRANSFER_PROXY_V2); | |
- balances[_to] = balances[_to].add(_value); | |
- depositLock[_to] = depositLock[_to] > now ? depositLock[_to] : now + 1 hours; | |
- balances[_from] = balances[_from].sub(_value); | |
- Transfer(_from, _to, _value); | |
- } | |
- | |
- function allowance(address _owner, address _spender) public constant returns (uint) { | |
- if (_spender == TRANSFER_PROXY_VEFX || _spender == TRANSFER_PROXY_V2) { | |
- return 2**256 - 1; | |
- } | |
- } | |
- | |
- function balanceOf(address _owner) public constant returns (uint256) { | |
- return balances[_owner]; | |
- } | |
- | |
- function isValidSignature( | |
- bytes32 hash, | |
- uint8 v, | |
- bytes32 r, | |
- bytes32 s | |
- ) | |
- public | |
- constant | |
- returns (bool) | |
- { | |
- return isSigner[ecrecover( | |
- keccak256("\x19Ethereum Signed Message:\n32", hash), | |
- v, | |
- r, | |
- s | |
- )]; | |
- } | |
- | |
- function addSigner(address _newSigner) public { | |
- require(isSigner[msg.sender]); | |
- isSigner[_newSigner] = true; | |
- } | |
- | |
- function keccak(address _sender, address _wrapper, uint _validTill) public constant returns(bytes32) { | |
- return keccak256(_sender, _wrapper, _validTill); | |
- } | |
- | |
-} | |
\ No newline at end of file | |
diff --git a/src/contracts/exchanges/third-party/ethfinex/WrapperLockEth.sol b/src/contracts/exchanges/third-party/ethfinex/WrapperLockEth.sol | |
deleted file mode 100644 | |
index eb63c459..00000000 | |
--- a/src/contracts/exchanges/third-party/ethfinex/WrapperLockEth.sol | |
+++ /dev/null | |
@@ -1,136 +0,0 @@ | |
-pragma solidity ^0.4.21; | |
- | |
-import "WrapperDependencies.sol"; | |
-import "SafeMath.sol"; | |
-import "Ownable.sol"; | |
- | |
-/* | |
- | |
- Copyright Ethfinex Inc 2018 | |
- | |
- Licensed under the Apache License, Version 2.0 | |
- http://www.apache.org/licenses/LICENSE-2.0 | |
- | |
- will@ethfinex.com | |
- | |
-*/ | |
- | |
-contract WrapperLockEth is BasicToken, Ownable { | |
- using SafeMath for uint256; | |
- | |
- address public TRANSFER_PROXY_VEFX; | |
- address public TRANSFER_PROXY_V2; | |
- mapping (address => bool) public isSigner; | |
- | |
- string public name; | |
- string public symbol; | |
- uint public decimals; | |
- address public originalToken = 0x00; | |
- | |
- mapping (address => uint) public depositLock; | |
- mapping (address => uint256) public balances; | |
- | |
- function WrapperLockEth(string _name, string _symbol, uint _decimals, address _proxyEfx, address _proxyV2) Ownable() { | |
- name = _name; | |
- symbol = _symbol; | |
- decimals = _decimals; | |
- isSigner[msg.sender] = true; | |
- TRANSFER_PROXY_VEFX = _proxyEfx; | |
- TRANSFER_PROXY_V2 = _proxyV2; | |
- } | |
- | |
- function deposit(uint _value, uint _forTime) public payable returns (bool success) { | |
- require(_forTime >= 1); | |
- require(now + _forTime * 1 hours >= depositLock[msg.sender]); | |
- balances[msg.sender] = balances[msg.sender].add(msg.value); | |
- totalSupply_ = totalSupply_.add(msg.value); | |
- depositLock[msg.sender] = now + _forTime * 1 hours; | |
- return true; | |
- } | |
- | |
- function withdraw( | |
- uint _value, | |
- uint8 v, | |
- bytes32 r, | |
- bytes32 s, | |
- uint signatureValidUntilBlock | |
- ) | |
- public | |
- returns | |
- (bool) | |
- { | |
- require(balanceOf(msg.sender) >= _value); | |
- if (now > depositLock[msg.sender]) { | |
- balances[msg.sender] = balances[msg.sender].sub(_value); | |
- totalSupply_ = totalSupply_.sub(msg.value); | |
- msg.sender.transfer(_value); | |
- } else { | |
- require(block.number < signatureValidUntilBlock); | |
- require(isValidSignature(keccak256(msg.sender, address(this), signatureValidUntilBlock), v, r, s)); | |
- balances[msg.sender] = balances[msg.sender].sub(_value); | |
- totalSupply_ = totalSupply_.sub(msg.value); | |
- depositLock[msg.sender] = 0; | |
- msg.sender.transfer(_value); | |
- } | |
- return true; | |
- } | |
- | |
- function withdrawDifferentToken(address _token, bool _erc20old) public onlyOwner returns (bool) { | |
- require(ERC20Extended(_token).balanceOf(address(this)) > 0); | |
- if (_erc20old) { | |
- ERC20Old(_token).transfer(msg.sender, ERC20Extended(_token).balanceOf(address(this))); | |
- } else { | |
- ERC20Extended(_token).transfer(msg.sender, ERC20Extended(_token).balanceOf(address(this))); | |
- } | |
- return true; | |
- } | |
- | |
- function transfer(address _to, uint256 _value) public returns (bool) { | |
- return false; | |
- } | |
- | |
- function transferFrom(address _from, address _to, uint _value) public { | |
- require(isSigner[_to] || isSigner[_from]); | |
- assert(msg.sender == TRANSFER_PROXY_VEFX || msg.sender == TRANSFER_PROXY_V2); | |
- balances[_to] = balances[_to].add(_value); | |
- depositLock[_to] = depositLock[_to] > now ? depositLock[_to] : now + 1 hours; | |
- balances[_from] = balances[_from].sub(_value); | |
- Transfer(_from, _to, _value); | |
- } | |
- | |
- function allowance(address _owner, address _spender) public constant returns (uint) { | |
- if (_spender == TRANSFER_PROXY_VEFX || _spender == TRANSFER_PROXY_V2) { | |
- return 2**256 - 1; | |
- } | |
- } | |
- | |
- function balanceOf(address _owner) public constant returns (uint256) { | |
- return balances[_owner]; | |
- } | |
- | |
- function isValidSignature( | |
- bytes32 hash, | |
- uint8 v, | |
- bytes32 r, | |
- bytes32 s) | |
- public | |
- constant | |
- returns (bool) | |
- { | |
- return isSigner[ecrecover( | |
- keccak256("\x19Ethereum Signed Message:\n32", hash), | |
- v, | |
- r, | |
- s | |
- )]; | |
- } | |
- | |
- function addSigner(address _newSigner) public { | |
- require(isSigner[msg.sender]); | |
- isSigner[_newSigner] = true; | |
- } | |
- | |
- function keccak(address _sender, address _wrapper, uint _validTill) public constant returns(bytes32) { | |
- return keccak256(_sender, _wrapper, _validTill); | |
- } | |
-} | |
\ No newline at end of file | |
diff --git a/src/contracts/exchanges/third-party/ethfinex/WrapperRegistryEFX.sol b/src/contracts/exchanges/third-party/ethfinex/WrapperRegistryEFX.sol | |
deleted file mode 100644 | |
index 4822eb67..00000000 | |
--- a/src/contracts/exchanges/third-party/ethfinex/WrapperRegistryEFX.sol | |
+++ /dev/null | |
@@ -1,19 +0,0 @@ | |
-pragma solidity ^0.4.21; | |
- | |
-import "Ownable.sol"; | |
- | |
-contract WrapperRegistryEFX is Ownable{ | |
- | |
- mapping (address => address) public wrapper2TokenLookup; | |
- mapping (address => address) public token2WrapperLookup; | |
- event AddNewPair(address token, address wrapper); | |
- | |
- function addNewWrapperPair(address[] memory originalTokens, address[] memory wrapperTokens) public onlyOwner { | |
- for (uint i = 0; i < originalTokens.length; i++) { | |
- require(token2WrapperLookup[originalTokens[i]] == address(0)); | |
- wrapper2TokenLookup[wrapperTokens[i]] = originalTokens[i]; | |
- token2WrapperLookup[originalTokens[i]] = wrapperTokens[i]; | |
- emit AddNewPair(originalTokens[i],wrapperTokens[i]); | |
- } | |
- } | |
-} | |
\ No newline at end of file | |
diff --git a/src/contracts/exchanges/third-party/kyber/ConversionRates.sol b/src/contracts/exchanges/third-party/kyber/ConversionRates.sol | |
deleted file mode 100644 | |
index be47664c..00000000 | |
--- a/src/contracts/exchanges/third-party/kyber/ConversionRates.sol | |
+++ /dev/null | |
@@ -1,575 +0,0 @@ | |
-pragma solidity ^0.4.21; | |
- | |
-import "KyberDependencies.sol"; | |
- | |
- | |
-contract VolumeImbalanceRecorder is Withdrawable { | |
- | |
- uint constant internal SLIDING_WINDOW_SIZE = 5; | |
- uint constant internal POW_2_64 = 2 ** 64; | |
- | |
- struct TokenControlInfo { | |
- uint minimalRecordResolution; // can be roughly 1 cent | |
- uint maxPerBlockImbalance; // in twei resolution | |
- uint maxTotalImbalance; // max total imbalance (between rate updates) | |
- // before halting trade | |
- } | |
- | |
- mapping(address => TokenControlInfo) internal tokenControlInfo; | |
- | |
- struct TokenImbalanceData { | |
- int lastBlockBuyUnitsImbalance; | |
- uint lastBlock; | |
- | |
- int totalBuyUnitsImbalance; | |
- uint lastRateUpdateBlock; | |
- } | |
- | |
- mapping(address => mapping(uint=>uint)) public tokenImbalanceData; | |
- | |
- function VolumeImbalanceRecorder(address _admin) public { | |
- require(_admin != address(0)); | |
- admin = _admin; | |
- } | |
- | |
- function setTokenControlInfo( | |
- ERC20Clone token, | |
- uint minimalRecordResolution, | |
- uint maxPerBlockImbalance, | |
- uint maxTotalImbalance | |
- ) | |
- public | |
- onlyAdmin | |
- { | |
- tokenControlInfo[token] = | |
- TokenControlInfo( | |
- minimalRecordResolution, | |
- maxPerBlockImbalance, | |
- maxTotalImbalance | |
- ); | |
- } | |
- | |
- function getTokenControlInfo(ERC20Clone token) public view returns(uint, uint, uint) { | |
- return (tokenControlInfo[token].minimalRecordResolution, | |
- tokenControlInfo[token].maxPerBlockImbalance, | |
- tokenControlInfo[token].maxTotalImbalance); | |
- } | |
- | |
- function addImbalance( | |
- ERC20Clone token, | |
- int buyAmount, | |
- uint rateUpdateBlock, | |
- uint currentBlock | |
- ) | |
- internal | |
- { | |
- uint currentBlockIndex = currentBlock % SLIDING_WINDOW_SIZE; | |
- int recordedBuyAmount = int(buyAmount / int(tokenControlInfo[token].minimalRecordResolution)); | |
- | |
- int prevImbalance = 0; | |
- | |
- TokenImbalanceData memory currentBlockData = | |
- decodeTokenImbalanceData(tokenImbalanceData[token][currentBlockIndex]); | |
- | |
- // first scenario - this is not the first tx in the current block | |
- if (currentBlockData.lastBlock == currentBlock) { | |
- if (uint(currentBlockData.lastRateUpdateBlock) == rateUpdateBlock) { | |
- // just increase imbalance | |
- currentBlockData.lastBlockBuyUnitsImbalance += recordedBuyAmount; | |
- currentBlockData.totalBuyUnitsImbalance += recordedBuyAmount; | |
- } else { | |
- // imbalance was changed in the middle of the block | |
- prevImbalance = getImbalanceInRange(token, rateUpdateBlock, currentBlock); | |
- currentBlockData.totalBuyUnitsImbalance = int(prevImbalance) + recordedBuyAmount; | |
- currentBlockData.lastBlockBuyUnitsImbalance += recordedBuyAmount; | |
- currentBlockData.lastRateUpdateBlock = uint(rateUpdateBlock); | |
- } | |
- } else { | |
- // first tx in the current block | |
- int currentBlockImbalance; | |
- (prevImbalance, currentBlockImbalance) = getImbalanceSinceRateUpdate(token, rateUpdateBlock, currentBlock); | |
- | |
- currentBlockData.lastBlockBuyUnitsImbalance = recordedBuyAmount; | |
- currentBlockData.lastBlock = uint(currentBlock); | |
- currentBlockData.lastRateUpdateBlock = uint(rateUpdateBlock); | |
- currentBlockData.totalBuyUnitsImbalance = int(prevImbalance) + recordedBuyAmount; | |
- } | |
- | |
- tokenImbalanceData[token][currentBlockIndex] = encodeTokenImbalanceData(currentBlockData); | |
- } | |
- | |
- function setGarbageToVolumeRecorder(ERC20Clone token) internal { | |
- for (uint i = 0; i < SLIDING_WINDOW_SIZE; i++) { | |
- tokenImbalanceData[token][i] = 0x1; | |
- } | |
- } | |
- | |
- function getImbalanceInRange(ERC20Clone token, uint startBlock, uint endBlock) internal view returns(int buyImbalance) { | |
- // check the imbalance in the sliding window | |
- require(startBlock <= endBlock); | |
- | |
- buyImbalance = 0; | |
- | |
- for (uint windowInd = 0; windowInd < SLIDING_WINDOW_SIZE; windowInd++) { | |
- TokenImbalanceData memory perBlockData = decodeTokenImbalanceData(tokenImbalanceData[token][windowInd]); | |
- | |
- if (perBlockData.lastBlock <= endBlock && perBlockData.lastBlock >= startBlock) { | |
- buyImbalance += int(perBlockData.lastBlockBuyUnitsImbalance); | |
- } | |
- } | |
- } | |
- | |
- function getImbalanceSinceRateUpdate(ERC20Clone token, uint rateUpdateBlock, uint currentBlock) | |
- internal view | |
- returns(int buyImbalance, int currentBlockImbalance) | |
- { | |
- buyImbalance = 0; | |
- currentBlockImbalance = 0; | |
- uint latestBlock = 0; | |
- int imbalanceInRange = 0; | |
- uint startBlock = rateUpdateBlock; | |
- uint endBlock = currentBlock; | |
- | |
- for (uint windowInd = 0; windowInd < SLIDING_WINDOW_SIZE; windowInd++) { | |
- TokenImbalanceData memory perBlockData = decodeTokenImbalanceData(tokenImbalanceData[token][windowInd]); | |
- | |
- if (perBlockData.lastBlock <= endBlock && perBlockData.lastBlock >= startBlock) { | |
- imbalanceInRange += perBlockData.lastBlockBuyUnitsImbalance; | |
- } | |
- | |
- if (perBlockData.lastRateUpdateBlock != rateUpdateBlock) continue; | |
- if (perBlockData.lastBlock < latestBlock) continue; | |
- | |
- latestBlock = perBlockData.lastBlock; | |
- buyImbalance = perBlockData.totalBuyUnitsImbalance; | |
- if (uint(perBlockData.lastBlock) == currentBlock) { | |
- currentBlockImbalance = perBlockData.lastBlockBuyUnitsImbalance; | |
- } | |
- } | |
- | |
- if (buyImbalance == 0) { | |
- buyImbalance = imbalanceInRange; | |
- } | |
- } | |
- | |
- function getImbalance(ERC20Clone token, uint rateUpdateBlock, uint currentBlock) | |
- internal view | |
- returns(int totalImbalance, int currentBlockImbalance) | |
- { | |
- | |
- int resolution = int(tokenControlInfo[token].minimalRecordResolution); | |
- | |
- (totalImbalance, currentBlockImbalance) = | |
- getImbalanceSinceRateUpdate( | |
- token, | |
- rateUpdateBlock, | |
- currentBlock); | |
- | |
- totalImbalance *= resolution; | |
- currentBlockImbalance *= resolution; | |
- } | |
- | |
- function getMaxPerBlockImbalance(ERC20Clone token) internal view returns(uint) { | |
- return tokenControlInfo[token].maxPerBlockImbalance; | |
- } | |
- | |
- function getMaxTotalImbalance(ERC20Clone token) internal view returns(uint) { | |
- return tokenControlInfo[token].maxTotalImbalance; | |
- } | |
- | |
- function encodeTokenImbalanceData(TokenImbalanceData data) internal pure returns(uint) { | |
- // check for overflows | |
- require(data.lastBlockBuyUnitsImbalance < int(POW_2_64 / 2)); | |
- require(data.lastBlockBuyUnitsImbalance > int(-1 * int(POW_2_64) / 2)); | |
- require(data.lastBlock < POW_2_64); | |
- require(data.totalBuyUnitsImbalance < int(POW_2_64 / 2)); | |
- require(data.totalBuyUnitsImbalance > int(-1 * int(POW_2_64) / 2)); | |
- require(data.lastRateUpdateBlock < POW_2_64); | |
- | |
- // do encoding | |
- uint result = uint(data.lastBlockBuyUnitsImbalance) & (POW_2_64 - 1); | |
- result |= data.lastBlock * POW_2_64; | |
- result |= (uint(data.totalBuyUnitsImbalance) & (POW_2_64 - 1)) * POW_2_64 * POW_2_64; | |
- result |= data.lastRateUpdateBlock * POW_2_64 * POW_2_64 * POW_2_64; | |
- | |
- return result; | |
- } | |
- | |
- function decodeTokenImbalanceData(uint input) internal pure returns(TokenImbalanceData) { | |
- TokenImbalanceData memory data; | |
- | |
- data.lastBlockBuyUnitsImbalance = int(int64(input & (POW_2_64 - 1))); | |
- data.lastBlock = uint(uint64((input / POW_2_64) & (POW_2_64 - 1))); | |
- data.totalBuyUnitsImbalance = int(int64((input / (POW_2_64 * POW_2_64)) & (POW_2_64 - 1))); | |
- data.lastRateUpdateBlock = uint(uint64((input / (POW_2_64 * POW_2_64 * POW_2_64)))); | |
- | |
- return data; | |
- } | |
-} | |
- | |
-contract ConversionRates is ConversionRatesInterface, VolumeImbalanceRecorder, Utils { | |
- | |
- // bps - basic rate steps. one step is 1 / 10000 of the rate. | |
- struct StepFunction { | |
- int[] x; // quantity for each step. Quantity of each step includes previous steps. | |
- int[] y; // rate change per quantity step in bps. | |
- } | |
- | |
- struct TokenData { | |
- bool listed; // was added to reserve | |
- bool enabled; // whether trade is enabled | |
- | |
- // position in the compact data | |
- uint compactDataArrayIndex; | |
- uint compactDataFieldIndex; | |
- | |
- // rate data. base and changes according to quantity and reserve balance. | |
- // generally speaking. Sell rate is 1 / buy rate i.e. the buy in the other direction. | |
- uint baseBuyRate; // in PRECISION units. see KyberConstants | |
- uint baseSellRate; // PRECISION units. without (sell / buy) spread it is 1 / baseBuyRate | |
- StepFunction buyRateQtyStepFunction; // in bps. higher quantity - bigger the rate. | |
- StepFunction sellRateQtyStepFunction;// in bps. higher the qua | |
- StepFunction buyRateImbalanceStepFunction; // in BPS. higher reserve imbalance - bigger the rate. | |
- StepFunction sellRateImbalanceStepFunction; | |
- } | |
- | |
- /* | |
- this is the data for tokenRatesCompactData | |
- but solidity compiler optimizer is sub-optimal, and cannot write this structure in a single storage write | |
- so we represent it as bytes32 and do the byte tricks ourselves. | |
- struct TokenRatesCompactData { | |
- bytes14 buy; // change buy rate of token from baseBuyRate in 10 bps | |
- bytes14 sell; // change sell rate of token from baseSellRate in 10 bps | |
- | |
- uint32 blockNumber; | |
- } */ | |
- uint public validRateDurationInBlocks = 10; // rates are valid for this amount of blocks | |
- ERC20Clone[] internal listedTokens; | |
- mapping(address=>TokenData) internal tokenData; | |
- bytes32[] internal tokenRatesCompactData; | |
- uint public numTokensInCurrentCompactData = 0; | |
- address public reserveContract; | |
- uint constant internal NUM_TOKENS_IN_COMPACT_DATA = 14; | |
- uint constant internal BYTES_14_OFFSET = (2 ** (8 * NUM_TOKENS_IN_COMPACT_DATA)); | |
- uint constant internal MAX_STEPS_IN_FUNCTION = 10; | |
- int constant internal MAX_BPS_ADJUSTMENT = 10 ** 11; // 1B % | |
- int constant internal MIN_BPS_ADJUSTMENT = -100 * 100; // cannot go down by more than 100% | |
- | |
- function ConversionRates(address _admin) public VolumeImbalanceRecorder(_admin) | |
- { } // solhint-disable-line no-empty-blocks | |
- | |
- function addToken(ERC20Clone token) public onlyAdmin { | |
- | |
- require(!tokenData[token].listed); | |
- tokenData[token].listed = true; | |
- listedTokens.push(token); | |
- | |
- if (numTokensInCurrentCompactData == 0) { | |
- tokenRatesCompactData.length++; // add new structure | |
- } | |
- | |
- tokenData[token].compactDataArrayIndex = tokenRatesCompactData.length - 1; | |
- tokenData[token].compactDataFieldIndex = numTokensInCurrentCompactData; | |
- | |
- numTokensInCurrentCompactData = (numTokensInCurrentCompactData + 1) % NUM_TOKENS_IN_COMPACT_DATA; | |
- | |
- setGarbageToVolumeRecorder(token); | |
- | |
- setDecimals(token); | |
- } | |
- | |
- function setCompactData(bytes14[] buy, bytes14[] sell, uint blockNumber, uint[] indices) public onlyOperator { | |
- | |
- require(buy.length == sell.length); | |
- require(indices.length == buy.length); | |
- require(blockNumber <= 0xFFFFFFFF); | |
- | |
- uint bytes14Offset = BYTES_14_OFFSET; | |
- | |
- for (uint i = 0; i < indices.length; i++) { | |
- require(indices[i] < tokenRatesCompactData.length); | |
- uint data = uint(buy[i]) | uint(sell[i]) * bytes14Offset | (blockNumber * (bytes14Offset * bytes14Offset)); | |
- tokenRatesCompactData[indices[i]] = bytes32(data); | |
- } | |
- } | |
- | |
- function setBaseRate( | |
- ERC20Clone[] tokens, | |
- uint[] baseBuy, | |
- uint[] baseSell, | |
- bytes14[] buy, | |
- bytes14[] sell, | |
- uint blockNumber, | |
- uint[] indices | |
- ) | |
- public | |
- onlyOperator | |
- { | |
- require(tokens.length == baseBuy.length); | |
- require(tokens.length == baseSell.length); | |
- require(sell.length == buy.length); | |
- require(sell.length == indices.length); | |
- | |
- for (uint ind = 0; ind < tokens.length; ind++) { | |
- require(tokenData[tokens[ind]].listed); | |
- tokenData[tokens[ind]].baseBuyRate = baseBuy[ind]; | |
- tokenData[tokens[ind]].baseSellRate = baseSell[ind]; | |
- } | |
- | |
- setCompactData(buy, sell, blockNumber, indices); | |
- } | |
- | |
- function setQtyStepFunction( | |
- ERC20Clone token, | |
- int[] xBuy, | |
- int[] yBuy, | |
- int[] xSell, | |
- int[] ySell | |
- ) | |
- public | |
- onlyOperator | |
- { | |
- require(xBuy.length == yBuy.length); | |
- require(xSell.length == ySell.length); | |
- require(xBuy.length <= MAX_STEPS_IN_FUNCTION); | |
- require(xSell.length <= MAX_STEPS_IN_FUNCTION); | |
- require(tokenData[token].listed); | |
- | |
- tokenData[token].buyRateQtyStepFunction = StepFunction(xBuy, yBuy); | |
- tokenData[token].sellRateQtyStepFunction = StepFunction(xSell, ySell); | |
- } | |
- | |
- function setImbalanceStepFunction( | |
- ERC20Clone token, | |
- int[] xBuy, | |
- int[] yBuy, | |
- int[] xSell, | |
- int[] ySell | |
- ) | |
- public | |
- onlyOperator | |
- { | |
- require(xBuy.length == yBuy.length); | |
- require(xSell.length == ySell.length); | |
- require(xBuy.length <= MAX_STEPS_IN_FUNCTION); | |
- require(xSell.length <= MAX_STEPS_IN_FUNCTION); | |
- require(tokenData[token].listed); | |
- | |
- tokenData[token].buyRateImbalanceStepFunction = StepFunction(xBuy, yBuy); | |
- tokenData[token].sellRateImbalanceStepFunction = StepFunction(xSell, ySell); | |
- } | |
- | |
- function setValidRateDurationInBlocks(uint duration) public onlyAdmin { | |
- validRateDurationInBlocks = duration; | |
- } | |
- | |
- function enableTokenTrade(ERC20Clone token) public onlyAdmin { | |
- require(tokenData[token].listed); | |
- require(tokenControlInfo[token].minimalRecordResolution != 0); | |
- tokenData[token].enabled = true; | |
- } | |
- | |
- function disableTokenTrade(ERC20Clone token) public onlyAlerter { | |
- require(tokenData[token].listed); | |
- tokenData[token].enabled = false; | |
- } | |
- | |
- function setReserveAddress(address reserve) public onlyAdmin { | |
- reserveContract = reserve; | |
- } | |
- | |
- function recordImbalance( | |
- ERC20Clone token, | |
- int buyAmount, | |
- uint rateUpdateBlock, | |
- uint currentBlock | |
- ) | |
- public | |
- { | |
- require(msg.sender == reserveContract); | |
- | |
- if (rateUpdateBlock == 0) rateUpdateBlock = getRateUpdateBlock(token); | |
- | |
- return addImbalance(token, buyAmount, rateUpdateBlock, currentBlock); | |
- } | |
- | |
- /* solhint-disable function-max-lines */ | |
- function getRate(ERC20Clone token, uint currentBlockNumber, bool buy, uint qty) public view returns(uint) { | |
- // check if trade is enabled | |
- if (!tokenData[token].enabled) return 0; | |
- if (tokenControlInfo[token].minimalRecordResolution == 0) return 0; // token control info not set | |
- | |
- // get rate update block | |
- bytes32 compactData = tokenRatesCompactData[tokenData[token].compactDataArrayIndex]; | |
- | |
- uint updateRateBlock = getLast4Bytes(compactData); | |
- if (currentBlockNumber >= updateRateBlock + validRateDurationInBlocks) return 0; // rate is expired | |
- // check imbalance | |
- int totalImbalance; | |
- int blockImbalance; | |
- (totalImbalance, blockImbalance) = getImbalance(token, updateRateBlock, currentBlockNumber); | |
- | |
- // calculate actual rate | |
- int imbalanceQty; | |
- int extraBps; | |
- int8 rateUpdate; | |
- uint rate; | |
- | |
- if (buy) { | |
- // start with base rate | |
- rate = tokenData[token].baseBuyRate; | |
- | |
- // add rate update | |
- rateUpdate = getRateByteFromCompactData(compactData, token, true); | |
- extraBps = int(rateUpdate) * 10; | |
- rate = addBps(rate, extraBps); | |
- | |
- // compute token qty | |
- qty = getTokenQty(token, rate, qty); | |
- imbalanceQty = int(qty); | |
- totalImbalance += imbalanceQty; | |
- | |
- // add qty overhead | |
- extraBps = executeStepFunction(tokenData[token].buyRateQtyStepFunction, int(qty)); | |
- rate = addBps(rate, extraBps); | |
- | |
- // add imbalance overhead | |
- extraBps = executeStepFunction(tokenData[token].buyRateImbalanceStepFunction, totalImbalance); | |
- rate = addBps(rate, extraBps); | |
- } else { | |
- // start with base rate | |
- rate = tokenData[token].baseSellRate; | |
- | |
- // add rate update | |
- rateUpdate = getRateByteFromCompactData(compactData, token, false); | |
- extraBps = int(rateUpdate) * 10; | |
- rate = addBps(rate, extraBps); | |
- | |
- // compute token qty | |
- imbalanceQty = -1 * int(qty); | |
- totalImbalance += imbalanceQty; | |
- | |
- // add qty overhead | |
- extraBps = executeStepFunction(tokenData[token].sellRateQtyStepFunction, int(qty)); | |
- rate = addBps(rate, extraBps); | |
- | |
- // add imbalance overhead | |
- extraBps = executeStepFunction(tokenData[token].sellRateImbalanceStepFunction, totalImbalance); | |
- rate = addBps(rate, extraBps); | |
- } | |
- | |
- if (abs(totalImbalance) >= getMaxTotalImbalance(token)) return 0; | |
- if (abs(blockImbalance + imbalanceQty) >= getMaxPerBlockImbalance(token)) return 0; | |
- | |
- return rate; | |
- } | |
- /* solhint-enable function-max-lines */ | |
- | |
- function getBasicRate(ERC20Clone token, bool buy) public view returns(uint) { | |
- if (buy) | |
- return tokenData[token].baseBuyRate; | |
- else | |
- return tokenData[token].baseSellRate; | |
- } | |
- | |
- function getCompactData(ERC20Clone token) public view returns(uint, uint, byte, byte) { | |
- require(tokenData[token].listed); | |
- | |
- uint arrayIndex = tokenData[token].compactDataArrayIndex; | |
- uint fieldOffset = tokenData[token].compactDataFieldIndex; | |
- | |
- return ( | |
- arrayIndex, | |
- fieldOffset, | |
- byte(getRateByteFromCompactData(tokenRatesCompactData[arrayIndex], token, true)), | |
- byte(getRateByteFromCompactData(tokenRatesCompactData[arrayIndex], token, false)) | |
- ); | |
- } | |
- | |
- function getTokenBasicData(ERC20Clone token) public view returns(bool, bool) { | |
- return (tokenData[token].listed, tokenData[token].enabled); | |
- } | |
- | |
- /* solhint-disable code-complexity */ | |
- function getStepFunctionData(ERC20Clone token, uint command, uint param) public view returns(int) { | |
- if (command == 0) return int(tokenData[token].buyRateQtyStepFunction.x.length); | |
- if (command == 1) return tokenData[token].buyRateQtyStepFunction.x[param]; | |
- if (command == 2) return int(tokenData[token].buyRateQtyStepFunction.y.length); | |
- if (command == 3) return tokenData[token].buyRateQtyStepFunction.y[param]; | |
- | |
- if (command == 4) return int(tokenData[token].sellRateQtyStepFunction.x.length); | |
- if (command == 5) return tokenData[token].sellRateQtyStepFunction.x[param]; | |
- if (command == 6) return int(tokenData[token].sellRateQtyStepFunction.y.length); | |
- if (command == 7) return tokenData[token].sellRateQtyStepFunction.y[param]; | |
- | |
- if (command == 8) return int(tokenData[token].buyRateImbalanceStepFunction.x.length); | |
- if (command == 9) return tokenData[token].buyRateImbalanceStepFunction.x[param]; | |
- if (command == 10) return int(tokenData[token].buyRateImbalanceStepFunction.y.length); | |
- if (command == 11) return tokenData[token].buyRateImbalanceStepFunction.y[param]; | |
- | |
- if (command == 12) return int(tokenData[token].sellRateImbalanceStepFunction.x.length); | |
- if (command == 13) return tokenData[token].sellRateImbalanceStepFunction.x[param]; | |
- if (command == 14) return int(tokenData[token].sellRateImbalanceStepFunction.y.length); | |
- if (command == 15) return tokenData[token].sellRateImbalanceStepFunction.y[param]; | |
- | |
- revert(); | |
- } | |
- /* solhint-enable code-complexity */ | |
- | |
- function getRateUpdateBlock(ERC20Clone token) public view returns(uint) { | |
- bytes32 compactData = tokenRatesCompactData[tokenData[token].compactDataArrayIndex]; | |
- return getLast4Bytes(compactData); | |
- } | |
- | |
- function getListedTokens() public view returns(ERC20Clone[]) { | |
- return listedTokens; | |
- } | |
- | |
- function getTokenQty(ERC20Clone token, uint ethQty, uint rate) internal view returns(uint) { | |
- uint dstDecimals = getDecimals(token); | |
- uint srcDecimals = ETH_DECIMALS; | |
- | |
- return calcDstQty(ethQty, srcDecimals, dstDecimals, rate); | |
- } | |
- | |
- function getLast4Bytes(bytes32 b) internal pure returns(uint) { | |
- // cannot trust compiler with not turning bit operations into EXP opcode | |
- return uint(b) / (BYTES_14_OFFSET * BYTES_14_OFFSET); | |
- } | |
- | |
- function getRateByteFromCompactData(bytes32 data, ERC20Clone token, bool buy) internal view returns(int8) { | |
- uint fieldOffset = tokenData[token].compactDataFieldIndex; | |
- uint byteOffset; | |
- if (buy) | |
- byteOffset = 32 - NUM_TOKENS_IN_COMPACT_DATA + fieldOffset; | |
- else | |
- byteOffset = 4 + fieldOffset; | |
- | |
- return int8(data[byteOffset]); | |
- } | |
- | |
- function executeStepFunction(StepFunction f, int x) internal pure returns(int) { | |
- uint len = f.y.length; | |
- for (uint ind = 0; ind < len; ind++) { | |
- if (x <= f.x[ind]) return f.y[ind]; | |
- } | |
- | |
- return f.y[len-1]; | |
- } | |
- | |
- function addBps(uint rate, int bps) internal pure returns(uint) { | |
- require(rate <= MAX_RATE); | |
- require(bps >= MIN_BPS_ADJUSTMENT); | |
- require(bps <= MAX_BPS_ADJUSTMENT); | |
- | |
- uint maxBps = 100 * 100; | |
- return (rate * uint(int(maxBps) + bps)) / maxBps; | |
- } | |
- | |
- function abs(int x) internal pure returns(uint) { | |
- if (x < 0) | |
- return uint(-1 * x); | |
- else | |
- return uint(x); | |
- } | |
-} | |
diff --git a/src/contracts/exchanges/third-party/kyber/ExpectedRate.sol b/src/contracts/exchanges/third-party/kyber/ExpectedRate.sol | |
deleted file mode 100644 | |
index d0b29151..00000000 | |
--- a/src/contracts/exchanges/third-party/kyber/ExpectedRate.sol | |
+++ /dev/null | |
@@ -1,80 +0,0 @@ | |
-pragma solidity ^0.4.21; | |
- | |
-import "KyberDependencies.sol"; | |
-import "KyberNetwork.sol"; | |
- | |
-contract ExpectedRate is Withdrawable, ExpectedRateInterface, Utils { | |
- | |
- KyberNetwork public kyberNetwork; | |
- uint public quantityFactor = 2; | |
- uint public worstCaseRateFactorInBps = 50; | |
- | |
- function ExpectedRate(KyberNetwork _kyberNetwork, address _admin) public { | |
- require(_admin != address(0)); | |
- require(_kyberNetwork != address(0)); | |
- kyberNetwork = _kyberNetwork; | |
- admin = _admin; | |
- } | |
- | |
- event QuantityFactorSet (uint newFactor, uint oldFactor, address sender); | |
- | |
- function setQuantityFactor(uint newFactor) public onlyOperator { | |
- require(newFactor <= 100); | |
- | |
- QuantityFactorSet(newFactor, quantityFactor, msg.sender); | |
- quantityFactor = newFactor; | |
- } | |
- | |
- event MinSlippageFactorSet (uint newMin, uint oldMin, address sender); | |
- | |
- function setWorstCaseRateFactor(uint bps) public onlyOperator { | |
- require(bps <= 100 * 100); | |
- | |
- MinSlippageFactorSet(bps, worstCaseRateFactorInBps, msg.sender); | |
- worstCaseRateFactorInBps = bps; | |
- } | |
- | |
- //@dev when srcQty too small or 0 the expected rate will be calculated without quantity, | |
- // will enable rate reference before committing to any quantity | |
- //@dev when srcQty too small (no actual dest qty) slippage rate will be 0. | |
- function getExpectedRate(ERC20Clone src, ERC20Clone dest, uint srcQty) | |
- public view | |
- returns (uint expectedRate, uint slippageRate) | |
- { | |
- require(quantityFactor != 0); | |
- require(srcQty <= MAX_QTY); | |
- require(srcQty * quantityFactor <= MAX_QTY); | |
- | |
- if (srcQty == 0) srcQty = 1; | |
- | |
- uint bestReserve; | |
- uint worstCaseSlippageRate; | |
- | |
- (bestReserve, expectedRate) = kyberNetwork.findBestRate(src, dest, srcQty); | |
- (bestReserve, slippageRate) = kyberNetwork.findBestRate(src, dest, (srcQty * quantityFactor)); | |
- | |
- if (expectedRate == 0) { | |
- expectedRate = expectedRateSmallQty(src, dest); | |
- } | |
- | |
- require(expectedRate <= MAX_RATE); | |
- | |
- worstCaseSlippageRate = ((10000 - worstCaseRateFactorInBps) * expectedRate) / 10000; | |
- if (slippageRate >= worstCaseSlippageRate) { | |
- slippageRate = worstCaseSlippageRate; | |
- } | |
- | |
- return (expectedRate, slippageRate); | |
- } | |
- | |
- //@dev for small src quantities dest qty might be 0, then returned rate is zero. | |
- //@dev for backward compatibility we would like to return non zero rate (correct one) for small src qty | |
- function expectedRateSmallQty(ERC20Clone src, ERC20Clone dest) internal view returns(uint) { | |
- address reserve; | |
- uint rateSrcToEth; | |
- uint rateEthToDest; | |
- (reserve, rateSrcToEth) = kyberNetwork.searchBestRate(src, ETH_TOKEN_ADDRESS, 0); | |
- (reserve, rateEthToDest) = kyberNetwork.searchBestRate(ETH_TOKEN_ADDRESS, dest, 0); | |
- return rateSrcToEth * rateEthToDest / PRECISION; | |
- } | |
-} | |
diff --git a/src/contracts/exchanges/third-party/kyber/KyberDependencies.sol b/src/contracts/exchanges/third-party/kyber/KyberDependencies.sol | |
deleted file mode 100644 | |
index 73fee2da..00000000 | |
--- a/src/contracts/exchanges/third-party/kyber/KyberDependencies.sol | |
+++ /dev/null | |
@@ -1,581 +0,0 @@ | |
-pragma solidity ^0.4.21; | |
- | |
-import "StandardToken.sol"; | |
- | |
-contract ERC20Clone is ERC20 { | |
- function totalSupply() public view returns (uint supply); | |
- function balanceOf(address _owner) public view returns (uint balance); | |
- function transfer(address _to, uint _value) public returns (bool success); | |
- function transferFrom(address _from, address _to, uint _value) public returns (bool success); | |
- function approve(address _spender, uint _value) public returns (bool success); | |
- function allowance(address _owner, address _spender) public view returns (uint remaining); | |
- function decimals() public view returns(uint digits); | |
- event Approval(address indexed _owner, address indexed _spender, uint _value); | |
-} | |
- | |
-/// @title Kyber Reserve contract | |
-interface KyberReserveInterface { | |
- | |
- function trade( | |
- ERC20Clone srcToken, | |
- uint srcAmount, | |
- ERC20Clone destToken, | |
- address destAddress, | |
- uint conversionRate, | |
- bool validate | |
- ) | |
- public | |
- payable | |
- returns(bool); | |
- | |
- function getConversionRate(ERC20Clone src, ERC20Clone dest, uint srcQty, uint blockNumber) public view returns(uint); | |
-} | |
- | |
-/// @title Kyber Network interface | |
-interface KyberNetworkInterface { | |
- function maxGasPrice() public view returns(uint); | |
- function getUserCapInWei(address user) public view returns(uint); | |
- function getUserCapInTokenWei(address user, ERC20Clone token) public view returns(uint); | |
- function enabled() public view returns(bool); | |
- function info(bytes32 id) public view returns(uint); | |
- | |
- function getExpectedRate(ERC20Clone src, ERC20Clone dest, uint srcQty) public view | |
- returns (uint expectedRate, uint slippageRate); | |
- | |
- function tradeWithHint(address trader, ERC20Clone src, uint srcAmount, ERC20Clone dest, address destAddress, | |
- uint maxDestAmount, uint minConversionRate, address walletId, bytes hint) public payable returns(uint); | |
-} | |
- | |
-contract PermissionGroups { | |
- | |
- address public admin; | |
- address public pendingAdmin; | |
- mapping(address=>bool) internal operators; | |
- mapping(address=>bool) internal alerters; | |
- address[] internal operatorsGroup; | |
- address[] internal alertersGroup; | |
- uint constant internal MAX_GROUP_SIZE = 50; | |
- | |
- function PermissionGroups() public { | |
- admin = msg.sender; | |
- } | |
- | |
- modifier onlyAdmin() { | |
- require(msg.sender == admin); | |
- _; | |
- } | |
- | |
- modifier onlyOperator() { | |
- require(operators[msg.sender]); | |
- _; | |
- } | |
- | |
- modifier onlyAlerter() { | |
- require(alerters[msg.sender]); | |
- _; | |
- } | |
- | |
- function getOperators () external view returns(address[]) { | |
- return operatorsGroup; | |
- } | |
- | |
- function getAlerters () external view returns(address[]) { | |
- return alertersGroup; | |
- } | |
- | |
- event TransferAdminPending(address pendingAdmin); | |
- | |
- /** | |
- * @dev Allows the current admin to set the pendingAdmin address. | |
- * @param newAdmin The address to transfer ownership to. | |
- */ | |
- function transferAdmin(address newAdmin) public onlyAdmin { | |
- require(newAdmin != address(0)); | |
- TransferAdminPending(pendingAdmin); | |
- pendingAdmin = newAdmin; | |
- } | |
- | |
- /** | |
- * @dev Allows the current admin to set the admin in one tx. Useful initial deployment. | |
- * @param newAdmin The address to transfer ownership to. | |
- */ | |
- function transferAdminQuickly(address newAdmin) public onlyAdmin { | |
- require(newAdmin != address(0)); | |
- TransferAdminPending(newAdmin); | |
- AdminClaimed(newAdmin, admin); | |
- admin = newAdmin; | |
- } | |
- | |
- event AdminClaimed( address newAdmin, address previousAdmin); | |
- | |
- /** | |
- * @dev Allows the pendingAdmin address to finalize the change admin process. | |
- */ | |
- function claimAdmin() public { | |
- require(pendingAdmin == msg.sender); | |
- AdminClaimed(pendingAdmin, admin); | |
- admin = pendingAdmin; | |
- pendingAdmin = address(0); | |
- } | |
- | |
- event AlerterAdded (address newAlerter, bool isAdd); | |
- | |
- function addAlerter(address newAlerter) public onlyAdmin { | |
- require(!alerters[newAlerter]); // prevent duplicates. | |
- require(alertersGroup.length < MAX_GROUP_SIZE); | |
- | |
- AlerterAdded(newAlerter, true); | |
- alerters[newAlerter] = true; | |
- alertersGroup.push(newAlerter); | |
- } | |
- | |
- function removeAlerter (address alerter) public onlyAdmin { | |
- require(alerters[alerter]); | |
- alerters[alerter] = false; | |
- | |
- for (uint i = 0; i < alertersGroup.length; ++i) { | |
- if (alertersGroup[i] == alerter) { | |
- alertersGroup[i] = alertersGroup[alertersGroup.length - 1]; | |
- alertersGroup.length--; | |
- AlerterAdded(alerter, false); | |
- break; | |
- } | |
- } | |
- } | |
- | |
- event OperatorAdded(address newOperator, bool isAdd); | |
- | |
- function addOperator(address newOperator) public onlyAdmin { | |
- require(!operators[newOperator]); // prevent duplicates. | |
- require(operatorsGroup.length < MAX_GROUP_SIZE); | |
- | |
- OperatorAdded(newOperator, true); | |
- operators[newOperator] = true; | |
- operatorsGroup.push(newOperator); | |
- } | |
- | |
- function removeOperator (address operator) public onlyAdmin { | |
- require(operators[operator]); | |
- operators[operator] = false; | |
- | |
- for (uint i = 0; i < operatorsGroup.length; ++i) { | |
- if (operatorsGroup[i] == operator) { | |
- operatorsGroup[i] = operatorsGroup[operatorsGroup.length - 1]; | |
- operatorsGroup.length -= 1; | |
- OperatorAdded(operator, false); | |
- break; | |
- } | |
- } | |
- } | |
-} | |
- | |
-/** | |
- * @title Contracts that should be able to recover tokens or ethers | |
- * @author Ilan Doron | |
- * @dev This allows to recover any tokens or Ethers received in a contract. | |
- * This will prevent any accidental loss of tokens. | |
- */ | |
-contract Withdrawable is PermissionGroups { | |
- | |
- event TokenWithdraw(ERC20Clone token, uint amount, address sendTo); | |
- | |
- /** | |
- * @dev Withdraw all ERC20Clone compatible tokens | |
- * @param token ERC20Clone The address of the token contract | |
- */ | |
- function withdrawToken(ERC20Clone token, uint amount, address sendTo) external onlyAdmin { | |
- require(token.transfer(sendTo, amount)); | |
- TokenWithdraw(token, amount, sendTo); | |
- } | |
- | |
- event EtherWithdraw(uint amount, address sendTo); | |
- | |
- /** | |
- * @dev Withdraw Ethers | |
- */ | |
- function withdrawEther(uint amount, address sendTo) external onlyAdmin { | |
- sendTo.transfer(amount); | |
- EtherWithdraw(amount, sendTo); | |
- } | |
-} | |
- | |
-contract Utils { | |
- | |
- ERC20Clone constant internal ETH_TOKEN_ADDRESS = ERC20Clone(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee); | |
- uint constant internal PRECISION = (10**18); | |
- uint constant internal MAX_QTY = (10**28); // 10B tokens | |
- uint constant internal MAX_RATE = (PRECISION * 10**6); // up to 1M tokens per ETH | |
- uint constant internal MAX_DECIMALS = 18; | |
- uint constant internal ETH_DECIMALS = 18; | |
- mapping(address=>uint) internal decimals; | |
- | |
- function setDecimals(ERC20Clone token) internal { | |
- if (token == ETH_TOKEN_ADDRESS) decimals[token] = ETH_DECIMALS; | |
- else decimals[token] = token.decimals(); | |
- } | |
- | |
- function getDecimals(ERC20Clone token) internal view returns(uint) { | |
- if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access | |
- uint tokenDecimals = decimals[token]; | |
- // technically, there might be token with decimals 0 | |
- // moreover, very possible that old tokens have decimals 0 | |
- // these tokens will just have higher gas fees. | |
- if(tokenDecimals == 0) return token.decimals(); | |
- | |
- return tokenDecimals; | |
- } | |
- | |
- function calcDstQty(uint srcQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) { | |
- require(srcQty <= MAX_QTY); | |
- require(rate <= MAX_RATE); | |
- | |
- if (dstDecimals >= srcDecimals) { | |
- require((dstDecimals - srcDecimals) <= MAX_DECIMALS); | |
- return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION; | |
- } else { | |
- require((srcDecimals - dstDecimals) <= MAX_DECIMALS); | |
- return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals))); | |
- } | |
- } | |
- | |
- function calcSrcQty(uint dstQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) { | |
- require(dstQty <= MAX_QTY); | |
- require(rate <= MAX_RATE); | |
- | |
- //source quantity is rounded up. to avoid dest quantity being too low. | |
- uint numerator; | |
- uint denominator; | |
- if (srcDecimals >= dstDecimals) { | |
- require((srcDecimals - dstDecimals) <= MAX_DECIMALS); | |
- numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals))); | |
- denominator = rate; | |
- } else { | |
- require((dstDecimals - srcDecimals) <= MAX_DECIMALS); | |
- numerator = (PRECISION * dstQty); | |
- denominator = (rate * (10**(dstDecimals - srcDecimals))); | |
- } | |
- return (numerator + denominator - 1) / denominator; //avoid rounding down errors | |
- } | |
-} | |
- | |
-contract Utils2 is Utils { | |
- | |
- /// @dev get the balance of a user. | |
- /// @param token The token type | |
- /// @return The balance | |
- function getBalance(ERC20Clone token, address user) public view returns(uint) { | |
- if (token == ETH_TOKEN_ADDRESS) | |
- return user.balance; | |
- else | |
- return token.balanceOf(user); | |
- } | |
- | |
- function getDecimalsSafe(ERC20Clone token) internal returns(uint) { | |
- | |
- if (decimals[token] == 0) { | |
- setDecimals(token); | |
- } | |
- | |
- return decimals[token]; | |
- } | |
- | |
- /// @dev notice, overrides previous implementation. | |
- function setDecimals(ERC20Clone token) internal { | |
- uint decimal; | |
- | |
- if (token == ETH_TOKEN_ADDRESS) { | |
- decimal = ETH_DECIMALS; | |
- } else { | |
- if (!address(token).call(bytes4(keccak256("decimals()")))) {/* solhint-disable-line avoid-low-level-calls */ | |
- //above code can only be performed with low level call. otherwise all operation will revert. | |
- // call failed | |
- decimal = 18; | |
- } else { | |
- decimal = token.decimals(); | |
- } | |
- } | |
- | |
- decimals[token] = decimal; | |
- } | |
- | |
- function calcDestAmount(ERC20Clone src, ERC20Clone dest, uint srcAmount, uint rate) internal view returns(uint) { | |
- return calcDstQty(srcAmount, getDecimals(src), getDecimals(dest), rate); | |
- } | |
- | |
- function calcSrcAmount(ERC20Clone src, ERC20Clone dest, uint destAmount, uint rate) internal view returns(uint) { | |
- return calcSrcQty(destAmount, getDecimals(src), getDecimals(dest), rate); | |
- } | |
- | |
- function calcRateFromQty(uint srcAmount, uint destAmount, uint srcDecimals, uint dstDecimals) | |
- internal pure returns(uint) | |
- { | |
- require(srcAmount <= MAX_QTY); | |
- require(destAmount <= MAX_QTY); | |
- | |
- if (dstDecimals >= srcDecimals) { | |
- require((dstDecimals - srcDecimals) <= MAX_DECIMALS); | |
- return (destAmount * PRECISION / ((10 ** (dstDecimals - srcDecimals)) * srcAmount)); | |
- } else { | |
- require((srcDecimals - dstDecimals) <= MAX_DECIMALS); | |
- return (destAmount * PRECISION * (10 ** (srcDecimals - dstDecimals)) / srcAmount); | |
- } | |
- } | |
-} | |
- | |
-contract WhiteListInterface { | |
- function getUserCapInWei(address user) external view returns (uint userCapWei); | |
-} | |
- | |
-interface ExpectedRateInterface { | |
- function getExpectedRate(ERC20Clone src, ERC20Clone dest, uint srcQty) public view | |
- returns (uint expectedRate, uint slippageRate); | |
-} | |
- | |
-interface FeeBurnerInterface { | |
- function handleFees (uint tradeWeiAmount, address reserve, address wallet) public returns(bool); | |
-} | |
- | |
-interface ConversionRatesInterface { | |
- | |
- function recordImbalance( | |
- ERC20Clone token, | |
- int buyAmount, | |
- uint rateUpdateBlock, | |
- uint currentBlock | |
- ) | |
- public; | |
- | |
- function getRate(ERC20Clone token, uint currentBlockNumber, bool buy, uint qty) public view returns(uint); | |
-} | |
- | |
-interface SanityRatesInterface { | |
- function getSanityRate(ERC20Clone src, ERC20Clone dest) public view returns(uint); | |
-} | |
- | |
-//////////////////////////////////////////////////////////////////////////////// | |
- | |
-/* | |
- * SimpleToken | |
- * | |
- * Very simple ERC20Clone Token example, where all tokens are pre-assigned | |
- * to the creator. Note they can later distribute these tokens | |
- * as they wish using `transfer` and other `StandardToken` functions. | |
- */ | |
-contract TestToken is StandardToken { | |
- | |
- string public name = "Test"; | |
- string public symbol = "TST"; | |
- uint public decimals = 18; | |
- uint public INITIAL_SUPPLY = 10 ** 27; | |
- | |
- function TestToken(string _name, string _symbol, uint _decimals) public { | |
- totalSupply_ = INITIAL_SUPPLY; | |
- balances[msg.sender] = INITIAL_SUPPLY; | |
- name = _name; | |
- symbol = _symbol; | |
- decimals = _decimals; | |
- } | |
- | |
- event Burn(address indexed _burner, uint _value); | |
- | |
- function burn(uint _value) public returns (bool) { | |
- balances[msg.sender] = balances[msg.sender].sub(_value); | |
- totalSupply_ = totalSupply_.sub(_value); | |
- Burn(msg.sender, _value); | |
- Transfer(msg.sender, address(0x0), _value); | |
- return true; | |
- } | |
- | |
- // save some gas by making only one contract call | |
- function burnFrom(address _from, uint256 _value) public returns (bool) { | |
- transferFrom( _from, msg.sender, _value ); | |
- return burn(_value); | |
- } | |
-} | |
- | |
-contract KyberWhiteList is WhiteListInterface, Withdrawable { | |
- | |
- uint public weiPerSgd; // amount of weis in 1 singapore dollar | |
- mapping (address=>uint) public userCategory; // each user has a category defining cap on trade. 0 for standard. | |
- mapping (uint=>uint) public categoryCap; // will define cap on trade amount per category in singapore Dollar. | |
- uint constant public kgtHolderCategory = 2; | |
- ERC20Clone public kgtToken; | |
- | |
- function KyberWhiteList(address _admin, ERC20Clone _kgtToken) public { | |
- require(_admin != address(0)); | |
- require(_kgtToken != address(0)); | |
- kgtToken = _kgtToken; | |
- admin = _admin; | |
- } | |
- | |
- function getUserCapInWei(address user) external view returns (uint) { | |
- uint category = getUserCategory(user); | |
- return (categoryCap[category] * weiPerSgd); | |
- } | |
- | |
- event UserCategorySet(address user, uint category); | |
- | |
- function setUserCategory(address user, uint category) public onlyOperator { | |
- userCategory[user] = category; | |
- UserCategorySet(user, category); | |
- } | |
- | |
- event CategoryCapSet (uint category, uint sgdCap); | |
- | |
- function setCategoryCap(uint category, uint sgdCap) public onlyOperator { | |
- categoryCap[category] = sgdCap; | |
- CategoryCapSet(category, sgdCap); | |
- } | |
- | |
- event SgdToWeiRateSet (uint rate); | |
- | |
- function setSgdToEthRate(uint _sgdToWeiRate) public onlyOperator { | |
- weiPerSgd = _sgdToWeiRate; | |
- SgdToWeiRateSet(_sgdToWeiRate); | |
- } | |
- | |
- function getUserCategory (address user) public view returns(uint) { | |
- uint category = userCategory[user]; | |
- if (category == 0) { | |
- //0 = default category. means category wasn't set. | |
- if (kgtToken.balanceOf(user) > 0) { | |
- category = kgtHolderCategory; | |
- } | |
- } | |
- return category; | |
- } | |
-} | |
- | |
-interface BurnableTokenClone { | |
- function transferFrom(address _from, address _to, uint _value) public returns (bool); | |
- function burnFrom(address _from, uint256 _value) public returns (bool); | |
-} | |
- | |
- | |
-contract FeeBurner is Withdrawable, FeeBurnerInterface, Utils { | |
- | |
- mapping(address=>uint) public reserveFeesInBps; | |
- mapping(address=>address) public reserveKNCWallet; //wallet holding knc per reserve. from here burn and send fees. | |
- mapping(address=>uint) public walletFeesInBps; // wallet that is the source of tx is entitled so some fees. | |
- mapping(address=>uint) public reserveFeeToBurn; | |
- mapping(address=>uint) public feePayedPerReserve; // track burned fees and sent wallet fees per reserve. | |
- mapping(address=>mapping(address=>uint)) public reserveFeeToWallet; | |
- address public taxWallet; | |
- uint public taxFeeBps = 0; // burned fees are taxed. % out of burned fees. | |
- | |
- BurnableTokenClone public knc; | |
- address public kyberNetwork; | |
- uint public kncPerETHRate = 300; | |
- | |
- function FeeBurner(address _admin, BurnableTokenClone kncToken, address _kyberNetwork) public { | |
- require(_admin != address(0)); | |
- require(kncToken != address(0)); | |
- require(_kyberNetwork != address(0)); | |
- kyberNetwork = _kyberNetwork; | |
- admin = _admin; | |
- knc = kncToken; | |
- } | |
- | |
- event ReserveDataSet(address reserve, uint feeInBps, address kncWallet); | |
- function setReserveData(address reserve, uint feesInBps, address kncWallet) public onlyAdmin { | |
- require(feesInBps < 100); // make sure it is always < 1% | |
- require(kncWallet != address(0)); | |
- reserveFeesInBps[reserve] = feesInBps; | |
- reserveKNCWallet[reserve] = kncWallet; | |
- ReserveDataSet(reserve, feesInBps, kncWallet); | |
- } | |
- | |
- event WalletFeesSet(address wallet, uint feesInBps); | |
- function setWalletFees(address wallet, uint feesInBps) public onlyAdmin { | |
- require(feesInBps < 10000); // under 100% | |
- walletFeesInBps[wallet] = feesInBps; | |
- WalletFeesSet(wallet, feesInBps); | |
- } | |
- | |
- event TaxFeesSet(uint feesInBps); | |
- function setTaxInBps(uint _taxFeeBps) public onlyAdmin { | |
- require(_taxFeeBps < 10000); // under 100% | |
- taxFeeBps = _taxFeeBps; | |
- TaxFeesSet(_taxFeeBps); | |
- } | |
- | |
- event TaxWalletSet(address taxWallet); | |
- function setTaxWallet(address _taxWallet) public onlyAdmin { | |
- require(_taxWallet != address(0)); | |
- taxWallet = _taxWallet; | |
- TaxWalletSet(_taxWallet); | |
- } | |
- | |
- function setKNCRate(uint rate) public onlyAdmin { | |
- require(rate <= MAX_RATE); | |
- kncPerETHRate = rate; | |
- } | |
- | |
- event AssignFeeToWallet(address reserve, address wallet, uint walletFee); | |
- event AssignBurnFees(address reserve, uint burnFee); | |
- | |
- function handleFees(uint tradeWeiAmount, address reserve, address wallet) public returns(bool) { | |
- require(msg.sender == kyberNetwork); | |
- require(tradeWeiAmount <= MAX_QTY); | |
- require(kncPerETHRate <= MAX_RATE); | |
- | |
- uint kncAmount = tradeWeiAmount * kncPerETHRate; | |
- uint fee = kncAmount * reserveFeesInBps[reserve] / 10000; | |
- | |
- uint walletFee = fee * walletFeesInBps[wallet] / 10000; | |
- require(fee >= walletFee); | |
- uint feeToBurn = fee - walletFee; | |
- | |
- if (walletFee > 0) { | |
- reserveFeeToWallet[reserve][wallet] += walletFee; | |
- AssignFeeToWallet(reserve, wallet, walletFee); | |
- } | |
- | |
- if (feeToBurn > 0) { | |
- AssignBurnFees(reserve, feeToBurn); | |
- reserveFeeToBurn[reserve] += feeToBurn; | |
- } | |
- | |
- return true; | |
- } | |
- | |
- event BurnAssignedFees(address indexed reserve, address sender, uint quantity); | |
- | |
- event SendTaxFee(address indexed reserve, address sender, address taxWallet, uint quantity); | |
- | |
- // this function is callable by anyone | |
- function burnReserveFees(address reserve) public { | |
- uint burnAmount = reserveFeeToBurn[reserve]; | |
- uint taxToSend = 0; | |
- require(burnAmount > 2); | |
- reserveFeeToBurn[reserve] = 1; // leave 1 twei to avoid spikes in gas fee | |
- if (taxWallet != address(0) && taxFeeBps != 0) { | |
- taxToSend = (burnAmount - 1) * taxFeeBps / 10000; | |
- require(burnAmount - 1 > taxToSend); | |
- burnAmount -= taxToSend; | |
- if (taxToSend > 0) { | |
- require(knc.transferFrom(reserveKNCWallet[reserve], taxWallet, taxToSend)); | |
- SendTaxFee(reserve, msg.sender, taxWallet, taxToSend); | |
- } | |
- } | |
- require(knc.burnFrom(reserveKNCWallet[reserve], burnAmount - 1)); | |
- | |
- //update reserve "payments" so far | |
- feePayedPerReserve[reserve] += (taxToSend + burnAmount - 1); | |
- | |
- BurnAssignedFees(reserve, msg.sender, (burnAmount - 1)); | |
- } | |
- | |
- event SendWalletFees(address indexed wallet, address reserve, address sender); | |
- | |
- // this function is callable by anyone | |
- function sendFeeToWallet(address wallet, address reserve) public { | |
- uint feeAmount = reserveFeeToWallet[reserve][wallet]; | |
- require(feeAmount > 1); | |
- reserveFeeToWallet[reserve][wallet] = 1; // leave 1 twei to avoid spikes in gas fee | |
- require(knc.transferFrom(reserveKNCWallet[reserve], wallet, feeAmount - 1)); | |
- | |
- feePayedPerReserve[reserve] += (feeAmount - 1); | |
- SendWalletFees(wallet, reserve, msg.sender); | |
- } | |
-} | |
diff --git a/src/contracts/exchanges/third-party/kyber/KyberNetwork.sol b/src/contracts/exchanges/third-party/kyber/KyberNetwork.sol | |
deleted file mode 100644 | |
index cbdf3e18..00000000 | |
--- a/src/contracts/exchanges/third-party/kyber/KyberNetwork.sol | |
+++ /dev/null | |
@@ -1,523 +0,0 @@ | |
-pragma solidity ^0.4.21; | |
- | |
-import "KyberDependencies.sol"; | |
- | |
-//////////////////////////////////////////////////////////////////////////////////////////////////////// | |
-/// @title Kyber Network main contract | |
-contract KyberNetwork is Withdrawable, Utils2, KyberNetworkInterface { | |
- | |
- uint public negligibleRateDiff = 10; // basic rate steps will be in 0.01% | |
- KyberReserveInterface[] public reserves; | |
- mapping(address=>bool) public isReserve; | |
- WhiteListInterface public whiteListContract; | |
- ExpectedRateInterface public expectedRateContract; | |
- FeeBurnerInterface public feeBurnerContract; | |
- address public kyberNetworkProxyContract; | |
- uint public maxGasPriceValue = 50 * 1000 * 1000 * 1000; // 50 gwei | |
- bool public isEnabled = false; // network is enabled | |
- mapping(bytes32=>uint) public infoFields; // this is only a UI field for external app. | |
- mapping(address=>address[]) public reservesPerTokenSrc; //reserves supporting token to eth | |
- mapping(address=>address[]) public reservesPerTokenDest;//reserves support eth to token | |
- | |
- function KyberNetwork(address _admin) public { | |
- require(_admin != address(0)); | |
- admin = _admin; | |
- } | |
- | |
- event EtherReceival(address indexed sender, uint amount); | |
- | |
- /* solhint-disable no-complex-fallback */ | |
- // To avoid users trying to swap tokens using default payable function. We added this short code | |
- // to verify Ethers will be received only from reserves if transferred without a specific function call. | |
- function() public payable { | |
- require(isReserve[msg.sender]); | |
- EtherReceival(msg.sender, msg.value); | |
- } | |
- /* solhint-enable no-complex-fallback */ | |
- | |
- struct TradeInput { | |
- address trader; | |
- ERC20Clone src; | |
- uint srcAmount; | |
- ERC20Clone dest; | |
- address destAddress; | |
- uint maxDestAmount; | |
- uint minConversionRate; | |
- address walletId; | |
- bytes hint; | |
- } | |
- | |
- function tradeWithHint( | |
- address trader, | |
- ERC20Clone src, | |
- uint srcAmount, | |
- ERC20Clone dest, | |
- address destAddress, | |
- uint maxDestAmount, | |
- uint minConversionRate, | |
- address walletId, | |
- bytes hint | |
- ) | |
- public | |
- payable | |
- returns(uint) | |
- { | |
- require(hint.length == 0); | |
- require(msg.sender == kyberNetworkProxyContract); | |
- | |
- TradeInput memory tradeInput; | |
- | |
- tradeInput.trader = trader; | |
- tradeInput.src = src; | |
- tradeInput.srcAmount = srcAmount; | |
- tradeInput.dest = dest; | |
- tradeInput.destAddress = destAddress; | |
- tradeInput.maxDestAmount = maxDestAmount; | |
- tradeInput.minConversionRate = minConversionRate; | |
- tradeInput.walletId = walletId; | |
- tradeInput.hint = hint; | |
- | |
- return trade(tradeInput); | |
- } | |
- | |
- event AddReserveToNetwork(KyberReserveInterface reserve, bool add); | |
- | |
- /// @notice can be called only by admin | |
- /// @dev add or deletes a reserve to/from the network. | |
- /// @param reserve The reserve address. | |
- /// @param add If true, the add reserve. Otherwise delete reserve. | |
- function addReserve(KyberReserveInterface reserve, bool add) public onlyAdmin { | |
- | |
- if (add) { | |
- require(!isReserve[reserve]); | |
- reserves.push(reserve); | |
- isReserve[reserve] = true; | |
- AddReserveToNetwork(reserve, true); | |
- } else { | |
- isReserve[reserve] = false; | |
- // will have trouble if more than 50k reserves... | |
- for (uint i = 0; i < reserves.length; i++) { | |
- if (reserves[i] == reserve) { | |
- reserves[i] = reserves[reserves.length - 1]; | |
- reserves.length--; | |
- AddReserveToNetwork(reserve, false); | |
- break; | |
- } | |
- } | |
- } | |
- } | |
- | |
- event ListReservePairs(address reserve, ERC20Clone src, ERC20Clone dest, bool add); | |
- | |
- /// @notice can be called only by admin | |
- /// @dev allow or prevent a specific reserve to trade a pair of tokens | |
- /// @param reserve The reserve address. | |
- /// @param token token address | |
- /// @param ethToToken will it support ether to token trade | |
- /// @param tokenToEth will it support token to ether trade | |
- /// @param add If true then list this pair, otherwise unlist it. | |
- function listPairForReserve(address reserve, ERC20Clone token, bool ethToToken, bool tokenToEth, bool add) | |
- public onlyAdmin | |
- { | |
- require(isReserve[reserve]); | |
- | |
- if (ethToToken) { | |
- listPairs(reserve, token, false, add); | |
- | |
- ListReservePairs(reserve, ETH_TOKEN_ADDRESS, token, add); | |
- } | |
- | |
- if (tokenToEth) { | |
- listPairs(reserve, token, true, add); | |
- if (add) { | |
- token.approve(reserve, 2**255); // approve infinity | |
- } else { | |
- token.approve(reserve, 0); | |
- } | |
- | |
- ListReservePairs(reserve, token, ETH_TOKEN_ADDRESS, add); | |
- } | |
- | |
- setDecimals(token); | |
- } | |
- | |
- function setWhiteList(WhiteListInterface whiteList) public onlyAdmin { | |
- require(whiteList != address(0)); | |
- whiteListContract = whiteList; | |
- } | |
- | |
- function setExpectedRate(ExpectedRateInterface expectedRate) public onlyAdmin { | |
- require(expectedRate != address(0)); | |
- expectedRateContract = expectedRate; | |
- } | |
- | |
- function setFeeBurner(FeeBurnerInterface feeBurner) public onlyAdmin { | |
- require(feeBurner != address(0)); | |
- feeBurnerContract = feeBurner; | |
- } | |
- | |
- function setParams( | |
- uint _maxGasPrice, | |
- uint _negligibleRateDiff | |
- ) | |
- public | |
- onlyAdmin | |
- { | |
- require(_negligibleRateDiff <= 100 * 100); // at most 100% | |
- | |
- maxGasPriceValue = _maxGasPrice; | |
- negligibleRateDiff = _negligibleRateDiff; | |
- } | |
- | |
- function setEnable(bool _enable) public onlyAdmin { | |
- if (_enable) { | |
- require(whiteListContract != address(0)); | |
- require(feeBurnerContract != address(0)); | |
- require(expectedRateContract != address(0)); | |
- require(kyberNetworkProxyContract != address(0)); | |
- } | |
- isEnabled = _enable; | |
- } | |
- | |
- function setInfo(bytes32 field, uint value) public onlyOperator { | |
- infoFields[field] = value; | |
- } | |
- | |
- event KyberProxySet(address proxy, address sender); | |
- | |
- function setKyberProxy(address networkProxy) public onlyAdmin { | |
- require(networkProxy != address(0)); | |
- kyberNetworkProxyContract = networkProxy; | |
- KyberProxySet(kyberNetworkProxyContract, msg.sender); | |
- } | |
- | |
- /// @dev returns number of reserves | |
- /// @return number of reserves | |
- function getNumReserves() public view returns(uint) { | |
- return reserves.length; | |
- } | |
- | |
- /// @notice should be called off chain with as much gas as needed | |
- /// @dev get an array of all reserves | |
- /// @return An array of all reserves | |
- function getReserves() public view returns(KyberReserveInterface[]) { | |
- return reserves; | |
- } | |
- | |
- function maxGasPrice() public view returns(uint) { | |
- return maxGasPriceValue; | |
- } | |
- | |
- function getExpectedRate(ERC20Clone src, ERC20Clone dest, uint srcQty) | |
- public view | |
- returns(uint expectedRate, uint slippageRate) | |
- { | |
- require(expectedRateContract != address(0)); | |
- return expectedRateContract.getExpectedRate(src, dest, srcQty); | |
- } | |
- | |
- function getUserCapInWei(address user) public view returns(uint) { | |
- return whiteListContract.getUserCapInWei(user); | |
- } | |
- | |
- function getUserCapInTokenWei(address user, ERC20Clone token) public view returns(uint) { | |
- //future feature | |
- user; | |
- token; | |
- require(false); | |
- } | |
- | |
- struct BestRateResult { | |
- uint rate; | |
- address reserve1; | |
- address reserve2; | |
- uint weiAmount; | |
- uint rateSrcToEth; | |
- uint rateEthToDest; | |
- uint destAmount; | |
- } | |
- | |
- /// @notice use token address ETH_TOKEN_ADDRESS for ether | |
- /// @dev best conversion rate for a pair of tokens, if number of reserves have small differences. randomize | |
- /// @param src Src token | |
- /// @param dest Destination token | |
- /// @return obsolete - used to return best reserve index. not relevant anymore for this API. | |
- function findBestRate(ERC20Clone src, ERC20Clone dest, uint srcAmount) public view returns(uint obsolete, uint rate) { | |
- BestRateResult memory result = findBestRateTokenToToken(src, dest, srcAmount); | |
- return(0, result.rate); | |
- } | |
- | |
- function enabled() public view returns(bool) { | |
- return isEnabled; | |
- } | |
- | |
- function info(bytes32 field) public view returns(uint) { | |
- return infoFields[field]; | |
- } | |
- | |
- /* solhint-disable code-complexity */ | |
- // Not sure how solhing defines complexity. Anyway, from our point of view, below code follows the required | |
- // algorithm to choose a reserve, it has been tested, reviewed and found to be clear enough. | |
- //@dev this function always src or dest are ether. can't do token to token | |
- function searchBestRate(ERC20Clone src, ERC20Clone dest, uint srcAmount) public view returns(address, uint) { | |
- uint bestRate = 0; | |
- uint bestReserve = 0; | |
- uint numRelevantReserves = 0; | |
- | |
- //return 1 for ether to ether | |
- if (src == dest) return (reserves[bestReserve], PRECISION); | |
- | |
- address[] memory reserveArr; | |
- | |
- if (src == ETH_TOKEN_ADDRESS) { | |
- reserveArr = reservesPerTokenDest[dest]; | |
- } else { | |
- reserveArr = reservesPerTokenSrc[src]; | |
- } | |
- | |
- if (reserveArr.length == 0) return (reserves[bestReserve], bestRate); | |
- | |
- uint[] memory rates = new uint[](reserveArr.length); | |
- uint[] memory reserveCandidates = new uint[](reserveArr.length); | |
- | |
- for (uint i = 0; i < reserveArr.length; i++) { | |
- //list all reserves that have this token. | |
- rates[i] = (KyberReserveInterface(reserveArr[i])).getConversionRate(src, dest, srcAmount, block.number); | |
- | |
- if (rates[i] > bestRate) { | |
- //best rate is highest rate | |
- bestRate = rates[i]; | |
- } | |
- } | |
- | |
- if (bestRate > 0) { | |
- uint random = 0; | |
- uint smallestRelevantRate = (bestRate * 10000) / (10000 + negligibleRateDiff); | |
- | |
- for (i = 0; i < reserveArr.length; i++) { | |
- if (rates[i] >= smallestRelevantRate) { | |
- reserveCandidates[numRelevantReserves++] = i; | |
- } | |
- } | |
- | |
- if (numRelevantReserves > 1) { | |
- //when encountering small rate diff from bestRate. draw from relevant reserves | |
- random = uint(block.blockhash(block.number-1)) % numRelevantReserves; | |
- } | |
- | |
- bestReserve = reserveCandidates[random]; | |
- bestRate = rates[bestReserve]; | |
- } | |
- | |
- return (reserveArr[bestReserve], bestRate); | |
- } | |
- /* solhint-enable code-complexity */ | |
- | |
- function findBestRateTokenToToken(ERC20Clone src, ERC20Clone dest, uint srcAmount) internal view | |
- returns(BestRateResult result) | |
- { | |
- (result.reserve1, result.rateSrcToEth) = searchBestRate(src, ETH_TOKEN_ADDRESS, srcAmount); | |
- result.weiAmount = calcDestAmount(src, ETH_TOKEN_ADDRESS, srcAmount, result.rateSrcToEth); | |
- | |
- (result.reserve2, result.rateEthToDest) = searchBestRate(ETH_TOKEN_ADDRESS, dest, result.weiAmount); | |
- result.destAmount = calcDestAmount(ETH_TOKEN_ADDRESS, dest, result.weiAmount, result.rateEthToDest); | |
- | |
- result.rate = calcRateFromQty(srcAmount, result.destAmount, getDecimals(src), getDecimals(dest)); | |
- } | |
- | |
- function listPairs(address reserve, ERC20Clone token, bool isTokenToEth, bool add) internal { | |
- uint i; | |
- address[] storage reserveArr = reservesPerTokenDest[token]; | |
- | |
- if (isTokenToEth) { | |
- reserveArr = reservesPerTokenSrc[token]; | |
- } | |
- | |
- for (i = 0; i < reserveArr.length; i++) { | |
- if (reserve == reserveArr[i]) { | |
- if (add) { | |
- break; //already added | |
- } else { | |
- //remove | |
- reserveArr[i] = reserveArr[reserveArr.length - 1]; | |
- reserveArr.length--; | |
- } | |
- } | |
- } | |
- | |
- if (add && i == reserveArr.length) { | |
- //if reserve wasn't found add it | |
- reserveArr.push(reserve); | |
- } | |
- } | |
- | |
- event KyberTrade(address srcAddress, ERC20Clone srcToken, uint srcAmount, address destAddress, ERC20Clone destToken, | |
- uint destAmount); | |
- /* solhint-disable function-max-lines */ | |
- // Most of the lins here are functions calls spread over multiple lines. We find this function readable enough | |
- // and keep its size as is. | |
- /// @notice use token address ETH_TOKEN_ADDRESS for ether | |
- /// @dev trade api for kyber network. | |
- /// @param tradeInput structure of trade inputs | |
- function trade(TradeInput tradeInput) internal returns(uint) { | |
- require(isEnabled); | |
- require(tx.gasprice <= maxGasPriceValue); | |
- require(validateTradeInput(tradeInput.src, tradeInput.srcAmount, tradeInput.dest, tradeInput.destAddress)); | |
- | |
- BestRateResult memory rateResult = | |
- findBestRateTokenToToken(tradeInput.src, tradeInput.dest, tradeInput.srcAmount); | |
- | |
- require(rateResult.rate > 0); | |
- require(rateResult.rate < MAX_RATE); | |
- require(rateResult.rate >= tradeInput.minConversionRate); | |
- | |
- uint actualDestAmount; | |
- uint weiAmount; | |
- uint actualSrcAmount; | |
- | |
- (actualSrcAmount, weiAmount, actualDestAmount) = calcActualAmounts(tradeInput.src, | |
- tradeInput.dest, | |
- tradeInput.srcAmount, | |
- tradeInput.maxDestAmount, | |
- rateResult); | |
- | |
- if (actualSrcAmount < tradeInput.srcAmount) { | |
- //if there is "change" send back to trader | |
- if (tradeInput.src == ETH_TOKEN_ADDRESS) { | |
- tradeInput.trader.transfer(tradeInput.srcAmount - actualSrcAmount); | |
- } else { | |
- tradeInput.src.transfer(tradeInput.trader, (tradeInput.srcAmount - actualSrcAmount)); | |
- } | |
- } | |
- | |
- // verify trade size is smaller than user cap | |
- require(weiAmount <= getUserCapInWei(tradeInput.trader)); | |
- | |
- //do the trade | |
- //src to ETH | |
- require(doReserveTrade( | |
- tradeInput.src, | |
- actualSrcAmount, | |
- ETH_TOKEN_ADDRESS, | |
- this, | |
- weiAmount, | |
- KyberReserveInterface(rateResult.reserve1), | |
- rateResult.rateSrcToEth, | |
- true)); | |
- | |
- //Eth to dest | |
- require(doReserveTrade( | |
- ETH_TOKEN_ADDRESS, | |
- weiAmount, | |
- tradeInput.dest, | |
- tradeInput.destAddress, | |
- actualDestAmount, | |
- KyberReserveInterface(rateResult.reserve2), | |
- rateResult.rateEthToDest, | |
- true)); | |
- | |
- //when src is ether, reserve1 is doing a "fake" trade. (ether to ether) - don't burn. | |
- //when dest is ether, reserve2 is doing a "fake" trade. (ether to ether) - don't burn. | |
- if (tradeInput.src != ETH_TOKEN_ADDRESS) | |
- require(feeBurnerContract.handleFees(weiAmount, rateResult.reserve1, tradeInput.walletId)); | |
- if (tradeInput.dest != ETH_TOKEN_ADDRESS) | |
- require(feeBurnerContract.handleFees(weiAmount, rateResult.reserve2, tradeInput.walletId)); | |
- | |
- KyberTrade(tradeInput.trader, tradeInput.src, actualSrcAmount, tradeInput.destAddress, tradeInput.dest, | |
- actualDestAmount); | |
- | |
- return actualDestAmount; | |
- } | |
- /* solhint-enable function-max-lines */ | |
- | |
- function calcActualAmounts (ERC20Clone src, ERC20Clone dest, uint srcAmount, uint maxDestAmount, BestRateResult rateResult) | |
- internal view returns(uint actualSrcAmount, uint weiAmount, uint actualDestAmount) | |
- { | |
- if (rateResult.destAmount > maxDestAmount) { | |
- actualDestAmount = maxDestAmount; | |
- weiAmount = calcSrcAmount(ETH_TOKEN_ADDRESS, dest, actualDestAmount, rateResult.rateEthToDest); | |
- actualSrcAmount = calcSrcAmount(src, ETH_TOKEN_ADDRESS, weiAmount, rateResult.rateSrcToEth); | |
- require(actualSrcAmount <= srcAmount); | |
- } else { | |
- actualDestAmount = rateResult.destAmount; | |
- actualSrcAmount = srcAmount; | |
- weiAmount = rateResult.weiAmount; | |
- } | |
- } | |
- | |
- /// @notice use token address ETH_TOKEN_ADDRESS for ether | |
- /// @dev do one trade with a reserve | |
- /// @param src Src token | |
- /// @param amount amount of src tokens | |
- /// @param dest Destination token | |
- /// @param destAddress Address to send tokens to | |
- /// @param reserve Reserve to use | |
- /// @param validate If true, additional validations are applicable | |
- /// @return true if trade is successful | |
- function doReserveTrade( | |
- ERC20Clone src, | |
- uint amount, | |
- ERC20Clone dest, | |
- address destAddress, | |
- uint expectedDestAmount, | |
- KyberReserveInterface reserve, | |
- uint conversionRate, | |
- bool validate | |
- ) | |
- internal | |
- returns(bool) | |
- { | |
- uint callValue = 0; | |
- | |
- if (src == dest) { | |
- //this is for a "fake" trade when both src and dest are ethers. | |
- if (destAddress != (address(this))) | |
- destAddress.transfer(amount); | |
- return true; | |
- } | |
- | |
- if (src == ETH_TOKEN_ADDRESS) { | |
- callValue = amount; | |
- } | |
- | |
- // reserve sends tokens/eth to network. network sends it to destination | |
- require(reserve.trade.value(callValue)(src, amount, dest, this, conversionRate, validate)); | |
- | |
- if (destAddress != address(this)) { | |
- //for token to token dest address is network. and Ether / token already here... | |
- if (dest == ETH_TOKEN_ADDRESS) { | |
- destAddress.transfer(expectedDestAmount); | |
- } else { | |
- require(dest.transfer(destAddress, expectedDestAmount)); | |
- } | |
- } | |
- | |
- return true; | |
- } | |
- | |
- /// @notice use token address ETH_TOKEN_ADDRESS for ether | |
- /// @dev checks that user sent ether/tokens to contract before trade | |
- /// @param src Src token | |
- /// @param srcAmount amount of src tokens | |
- /// @return true if tradeInput is valid | |
- function validateTradeInput(ERC20Clone src, uint srcAmount, ERC20Clone dest, address destAddress) | |
- internal | |
- view | |
- returns(bool) | |
- { | |
- require(srcAmount <= MAX_QTY); | |
- require(srcAmount != 0); | |
- require(destAddress != address(0)); | |
- require(src != dest); | |
- | |
- if (src == ETH_TOKEN_ADDRESS) { | |
- require(msg.value == srcAmount); | |
- } else { | |
- require(msg.value == 0); | |
- //funds should have been moved to this contract already. | |
- require(src.balanceOf(this) >= srcAmount); | |
- } | |
- | |
- return true; | |
- } | |
-} | |
diff --git a/src/contracts/exchanges/third-party/kyber/KyberNetworkProxy.sol b/src/contracts/exchanges/third-party/kyber/KyberNetworkProxy.sol | |
deleted file mode 100644 | |
index 11b09a95..00000000 | |
--- a/src/contracts/exchanges/third-party/kyber/KyberNetworkProxy.sol | |
+++ /dev/null | |
@@ -1,285 +0,0 @@ | |
-pragma solidity ^0.4.21; | |
- | |
-import "KyberDependencies.sol"; | |
- | |
-/// @title Kyber Network interface | |
-interface KyberNetworkProxyInterface { | |
- function maxGasPrice() public view returns(uint); | |
- function getUserCapInWei(address user) public view returns(uint); | |
- function getUserCapInTokenWei(address user, ERC20Clone token) public view returns(uint); | |
- function enabled() public view returns(bool); | |
- function info(bytes32 id) public view returns(uint); | |
- | |
- function getExpectedRate(ERC20Clone src, ERC20Clone dest, uint srcQty) public view | |
- returns (uint expectedRate, uint slippageRate); | |
- | |
- function tradeWithHint(ERC20Clone src, uint srcAmount, ERC20Clone dest, address destAddress, uint maxDestAmount, | |
- uint minConversionRate, address walletId, bytes hint) public payable returns(uint); | |
-} | |
- | |
- | |
-/// @title simple interface for Kyber Network | |
-interface SimpleNetworkInterface { | |
- function swapTokenToToken(ERC20Clone src, uint srcAmount, ERC20Clone dest, uint minConversionRate) public returns(uint); | |
- function swapEtherToToken(ERC20Clone token, uint minConversionRate) public payable returns(uint); | |
- function swapTokenToEther(ERC20Clone token, uint srcAmount, uint minConversionRate) public returns(uint); | |
-} | |
- | |
-//////////////////////////////////////////////////////////////////////////////////////////////////////// | |
-/// @title Kyber Network proxy for main contract | |
-contract KyberNetworkProxy is KyberNetworkProxyInterface, SimpleNetworkInterface, Withdrawable, Utils2 { | |
- | |
- KyberNetworkInterface public kyberNetworkContract; | |
- | |
- function KyberNetworkProxy(address _admin) public { | |
- require(_admin != address(0)); | |
- admin = _admin; | |
- } | |
- | |
- /// @notice use token address ETH_TOKEN_ADDRESS for ether | |
- /// @dev makes a trade between src and dest token and send dest token to destAddress | |
- /// @param src Src token | |
- /// @param srcAmount amount of src tokens | |
- /// @param dest Destination token | |
- /// @param destAddress Address to send tokens to | |
- /// @param maxDestAmount A limit on the amount of dest tokens | |
- /// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled. | |
- /// @param walletId is the wallet ID to send part of the fees | |
- /// @return amount of actual dest tokens | |
- function trade( | |
- ERC20Clone src, | |
- uint srcAmount, | |
- ERC20Clone dest, | |
- address destAddress, | |
- uint maxDestAmount, | |
- uint minConversionRate, | |
- address walletId | |
- ) | |
- public | |
- payable | |
- returns(uint) | |
- { | |
- bytes memory hint; | |
- | |
- return tradeWithHint( | |
- src, | |
- srcAmount, | |
- dest, | |
- destAddress, | |
- maxDestAmount, | |
- minConversionRate, | |
- walletId, | |
- hint | |
- ); | |
- } | |
- | |
- /// @dev makes a trade between src and dest token and send dest tokens to msg sender | |
- /// @param src Src token | |
- /// @param srcAmount amount of src tokens | |
- /// @param dest Destination token | |
- /// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled. | |
- /// @return amount of actual dest tokens | |
- function swapTokenToToken( | |
- ERC20Clone src, | |
- uint srcAmount, | |
- ERC20Clone dest, | |
- uint minConversionRate | |
- ) | |
- public | |
- returns(uint) | |
- { | |
- bytes memory hint; | |
- | |
- return tradeWithHint( | |
- src, | |
- srcAmount, | |
- dest, | |
- msg.sender, | |
- MAX_QTY, | |
- minConversionRate, | |
- 0, | |
- hint | |
- ); | |
- } | |
- | |
- /// @dev makes a trade from Ether to token. Sends token to msg sender | |
- /// @param token Destination token | |
- /// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled. | |
- /// @return amount of actual dest tokens | |
- function swapEtherToToken(ERC20Clone token, uint minConversionRate) public payable returns(uint) { | |
- bytes memory hint; | |
- | |
- return tradeWithHint( | |
- ETH_TOKEN_ADDRESS, | |
- msg.value, | |
- token, | |
- msg.sender, | |
- MAX_QTY, | |
- minConversionRate, | |
- 0, | |
- hint | |
- ); | |
- } | |
- | |
- /// @dev makes a trade from token to Ether, sends Ether to msg sender | |
- /// @param token Src token | |
- /// @param srcAmount amount of src tokens | |
- /// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled. | |
- /// @return amount of actual dest tokens | |
- function swapTokenToEther(ERC20Clone token, uint srcAmount, uint minConversionRate) public returns(uint) { | |
- bytes memory hint; | |
- | |
- return tradeWithHint( | |
- token, | |
- srcAmount, | |
- ETH_TOKEN_ADDRESS, | |
- msg.sender, | |
- MAX_QTY, | |
- minConversionRate, | |
- 0, | |
- hint | |
- ); | |
- } | |
- | |
- struct UserBalance { | |
- uint srcBalance; | |
- uint destBalance; | |
- } | |
- | |
- event ExecuteTrade(address indexed trader, ERC20Clone src, ERC20Clone dest, uint actualSrcAmount, uint actualDestAmount); | |
- | |
- /// @notice use token address ETH_TOKEN_ADDRESS for ether | |
- /// @dev makes a trade between src and dest token and send dest token to destAddress | |
- /// @param src Src token | |
- /// @param srcAmount amount of src tokens | |
- /// @param dest Destination token | |
- /// @param destAddress Address to send tokens to | |
- /// @param maxDestAmount A limit on the amount of dest tokens | |
- /// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled. | |
- /// @param walletId is the wallet ID to send part of the fees | |
- /// @param hint will give hints for the trade. | |
- /// @return amount of actual dest tokens | |
- function tradeWithHint( | |
- ERC20Clone src, | |
- uint srcAmount, | |
- ERC20Clone dest, | |
- address destAddress, | |
- uint maxDestAmount, | |
- uint minConversionRate, | |
- address walletId, | |
- bytes hint | |
- ) | |
- public | |
- payable | |
- returns(uint) | |
- { | |
- require(src == ETH_TOKEN_ADDRESS || msg.value == 0); | |
- | |
- UserBalance memory userBalanceBefore; | |
- | |
- userBalanceBefore.srcBalance = getBalance(src, msg.sender); | |
- userBalanceBefore.destBalance = getBalance(dest, destAddress); | |
- | |
- if (src == ETH_TOKEN_ADDRESS) { | |
- userBalanceBefore.srcBalance += msg.value; | |
- } else { | |
- require(src.transferFrom(msg.sender, kyberNetworkContract, srcAmount)); | |
- } | |
- | |
- uint reportedDestAmount = kyberNetworkContract.tradeWithHint.value(msg.value)( | |
- msg.sender, | |
- src, | |
- srcAmount, | |
- dest, | |
- destAddress, | |
- maxDestAmount, | |
- minConversionRate, | |
- walletId, | |
- hint | |
- ); | |
- | |
- TradeOutcome memory tradeOutcome = calculateTradeOutcome( | |
- userBalanceBefore.srcBalance, | |
- userBalanceBefore.destBalance, | |
- src, | |
- dest, | |
- destAddress | |
- ); | |
- | |
- require(reportedDestAmount == tradeOutcome.userDeltaDestAmount); | |
- require(tradeOutcome.userDeltaDestAmount <= maxDestAmount); | |
- require(tradeOutcome.actualRate >= minConversionRate); | |
- | |
- ExecuteTrade(msg.sender, src, dest, tradeOutcome.userDeltaSrcAmount, tradeOutcome.userDeltaDestAmount); | |
- return tradeOutcome.userDeltaDestAmount; | |
- } | |
- | |
- event KyberNetworkSet(address newNetworkContract, address oldNetworkContract); | |
- | |
- function setKyberNetworkContract(KyberNetworkInterface _kyberNetworkContract) public onlyAdmin { | |
- | |
- require(_kyberNetworkContract != address(0)); | |
- | |
- KyberNetworkSet(_kyberNetworkContract, kyberNetworkContract); | |
- | |
- kyberNetworkContract = _kyberNetworkContract; | |
- } | |
- | |
- function getExpectedRate(ERC20Clone src, ERC20Clone dest, uint srcQty) | |
- public view | |
- returns(uint expectedRate, uint slippageRate) | |
- { | |
- return kyberNetworkContract.getExpectedRate(src, dest, srcQty); | |
- } | |
- | |
- function getUserCapInWei(address user) public view returns(uint) { | |
- return kyberNetworkContract.getUserCapInWei(user); | |
- } | |
- | |
- function getUserCapInTokenWei(address user, ERC20Clone token) public view returns(uint) { | |
- return kyberNetworkContract.getUserCapInTokenWei(user, token); | |
- } | |
- | |
- function maxGasPrice() public view returns(uint) { | |
- return kyberNetworkContract.maxGasPrice(); | |
- } | |
- | |
- function enabled() public view returns(bool) { | |
- return kyberNetworkContract.enabled(); | |
- } | |
- | |
- function info(bytes32 field) public view returns(uint) { | |
- return kyberNetworkContract.info(field); | |
- } | |
- | |
- struct TradeOutcome { | |
- uint userDeltaSrcAmount; | |
- uint userDeltaDestAmount; | |
- uint actualRate; | |
- } | |
- | |
- function calculateTradeOutcome (uint srcBalanceBefore, uint destBalanceBefore, ERC20Clone src, ERC20Clone dest, | |
- address destAddress) | |
- internal returns(TradeOutcome outcome) | |
- { | |
- uint userSrcBalanceAfter; | |
- uint userDestBalanceAfter; | |
- | |
- userSrcBalanceAfter = getBalance(src, msg.sender); | |
- userDestBalanceAfter = getBalance(dest, destAddress); | |
- | |
- //protect from underflow | |
- require(userDestBalanceAfter > destBalanceBefore); | |
- require(srcBalanceBefore > userSrcBalanceAfter); | |
- | |
- outcome.userDeltaDestAmount = userDestBalanceAfter - destBalanceBefore; | |
- outcome.userDeltaSrcAmount = srcBalanceBefore - userSrcBalanceAfter; | |
- | |
- outcome.actualRate = calcRateFromQty( | |
- outcome.userDeltaSrcAmount, | |
- outcome.userDeltaDestAmount, | |
- getDecimalsSafe(src), | |
- getDecimalsSafe(dest) | |
- ); | |
- } | |
-} | |
diff --git a/src/contracts/exchanges/third-party/kyber/KyberReserve.sol b/src/contracts/exchanges/third-party/kyber/KyberReserve.sol | |
deleted file mode 100644 | |
index f9f17fa2..00000000 | |
--- a/src/contracts/exchanges/third-party/kyber/KyberReserve.sol | |
+++ /dev/null | |
@@ -1,256 +0,0 @@ | |
-pragma solidity ^0.4.21; | |
- | |
-import "KyberDependencies.sol"; | |
- | |
-/// @title Kyber Reserve contract | |
-contract KyberReserve is KyberReserveInterface, Withdrawable, Utils { | |
- | |
- address public kyberNetwork; | |
- bool public tradeEnabled; | |
- ConversionRatesInterface public conversionRatesContract; | |
- SanityRatesInterface public sanityRatesContract; | |
- mapping(bytes32=>bool) public approvedWithdrawAddresses; // sha3(token,address)=>bool | |
- mapping(address=>address) public tokenWallet; | |
- | |
- function KyberReserve(address _kyberNetwork, ConversionRatesInterface _ratesContract, address _admin) public { | |
- require(_admin != address(0)); | |
- require(_ratesContract != address(0)); | |
- require(_kyberNetwork != address(0)); | |
- kyberNetwork = _kyberNetwork; | |
- conversionRatesContract = _ratesContract; | |
- admin = _admin; | |
- tradeEnabled = true; | |
- } | |
- | |
- event DepositToken(ERC20Clone token, uint amount); | |
- | |
- function() public payable { | |
- DepositToken(ETH_TOKEN_ADDRESS, msg.value); | |
- } | |
- | |
- event TradeExecute( | |
- address indexed origin, | |
- address src, | |
- uint srcAmount, | |
- address destToken, | |
- uint destAmount, | |
- address destAddress | |
- ); | |
- | |
- function trade( | |
- ERC20Clone srcToken, | |
- uint srcAmount, | |
- ERC20Clone destToken, | |
- address destAddress, | |
- uint conversionRate, | |
- bool validate | |
- ) | |
- public | |
- payable | |
- returns(bool) | |
- { | |
- require(tradeEnabled); | |
- require(msg.sender == kyberNetwork); | |
- | |
- require(doTrade(srcToken, srcAmount, destToken, destAddress, conversionRate, validate)); | |
- | |
- return true; | |
- } | |
- | |
- event TradeEnabled(bool enable); | |
- | |
- function enableTrade() public onlyAdmin returns(bool) { | |
- tradeEnabled = true; | |
- TradeEnabled(true); | |
- | |
- return true; | |
- } | |
- | |
- function disableTrade() public onlyAlerter returns(bool) { | |
- tradeEnabled = false; | |
- TradeEnabled(false); | |
- | |
- return true; | |
- } | |
- | |
- event WithdrawAddressApproved(ERC20Clone token, address addr, bool approve); | |
- | |
- function approveWithdrawAddress(ERC20Clone token, address addr, bool approve) public onlyAdmin { | |
- approvedWithdrawAddresses[keccak256(token, addr)] = approve; | |
- WithdrawAddressApproved(token, addr, approve); | |
- | |
- setDecimals(token); | |
- if ((tokenWallet[token] == address(0x0)) && (token != ETH_TOKEN_ADDRESS)) { | |
- tokenWallet[token] = this; // by default | |
- require(token.approve(this, 2 ** 255)); | |
- } | |
- } | |
- | |
- event NewTokenWallet(ERC20Clone token, address wallet); | |
- | |
- function setTokenWallet(ERC20Clone token, address wallet) public onlyAdmin { | |
- require(wallet != address(0x0)); | |
- tokenWallet[token] = wallet; | |
- NewTokenWallet(token, wallet); | |
- } | |
- | |
- event WithdrawFunds(ERC20Clone token, uint amount, address destination); | |
- | |
- function withdraw(ERC20Clone token, uint amount, address destination) public onlyOperator returns(bool) { | |
- require(approvedWithdrawAddresses[keccak256(token, destination)]); | |
- | |
- if (token == ETH_TOKEN_ADDRESS) { | |
- destination.transfer(amount); | |
- } else { | |
- require(token.transferFrom(tokenWallet[token], destination, amount)); | |
- } | |
- | |
- WithdrawFunds(token, amount, destination); | |
- | |
- return true; | |
- } | |
- | |
- event SetContractAddresses(address network, address rate, address sanity); | |
- | |
- function setContracts( | |
- address _kyberNetwork, | |
- ConversionRatesInterface _conversionRates, | |
- SanityRatesInterface _sanityRates | |
- ) | |
- public | |
- onlyAdmin | |
- { | |
- require(_kyberNetwork != address(0)); | |
- require(_conversionRates != address(0)); | |
- | |
- kyberNetwork = _kyberNetwork; | |
- conversionRatesContract = _conversionRates; | |
- sanityRatesContract = _sanityRates; | |
- | |
- SetContractAddresses(kyberNetwork, conversionRatesContract, sanityRatesContract); | |
- } | |
- | |
- //////////////////////////////////////////////////////////////////////////// | |
- /// status functions /////////////////////////////////////////////////////// | |
- //////////////////////////////////////////////////////////////////////////// | |
- function getBalance(ERC20Clone token) public view returns(uint) { | |
- if (token == ETH_TOKEN_ADDRESS) | |
- return this.balance; | |
- else { | |
- address wallet = tokenWallet[token]; | |
- uint balanceOfWallet = token.balanceOf(wallet); | |
- uint allowanceOfWallet = token.allowance(wallet, this); | |
- | |
- return (balanceOfWallet < allowanceOfWallet) ? balanceOfWallet : allowanceOfWallet; | |
- } | |
- } | |
- | |
- function getDestQty(ERC20Clone src, ERC20Clone dest, uint srcQty, uint rate) public view returns(uint) { | |
- uint dstDecimals = getDecimals(dest); | |
- uint srcDecimals = getDecimals(src); | |
- | |
- return calcDstQty(srcQty, srcDecimals, dstDecimals, rate); | |
- } | |
- | |
- function getSrcQty(ERC20Clone src, ERC20Clone dest, uint dstQty, uint rate) public view returns(uint) { | |
- uint dstDecimals = getDecimals(dest); | |
- uint srcDecimals = getDecimals(src); | |
- | |
- return calcSrcQty(dstQty, srcDecimals, dstDecimals, rate); | |
- } | |
- | |
- function getConversionRate(ERC20Clone src, ERC20Clone dest, uint srcQty, uint blockNumber) public view returns(uint) { | |
- ERC20Clone token; | |
- bool isBuy; | |
- | |
- if (!tradeEnabled) return 0; | |
- | |
- if (ETH_TOKEN_ADDRESS == src) { | |
- isBuy = true; | |
- token = dest; | |
- } else if (ETH_TOKEN_ADDRESS == dest) { | |
- isBuy = false; | |
- token = src; | |
- } else { | |
- return 0; // pair is not listed | |
- } | |
- | |
- uint rate = conversionRatesContract.getRate(token, blockNumber, isBuy, srcQty); | |
- uint destQty = getDestQty(src, dest, srcQty, rate); | |
- | |
- if (getBalance(dest) < destQty) return 0; | |
- | |
- if (sanityRatesContract != address(0)) { | |
- uint sanityRate = sanityRatesContract.getSanityRate(src, dest); | |
- if (rate > sanityRate) return 0; | |
- } | |
- | |
- return rate; | |
- } | |
- | |
- /// @dev do a trade | |
- /// @param srcToken Src token | |
- /// @param srcAmount Amount of src token | |
- /// @param destToken Destination token | |
- /// @param destAddress Destination address to send tokens to | |
- /// @param validate If true, additional validations are applicable | |
- /// @return true iff trade is successful | |
- function doTrade( | |
- ERC20Clone srcToken, | |
- uint srcAmount, | |
- ERC20Clone destToken, | |
- address destAddress, | |
- uint conversionRate, | |
- bool validate | |
- ) | |
- internal | |
- returns(bool) | |
- { | |
- // can skip validation if done at kyber network level | |
- if (validate) { | |
- require(conversionRate > 0); | |
- if (srcToken == ETH_TOKEN_ADDRESS) | |
- require(msg.value == srcAmount); | |
- else | |
- require(msg.value == 0); | |
- } | |
- | |
- uint destAmount = getDestQty(srcToken, destToken, srcAmount, conversionRate); | |
- // sanity check | |
- require(destAmount > 0); | |
- | |
- // add to imbalance | |
- ERC20Clone token; | |
- int tradeAmount; | |
- if (srcToken == ETH_TOKEN_ADDRESS) { | |
- tradeAmount = int(destAmount); | |
- token = destToken; | |
- } else { | |
- tradeAmount = -1 * int(srcAmount); | |
- token = srcToken; | |
- } | |
- | |
- conversionRatesContract.recordImbalance( | |
- token, | |
- tradeAmount, | |
- 0, | |
- block.number | |
- ); | |
- | |
- // collect src tokens | |
- if (srcToken != ETH_TOKEN_ADDRESS) { | |
- require(srcToken.transferFrom(msg.sender, tokenWallet[srcToken], srcAmount)); | |
- } | |
- | |
- // send dest tokens | |
- if (destToken == ETH_TOKEN_ADDRESS) { | |
- destAddress.transfer(destAmount); | |
- } else { | |
- require(destToken.transferFrom(tokenWallet[destToken], destAddress, destAmount)); | |
- } | |
- | |
- TradeExecute(msg.sender, srcToken, srcAmount, destToken, destAmount, destAddress); | |
- | |
- return true; | |
- } | |
-} | |
diff --git a/src/contracts/exchanges/third-party/kyber/SanityRates.sol b/src/contracts/exchanges/third-party/kyber/SanityRates.sol | |
deleted file mode 100644 | |
index ba271016..00000000 | |
--- a/src/contracts/exchanges/third-party/kyber/SanityRates.sol | |
+++ /dev/null | |
@@ -1,46 +0,0 @@ | |
-pragma solidity ^0.4.21; | |
- | |
-import "KyberDependencies.sol"; | |
- | |
-contract SanityRates is SanityRatesInterface, Withdrawable, Utils { | |
- mapping(address=>uint) public tokenRate; | |
- mapping(address=>uint) public reasonableDiffInBps; | |
- | |
- function SanityRates(address _admin) public { | |
- require(_admin != address(0)); | |
- admin = _admin; | |
- } | |
- | |
- function setReasonableDiff(ERC20Clone[] srcs, uint[] diff) public onlyAdmin { | |
- require(srcs.length == diff.length); | |
- for (uint i = 0; i < srcs.length; i++) { | |
- require(diff[i] <= 100 * 100); | |
- reasonableDiffInBps[srcs[i]] = diff[i]; | |
- } | |
- } | |
- | |
- function setSanityRates(ERC20Clone[] srcs, uint[] rates) public onlyOperator { | |
- require(srcs.length == rates.length); | |
- | |
- for (uint i = 0; i < srcs.length; i++) { | |
- require(rates[i] <= MAX_RATE); | |
- tokenRate[srcs[i]] = rates[i]; | |
- } | |
- } | |
- | |
- function getSanityRate(ERC20Clone src, ERC20Clone dest) public view returns(uint) { | |
- if (src != ETH_TOKEN_ADDRESS && dest != ETH_TOKEN_ADDRESS) return 0; | |
- | |
- uint rate; | |
- address token; | |
- if (src == ETH_TOKEN_ADDRESS) { | |
- rate = (PRECISION*PRECISION)/tokenRate[dest]; | |
- token = dest; | |
- } else { | |
- rate = tokenRate[src]; | |
- token = src; | |
- } | |
- | |
- return rate * (10000 + reasonableDiffInBps[token])/10000; | |
- } | |
-} | |
diff --git a/src/contracts/exchanges/third-party/oasisdex/MatchingMarket.sol b/src/contracts/exchanges/third-party/oasisdex/MatchingMarket.sol | |
deleted file mode 100644 | |
index b53321e4..00000000 | |
--- a/src/contracts/exchanges/third-party/oasisdex/MatchingMarket.sol | |
+++ /dev/null | |
@@ -1,1021 +0,0 @@ | |
-pragma solidity ^0.4.21; | |
- | |
-import "thing.sol"; | |
-import "ERC20.i.sol"; | |
- | |
-contract EventfulMarket { | |
- event LogItemUpdate(uint id); | |
- event LogTrade(uint pay_amt, address indexed pay_gem, | |
- uint buy_amt, address indexed buy_gem); | |
- | |
- event LogMake( | |
- bytes32 indexed id, | |
- bytes32 indexed pair, | |
- address indexed maker, | |
- ERC20 pay_gem, | |
- ERC20 buy_gem, | |
- uint128 pay_amt, | |
- uint128 buy_amt, | |
- uint64 timestamp | |
- ); | |
- | |
- event LogBump( | |
- bytes32 indexed id, | |
- bytes32 indexed pair, | |
- address indexed maker, | |
- ERC20 pay_gem, | |
- ERC20 buy_gem, | |
- uint128 pay_amt, | |
- uint128 buy_amt, | |
- uint64 timestamp | |
- ); | |
- | |
- event LogTake( | |
- bytes32 id, | |
- bytes32 indexed pair, | |
- address indexed maker, | |
- ERC20 pay_gem, | |
- ERC20 buy_gem, | |
- address indexed taker, | |
- uint128 take_amt, | |
- uint128 give_amt, | |
- uint64 timestamp | |
- ); | |
- | |
- event LogKill( | |
- bytes32 indexed id, | |
- bytes32 indexed pair, | |
- address indexed maker, | |
- ERC20 pay_gem, | |
- ERC20 buy_gem, | |
- uint128 pay_amt, | |
- uint128 buy_amt, | |
- uint64 timestamp | |
- ); | |
-} | |
- | |
-contract SimpleMarket is EventfulMarket, DSMath { | |
- | |
- uint public last_offer_id; | |
- | |
- mapping (uint => OfferInfo) public offers; | |
- | |
- bool locked; | |
- | |
- struct OfferInfo { | |
- uint pay_amt; | |
- ERC20 pay_gem; | |
- uint buy_amt; | |
- ERC20 buy_gem; | |
- address owner; | |
- uint64 timestamp; | |
- } | |
- | |
- modifier can_buy(uint id) { | |
- require(isActive(id)); | |
- _; | |
- } | |
- | |
- modifier can_cancel(uint id) { | |
- require(isActive(id)); | |
- require(getOwner(id) == msg.sender); | |
- _; | |
- } | |
- | |
- modifier can_offer { | |
- _; | |
- } | |
- | |
- modifier synchronized { | |
- require(!locked); | |
- locked = true; | |
- _; | |
- locked = false; | |
- } | |
- | |
- function isActive(uint id) public constant returns (bool active) { | |
- return offers[id].timestamp > 0; | |
- } | |
- | |
- function getOwner(uint id) public constant returns (address owner) { | |
- return offers[id].owner; | |
- } | |
- | |
- function getOffer(uint id) public constant returns (uint, ERC20, uint, ERC20) { | |
- var offer = offers[id]; | |
- return (offer.pay_amt, offer.pay_gem, | |
- offer.buy_amt, offer.buy_gem); | |
- } | |
- | |
- // ---- Public entrypoints ---- // | |
- | |
- function bump(bytes32 id_) | |
- public | |
- can_buy(uint256(id_)) | |
- { | |
- var id = uint256(id_); | |
- LogBump( | |
- id_, | |
- keccak256(offers[id].pay_gem, offers[id].buy_gem), | |
- offers[id].owner, | |
- offers[id].pay_gem, | |
- offers[id].buy_gem, | |
- uint128(offers[id].pay_amt), | |
- uint128(offers[id].buy_amt), | |
- offers[id].timestamp | |
- ); | |
- } | |
- | |
- // Accept given `quantity` of an offer. Transfers funds from caller to | |
- // offer maker, and from market to caller. | |
- function buy(uint id, uint quantity) | |
- public | |
- can_buy(id) | |
- synchronized | |
- returns (bool) | |
- { | |
- OfferInfo memory offer = offers[id]; | |
- uint spend = mul(quantity, offer.buy_amt) / offer.pay_amt; | |
- | |
- require(uint128(spend) == spend); | |
- require(uint128(quantity) == quantity); | |
- | |
- // For backwards semantic compatibility. | |
- if (quantity == 0 || spend == 0 || | |
- quantity > offer.pay_amt || spend > offer.buy_amt) | |
- { | |
- return false; | |
- } | |
- | |
- offers[id].pay_amt = sub(offer.pay_amt, quantity); | |
- offers[id].buy_amt = sub(offer.buy_amt, spend); | |
- require( offer.buy_gem.transferFrom(msg.sender, offer.owner, spend) ); | |
- require( offer.pay_gem.transfer(msg.sender, quantity) ); | |
- | |
- LogItemUpdate(id); | |
- LogTake( | |
- bytes32(id), | |
- keccak256(offer.pay_gem, offer.buy_gem), | |
- offer.owner, | |
- offer.pay_gem, | |
- offer.buy_gem, | |
- msg.sender, | |
- uint128(quantity), | |
- uint128(spend), | |
- uint64(now) | |
- ); | |
- LogTrade(quantity, offer.pay_gem, spend, offer.buy_gem); | |
- | |
- if (offers[id].pay_amt == 0) { | |
- delete offers[id]; | |
- } | |
- | |
- return true; | |
- } | |
- | |
- // Cancel an offer. Refunds offer maker. | |
- function cancel(uint id) | |
- public | |
- can_cancel(id) | |
- synchronized | |
- returns (bool success) | |
- { | |
- // read-only offer. Modify an offer by directly accessing offers[id] | |
- OfferInfo memory offer = offers[id]; | |
- delete offers[id]; | |
- | |
- require( offer.pay_gem.transfer(offer.owner, offer.pay_amt) ); | |
- | |
- LogItemUpdate(id); | |
- LogKill( | |
- bytes32(id), | |
- keccak256(offer.pay_gem, offer.buy_gem), | |
- offer.owner, | |
- offer.pay_gem, | |
- offer.buy_gem, | |
- uint128(offer.pay_amt), | |
- uint128(offer.buy_amt), | |
- uint64(now) | |
- ); | |
- | |
- success = true; | |
- } | |
- | |
- function kill(bytes32 id) | |
- public | |
- { | |
- require(cancel(uint256(id))); | |
- } | |
- | |
- function make( | |
- ERC20 pay_gem, | |
- ERC20 buy_gem, | |
- uint128 pay_amt, | |
- uint128 buy_amt | |
- ) | |
- public | |
- returns (bytes32 id) | |
- { | |
- return bytes32(offer(pay_amt, pay_gem, buy_amt, buy_gem)); | |
- } | |
- | |
- // Make a new offer. Takes funds from the caller into market escrow. | |
- function offer(uint pay_amt, ERC20 pay_gem, uint buy_amt, ERC20 buy_gem) | |
- public | |
- can_offer | |
- synchronized | |
- returns (uint id) | |
- { | |
- require(uint128(pay_amt) == pay_amt); | |
- require(uint128(buy_amt) == buy_amt); | |
- require(pay_amt > 0); | |
- require(pay_gem != ERC20(0x0)); | |
- require(buy_amt > 0); | |
- require(buy_gem != ERC20(0x0)); | |
- require(pay_gem != buy_gem); | |
- | |
- OfferInfo memory info; | |
- info.pay_amt = pay_amt; | |
- info.pay_gem = pay_gem; | |
- info.buy_amt = buy_amt; | |
- info.buy_gem = buy_gem; | |
- info.owner = msg.sender; | |
- info.timestamp = uint64(now); | |
- id = _next_id(); | |
- offers[id] = info; | |
- | |
- require( pay_gem.transferFrom(msg.sender, this, pay_amt) ); | |
- | |
- LogItemUpdate(id); | |
- LogMake( | |
- bytes32(id), | |
- keccak256(pay_gem, buy_gem), | |
- msg.sender, | |
- pay_gem, | |
- buy_gem, | |
- uint128(pay_amt), | |
- uint128(buy_amt), | |
- uint64(now) | |
- ); | |
- } | |
- | |
- function take(bytes32 id, uint128 maxTakeAmount) | |
- public | |
- { | |
- require(buy(uint256(id), maxTakeAmount)); | |
- } | |
- | |
- function _next_id() | |
- internal | |
- returns (uint) | |
- { | |
- last_offer_id++; return last_offer_id; | |
- } | |
-} | |
- | |
-// Simple Market with a market lifetime. When the close_time has been reached, | |
-// offers can only be cancelled (offer and buy will throw). | |
- | |
-contract ExpiringMarket is DSAuth, SimpleMarket { | |
- uint64 public close_time; | |
- bool public stopped; | |
- | |
- // after close_time has been reached, no new offers are allowed | |
- modifier can_offer { | |
- require(!isClosed()); | |
- _; | |
- } | |
- | |
- // after close, no new buys are allowed | |
- modifier can_buy(uint id) { | |
- require(isActive(id)); | |
- require(!isClosed()); | |
- _; | |
- } | |
- | |
- // after close, anyone can cancel an offer | |
- modifier can_cancel(uint id) { | |
- require(isActive(id)); | |
- require(isClosed() || (msg.sender == getOwner(id))); | |
- _; | |
- } | |
- | |
- function ExpiringMarket(uint64 _close_time) | |
- public | |
- { | |
- close_time = _close_time; | |
- } | |
- | |
- function isClosed() public constant returns (bool closed) { | |
- return stopped || getTime() > close_time; | |
- } | |
- | |
- function getTime() public constant returns (uint64) { | |
- return uint64(now); | |
- } | |
- | |
- function stop() public auth { | |
- stopped = true; | |
- } | |
-} | |
- | |
-contract MatchingEvents { | |
- event LogBuyEnabled(bool isEnabled); | |
- event LogMinSell(address pay_gem, uint min_amount); | |
- event LogMatchingEnabled(bool isEnabled); | |
- event LogUnsortedOffer(uint id); | |
- event LogSortedOffer(uint id); | |
- event LogAddTokenPairWhitelist(ERC20 baseToken, ERC20 quoteToken); | |
- event LogRemTokenPairWhitelist(ERC20 baseToken, ERC20 quoteToken); | |
- event LogInsert(address keeper, uint id); | |
- event LogDelete(address keeper, uint id); | |
-} | |
- | |
-contract MatchingMarket is MatchingEvents, ExpiringMarket, DSNote { | |
- bool public buyEnabled = true; //buy enabled | |
- bool public matchingEnabled = true; //true: enable matching, | |
- //false: revert to expiring market | |
- struct sortInfo { | |
- uint next; //points to id of next higher offer | |
- uint prev; //points to id of previous lower offer | |
- uint delb; //the blocknumber where this entry was marked for delete | |
- } | |
- mapping(uint => sortInfo) public _rank; //doubly linked lists of sorted offer ids | |
- mapping(address => mapping(address => uint)) public _best; //id of the highest offer for a token pair | |
- mapping(address => mapping(address => uint)) public _span; //number of offers stored for token pair in sorted orderbook | |
- mapping(address => uint) public _dust; //minimum sell amount for a token to avoid dust offers | |
- mapping(uint => uint) public _near; //next unsorted offer id | |
- mapping(bytes32 => bool) public _menu; //whitelist tracking which token pairs can be traded | |
- uint _head; //first unsorted offer id | |
- | |
- //check if token pair is enabled | |
- modifier isWhitelist(ERC20 buy_gem, ERC20 pay_gem) { | |
- require(_menu[keccak256(buy_gem, pay_gem)] || _menu[keccak256(pay_gem, buy_gem)]); | |
- _; | |
- } | |
- | |
- function MatchingMarket(uint64 close_time) ExpiringMarket(close_time) public { | |
- } | |
- | |
- // ---- Public entrypoints ---- // | |
- | |
- function make( | |
- ERC20 pay_gem, | |
- ERC20 buy_gem, | |
- uint128 pay_amt, | |
- uint128 buy_amt | |
- ) | |
- public | |
- returns (bytes32) | |
- { | |
- return bytes32(offer(pay_amt, pay_gem, buy_amt, buy_gem)); | |
- } | |
- | |
- function take(bytes32 id, uint128 maxTakeAmount) public { | |
- require(buy(uint256(id), maxTakeAmount)); | |
- } | |
- | |
- function kill(bytes32 id) public { | |
- require(cancel(uint256(id))); | |
- } | |
- | |
- // Make a new offer. Takes funds from the caller into market escrow. | |
- // | |
- // If matching is enabled: | |
- // * creates new offer without putting it in | |
- // the sorted list. | |
- // * available to authorized contracts only! | |
- // * keepers should call insert(id,pos) | |
- // to put offer in the sorted list. | |
- // | |
- // If matching is disabled: | |
- // * calls expiring market's offer(). | |
- // * available to everyone without authorization. | |
- // * no sorting is done. | |
- // | |
- function offer( | |
- uint pay_amt, //maker (ask) sell how much | |
- ERC20 pay_gem, //maker (ask) sell which token | |
- uint buy_amt, //taker (ask) buy how much | |
- ERC20 buy_gem //taker (ask) buy which token | |
- ) | |
- public | |
- isWhitelist(pay_gem, buy_gem) | |
- /* NOT synchronized!!! */ | |
- returns (uint) | |
- { | |
- var fn = matchingEnabled ? _offeru : super.offer; | |
- return fn(pay_amt, pay_gem, buy_amt, buy_gem); | |
- } | |
- | |
- // Make a new offer. Takes funds from the caller into market escrow. | |
- function offer( | |
- uint pay_amt, //maker (ask) sell how much | |
- ERC20 pay_gem, //maker (ask) sell which token | |
- uint buy_amt, //maker (ask) buy how much | |
- ERC20 buy_gem, //maker (ask) buy which token | |
- uint pos //position to insert offer, 0 should be used if unknown | |
- ) | |
- public | |
- isWhitelist(pay_gem, buy_gem) | |
- /*NOT synchronized!!! */ | |
- can_offer | |
- returns (uint) | |
- { | |
- return offer(pay_amt, pay_gem, buy_amt, buy_gem, pos, false); | |
- } | |
- | |
- function offer( | |
- uint pay_amt, //maker (ask) sell how much | |
- ERC20 pay_gem, //maker (ask) sell which token | |
- uint buy_amt, //maker (ask) buy how much | |
- ERC20 buy_gem, //maker (ask) buy which token | |
- uint pos, //position to insert offer, 0 should be used if unknown | |
- bool rounding //match "close enough" orders? | |
- ) | |
- public | |
- isWhitelist(pay_gem, buy_gem) | |
- /*NOT synchronized!!! */ | |
- can_offer | |
- returns (uint) | |
- { | |
- require(_dust[pay_gem] <= pay_amt); | |
- | |
- if (matchingEnabled) { | |
- return _matcho(pay_amt, pay_gem, buy_amt, buy_gem, pos, rounding); | |
- } | |
- return super.offer(pay_amt, pay_gem, buy_amt, buy_gem); | |
- } | |
- | |
- //Transfers funds from caller to offer maker, and from market to caller. | |
- function buy(uint id, uint amount) | |
- public | |
- /*NOT synchronized!!! */ | |
- can_buy(id) | |
- returns (bool) | |
- { | |
- var fn = matchingEnabled ? _buys : super.buy; | |
- return fn(id, amount); | |
- } | |
- | |
- // Cancel an offer. Refunds offer maker. | |
- function cancel(uint id) | |
- public | |
- /*NOT synchronized!!! */ | |
- can_cancel(id) | |
- returns (bool success) | |
- { | |
- if (matchingEnabled) { | |
- if (isOfferSorted(id)) { | |
- require(_unsort(id)); | |
- } else { | |
- require(_hide(id)); | |
- } | |
- } | |
- return super.cancel(id); //delete the offer. | |
- } | |
- | |
- //insert offer into the sorted list | |
- //keepers need to use this function | |
- function insert( | |
- uint id, //maker (ask) id | |
- uint pos //position to insert into | |
- ) | |
- public | |
- returns (bool) | |
- { | |
- require(!isOfferSorted(id)); //make sure offers[id] is not yet sorted | |
- require(isActive(id)); //make sure offers[id] is active | |
- | |
- _hide(id); //remove offer from unsorted offers list | |
- _sort(id, pos); //put offer into the sorted offers list | |
- LogInsert(msg.sender, id); | |
- return true; | |
- } | |
- | |
- //deletes _rank [id] | |
- // Function should be called by keepers. | |
- function del_rank(uint id) | |
- public | |
- returns (bool) | |
- { | |
- require(!isActive(id) && _rank[id].delb != 0 && _rank[id].delb < block.number - 10); | |
- delete _rank[id]; | |
- LogDelete(msg.sender, id); | |
- return true; | |
- } | |
- | |
- //returns true if token is succesfully added to whitelist | |
- // Function is used to add a token pair to the whitelist | |
- // All incoming offers are checked against the whitelist. | |
- function addTokenPairWhitelist( | |
- ERC20 baseToken, | |
- ERC20 quoteToken | |
- ) | |
- public | |
- auth | |
- note | |
- returns (bool) | |
- { | |
- require(!isTokenPairWhitelisted(baseToken, quoteToken)); | |
- require(address(baseToken) != 0x0 && address(quoteToken) != 0x0); | |
- | |
- _menu[keccak256(baseToken, quoteToken)] = true; | |
- LogAddTokenPairWhitelist(baseToken, quoteToken); | |
- return true; | |
- } | |
- | |
- //returns true if token is successfully removed from whitelist | |
- // Function is used to remove a token pair from the whitelist. | |
- // All incoming offers are checked against the whitelist. | |
- function remTokenPairWhitelist( | |
- ERC20 baseToken, | |
- ERC20 quoteToken | |
- ) | |
- public | |
- auth | |
- note | |
- returns (bool) | |
- { | |
- require(isTokenPairWhitelisted(baseToken, quoteToken)); | |
- | |
- delete _menu[keccak256(baseToken, quoteToken)]; | |
- delete _menu[keccak256(quoteToken, baseToken)]; | |
- LogRemTokenPairWhitelist(baseToken, quoteToken); | |
- return true; | |
- } | |
- | |
- function isTokenPairWhitelisted( | |
- ERC20 baseToken, | |
- ERC20 quoteToken | |
- ) | |
- public | |
- constant | |
- returns (bool) | |
- { | |
- return (_menu[keccak256(baseToken, quoteToken)] || _menu[keccak256(quoteToken, baseToken)]); | |
- } | |
- | |
- //set the minimum sell amount for a token | |
- // Function is used to avoid "dust offers" that have | |
- // very small amount of tokens to sell, and it would | |
- // cost more gas to accept the offer, than the value | |
- // of tokens received. | |
- function setMinSell( | |
- ERC20 pay_gem, //token to assign minimum sell amount to | |
- uint dust //maker (ask) minimum sell amount | |
- ) | |
- public | |
- auth | |
- note | |
- returns (bool) | |
- { | |
- _dust[pay_gem] = dust; | |
- LogMinSell(pay_gem, dust); | |
- return true; | |
- } | |
- | |
- //returns the minimum sell amount for an offer | |
- function getMinSell( | |
- ERC20 pay_gem //token for which minimum sell amount is queried | |
- ) | |
- public | |
- constant | |
- returns (uint) | |
- { | |
- return _dust[pay_gem]; | |
- } | |
- | |
- //set buy functionality enabled/disabled | |
- function setBuyEnabled(bool buyEnabled_) public auth returns (bool) { | |
- buyEnabled = buyEnabled_; | |
- LogBuyEnabled(buyEnabled); | |
- return true; | |
- } | |
- | |
- //set matching enabled/disabled | |
- // If matchingEnabled true(default), then inserted offers are matched. | |
- // Except the ones inserted by contracts, because those end up | |
- // in the unsorted list of offers, that must be later sorted by | |
- // keepers using insert(). | |
- // If matchingEnabled is false then MatchingMarket is reverted to ExpiringMarket, | |
- // and matching is not done, and sorted lists are disabled. | |
- function setMatchingEnabled(bool matchingEnabled_) public auth returns (bool) { | |
- matchingEnabled = matchingEnabled_; | |
- LogMatchingEnabled(matchingEnabled); | |
- return true; | |
- } | |
- | |
- //return the best offer for a token pair | |
- // the best offer is the lowest one if it's an ask, | |
- // and highest one if it's a bid offer | |
- function getBestOffer(ERC20 sell_gem, ERC20 buy_gem) public constant returns(uint) { | |
- return _best[sell_gem][buy_gem]; | |
- } | |
- | |
- //return the next worse offer in the sorted list | |
- // the worse offer is the higher one if its an ask, | |
- // a lower one if its a bid offer, | |
- // and in both cases the newer one if they're equal. | |
- function getWorseOffer(uint id) public constant returns(uint) { | |
- return _rank[id].prev; | |
- } | |
- | |
- //return the next better offer in the sorted list | |
- // the better offer is in the lower priced one if its an ask, | |
- // the next higher priced one if its a bid offer | |
- // and in both cases the older one if they're equal. | |
- function getBetterOffer(uint id) public constant returns(uint) { | |
- | |
- return _rank[id].next; | |
- } | |
- | |
- //return the amount of better offers for a token pair | |
- function getOfferCount(ERC20 sell_gem, ERC20 buy_gem) public constant returns(uint) { | |
- return _span[sell_gem][buy_gem]; | |
- } | |
- | |
- //get the first unsorted offer that was inserted by a contract | |
- // Contracts can't calculate the insertion position of their offer because it is not an O(1) operation. | |
- // Their offers get put in the unsorted list of offers. | |
- // Keepers can calculate the insertion position offchain and pass it to the insert() function to insert | |
- // the unsorted offer into the sorted list. Unsorted offers will not be matched, but can be bought with buy(). | |
- function getFirstUnsortedOffer() public constant returns(uint) { | |
- return _head; | |
- } | |
- | |
- //get the next unsorted offer | |
- // Can be used to cycle through all the unsorted offers. | |
- function getNextUnsortedOffer(uint id) public constant returns(uint) { | |
- return _near[id]; | |
- } | |
- | |
- function isOfferSorted(uint id) public constant returns(bool) { | |
- return _rank[id].next != 0 | |
- || _rank[id].prev != 0 | |
- || _best[offers[id].pay_gem][offers[id].buy_gem] == id; | |
- } | |
- | |
- function sellAllAmount(ERC20 pay_gem, uint pay_amt, ERC20 buy_gem, uint min_fill_amount) | |
- public | |
- returns (uint fill_amt) | |
- { | |
- uint offerId; | |
- while (pay_amt > 0) { //while there is amount to sell | |
- offerId = getBestOffer(buy_gem, pay_gem); //Get the best offer for the token pair | |
- require(offerId != 0); //Fails if there are not more offers | |
- | |
- // There is a chance that pay_amt is smaller than 1 wei of the other token | |
- if (pay_amt * 1 ether < wdiv(offers[offerId].buy_amt, offers[offerId].pay_amt)) { | |
- break; //We consider that all amount is sold | |
- } | |
- if (pay_amt >= offers[offerId].buy_amt) { //If amount to sell is higher or equal than current offer amount to buy | |
- fill_amt = add(fill_amt, offers[offerId].pay_amt); //Add amount bought to acumulator | |
- pay_amt = sub(pay_amt, offers[offerId].buy_amt); //Decrease amount to sell | |
- take(bytes32(offerId), uint128(offers[offerId].pay_amt)); //We take the whole offer | |
- } else { // if lower | |
- var baux = rmul(pay_amt * 10 ** 9, rdiv(offers[offerId].pay_amt, offers[offerId].buy_amt)) / 10 ** 9; | |
- fill_amt = add(fill_amt, baux); //Add amount bought to acumulator | |
- take(bytes32(offerId), uint128(baux)); //We take the portion of the offer that we need | |
- pay_amt = 0; //All amount is sold | |
- } | |
- } | |
- require(fill_amt >= min_fill_amount); | |
- } | |
- | |
- function buyAllAmount(ERC20 buy_gem, uint buy_amt, ERC20 pay_gem, uint max_fill_amount) | |
- public | |
- returns (uint fill_amt) | |
- { | |
- uint offerId; | |
- while (buy_amt > 0) { //Meanwhile there is amount to buy | |
- offerId = getBestOffer(buy_gem, pay_gem); //Get the best offer for the token pair | |
- require(offerId != 0); | |
- | |
- // There is a chance that buy_amt is smaller than 1 wei of the other token | |
- if (buy_amt * 1 ether < wdiv(offers[offerId].pay_amt, offers[offerId].buy_amt)) { | |
- break; //We consider that all amount is sold | |
- } | |
- if (buy_amt >= offers[offerId].pay_amt) { //If amount to buy is higher or equal than current offer amount to sell | |
- fill_amt = add(fill_amt, offers[offerId].buy_amt); //Add amount sold to acumulator | |
- buy_amt = sub(buy_amt, offers[offerId].pay_amt); //Decrease amount to buy | |
- take(bytes32(offerId), uint128(offers[offerId].pay_amt)); //We take the whole offer | |
- } else { //if lower | |
- fill_amt = add(fill_amt, rmul(buy_amt * 10 ** 9, rdiv(offers[offerId].buy_amt, offers[offerId].pay_amt)) / 10 ** 9); //Add amount sold to acumulator | |
- take(bytes32(offerId), uint128(buy_amt)); //We take the portion of the offer that we need | |
- buy_amt = 0; //All amount is bought | |
- } | |
- } | |
- require(fill_amt <= max_fill_amount); | |
- } | |
- | |
- function getBuyAmount(ERC20 buy_gem, ERC20 pay_gem, uint pay_amt) public constant returns (uint fill_amt) { | |
- var offerId = getBestOffer(buy_gem, pay_gem); //Get best offer for the token pair | |
- while (pay_amt > offers[offerId].buy_amt) { | |
- fill_amt = add(fill_amt, offers[offerId].pay_amt); //Add amount to buy accumulator | |
- pay_amt = sub(pay_amt, offers[offerId].buy_amt); //Decrease amount to pay | |
- if (pay_amt > 0) { //If we still need more offers | |
- offerId = getWorseOffer(offerId); //We look for the next best offer | |
- require(offerId != 0); //Fails if there are not enough offers to complete | |
- } | |
- } | |
- fill_amt = add(fill_amt, rmul(pay_amt * 10 ** 9, rdiv(offers[offerId].pay_amt, offers[offerId].buy_amt)) / 10 ** 9); //Add proportional amount of last offer to buy accumulator | |
- } | |
- | |
- function getPayAmount(ERC20 pay_gem, ERC20 buy_gem, uint buy_amt) public constant returns (uint fill_amt) { | |
- var offerId = getBestOffer(buy_gem, pay_gem); //Get best offer for the token pair | |
- while (buy_amt > offers[offerId].pay_amt) { | |
- fill_amt = add(fill_amt, offers[offerId].buy_amt); //Add amount to pay accumulator | |
- buy_amt = sub(buy_amt, offers[offerId].pay_amt); //Decrease amount to buy | |
- if (buy_amt > 0) { //If we still need more offers | |
- offerId = getWorseOffer(offerId); //We look for the next best offer | |
- require(offerId != 0); //Fails if there are not enough offers to complete | |
- } | |
- } | |
- fill_amt = add(fill_amt, rmul(buy_amt * 10 ** 9, rdiv(offers[offerId].buy_amt, offers[offerId].pay_amt)) / 10 ** 9); //Add proportional amount of last offer to pay accumulator | |
- } | |
- | |
- // ---- Internal Functions ---- // | |
- | |
- function _buys(uint id, uint amount) | |
- internal | |
- returns (bool) | |
- { | |
- require(buyEnabled); | |
- | |
- if (amount == offers[id].pay_amt && isOfferSorted(id)) { | |
- //offers[id] must be removed from sorted list because all of it is bought | |
- _unsort(id); | |
- } | |
- require(super.buy(id, amount)); | |
- return true; | |
- } | |
- | |
- //find the id of the next higher offer after offers[id] | |
- function _find(uint id) | |
- internal | |
- view | |
- returns (uint) | |
- { | |
- require( id > 0 ); | |
- | |
- address buy_gem = address(offers[id].buy_gem); | |
- address pay_gem = address(offers[id].pay_gem); | |
- uint top = _best[pay_gem][buy_gem]; | |
- uint old_top = 0; | |
- | |
- // Find the larger-than-id order whose successor is less-than-id. | |
- while (top != 0 && _isPricedLtOrEq(id, top)) { | |
- old_top = top; | |
- top = _rank[top].prev; | |
- } | |
- return old_top; | |
- } | |
- | |
- //find the id of the next higher offer after offers[id] | |
- function _findpos(uint id, uint pos) | |
- internal | |
- view | |
- returns (uint) | |
- { | |
- require(id > 0); | |
- | |
- // Look for an active order. | |
- while (pos != 0 && !isActive(pos)) { | |
- pos = _rank[pos].prev; | |
- } | |
- | |
- if (pos == 0) { | |
- //if we got to the end of list without a single active offer | |
- return _find(id); | |
- | |
- } else { | |
- // if we did find a nearby active offer | |
- // Walk the order book down from there... | |
- if(_isPricedLtOrEq(id, pos)) { | |
- uint old_pos; | |
- | |
- // Guaranteed to run at least once because of | |
- // the prior if statements. | |
- while (pos != 0 && _isPricedLtOrEq(id, pos)) { | |
- old_pos = pos; | |
- pos = _rank[pos].prev; | |
- } | |
- return old_pos; | |
- | |
- // ...or walk it up. | |
- } else { | |
- while (pos != 0 && !_isPricedLtOrEq(id, pos)) { | |
- pos = _rank[pos].next; | |
- } | |
- return pos; | |
- } | |
- } | |
- } | |
- | |
- //return true if offers[low] priced less than or equal to offers[high] | |
- function _isPricedLtOrEq( | |
- uint low, //lower priced offer's id | |
- uint high //higher priced offer's id | |
- ) | |
- internal | |
- view | |
- returns (bool) | |
- { | |
- return mul(offers[low].buy_amt, offers[high].pay_amt) | |
- >= mul(offers[high].buy_amt, offers[low].pay_amt); | |
- } | |
- | |
- //these variables are global only because of solidity local variable limit | |
- | |
- //match offers with taker offer, and execute token transactions | |
- function _matcho( | |
- uint t_pay_amt, //taker sell how much | |
- ERC20 t_pay_gem, //taker sell which token | |
- uint t_buy_amt, //taker buy how much | |
- ERC20 t_buy_gem, //taker buy which token | |
- uint pos, //position id | |
- bool rounding //match "close enough" orders? | |
- ) | |
- internal | |
- returns (uint id) | |
- { | |
- uint best_maker_id; //highest maker id | |
- uint t_buy_amt_old; //taker buy how much saved | |
- uint m_buy_amt; //maker offer wants to buy this much token | |
- uint m_pay_amt; //maker offer wants to sell this much token | |
- | |
- // there is at least one offer stored for token pair | |
- while (_best[t_buy_gem][t_pay_gem] > 0) { | |
- best_maker_id = _best[t_buy_gem][t_pay_gem]; | |
- m_buy_amt = offers[best_maker_id].buy_amt; | |
- m_pay_amt = offers[best_maker_id].pay_amt; | |
- | |
- // Ugly hack to work around rounding errors. Based on the idea that | |
- // the furthest the amounts can stray from their "true" values is 1. | |
- // Ergo the worst case has t_pay_amt and m_pay_amt at +1 away from | |
- // their "correct" values and m_buy_amt and t_buy_amt at -1. | |
- // Since (c - 1) * (d - 1) > (a + 1) * (b + 1) is equivalent to | |
- // c * d > a * b + a + b + c + d, we write... | |
- if (mul(m_buy_amt, t_buy_amt) > mul(t_pay_amt, m_pay_amt) + | |
- (rounding ? m_buy_amt + t_buy_amt + t_pay_amt + m_pay_amt : 0)) | |
- { | |
- break; | |
- } | |
- // ^ The `rounding` parameter is a compromise borne of a couple days | |
- // of discussion. | |
- | |
- buy(best_maker_id, min(m_pay_amt, t_buy_amt)); | |
- t_buy_amt_old = t_buy_amt; | |
- t_buy_amt = sub(t_buy_amt, min(m_pay_amt, t_buy_amt)); | |
- t_pay_amt = mul(t_buy_amt, t_pay_amt) / t_buy_amt_old; | |
- | |
- if (t_pay_amt == 0 || t_buy_amt == 0) { | |
- break; | |
- } | |
- } | |
- | |
- if (t_buy_amt > 0 && t_pay_amt > 0) { | |
- //new offer should be created | |
- id = super.offer(t_pay_amt, t_pay_gem, t_buy_amt, t_buy_gem); | |
- //insert offer into the sorted list | |
- _sort(id, pos); | |
- } | |
- } | |
- | |
- // Make a new offer without putting it in the sorted list. | |
- // Takes funds from the caller into market escrow. | |
- // ****Available to authorized contracts only!********** | |
- // Keepers should call insert(id,pos) to put offer in the sorted list. | |
- function _offeru( | |
- uint pay_amt, //maker (ask) sell how much | |
- ERC20 pay_gem, //maker (ask) sell which token | |
- uint buy_amt, //maker (ask) buy how much | |
- ERC20 buy_gem //maker (ask) buy which token | |
- ) | |
- internal | |
- /*NOT synchronized!!! */ | |
- returns (uint id) | |
- { | |
- require(_dust[pay_gem] <= pay_amt); | |
- id = super.offer(pay_amt, pay_gem, buy_amt, buy_gem); | |
- _near[id] = _head; | |
- _head = id; | |
- LogUnsortedOffer(id); | |
- } | |
- | |
- //put offer into the sorted list | |
- function _sort( | |
- uint id, //maker (ask) id | |
- uint pos //position to insert into | |
- ) | |
- internal | |
- { | |
- require(isActive(id)); | |
- | |
- address buy_gem = address(offers[id].buy_gem); | |
- address pay_gem = address(offers[id].pay_gem); | |
- uint prev_id; //maker (ask) id | |
- | |
- if (pos == 0 || !isOfferSorted(pos)) { | |
- pos = _find(id); | |
- } else { | |
- pos = _findpos(id, pos); | |
- | |
- //if user has entered a `pos` that belongs to another currency pair | |
- //we start from scratch | |
- if(pos != 0 && (offers[pos].pay_gem != offers[id].pay_gem | |
- || offers[pos].buy_gem != offers[id].buy_gem)) | |
- { | |
- pos = 0; | |
- pos=_find(id); | |
- } | |
- } | |
- | |
- | |
- //requirement below is satisfied by statements above | |
- //require(pos == 0 || isOfferSorted(pos)); | |
- | |
- | |
- if (pos != 0) { //offers[id] is not the highest offer | |
- //requirement below is satisfied by statements above | |
- //require(_isPricedLtOrEq(id, pos)); | |
- prev_id = _rank[pos].prev; | |
- _rank[pos].prev = id; | |
- _rank[id].next = pos; | |
- } else { //offers[id] is the highest offer | |
- prev_id = _best[pay_gem][buy_gem]; | |
- _best[pay_gem][buy_gem] = id; | |
- } | |
- | |
- if (prev_id != 0) { //if lower offer does exist | |
- //requirement below is satisfied by statements above | |
- //require(!_isPricedLtOrEq(id, prev_id)); | |
- _rank[prev_id].next = id; | |
- _rank[id].prev = prev_id; | |
- } | |
- | |
- _span[pay_gem][buy_gem]++; | |
- LogSortedOffer(id); | |
- } | |
- | |
- // Remove offer from the sorted list (does not cancel offer) | |
- function _unsort( | |
- uint id //id of maker (ask) offer to remove from sorted list | |
- ) | |
- internal | |
- returns (bool) | |
- { | |
- address buy_gem = address(offers[id].buy_gem); | |
- address pay_gem = address(offers[id].pay_gem); | |
- require(_span[pay_gem][buy_gem] > 0); | |
- | |
- require(_rank[id].delb == 0 && //assert id is in the sorted list | |
- isOfferSorted(id)); | |
- | |
- if (id != _best[pay_gem][buy_gem]) { // offers[id] is not the highest offer | |
- require(_rank[_rank[id].next].prev == id); | |
- _rank[_rank[id].next].prev = _rank[id].prev; | |
- } else { //offers[id] is the highest offer | |
- _best[pay_gem][buy_gem] = _rank[id].prev; | |
- } | |
- | |
- if (_rank[id].prev != 0) { //offers[id] is not the lowest offer | |
- require(_rank[_rank[id].prev].next == id); | |
- _rank[_rank[id].prev].next = _rank[id].next; | |
- } | |
- | |
- _span[pay_gem][buy_gem]--; | |
- _rank[id].delb = block.number; //mark _rank[id] for deletion | |
- return true; | |
- } | |
- | |
- //Hide offer from the unsorted order book (does not cancel offer) | |
- function _hide( | |
- uint id //id of maker offer to remove from unsorted list | |
- ) | |
- internal | |
- returns (bool) | |
- { | |
- uint uid = _head; //id of an offer in unsorted offers list | |
- uint pre = uid; //id of previous offer in unsorted offers list | |
- | |
- require(!isOfferSorted(id)); //make sure offer id is not in sorted offers list | |
- | |
- if (_head == id) { //check if offer is first offer in unsorted offers list | |
- _head = _near[id]; //set head to new first unsorted offer | |
- _near[id] = 0; //delete order from unsorted order list | |
- return true; | |
- } | |
- while (uid > 0 && uid != id) { //find offer in unsorted order list | |
- pre = uid; | |
- uid = _near[uid]; | |
- } | |
- if (uid != id) { //did not find offer id in unsorted offers list | |
- return false; | |
- } | |
- _near[pre] = _near[id]; //set previous unsorted offer to point to offer after offer id | |
- _near[id] = 0; //delete order from unsorted order list | |
- return true; | |
- } | |
-} | |
diff --git a/src/contracts/factory/FundRanking.sol b/src/contracts/factory/FundRanking.sol | |
deleted file mode 100644 | |
index 7e7db349..00000000 | |
--- a/src/contracts/factory/FundRanking.sol | |
+++ /dev/null | |
@@ -1,75 +0,0 @@ | |
-pragma solidity ^0.4.25; | |
-pragma experimental ABIEncoderV2; | |
- | |
-import "FundFactory.sol"; | |
-import "Accounting.sol"; | |
- | |
-contract FundRanking { | |
- function getFundDetails(address _factory) | |
- external | |
- view | |
- returns(address[], uint[], uint[], string[], address[]) | |
- { | |
- FundFactory factory = FundFactory(_factory); | |
- uint numberOfFunds = factory.getLastFundId() + 1; | |
- address[] memory hubs = new address[](numberOfFunds); | |
- uint[] memory sharePrices = new uint[](numberOfFunds); | |
- uint[] memory creationTimes = new uint[](numberOfFunds); | |
- string[] memory names = new string[](numberOfFunds); | |
- address[] memory denominationAssets = new address[](numberOfFunds); | |
- | |
- for (uint i = 0; i < numberOfFunds; i++) { | |
- address hubAddress = factory.funds(i); | |
- Hub hub = Hub(hubAddress); | |
- hubs[i] = hubAddress; | |
- sharePrices[i] = Accounting(hub.accounting()).calcSharePrice(); | |
- denominationAssets[i] = Accounting(hub.accounting()).DENOMINATION_ASSET(); | |
- creationTimes[i] = hub.creationTime(); | |
- names[i] = hub.name(); | |
- } | |
- return (hubs, sharePrices, creationTimes, names, denominationAssets); | |
- } | |
- | |
- function getFundGavs(address _factory) | |
- external | |
- view | |
- returns(address[], uint[]) | |
- { | |
- FundFactory factory = FundFactory(_factory); | |
- uint numberOfFunds = factory.getLastFundId() + 1; | |
- address[] memory hubs = new address[](numberOfFunds); | |
- uint[] memory gavs = new uint[](numberOfFunds); | |
- | |
- for (uint i = 0; i < numberOfFunds; i++) { | |
- address hubAddress = factory.funds(i); | |
- Hub hub = Hub(hubAddress); | |
- uint gav = Accounting(hub.accounting()).calcGav(); | |
- | |
- hubs[i] = hubAddress; | |
- gavs[i] = gav; | |
- } | |
- return (hubs, gavs); | |
- } | |
- | |
- function getFundVersions(address _factory) | |
- external | |
- view | |
- returns(address[], bytes32[]) | |
- { | |
- FundFactory factory = FundFactory(_factory); | |
- uint numberOfFunds = factory.getLastFundId() + 1; | |
- address[] memory hubs = new address[](numberOfFunds); | |
- bytes32[] memory versions = new bytes32[](numberOfFunds); | |
- | |
- for (uint i = 0; i < numberOfFunds; i++) { | |
- address hubAddress = factory.funds(i); | |
- Hub hub = Hub(hubAddress); | |
- | |
- (, bytes32 version) = Registry(hub.registry()).versionInformation(hub.version()); | |
- | |
- hubs[i] = hubAddress; | |
- versions[i] = version; | |
- } | |
- return (hubs, versions); | |
- } | |
-} | |
diff --git a/src/contracts/fund/accounting/Accounting.i.sol b/src/contracts/fund/accounting/Accounting.i.sol | |
deleted file mode 100644 | |
index 3bddbf87..00000000 | |
--- a/src/contracts/fund/accounting/Accounting.i.sol | |
+++ /dev/null | |
@@ -1,30 +0,0 @@ | |
-pragma solidity ^0.4.25; | |
- | |
-/// @notice Gives metrics about a Fund | |
-interface AccountingInterface { | |
- | |
- event AssetAddition( | |
- address indexed asset | |
- ); | |
- | |
- event AssetRemoval( | |
- address indexed asset | |
- ); | |
- | |
- function getOwnedAssetsLength() external view returns (uint); | |
- function getFundHoldings() external returns (uint[], address[]); | |
- function calcAssetGAV(address ofAsset) external returns (uint); | |
- function calcGav() public returns (uint gav); | |
- function calcNav(uint gav, uint unclaimedFees) pure returns (uint); | |
- function valuePerShare(uint totalValue, uint numShares) public view returns (uint); | |
- function performCalculations() public returns ( | |
- uint gav, | |
- uint unclaimedFees, | |
- uint feesInShares, | |
- uint nav, | |
- uint sharePrice, | |
- uint gavPerShareNetManagementFee | |
- ); | |
- function calcSharePrice() external returns (uint); | |
- function calcGavPerShareNetManagementFee() public returns (uint); | |
-} | |
diff --git a/src/contracts/fund/participation/Participation.i.sol b/src/contracts/fund/participation/Participation.i.sol | |
deleted file mode 100644 | |
index c2134915..00000000 | |
--- a/src/contracts/fund/participation/Participation.i.sol | |
+++ /dev/null | |
@@ -1,46 +0,0 @@ | |
-pragma solidity ^0.4.25; | |
- | |
-/// @notice Investor Fund interactions | |
-/// @notice Handles redemptions and requests for investment | |
-interface ParticipationInterface { | |
- event EnableInvestment (address[] asset); | |
- event DisableInvestment (address[] assets); | |
- | |
- event InvestmentRequest ( | |
- address indexed requestOwner, | |
- address indexed investmentAsset, | |
- uint requestedShares, | |
- uint investmentAmount | |
- ); | |
- | |
- event RequestExecution ( | |
- address indexed requestOwner, | |
- address indexed executor, | |
- address indexed investmentAsset, | |
- uint investmentAmount, | |
- uint requestedShares | |
- ); | |
- | |
- event CancelRequest ( | |
- address indexed requestOwner | |
- ); | |
- | |
- event Redemption ( | |
- address indexed redeemer, | |
- address[] assets, | |
- uint[] assetQuantities, | |
- uint redeemedShares | |
- ); | |
- | |
- function requestInvestment( | |
- uint requestedShares, | |
- uint investmentAmount, | |
- address investmentAsset | |
- ) external payable; | |
- function hasRequest(address) view returns (bool); | |
- function cancelRequest() external payable; | |
- function executeRequestFor(address requestOwner) external payable; | |
- function redeem() external; | |
- function redeemWithConstraints(uint shareQuantity, address[] requestedAssets) public; | |
-} | |
- | |
diff --git a/src/contracts/fund/policies/BooleanPolicy.sol b/src/contracts/fund/policies/BooleanPolicy.sol | |
deleted file mode 100644 | |
index f99ffc47..00000000 | |
--- a/src/contracts/fund/policies/BooleanPolicy.sol | |
+++ /dev/null | |
@@ -1,23 +0,0 @@ | |
-pragma solidity ^0.4.25; | |
- | |
-import "Policy.sol"; | |
- | |
-contract BooleanPolicy is Policy { | |
- bool allowed; | |
- | |
- function rule(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) external view returns (bool) { | |
- return allowed; | |
- } | |
- | |
- function position() external view returns (Applied) { return Applied.pre; } | |
-} | |
- | |
-contract TruePolicy is BooleanPolicy { | |
- constructor() { allowed = true; } | |
- function identifier() external view returns (string) { "TruePolicy"; } | |
-} | |
- | |
-contract FalsePolicy is BooleanPolicy { | |
- constructor() { allowed = false; } | |
- function identifier() external view returns (string) { "FalsePolicy"; } | |
-} | |
diff --git a/src/contracts/fund/policies/TradingSignatures.sol b/src/contracts/fund/policies/TradingSignatures.sol | |
deleted file mode 100644 | |
index 3abf7a50..00000000 | |
--- a/src/contracts/fund/policies/TradingSignatures.sol | |
+++ /dev/null | |
@@ -1,6 +0,0 @@ | |
-pragma solidity ^0.4.25; | |
- | |
-contract TradingSignatures { | |
- bytes4 constant public MAKE_ORDER = 0x79705be7; // makeOrderSignature | |
- bytes4 constant public TAKE_ORDER = 0xe51be6e8; // takeOrderSignature | |
-} | |
\ No newline at end of file | |
diff --git a/src/contracts/fund/shares/Shares.i.sol b/src/contracts/fund/shares/Shares.i.sol | |
deleted file mode 100644 | |
index 054eeb89..00000000 | |
--- a/src/contracts/fund/shares/Shares.i.sol | |
+++ /dev/null | |
@@ -1,8 +0,0 @@ | |
-pragma solidity ^0.4.25; | |
- | |
-/// @notice Token representing ownership of the Fund | |
-interface SharesInterface { | |
- function createFor(address who, uint amount); | |
- function destroyFor(address who, uint amount); | |
-} | |
- | |
diff --git a/src/contracts/fund/trading/Trading.i.sol b/src/contracts/fund/trading/Trading.i.sol | |
deleted file mode 100644 | |
index 94c2eabf..00000000 | |
--- a/src/contracts/fund/trading/Trading.i.sol | |
+++ /dev/null | |
@@ -1,40 +0,0 @@ | |
-pragma solidity ^0.4.25; | |
- | |
-/// @notice Mediation between a Fund and exchanges | |
-interface TradingInterface { | |
- event ExchangeMethodCall( | |
- address indexed exchangeAddress, | |
- string indexed methodSignature, | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
- bytes32 identifier, | |
- bytes makerAssetData, | |
- bytes takerAssetData, | |
- bytes signature | |
- ); | |
- | |
- function callOnExchange( | |
- uint exchangeIndex, | |
- string methodSignature, | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
- bytes32 identifier, | |
- bytes makerAssetData, | |
- bytes takerAssetData, | |
- bytes signature | |
- ) public; | |
- | |
- function addOpenMakeOrder( | |
- address ofExchange, | |
- address ofSellAsset, | |
- address ofBuyAsset, | |
- uint orderId, | |
- uint expiryTime | |
- ) public; | |
- | |
- function removeOpenMakeOrder( | |
- address ofExchange, | |
- address ofSellAsset | |
- ) public; | |
-} | |
- | |
diff --git a/src/contracts/fund/vault/Vault.i.sol b/src/contracts/fund/vault/Vault.i.sol | |
deleted file mode 100644 | |
index 6ee3c14f..00000000 | |
--- a/src/contracts/fund/vault/Vault.i.sol | |
+++ /dev/null | |
@@ -1,7 +0,0 @@ | |
-pragma solidity ^0.4.25; | |
- | |
-/// @notice Custody component | |
-interface VaultInterface { | |
- function withdraw(address token, uint amount) external; | |
-} | |
- | |
diff --git a/src/contracts/prices/CanonicalPriceFeed.sol b/src/contracts/prices/CanonicalPriceFeed.sol | |
deleted file mode 100644 | |
index 3f6c732e..00000000 | |
--- a/src/contracts/prices/CanonicalPriceFeed.sol | |
+++ /dev/null | |
@@ -1,428 +0,0 @@ | |
-pragma solidity ^0.4.25; | |
- | |
-import "CanonicalRegistrar.sol"; | |
-import "SimplePriceFeed.sol"; | |
-import "StakingPriceFeed.sol"; | |
-import "OperatorStaking.sol"; | |
-import "ERC20.i.sol"; | |
- | |
-/// @author Melonport AG <team@melonport.com> | |
-/// @notice Routes external data to smart contracts | |
-/// @notice PriceFeed operator could be staked and sharePrice input validated on chain | |
-contract CanonicalPriceFeed is PriceSourceInterface, OperatorStaking, SimplePriceFeed, CanonicalRegistrar { | |
- | |
- // EVENTS | |
- event SetupPriceFeed(address ofPriceFeed); | |
- | |
- struct HistoricalPrices { | |
- address[] assets; | |
- uint[] prices; | |
- uint timestamp; | |
- } | |
- | |
- // FIELDS | |
- bool public updatesAreAllowed = true; | |
- uint public minimumPriceCount = 1; | |
- uint public VALIDITY; | |
- uint public INTERVAL; | |
- mapping (address => bool) public isStakingFeed; // If the Staking Feed has been created through this contract | |
- HistoricalPrices[] public priceHistory; | |
- uint public lastUpdate; | |
- | |
- // METHODS | |
- | |
- // CONSTRUCTOR | |
- | |
- /// @dev Define and register a quote asset against which all prices are measured/based against | |
- /// @param ofStakingAsset Address of staking asset (may or may not be quoteAsset) | |
- /// @param ofQuoteAsset Address of quote asset | |
- /// @param quoteAssetName Name of quote asset | |
- /// @param quoteAssetSymbol Symbol for quote asset | |
- /// @param quoteAssetUrl URL related to quote asset | |
- /// @param quoteAssetIpfsHash IPFS hash associated with quote asset | |
- /// @param quoteAssetBreakInBreakOut Break-in/break-out for quote asset on destination chain | |
- /// @param quoteAssetStandards EIP standards quote asset adheres to | |
- /// @param quoteAssetFunctionSignatures Whitelisted functions of quote asset contract | |
- // /// @param interval Number of seconds between pricefeed updates (this interval is not enforced on-chain, but should be followed by the datafeed maintainer) | |
- // /// @param validity Number of seconds that datafeed update information is valid for | |
- /// @param ofGovernance Address of contract governing the Canonical PriceFeed | |
- constructor( | |
- address ofStakingAsset, | |
- address ofQuoteAsset, // Inital entry in asset registrar contract is Melon (QUOTE_ASSET) | |
- bytes32 quoteAssetName, | |
- bytes8 quoteAssetSymbol, | |
- string quoteAssetUrl, | |
- string quoteAssetIpfsHash, | |
- address[2] quoteAssetBreakInBreakOut, | |
- uint[] quoteAssetStandards, | |
- bytes4[] quoteAssetFunctionSignatures, | |
- uint[2] updateInfo, // interval, validity | |
- uint[3] stakingInfo, // minStake, numOperators, unstakeDelay | |
- address ofGovernance | |
- ) | |
- OperatorStaking( | |
- ERC20(ofStakingAsset), stakingInfo[0], stakingInfo[1], stakingInfo[2] | |
- ) | |
- SimplePriceFeed(address(this), ofQuoteAsset, address(0)) | |
- { | |
- registerAsset( | |
- ofQuoteAsset, | |
- quoteAssetName, | |
- quoteAssetSymbol, | |
- quoteAssetUrl, | |
- quoteAssetIpfsHash, | |
- quoteAssetBreakInBreakOut, | |
- quoteAssetStandards, | |
- quoteAssetFunctionSignatures | |
- ); | |
- INTERVAL = updateInfo[0]; | |
- VALIDITY = updateInfo[1]; | |
- setOwner(ofGovernance); | |
- } | |
- | |
- // EXTERNAL METHODS | |
- | |
- /// @notice Create a new StakingPriceFeed | |
- function setupStakingPriceFeed() external { | |
- address ofStakingPriceFeed = new StakingPriceFeed( | |
- address(this), | |
- stakingToken, | |
- address(this) | |
- ); | |
- isStakingFeed[ofStakingPriceFeed] = true; | |
- StakingPriceFeed(ofStakingPriceFeed).setOwner(msg.sender); | |
- emit SetupPriceFeed(ofStakingPriceFeed); | |
- } | |
- | |
- /// @dev override inherited update function to prevent manual update from authority | |
- function update(address[] ofAssets, uint[] newPrices) external { revert("Unimplemented"); } | |
- | |
- /// @dev Burn state for a pricefeed operator | |
- /// @param user Address of pricefeed operator to burn the stake from | |
- function burnStake(address user) | |
- external | |
- auth | |
- { | |
- uint totalToBurn = add(stakedAmounts[user], stakeToWithdraw[user]); | |
- stakedAmounts[user] = 0; | |
- stakeToWithdraw[user] = 0; | |
- updateStakerRanking(user); | |
- emit StakeBurned(user, totalToBurn, ""); | |
- } | |
- | |
- // PUBLIC METHODS | |
- | |
- // STAKING | |
- | |
- function stake( | |
- uint amount, | |
- bytes data | |
- ) | |
- public | |
- { | |
- require( | |
- isStakingFeed[msg.sender], | |
- "Only staking feeds can call this" | |
- ); | |
- OperatorStaking.stake(amount, data); | |
- } | |
- | |
- // AGGREGATION | |
- | |
- /// @dev Only Owner; Same sized input arrays | |
- /// @dev Updates price of asset relative to QUOTE_ASSET | |
- /** Ex: | |
- * Let QUOTE_ASSET == MLN (base units), let asset == EUR-T, | |
- * let Value of 1 EUR-T := 1 EUR == 0.080456789 MLN, hence price 0.080456789 MLN / EUR-T | |
- * and let EUR-T decimals == 8. | |
- * Input would be: information[EUR-T].price = 8045678 [MLN/ (EUR-T * 10**8)] | |
- */ | |
- /// @param ofAssets list of asset addresses | |
- function collectAndUpdate(address[] ofAssets) | |
- public | |
- auth | |
- { | |
- require( | |
- updatesAreAllowed, | |
- "Updates are not allowed right now" | |
- ); | |
- uint[] memory newPrices = pricesToCommit(ofAssets); | |
- priceHistory.push( | |
- HistoricalPrices({assets: ofAssets, prices: newPrices, timestamp: block.timestamp}) | |
- ); | |
- _updatePrices(ofAssets, newPrices); | |
- lastUpdate = block.timestamp; | |
- PriceUpdate(ofAssets, newPrices); | |
- } | |
- | |
- function pricesToCommit(address[] ofAssets) | |
- public | |
- view | |
- returns (uint[]) | |
- { | |
- address[] memory operators = getOperators(); | |
- uint[] memory newPrices = new uint[](ofAssets.length); | |
- for (uint i = 0; i < ofAssets.length; i++) { | |
- uint[] memory assetPrices = new uint[](operators.length); | |
- for (uint j = 0; j < operators.length; j++) { | |
- SimplePriceFeed feed = SimplePriceFeed(operators[j]); | |
- uint price; | |
- uint timestamp; | |
- (price, timestamp) = feed.assetsToPrices(ofAssets[i]); | |
- if (now > add(timestamp, VALIDITY)) { | |
- continue; // leaves a zero in the array (dealt with later) | |
- } | |
- assetPrices[j] = price; | |
- } | |
- newPrices[i] = medianize(assetPrices); | |
- } | |
- return newPrices; | |
- } | |
- | |
- /// @dev from MakerDao medianizer contract | |
- function medianize(uint[] unsorted) | |
- public | |
- view | |
- returns (uint) | |
- { | |
- uint numValidEntries; | |
- for (uint i = 0; i < unsorted.length; i++) { | |
- if (unsorted[i] != 0) { | |
- numValidEntries++; | |
- } | |
- } | |
- require(numValidEntries > minimumPriceCount, "Not enough prices"); | |
- uint counter; | |
- uint[] memory out = new uint[](numValidEntries); | |
- for (uint j = 0; j < unsorted.length; j++) { | |
- uint item = unsorted[j]; | |
- if (item != 0) { // skip zero (invalid) entries | |
- if (counter == 0 || item >= out[counter - 1]) { | |
- out[counter] = item; // item is larger than last in array (we are home) | |
- } else { | |
- uint k = 0; | |
- while (item >= out[k]) { | |
- k++; // get to where element belongs (between smaller and larger items) | |
- } | |
- for (uint m = counter; m > k; m--) { | |
- out[m] = out[m - 1]; // bump larger elements rightward to leave slot | |
- } | |
- out[k] = item; | |
- } | |
- counter++; | |
- } | |
- } | |
- | |
- uint value; | |
- if (counter % 2 == 0) { | |
- uint value1 = uint(out[(counter / 2) - 1]); | |
- uint value2 = uint(out[(counter / 2)]); | |
- value = add(value1, value2) / 2; | |
- } else { | |
- value = out[(counter - 1) / 2]; | |
- } | |
- return value; | |
- } | |
- | |
- function setMinimumPriceCount(uint newCount) public auth { minimumPriceCount = newCount; } | |
- function enableUpdates() public auth { updatesAreAllowed = true; } | |
- function disableUpdates() public auth { updatesAreAllowed = false; } | |
- | |
- // PUBLIC VIEW METHODS | |
- | |
- // FEED INFORMATION | |
- | |
- function getQuoteAsset() public view returns (address) { return QUOTE_ASSET; } | |
- function getInterval() public view returns (uint) { return INTERVAL; } | |
- function getValidity() public view returns (uint) { return VALIDITY; } | |
- function getLastUpdateId() public view returns (uint) { return updateId; } | |
- | |
- // PRICES | |
- | |
- /// @notice Whether price of asset has been updated less than VALIDITY seconds ago | |
- /// @param ofAsset Asset in registrar | |
- /// @return isValid Price information ofAsset is recent | |
- function hasValidPrice(address ofAsset) | |
- public | |
- view | |
- returns (bool isValid) | |
- { | |
- require( | |
- assetIsRegistered(ofAsset), | |
- "Asset is not registered" | |
- ); | |
- uint timestamp; | |
- ( , timestamp) = getPrice(ofAsset); | |
- return (sub(now, timestamp) <= VALIDITY); | |
- } | |
- | |
- /// @notice Whether prices of assets have been updated less than VALIDITY seconds ago | |
- /// @param ofAssets All assets in registrar | |
- /// @return isValid Price information ofAssets array is recent | |
- function hasValidPrices(address[] ofAssets) | |
- public | |
- view | |
- returns (bool areRecent) | |
- { | |
- for (uint i; i < ofAssets.length; i++) { | |
- if (!hasValidPrice(ofAssets[i])) { | |
- return false; | |
- } | |
- } | |
- return true; | |
- } | |
- | |
- function getPriceInfo(address ofAsset) | |
- public | |
- view | |
- returns (uint price, uint assetDecimals) | |
- { | |
- (price, ) = getPrice(ofAsset); | |
- assetDecimals = getDecimals(ofAsset); | |
- } | |
- | |
- /** | |
- @notice Gets inverted price of an asset | |
- @dev Asset has been initialised and its price is non-zero | |
- @dev Existing price ofAssets quoted in QUOTE_ASSET (convention) | |
- @param ofAsset Asset for which inverted price should be return | |
- @return { | |
- "isValid": "Whether the price is fresh, given VALIDITY interval", | |
- "invertedPrice": "Price based (instead of quoted) against QUOTE_ASSET", | |
- "assetDecimals": "Decimal places for this asset" | |
- } | |
- */ | |
- function getInvertedPriceInfo(address ofAsset) | |
- public | |
- view | |
- returns (uint invertedPrice, uint assetDecimals) | |
- { | |
- uint inputPrice; | |
- // inputPrice quoted in QUOTE_ASSET and multiplied by 10 ** assetDecimal | |
- (inputPrice, assetDecimals) = getPriceInfo(ofAsset); | |
- | |
- // outputPrice based in QUOTE_ASSET and multiplied by 10 ** quoteDecimal | |
- uint quoteDecimals = getDecimals(QUOTE_ASSET); | |
- | |
- return ( | |
- mul( | |
- 10 ** uint(quoteDecimals), | |
- 10 ** uint(assetDecimals) | |
- ) / inputPrice, | |
- quoteDecimals | |
- ); | |
- } | |
- | |
- /** | |
- @notice Gets reference price of an asset pair | |
- @dev One of the address is equal to quote asset | |
- @dev either ofBase == QUOTE_ASSET or ofQuote == QUOTE_ASSET | |
- @param ofBase Address of base asset | |
- @param ofQuote Address of quote asset | |
- @return { | |
- "isValid": "Whether the price is fresh, given VALIDITY interval", | |
- "referencePrice": "Reference price", | |
- "decimal": "Decimal places for this asset" | |
- } | |
- */ | |
- function getReferencePriceInfo(address ofBase, address ofQuote) | |
- public | |
- view | |
- returns (uint referencePrice, uint decimal) | |
- { | |
- require(hasValidPrice(ofBase)); | |
- require(hasValidPrice(ofQuote)); | |
- if (getQuoteAsset() == ofQuote) { | |
- (referencePrice, decimal) = getPriceInfo(ofBase); | |
- } else if (getQuoteAsset() == ofBase) { | |
- (referencePrice, decimal) = getInvertedPriceInfo(ofQuote); | |
- } else { | |
- revert("One of the parameters must be quoteAsset"); | |
- } | |
- } | |
- | |
- /// @notice Gets price of Order | |
- /// @param sellAsset Address of the asset to be sold | |
- /// @param buyAsset Address of the asset to be bought | |
- /// @param sellQuantity Quantity in base units being sold of sellAsset | |
- /// @param buyQuantity Quantity in base units being bought of buyAsset | |
- /// @return orderPrice Price as determined by an order | |
- function getOrderPriceInfo( | |
- address sellAsset, | |
- address buyAsset, | |
- uint sellQuantity, | |
- uint buyQuantity | |
- ) | |
- public | |
- view | |
- returns (uint orderPrice) | |
- { | |
- return mul(buyQuantity, 10 ** uint(getDecimals(sellAsset))) / sellQuantity; | |
- } | |
- | |
- /// @notice Checks whether data exists for a given asset pair | |
- /// @dev Prices are only upated against QUOTE_ASSET | |
- /// @param sellAsset Asset for which check to be done if data exists | |
- /// @param buyAsset Asset for which check to be done if data exists | |
- /// @return Whether assets exist for given asset pair | |
- function existsPriceOnAssetPair(address sellAsset, address buyAsset) | |
- public | |
- view | |
- returns (bool isExistent) | |
- { | |
- return | |
- hasValidPrice(sellAsset) && | |
- hasValidPrice(buyAsset) && | |
- (buyAsset == QUOTE_ASSET || sellAsset == QUOTE_ASSET) && // One asset must be QUOTE_ASSET | |
- (buyAsset != QUOTE_ASSET || sellAsset != QUOTE_ASSET); // Pair must consists of diffrent assets | |
- } | |
- | |
- /// @return Sparse array of addresses of owned pricefeeds | |
- function getPriceFeedsByOwner(address _owner) | |
- public | |
- view | |
- returns(address[]) | |
- { | |
- address[] memory ofPriceFeeds = new address[](numStakers); | |
- if (numStakers == 0) return ofPriceFeeds; | |
- uint current = stakeNodes[0].next; | |
- for (uint i; i < numStakers; i++) { | |
- StakingPriceFeed stakingFeed = StakingPriceFeed(stakeNodes[current].data.staker); | |
- if (stakingFeed.owner() == _owner) { | |
- ofPriceFeeds[i] = address(stakingFeed); | |
- } | |
- current = stakeNodes[current].next; | |
- } | |
- return ofPriceFeeds; | |
- } | |
- | |
- function getHistoryLength() public returns (uint) { return priceHistory.length; } | |
- | |
- function getHistoryAt(uint id) public returns (address[], uint[], uint) { | |
- address[] memory assets = priceHistory[id].assets; | |
- uint[] memory prices = priceHistory[id].prices; | |
- uint timestamp = priceHistory[id].timestamp; | |
- return (assets, prices, timestamp); | |
- } | |
- | |
- /// @notice Get quantity of toAsset equal in value to given quantity of fromAsset | |
- function convertQuantity( | |
- uint fromAssetQuantity, | |
- address fromAsset, | |
- address toAsset | |
- ) | |
- public | |
- view | |
- returns (uint) | |
- { | |
- uint fromAssetPrice; | |
- (fromAssetPrice,) = getReferencePriceInfo(fromAsset, toAsset); | |
- uint fromAssetDecimals = ERC20WithFields(fromAsset).decimals(); | |
- return mul( | |
- fromAssetQuantity, | |
- fromAssetPrice | |
- ) / (10 ** uint(fromAssetDecimals)); | |
- } | |
- | |
- function getLastUpdate() public view returns (uint) { return lastUpdate; } | |
-} | |
diff --git a/src/contracts/prices/CanonicalRegistrar.sol b/src/contracts/prices/CanonicalRegistrar.sol | |
deleted file mode 100644 | |
index 08d3ffdc..00000000 | |
--- a/src/contracts/prices/CanonicalRegistrar.sol | |
+++ /dev/null | |
@@ -1,288 +0,0 @@ | |
-pragma solidity ^0.4.25; | |
- | |
-import "thing.sol"; | |
-import "ERC20.i.sol"; | |
- | |
- | |
-/// @title Asset Registar Contract | |
-/// @author Melonport AG <team@melonport.com> | |
-/// @notice Chain independent asset registrar for the Melon protocol | |
-contract CanonicalRegistrar is DSThing { | |
- | |
- // TYPES | |
- | |
- struct Asset { | |
- bool exists; // True if asset is registered here | |
- bytes32 name; // Human-readable name of the Asset | |
- bytes8 symbol; // Human-readable symbol of the Asset | |
- uint decimals; // Decimal, order of magnitude of precision, of the Asset | |
- string url; // URL for additional information of Asset | |
- string ipfsHash; // Same as url but for ipfs | |
- address breakIn; // Break in contract on destination chain | |
- address breakOut; // Break out contract on this chain; A way to leave | |
- uint[] standards; // compliance with standards like ERC20, ERC223, ERC777, etc. (the uint is the standard number) | |
- bytes4[] functionSignatures; // Whitelisted function signatures that can be called using `useExternalFunction` in Fund contract. Note: Adhere to a naming convention for `Fund<->Asset` as much as possible. I.e. name same concepts with the same functionSignature. | |
- uint price; // Price of asset quoted against `QUOTE_ASSET` * 10 ** decimals | |
- uint timestamp; // Timestamp of last price update of this asset | |
- } | |
- | |
- struct Exchange { | |
- bool exists; | |
- address adapter; // adapter contract for this exchange | |
- // One-time note: takesCustody is inverse case of isApproveOnly | |
- bool takesCustody; // True in case of exchange implementation which requires are approved when an order is made instead of transfer | |
- bytes4[] functionSignatures; // Whitelisted function signatures that can be called using `useExternalFunction` in Fund contract. Note: Adhere to a naming convention for `Fund<->ExchangeAdapter` as much as possible. I.e. name same concepts with the same functionSignature. | |
- } | |
- | |
- // FIELDS | |
- | |
- // Methods fields | |
- mapping (address => Asset) public assetInformation; | |
- address[] public registeredAssets; | |
- | |
- mapping (address => Exchange) public exchangeInformation; | |
- address[] public registeredExchanges; | |
- | |
- // METHODS | |
- | |
- // PUBLIC METHODS | |
- | |
- /// @notice Registers an Asset information entry | |
- /// @dev Pre: Only registrar owner should be able to register | |
- /// @dev Post: Address ofAsset is registered | |
- /// @param ofAsset Address of asset to be registered | |
- /// @param inputName Human-readable name of the Asset | |
- /// @param inputSymbol Human-readable symbol of the Asset | |
- /// @param inputUrl Url for extended information of the asset | |
- /// @param inputIpfsHash Same as url but for ipfs | |
- /// @param breakInBreakOut Address of break in and break out contracts on destination chain | |
- /// @param inputStandards Integers of EIP standards this asset adheres to | |
- /// @param inputFunctionSignatures Function signatures for whitelisted asset functions | |
- function registerAsset( | |
- address ofAsset, | |
- bytes32 inputName, | |
- bytes8 inputSymbol, | |
- string inputUrl, | |
- string inputIpfsHash, | |
- address[2] breakInBreakOut, | |
- uint[] inputStandards, | |
- bytes4[] inputFunctionSignatures | |
- ) | |
- public | |
- auth | |
- { | |
- require( | |
- !assetInformation[ofAsset].exists, | |
- "Asset is already registered" | |
- ); | |
- assetInformation[ofAsset].exists = true; | |
- registeredAssets.push(ofAsset); | |
- updateAsset( | |
- ofAsset, | |
- inputName, | |
- inputSymbol, | |
- inputUrl, | |
- inputIpfsHash, | |
- breakInBreakOut, | |
- inputStandards, | |
- inputFunctionSignatures | |
- ); | |
- assert(assetInformation[ofAsset].exists); | |
- } | |
- | |
- /// @notice Register an exchange information entry | |
- /// @dev Pre: Only registrar owner should be able to register | |
- /// @dev Post: Address ofExchange is registered | |
- /// @param ofExchange Address of the exchange | |
- /// @param ofExchangeAdapter Address of exchange adapter for this exchange | |
- /// @param inputTakesCustody Whether this exchange takes custody of tokens before trading | |
- /// @param inputFunctionSignatures Function signatures for whitelisted exchange functions | |
- function registerExchange( | |
- address ofExchange, | |
- address ofExchangeAdapter, | |
- bool inputTakesCustody, | |
- bytes4[] inputFunctionSignatures | |
- ) | |
- public | |
- auth | |
- { | |
- require( | |
- !exchangeInformation[ofExchange].exists, | |
- "Exchange is already registered" | |
- ); | |
- exchangeInformation[ofExchange].exists = true; | |
- registeredExchanges.push(ofExchange); | |
- updateExchange( | |
- ofExchange, | |
- ofExchangeAdapter, | |
- inputTakesCustody, | |
- inputFunctionSignatures | |
- ); | |
- assert(exchangeInformation[ofExchange].exists); | |
- } | |
- | |
- /// @notice Updates description information of a registered Asset | |
- /// @dev Pre: Owner can change an existing entry | |
- /// @dev Post: Changed Name, Symbol, URL and/or IPFSHash | |
- /// @param ofAsset Address of the asset to be updated | |
- /// @param inputName Human-readable name of the Asset | |
- /// @param inputSymbol Human-readable symbol of the Asset | |
- /// @param inputUrl Url for extended information of the asset | |
- /// @param inputIpfsHash Same as url but for ipfs | |
- function updateAsset( | |
- address ofAsset, | |
- bytes32 inputName, | |
- bytes8 inputSymbol, | |
- string inputUrl, | |
- string inputIpfsHash, | |
- address[2] ofBreakInBreakOut, | |
- uint[] inputStandards, | |
- bytes4[] inputFunctionSignatures | |
- ) | |
- public | |
- auth | |
- { | |
- require( | |
- assetInformation[ofAsset].exists, | |
- "Asset is not registered" | |
- ); | |
- Asset asset = assetInformation[ofAsset]; | |
- asset.name = inputName; | |
- asset.symbol = inputSymbol; | |
- asset.decimals = ERC20WithFields(ofAsset).decimals(); | |
- asset.url = inputUrl; | |
- asset.ipfsHash = inputIpfsHash; | |
- asset.breakIn = ofBreakInBreakOut[0]; | |
- asset.breakOut = ofBreakInBreakOut[1]; | |
- asset.standards = inputStandards; | |
- asset.functionSignatures = inputFunctionSignatures; | |
- } | |
- | |
- function updateExchange( | |
- address ofExchange, | |
- address ofExchangeAdapter, | |
- bool inputTakesCustody, | |
- bytes4[] inputFunctionSignatures | |
- ) | |
- public | |
- auth | |
- { | |
- require( | |
- exchangeInformation[ofExchange].exists, | |
- "Exchange is not registered" | |
- ); | |
- Exchange exchange = exchangeInformation[ofExchange]; | |
- exchange.adapter = ofExchangeAdapter; | |
- exchange.takesCustody = inputTakesCustody; | |
- exchange.functionSignatures = inputFunctionSignatures; | |
- } | |
- | |
- /// @notice Deletes an existing entry | |
- /// @dev Owner can delete an existing entry | |
- /// @param ofAsset address for which specific information is requested | |
- function removeAsset( | |
- address ofAsset, | |
- uint assetIndex | |
- ) | |
- external | |
- auth | |
- { | |
- require( | |
- assetInformation[ofAsset].exists, | |
- "Asset is not registered" | |
- ); | |
- require(registeredAssets[assetIndex] == ofAsset, "Array slot mismatch"); | |
- delete assetInformation[ofAsset]; // Sets exists boolean to false | |
- delete registeredAssets[assetIndex]; | |
- for (uint i = assetIndex; i < registeredAssets.length-1; i++) { | |
- registeredAssets[i] = registeredAssets[i+1]; | |
- } | |
- registeredAssets.length--; | |
- assert(!assetInformation[ofAsset].exists); | |
- } | |
- | |
- /// @notice Deletes an existing entry | |
- /// @dev Owner can delete an existing entry | |
- /// @param ofExchange address for which specific information is requested | |
- /// @param exchangeIndex index of the exchange in array | |
- function removeExchange( | |
- address ofExchange, | |
- uint exchangeIndex | |
- ) | |
- external | |
- auth | |
- { | |
- require( | |
- exchangeInformation[ofExchange].exists, | |
- "Exchange is not registered" | |
- ); | |
- require(registeredExchanges[exchangeIndex] == ofExchange, "Array slot mismatch"); | |
- delete exchangeInformation[ofExchange]; | |
- delete registeredExchanges[exchangeIndex]; | |
- for (uint i = exchangeIndex; i < registeredExchanges.length-1; i++) { | |
- registeredExchanges[i] = registeredExchanges[i+1]; | |
- } | |
- registeredExchanges.length--; | |
- assert(!exchangeInformation[ofExchange].exists); | |
- } | |
- | |
- // PUBLIC VIEW METHODS | |
- | |
- // get asset specific information | |
- function getName(address ofAsset) external view returns (bytes32) { return assetInformation[ofAsset].name; } | |
- function getSymbol(address ofAsset) external view returns (bytes8) { return assetInformation[ofAsset].symbol; } | |
- function getDecimals(address ofAsset) public view returns (uint) { return assetInformation[ofAsset].decimals; } | |
- function assetIsRegistered(address ofAsset) public view returns (bool) { return assetInformation[ofAsset].exists; } | |
- function getRegisteredAssets() external view returns (address[]) { return registeredAssets; } | |
- function assetMethodIsAllowed( | |
- address ofAsset, bytes4 querySignature | |
- ) | |
- external | |
- returns (bool) | |
- { | |
- bytes4[] memory signatures = assetInformation[ofAsset].functionSignatures; | |
- for (uint i = 0; i < signatures.length; i++) { | |
- if (signatures[i] == querySignature) { | |
- return true; | |
- } | |
- } | |
- return false; | |
- } | |
- | |
- // get exchange-specific information | |
- function exchangeIsRegistered(address ofExchange) external view returns (bool) { return exchangeInformation[ofExchange].exists; } | |
- function getRegisteredExchanges() external view returns (address[]) { return registeredExchanges; } | |
- function getExchangeInformation(address ofExchange) | |
- external | |
- view | |
- returns (address, bool) | |
- { | |
- Exchange exchange = exchangeInformation[ofExchange]; | |
- return ( | |
- exchange.adapter, | |
- exchange.takesCustody | |
- ); | |
- } | |
- function getExchangeFunctionSignatures(address ofExchange) | |
- external | |
- view | |
- returns (bytes4[]) | |
- { | |
- return exchangeInformation[ofExchange].functionSignatures; | |
- } | |
- function exchangeMethodIsAllowed( | |
- address ofExchange, bytes4 querySignature | |
- ) | |
- external | |
- view | |
- returns (bool) | |
- { | |
- bytes4[] memory signatures = exchangeInformation[ofExchange].functionSignatures; | |
- for (uint i = 0; i < signatures.length; i++) { | |
- if (signatures[i] == querySignature) { | |
- return true; | |
- } | |
- } | |
- return false; | |
- } | |
-} | |
diff --git a/src/contracts/prices/OperatorStaking.sol b/src/contracts/prices/OperatorStaking.sol | |
deleted file mode 100644 | |
index efa6b080..00000000 | |
--- a/src/contracts/prices/OperatorStaking.sol | |
+++ /dev/null | |
@@ -1,269 +0,0 @@ | |
-pragma solidity ^0.4.25; | |
- | |
-import "group.sol"; | |
-import "Owned.sol"; | |
-import "ERC20.i.sol"; | |
- | |
-/// @title Operator Staking Contract | |
-/// @author Melonport AG <team@melonport.com> | |
-/// @notice Enables pricefeed operators to self-select via staking | |
-contract OperatorStaking { | |
- | |
- // EVENTS | |
- | |
- event Staked(address indexed user, uint256 amount, uint256 total, bytes data); | |
- event Unstaked(address indexed user, uint256 amount, uint256 total, bytes data); | |
- event StakeBurned(address indexed user, uint256 amount, bytes data); | |
- | |
- // TYPES | |
- | |
- struct StakeData { | |
- uint amount; | |
- address staker; | |
- } | |
- | |
- // Circular linked list | |
- struct Node { | |
- StakeData data; | |
- uint prev; | |
- uint next; | |
- } | |
- | |
- // FIELDS | |
- | |
- // INTERNAL FIELDS | |
- Node[] internal stakeNodes; // Sorted circular linked list nodes containing stake data (Built on top https://programtheblockchain.com/posts/2018/03/30/storage-patterns-doubly-linked-list/) | |
- | |
- // PUBLIC FIELDS | |
- uint public minimumStake; | |
- uint public numOperators; | |
- uint public withdrawalDelay; | |
- mapping (address => bool) public isRanked; | |
- mapping (address => uint) public latestUnstakeTime; | |
- mapping (address => uint) public stakeToWithdraw; | |
- mapping (address => uint) public stakedAmounts; | |
- uint public numStakers; // Current number of stakers (Needed because of array holes) | |
- ERC20 public stakingToken; | |
- | |
- // (i.e. is pricefeed staking itself?) | |
- constructor( | |
- ERC20 _stakingToken, | |
- uint _minimumStake, | |
- uint _numOperators, | |
- uint _withdrawalDelay | |
- ) | |
- public | |
- { | |
- require(address(_stakingToken) != address(0), "Staking token is null"); | |
- stakingToken = _stakingToken; | |
- minimumStake = _minimumStake; | |
- numOperators = _numOperators; | |
- withdrawalDelay = _withdrawalDelay; | |
- StakeData memory temp = StakeData({ amount: 0, staker: address(0) }); | |
- stakeNodes.push(Node(temp, 0, 0)); | |
- } | |
- | |
- // METHODS : STAKING | |
- | |
- function stake( | |
- uint amount, | |
- bytes data | |
- ) | |
- public | |
- { | |
- require(amount >= minimumStake, "Stake must be above minimum"); | |
- stakedAmounts[msg.sender] += amount; | |
- updateStakerRanking(msg.sender); | |
- require( | |
- stakingToken.transferFrom(msg.sender, address(this), amount), | |
- "Transferring staking token failed" | |
- ); | |
- } | |
- | |
- function unstake( | |
- uint amount, | |
- bytes data | |
- ) | |
- public | |
- { | |
- uint preStake = stakedAmounts[msg.sender]; | |
- uint postStake = preStake - amount; | |
- require( | |
- postStake >= minimumStake || postStake == 0, | |
- "Unstaking would put remaining amount below minimum" | |
- ); | |
- require( | |
- stakedAmounts[msg.sender] >= amount, | |
- "Cannot unstake more than has been staked" | |
- ); | |
- latestUnstakeTime[msg.sender] = block.timestamp; | |
- stakedAmounts[msg.sender] -= amount; | |
- stakeToWithdraw[msg.sender] += amount; | |
- updateStakerRanking(msg.sender); | |
- emit Unstaked(msg.sender, amount, stakedAmounts[msg.sender], data); | |
- } | |
- | |
- function withdrawStake() | |
- public | |
- { | |
- require( | |
- stakeToWithdraw[msg.sender] > 0, | |
- "Must withdraw some amount" | |
- ); | |
- require( | |
- block.timestamp >= latestUnstakeTime[msg.sender] + withdrawalDelay, | |
- "Withdrawal delay has not passed" | |
- ); | |
- uint amount = stakeToWithdraw[msg.sender]; | |
- stakeToWithdraw[msg.sender] = 0; | |
- require( | |
- stakingToken.transfer(msg.sender, amount), | |
- "Staking token transfer failed" | |
- ); | |
- } | |
- | |
- // VIEW FUNCTIONS | |
- | |
- function isValidNode(uint id) public view returns (bool) { | |
- // 0 is a sentinel and therefore invalid. | |
- // A valid node is the head or has a previous node. | |
- return id != 0 && (id == stakeNodes[0].next || stakeNodes[id].prev != 0); | |
- } | |
- | |
- function searchNode(address staker) public view returns (uint) { | |
- uint current = stakeNodes[0].next; | |
- while (isValidNode(current)) { | |
- if (staker == stakeNodes[current].data.staker) { | |
- return current; | |
- } | |
- current = stakeNodes[current].next; | |
- } | |
- return 0; | |
- } | |
- | |
- function isOperator(address user) public view returns (bool) { | |
- address[] memory operators = getOperators(); | |
- for (uint i; i < operators.length; i++) { | |
- if (operators[i] == user) { | |
- return true; | |
- } | |
- } | |
- return false; | |
- } | |
- | |
- function getOperators() | |
- public | |
- view | |
- returns (address[]) | |
- { | |
- uint arrLength = (numOperators > numStakers) ? | |
- numStakers : | |
- numOperators; | |
- address[] memory operators = new address[](arrLength); | |
- uint current = stakeNodes[0].next; | |
- for (uint i; i < arrLength; i++) { | |
- operators[i] = stakeNodes[current].data.staker; | |
- current = stakeNodes[current].next; | |
- } | |
- return operators; | |
- } | |
- | |
- function getStakersAndAmounts() | |
- public | |
- view | |
- returns (address[], uint[]) | |
- { | |
- address[] memory stakers = new address[](numStakers); | |
- uint[] memory amounts = new uint[](numStakers); | |
- uint current = stakeNodes[0].next; | |
- for (uint i; i < numStakers; i++) { | |
- stakers[i] = stakeNodes[current].data.staker; | |
- amounts[i] = stakeNodes[current].data.amount; | |
- current = stakeNodes[current].next; | |
- } | |
- return (stakers, amounts); | |
- } | |
- | |
- function totalStakedFor(address user) | |
- public | |
- view | |
- returns (uint) | |
- { | |
- return stakedAmounts[user]; | |
- } | |
- | |
- // INTERNAL METHODS | |
- | |
- // DOUBLY-LINKED LIST | |
- | |
- function insertNodeSorted(uint amount, address staker) internal returns (uint) { | |
- uint current = stakeNodes[0].next; | |
- if (current == 0) return insertNodeAfter(0, amount, staker); | |
- while (isValidNode(current)) { | |
- if (amount > stakeNodes[current].data.amount) { | |
- break; | |
- } | |
- current = stakeNodes[current].next; | |
- } | |
- return insertNodeBefore(current, amount, staker); | |
- } | |
- | |
- function insertNodeAfter(uint id, uint amount, address staker) internal returns (uint newID) { | |
- | |
- // 0 is allowed here to insert at the beginning. | |
- require(id == 0 || isValidNode(id), "Invalid node ID"); | |
- | |
- Node storage node = stakeNodes[id]; | |
- | |
- stakeNodes.push(Node({ | |
- data: StakeData(amount, staker), | |
- prev: id, | |
- next: node.next | |
- })); | |
- | |
- newID = stakeNodes.length - 1; | |
- | |
- stakeNodes[node.next].prev = newID; | |
- node.next = newID; | |
- numStakers++; | |
- } | |
- | |
- function insertNodeBefore(uint id, uint amount, address staker) internal returns (uint) { | |
- return insertNodeAfter(stakeNodes[id].prev, amount, staker); | |
- } | |
- | |
- function removeNode(uint id) internal { | |
- require(isValidNode(id), "Invalid node ID"); | |
- | |
- Node storage node = stakeNodes[id]; | |
- | |
- stakeNodes[node.next].prev = node.prev; | |
- stakeNodes[node.prev].next = node.next; | |
- | |
- delete stakeNodes[id]; | |
- numStakers--; | |
- } | |
- | |
- // UPDATING OPERATORS | |
- | |
- function updateStakerRanking(address _staker) internal { | |
- uint newStakedAmount = stakedAmounts[_staker]; | |
- if (newStakedAmount == 0) { | |
- isRanked[_staker] = false; | |
- removeStakerFromArray(_staker); | |
- } else if (isRanked[_staker]) { | |
- removeStakerFromArray(_staker); | |
- insertNodeSorted(newStakedAmount, _staker); | |
- } else { | |
- isRanked[_staker] = true; | |
- insertNodeSorted(newStakedAmount, _staker); | |
- } | |
- } | |
- | |
- function removeStakerFromArray(address _staker) internal { | |
- uint id = searchNode(_staker); | |
- require(id > 0, "Node ID cannot be zero"); | |
- removeNode(id); | |
- } | |
- | |
-} | |
diff --git a/src/contracts/prices/PriceSource.i.sol b/src/contracts/prices/PriceSource.i.sol | |
deleted file mode 100644 | |
index c5550b64..00000000 | |
--- a/src/contracts/prices/PriceSource.i.sol | |
+++ /dev/null | |
@@ -1,30 +0,0 @@ | |
-pragma solidity ^0.4.25; | |
- | |
-/// @notice Must return a value for an asset | |
-interface PriceSourceInterface { | |
- event PriceUpdate(address[] token, uint[] price); | |
- | |
- function getQuoteAsset() external view returns (address); | |
- function getLastUpdate() external view returns (uint); | |
- | |
- /// @notice Returns false if asset not applicable, or price not recent | |
- function hasValidPrice(address) public view returns (bool); | |
- function hasValidPrices(address[]) public view returns (bool); | |
- | |
- /// @notice Return the last known price, and when it was issued | |
- function getPrice(address _asset) public view returns (uint price, uint timestamp); | |
- function getPrices(address[] _assets) public view returns (uint[] prices, uint[] timestamps); | |
- | |
- /// @notice Get price info, and revert if not valid | |
- function getPriceInfo(address _asset) view returns (uint price, uint decimals); | |
- function getInvertedPriceInfo(address ofAsset) view returns (uint price, uint decimals); | |
- | |
- function getReferencePriceInfo(address _base, address _quote) public view returns (uint referencePrice, uint decimal); | |
- function getOrderPriceInfo(address sellAsset, address buyAsset, uint sellQuantity, uint buyQuantity) public view returns (uint orderPrice); | |
- function existsPriceOnAssetPair(address sellAsset, address buyAsset) public view returns (bool isExistent); | |
- function convertQuantity( | |
- uint fromAssetQuantity, | |
- address fromAsset, | |
- address toAsset | |
- ) public view returns (uint); | |
-} | |
diff --git a/src/contracts/prices/SimplePriceFeed.sol b/src/contracts/prices/SimplePriceFeed.sol | |
deleted file mode 100644 | |
index fefdd006..00000000 | |
--- a/src/contracts/prices/SimplePriceFeed.sol | |
+++ /dev/null | |
@@ -1,140 +0,0 @@ | |
-pragma solidity ^0.4.25; | |
- | |
-import "PriceSource.i.sol"; | |
-import "UpdatableFeed.i.sol"; | |
-import "CanonicalPriceFeed.sol"; | |
-import "thing.sol"; | |
-import "Registry.sol"; | |
- | |
-/// @title Price Feed Template | |
-/// @author Melonport AG <team@melonport.com> | |
-/// @notice Updates and exposes price information for consuming contracts | |
-contract SimplePriceFeed is UpdatableFeedInterface, DSThing { | |
- | |
- // TYPES | |
- struct Data { | |
- uint price; | |
- uint timestamp; | |
- } | |
- | |
- // FIELDS | |
- mapping(address => Data) public assetsToPrices; | |
- | |
- // Constructor fields | |
- address public QUOTE_ASSET; // Asset of a portfolio against which all other assets are priced | |
- | |
- // Contract-level variables | |
- uint public updateId; // Update counter for this pricefeed; used as a check during investment | |
- Registry public registry; | |
- CanonicalPriceFeed public superFeed; | |
- | |
- // METHODS | |
- | |
- // CONSTRUCTOR | |
- | |
- /// @param ofQuoteAsset Address of quote asset | |
- /// @param ofRegistrar Address of canonical registrar | |
- /// @param ofSuperFeed Address of superfeed | |
- constructor( | |
- address ofRegistrar, | |
- address ofQuoteAsset, | |
- address ofSuperFeed | |
- ) public { | |
- registry = Registry(ofRegistrar); | |
- QUOTE_ASSET = ofQuoteAsset; | |
- superFeed = CanonicalPriceFeed(ofSuperFeed); | |
- } | |
- | |
- // EXTERNAL METHODS | |
- | |
- /// @dev Only Owner; Same sized input arrays | |
- /// @dev Updates price of asset relative to QUOTE_ASSET | |
- /** Ex: | |
- * Let QUOTE_ASSET == MLN (base units), let asset == EUR-T, | |
- * let Value of 1 EUR-T := 1 EUR == 0.080456789 MLN, hence price 0.080456789 MLN / EUR-T | |
- * and let EUR-T decimals == 8. | |
- * Input would be: information[EUR-T].price = 8045678 [MLN/ (EUR-T * 10**8)] | |
- */ | |
- /// @param ofAssets list of asset addresses | |
- /// @param newPrices list of prices for each of the assets | |
- function update(address[] ofAssets, uint[] newPrices) | |
- external | |
- auth | |
- { | |
- _updatePrices(ofAssets, newPrices); | |
- } | |
- | |
- // PUBLIC VIEW METHODS | |
- | |
- // Get pricefeed specific information | |
- function getQuoteAsset() public view returns (address) { return QUOTE_ASSET; } | |
- function getLastUpdateId() public view returns (uint) { return updateId; } | |
- | |
- /** | |
- @notice Gets price of an asset multiplied by ten to the power of assetDecimals | |
- @dev Asset has been registered | |
- @param ofAsset Asset for which price should be returned | |
- @return { | |
- "price": "Price formatting: mul(exchangePrice, 10 ** decimal), to avoid floating numbers", | |
- "timestamp": "When the asset's price was updated" | |
- } | |
- */ | |
- function getPrice(address ofAsset) | |
- public | |
- view | |
- returns (uint price, uint timestamp) | |
- { | |
- Data data = assetsToPrices[ofAsset]; | |
- return (data.price, data.timestamp); | |
- } | |
- | |
- /** | |
- @notice Price of a registered asset in format (bool areRecent, uint[] prices, uint[] decimals) | |
- @dev Convention for price formatting: mul(price, 10 ** decimal), to avoid floating numbers | |
- @param ofAssets Assets for which prices should be returned | |
- @return { | |
- "prices": "Array of prices", | |
- "timestamps": "Array of timestamps", | |
- } | |
- */ | |
- function getPrices(address[] ofAssets) | |
- public | |
- view | |
- returns (uint[], uint[]) | |
- { | |
- uint[] memory prices = new uint[](ofAssets.length); | |
- uint[] memory timestamps = new uint[](ofAssets.length); | |
- for (uint i; i < ofAssets.length; i++) { | |
- uint price; | |
- uint timestamp; | |
- (price, timestamp) = getPrice(ofAssets[i]); | |
- prices[i] = price; | |
- timestamps[i] = timestamp; | |
- } | |
- return (prices, timestamps); | |
- } | |
- | |
- // INTERNAL METHODS | |
- | |
- /// @dev Internal so that feeds inheriting this one are not obligated to have an exposed update(...) method, but can still perform updates | |
- function _updatePrices(address[] ofAssets, uint[] newPrices) internal { | |
- require( | |
- ofAssets.length == newPrices.length, | |
- "Arrays must be same length" | |
- ); | |
- updateId++; | |
- for (uint i = 0; i < ofAssets.length; ++i) { | |
- require( | |
- registry.assetIsRegistered(ofAssets[i]), | |
- "Asset is not registered" | |
- ); | |
- require( | |
- assetsToPrices[ofAssets[i]].timestamp != now, | |
- "Cannot update twice in one block" | |
- ); | |
- assetsToPrices[ofAssets[i]].timestamp = now; | |
- assetsToPrices[ofAssets[i]].price = newPrices[i]; | |
- } | |
- emit PriceUpdated(keccak256(ofAssets, newPrices)); | |
- } | |
-} | |
diff --git a/src/contracts/prices/StakingPriceFeed.sol b/src/contracts/prices/StakingPriceFeed.sol | |
deleted file mode 100644 | |
index 6f7da3ee..00000000 | |
--- a/src/contracts/prices/StakingPriceFeed.sol | |
+++ /dev/null | |
@@ -1,71 +0,0 @@ | |
-pragma solidity ^0.4.25; | |
- | |
-import "SimplePriceFeed.sol"; | |
-import "OperatorStaking.sol"; | |
-import "ERC20.i.sol"; | |
- | |
-/// @title Staking Price Feed | |
-/// @author Melonport AG <team@melonport.com> | |
-/// @notice Simple pricefeed that can increase and decrease stake | |
-contract StakingPriceFeed is SimplePriceFeed { | |
- | |
- OperatorStaking public stakingContract; | |
- ERC20 public stakingToken; | |
- | |
- // CONSTRUCTOR | |
- | |
- /// @param ofQuoteAsset Address of quote asset | |
- /// @param ofRegistrar Address of canonical registrar | |
- /// @param ofSuperFeed Address of superfeed | |
- constructor( | |
- address ofRegistrar, | |
- address ofQuoteAsset, | |
- address ofSuperFeed | |
- ) | |
- public | |
- SimplePriceFeed(ofRegistrar, ofQuoteAsset, ofSuperFeed) | |
- { | |
- stakingContract = OperatorStaking(ofSuperFeed); // canonical feed *is* staking contract | |
- stakingToken = ERC20(stakingContract.stakingToken()); | |
- } | |
- | |
- // EXTERNAL METHODS | |
- | |
- /// @param amount Number of tokens to stake for this feed | |
- /// @param data Data may be needed for some future applications (can be empty for now) | |
- function depositStake(uint amount, bytes data) | |
- external | |
- auth | |
- { | |
- require( | |
- stakingToken.transferFrom(msg.sender, address(this), amount), | |
- "Transferring staking token to fee failed" | |
- ); | |
- require( | |
- stakingToken.approve(stakingContract, amount), | |
- "Approving staking token for staking contract failed" | |
- ); | |
- stakingContract.stake(amount, data); | |
- } | |
- | |
- /// @param amount Number of tokens to unstake for this feed | |
- /// @param data Data may be needed for some future applications (can be empty for now) | |
- function unstake(uint amount, bytes data) | |
- external | |
- auth | |
- { | |
- stakingContract.unstake(amount, data); | |
- } | |
- | |
- function withdrawStake() | |
- external | |
- auth | |
- { | |
- uint amountToWithdraw = stakingContract.stakeToWithdraw(address(this)); | |
- stakingContract.withdrawStake(); | |
- require( | |
- stakingToken.transfer(msg.sender, amountToWithdraw), | |
- "Staking token transfer to sender failed" | |
- ); | |
- } | |
-} | |
diff --git a/src/contracts/prices/UpdatableFeed.i.sol b/src/contracts/prices/UpdatableFeed.i.sol | |
deleted file mode 100644 | |
index 2c5864d1..00000000 | |
--- a/src/contracts/prices/UpdatableFeed.i.sol | |
+++ /dev/null | |
@@ -1,8 +0,0 @@ | |
-pragma solidity ^0.4.25; | |
- | |
-/// @notice Updates values stored internally | |
-interface UpdatableFeedInterface { | |
- event PriceUpdated(bytes32 hash); | |
- function update(address[] _assets, uint[] _prices) external; | |
- function getLastUpdateId() public view returns (uint); | |
-} | |
diff --git a/src/contracts/version/Version.i.sol b/src/contracts/version/Version.i.sol | |
deleted file mode 100644 | |
index ec5cc9ec..00000000 | |
--- a/src/contracts/version/Version.i.sol | |
+++ /dev/null | |
@@ -1,6 +0,0 @@ | |
-pragma solidity ^0.4.25; | |
- | |
-interface VersionInterface { | |
- function shutDownFund(address) external; | |
-} | |
- | |
diff --git a/src/contracts/dependencies/auth.sol b/src/dependencies/DSAuth.sol | |
similarity index 98% | |
rename from src/contracts/dependencies/auth.sol | |
rename to src/dependencies/DSAuth.sol | |
index 8a5ebcb5..ef8c7e87 100644 | |
--- a/src/contracts/dependencies/auth.sol | |
+++ b/src/dependencies/DSAuth.sol | |
@@ -1,6 +1,6 @@ | |
/// @notice Retrieved from DappHub (https://git.io/fpwrq) | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
contract DSAuthority { | |
function canCall( | |
diff --git a/src/contracts/dependencies/guard.sol b/src/dependencies/DSGuard.sol | |
similarity index 97% | |
rename from src/contracts/dependencies/guard.sol | |
rename to src/dependencies/DSGuard.sol | |
index b2b20ed6..b9302c21 100644 | |
--- a/src/contracts/dependencies/guard.sol | |
+++ b/src/dependencies/DSGuard.sol | |
@@ -1,8 +1,8 @@ | |
/// @notice Retrieved from DappHub (https://git.io/fpwMi) | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "auth.sol"; | |
+import "./DSAuth.sol"; | |
contract DSGuardEvents { | |
event LogPermit( | |
diff --git a/src/contracts/dependencies/math.sol b/src/dependencies/DSMath.sol | |
similarity index 98% | |
rename from src/contracts/dependencies/math.sol | |
rename to src/dependencies/DSMath.sol | |
index 0d12e179..5afbfb32 100644 | |
--- a/src/contracts/dependencies/math.sol | |
+++ b/src/dependencies/DSMath.sol | |
@@ -1,4 +1,4 @@ | |
-/// math.sol -- mixin for inline numerical wizardry | |
+/// DSMath.sol -- mixin for inline numerical wizardry | |
// This program is free software: you can redistribute it and/or modify | |
// it under the terms of the GNU General Public License as published by | |
diff --git a/src/contracts/dependencies/SafeMath.sol b/src/dependencies/SafeMath.sol | |
similarity index 98% | |
rename from src/contracts/dependencies/SafeMath.sol | |
rename to src/dependencies/SafeMath.sol | |
index f4b43631..5c32b4e2 100644 | |
--- a/src/contracts/dependencies/SafeMath.sol | |
+++ b/src/dependencies/SafeMath.sol | |
@@ -1,4 +1,4 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
/** | |
diff --git a/src/contracts/dependencies/TokenUser.sol b/src/dependencies/TokenUser.sol | |
similarity index 60% | |
rename from src/contracts/dependencies/TokenUser.sol | |
rename to src/dependencies/TokenUser.sol | |
index bf8d4076..36756011 100644 | |
--- a/src/contracts/dependencies/TokenUser.sol | |
+++ b/src/dependencies/TokenUser.sol | |
@@ -1,7 +1,7 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "ERC20.i.sol"; | |
-import "math.sol"; | |
+import "./token/IERC20.sol"; | |
+import "./DSMath.sol"; | |
/// @notice Wrapper to ensure tokens are received | |
contract TokenUser is DSMath { | |
@@ -10,9 +10,9 @@ contract TokenUser is DSMath { | |
address _to, | |
uint _value | |
) internal { | |
- uint receiverPreBalance = ERC20(_token).balanceOf(_to); | |
- ERC20(_token).transfer(_to, _value); | |
- uint receiverPostBalance = ERC20(_token).balanceOf(_to); | |
+ uint receiverPreBalance = IERC20(_token).balanceOf(_to); | |
+ IERC20(_token).transfer(_to, _value); | |
+ uint receiverPostBalance = IERC20(_token).balanceOf(_to); | |
require( | |
add(receiverPreBalance, _value) == receiverPostBalance, | |
"Receiver did not receive tokens in transfer" | |
@@ -25,9 +25,9 @@ contract TokenUser is DSMath { | |
address _to, | |
uint _value | |
) internal { | |
- uint receiverPreBalance = ERC20(_token).balanceOf(_to); | |
- ERC20(_token).transferFrom(_from, _to, _value); | |
- uint receiverPostBalance = ERC20(_token).balanceOf(_to); | |
+ uint receiverPreBalance = IERC20(_token).balanceOf(_to); | |
+ IERC20(_token).transferFrom(_from, _to, _value); | |
+ uint receiverPostBalance = IERC20(_token).balanceOf(_to); | |
require( | |
add(receiverPreBalance, _value) == receiverPostBalance, | |
"Receiver did not receive tokens in transferFrom" | |
diff --git a/src/contracts/dependencies/Weth.sol b/src/dependencies/WETH.sol | |
similarity index 85% | |
rename from src/contracts/dependencies/Weth.sol | |
rename to src/dependencies/WETH.sol | |
index 6ef1e129..2635e900 100644 | |
--- a/src/contracts/dependencies/Weth.sol | |
+++ b/src/dependencies/WETH.sol | |
@@ -1,4 +1,4 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
contract WETH { | |
string public name = "Wrapped Ether"; | |
@@ -13,27 +13,27 @@ contract WETH { | |
mapping (address => uint) public balanceOf; | |
mapping (address => mapping (address => uint)) public allowance; | |
- function() public payable { | |
+ function() external payable { | |
deposit(); | |
} | |
function deposit() public payable { | |
balanceOf[msg.sender] += msg.value; | |
- Deposit(msg.sender, msg.value); | |
+ emit Deposit(msg.sender, msg.value); | |
} | |
function withdraw(uint wad) public { | |
require(balanceOf[msg.sender] >= wad); | |
balanceOf[msg.sender] -= wad; | |
msg.sender.transfer(wad); | |
- Withdrawal(msg.sender, wad); | |
+ emit Withdrawal(msg.sender, wad); | |
} | |
function totalSupply() public view returns (uint) { | |
- return this.balance; | |
+ return address(this).balance; | |
} | |
function approve(address guy, uint wad) public returns (bool) { | |
allowance[msg.sender][guy] = wad; | |
- Approval(msg.sender, guy, wad); | |
+ emit Approval(msg.sender, guy, wad); | |
return true; | |
} | |
@@ -55,7 +55,7 @@ contract WETH { | |
balanceOf[src] -= wad; | |
balanceOf[dst] += wad; | |
- Transfer(src, dst, wad); | |
+ emit Transfer(src, dst, wad); | |
return true; | |
} | |
diff --git a/src/contracts/dependencies/token/BurnableToken.sol b/src/dependencies/token/BurnableToken.sol | |
similarity index 73% | |
rename from src/contracts/dependencies/token/BurnableToken.sol | |
rename to src/dependencies/token/BurnableToken.sol | |
index fb959b97..6b6f92bd 100644 | |
--- a/src/contracts/dependencies/token/BurnableToken.sol | |
+++ b/src/dependencies/token/BurnableToken.sol | |
@@ -1,10 +1,10 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "PreminedToken.sol"; | |
+import "./PreminedToken.sol"; | |
/// @dev Just a wrapper for premined tokens which can actually be burnt | |
contract BurnableToken is PreminedToken { | |
- constructor(string _symbol, uint8 _decimals, string _name) | |
+ constructor(string memory _symbol, uint8 _decimals, string memory _name) | |
public | |
PreminedToken(_symbol, _decimals, _name) | |
{} | |
@@ -12,7 +12,7 @@ contract BurnableToken is PreminedToken { | |
function burn(uint _amount) public { | |
_burn(msg.sender, _amount); | |
} | |
- | |
+ | |
function burnFrom(address from, uint256 value) public { | |
_burnFrom(from, value); | |
} | |
diff --git a/src/contracts/dependencies/token/ERC20.i.sol b/src/dependencies/token/IERC20.sol | |
similarity index 61% | |
rename from src/contracts/dependencies/token/ERC20.i.sol | |
rename to src/dependencies/token/IERC20.sol | |
index bf7e955b..f085c9bd 100644 | |
--- a/src/contracts/dependencies/token/ERC20.i.sol | |
+++ b/src/dependencies/token/IERC20.sol | |
@@ -1,23 +1,23 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
/** | |
* @title ERC20 interface | |
* @dev see https://github.com/ethereum/EIPs/issues/20 | |
* Altered from https://github.com/OpenZeppelin/openzeppelin-solidity/blob/a466e76d26c394b1faa6e2797aefe34668566392/contracts/token/ERC20/ERC20.sol | |
*/ | |
-interface ERC20 { | |
- function totalSupply() public view returns (uint256); | |
+interface IERC20 { | |
+ function totalSupply() external view returns (uint256); | |
- function balanceOf(address _who) public view returns (uint256); | |
+ function balanceOf(address _who) external view returns (uint256); | |
function allowance(address _owner, address _spender) | |
- public view returns (uint256); | |
+ external view returns (uint256); | |
- function transfer(address _to, uint256 _value) public returns (bool); | |
+ function transfer(address _to, uint256 _value) external returns (bool); | |
- function approve(address _spender, uint256 _value) public returns (bool); | |
+ function approve(address _spender, uint256 _value) external returns (bool); | |
- function transferFrom(address _from, address _to, uint256 _value) public returns (bool); | |
+ function transferFrom(address _from, address _to, uint256 _value) external returns (bool); | |
event Transfer( | |
address indexed from, | |
@@ -33,7 +33,7 @@ interface ERC20 { | |
} | |
/// @dev Just adds extra functions that we use elsewhere | |
-contract ERC20WithFields is ERC20 { | |
+contract ERC20WithFields is IERC20 { | |
string public symbol; | |
string public name; | |
uint8 public decimals; | |
diff --git a/src/contracts/dependencies/token/PreminedToken.sol b/src/dependencies/token/PreminedToken.sol | |
similarity index 72% | |
rename from src/contracts/dependencies/token/PreminedToken.sol | |
rename to src/dependencies/token/PreminedToken.sol | |
index a7d68477..0925a705 100644 | |
--- a/src/contracts/dependencies/token/PreminedToken.sol | |
+++ b/src/dependencies/token/PreminedToken.sol | |
@@ -1,13 +1,13 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "StandardToken.sol"; | |
+import "./StandardToken.sol"; | |
contract PreminedToken is StandardToken { | |
string public symbol; | |
string public name; | |
uint8 public decimals; | |
- constructor(string _symbol, uint8 _decimals, string _name) public { | |
+ constructor(string memory _symbol, uint8 _decimals, string memory _name) public { | |
symbol = _symbol; | |
decimals = _decimals; | |
name = _name; | |
diff --git a/src/contracts/dependencies/token/StandardToken.sol b/src/dependencies/token/StandardToken.sol | |
similarity index 97% | |
rename from src/contracts/dependencies/token/StandardToken.sol | |
rename to src/dependencies/token/StandardToken.sol | |
index faa56978..2cce35d6 100644 | |
--- a/src/contracts/dependencies/token/StandardToken.sol | |
+++ b/src/dependencies/token/StandardToken.sol | |
@@ -1,7 +1,7 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "ERC20.i.sol"; | |
-import "SafeMath.sol"; | |
+import "./IERC20.sol"; | |
+import "../SafeMath.sol"; | |
/** | |
* @title Standard ERC20 token | |
@@ -11,7 +11,7 @@ import "SafeMath.sol"; | |
* Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol | |
* Rearranged from https://github.com/OpenZeppelin/openzeppelin-solidity/blob/a466e76d26c394b1faa6e2797aefe34668566392/contracts/token/ERC20/StandardToken.sol | |
*/ | |
-contract StandardToken is ERC20 { | |
+contract StandardToken is IERC20 { | |
using SafeMath for uint256; | |
mapping (address => uint256) balances; | |
@@ -164,7 +164,7 @@ contract StandardToken is ERC20 { | |
* @param _amount The amount that will be created. | |
*/ | |
function _mint(address _account, uint256 _amount) internal { | |
- require(_account != 0); | |
+ require(_account != address(0)); | |
totalSupply_ = totalSupply_.add(_amount); | |
balances[_account] = balances[_account].add(_amount); | |
emit Transfer(address(0), _account, _amount); | |
@@ -177,7 +177,7 @@ contract StandardToken is ERC20 { | |
* @param _amount The amount that will be burnt. | |
*/ | |
function _burn(address _account, uint256 _amount) internal { | |
- require(_account != 0); | |
+ require(_account != address(0)); | |
require(_amount <= balances[_account]); | |
totalSupply_ = totalSupply_.sub(_amount); | |
diff --git a/src/contracts/engine/AmguConsumer.sol b/src/engine/AmguConsumer.sol | |
similarity index 58% | |
rename from src/contracts/engine/AmguConsumer.sol | |
rename to src/engine/AmguConsumer.sol | |
index 67c89509..55434ebd 100644 | |
--- a/src/contracts/engine/AmguConsumer.sol | |
+++ b/src/engine/AmguConsumer.sol | |
@@ -1,26 +1,33 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "math.sol"; | |
-import "ERC20.i.sol"; | |
-import "PriceSource.i.sol"; | |
-import "Version.i.sol"; | |
-import "Engine.sol"; | |
-import "Registry.sol"; | |
+import "../dependencies/DSMath.sol"; | |
+import "../dependencies/token/IERC20.sol"; | |
+import "../prices/IPriceSource.sol"; | |
+import "../version/IVersion.sol"; | |
+import "./IEngine.sol"; | |
+import "../version/Registry.sol"; | |
+/// @notice Abstract contracts | |
/// @notice inherit this to pay AMGU on a function call | |
contract AmguConsumer is DSMath { | |
+ /// @dev each of these must be implemented by the inheriting contract | |
+ function engine() public view returns (address); | |
+ function mlnToken() public view returns (address); | |
+ function priceSource() public view returns (address); | |
+ function registry() public view returns (address); | |
+ | |
/// bool deductIncentive is used when sending extra eth beyond amgu | |
modifier amguPayable(bool deductIncentive) { | |
uint initialGas = gasleft(); | |
_; | |
- uint mlnPerAmgu = Engine(engine()).getAmguPrice(); | |
+ uint mlnPerAmgu = IEngine(engine()).getAmguPrice(); | |
uint mlnQuantity = mul( | |
mlnPerAmgu, | |
sub(initialGas, gasleft()) | |
); | |
address nativeAsset = Registry(registry()).nativeAsset(); | |
- uint ethToPay = PriceSourceInterface(priceSource()).convertQuantity( | |
+ uint ethToPay = IPriceSource(priceSource()).convertQuantity( | |
mlnQuantity, | |
mlnToken(), | |
nativeAsset | |
@@ -35,7 +42,7 @@ contract AmguConsumer is DSMath { | |
msg.value >= add(ethToPay, incentiveAmount), | |
"Insufficent AMGU and/or incentive" | |
); | |
- Engine(engine()).payAmguInEther.value(ethToPay)(); | |
+ IEngine(engine()).payAmguInEther.value(ethToPay)(); | |
require( | |
msg.sender.send( | |
@@ -47,10 +54,4 @@ contract AmguConsumer is DSMath { | |
"Refund failed" | |
); | |
} | |
- | |
- function engine() view returns (address); | |
- function mlnToken() view returns (address); | |
- function priceSource() view returns (address); | |
- function registry() view returns (address); | |
} | |
- | |
diff --git a/src/contracts/engine/Engine.sol b/src/engine/Engine.sol | |
similarity index 92% | |
rename from src/contracts/engine/Engine.sol | |
rename to src/engine/Engine.sol | |
index 948457bd..ab1e90ee 100644 | |
--- a/src/contracts/engine/Engine.sol | |
+++ b/src/engine/Engine.sol | |
@@ -1,9 +1,9 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "math.sol"; | |
-import "BurnableToken.sol"; | |
-import "PriceSource.i.sol"; | |
-import "Registry.sol"; | |
+import "../dependencies/DSMath.sol"; | |
+import "../dependencies/token/BurnableToken.sol"; | |
+import "../prices/IPriceSource.sol"; | |
+import "../version/Registry.sol"; | |
/// @notice Liquidity contract and token sink | |
contract Engine is DSMath { | |
@@ -26,7 +26,7 @@ contract Engine is DSMath { | |
uint public totalAmguConsumed; | |
uint public totalMlnBurned; | |
- constructor(uint _delay, address _registry) { | |
+ constructor(uint _delay, address _registry) public { | |
lastThaw = block.timestamp; | |
thawingDelay = _delay; | |
_setRegistry(_registry); | |
@@ -51,7 +51,7 @@ contract Engine is DSMath { | |
function _setRegistry(address _registry) internal { | |
registry = Registry(_registry); | |
- emit RegistryChange(registry); | |
+ emit RegistryChange(address(registry)); | |
} | |
/// @dev only callable by MTC | |
@@ -163,9 +163,9 @@ contract Engine is DSMath { | |
function priceSource() | |
public | |
view | |
- returns (PriceSourceInterface) | |
+ returns (IPriceSource) | |
{ | |
- return PriceSourceInterface(registry.priceSource()); | |
+ return IPriceSource(registry.priceSource()); | |
} | |
} | |
diff --git a/src/engine/IEngine.sol b/src/engine/IEngine.sol | |
new file mode 100644 | |
index 00000000..c68718fd | |
--- /dev/null | |
+++ b/src/engine/IEngine.sol | |
@@ -0,0 +1,7 @@ | |
+pragma solidity 0.5.15; | |
+ | |
+ | |
+interface IEngine { | |
+ function payAmguInEther() external payable; | |
+ function getAmguPrice() external view returns (uint256); | |
+} | |
diff --git a/src/contracts/exchanges/EngineAdapter.sol b/src/exchanges/EngineAdapter.sol | |
similarity index 64% | |
rename from src/contracts/exchanges/EngineAdapter.sol | |
rename to src/exchanges/EngineAdapter.sol | |
index 6ea2cfb7..fc5bf960 100644 | |
--- a/src/contracts/exchanges/EngineAdapter.sol | |
+++ b/src/exchanges/EngineAdapter.sol | |
@@ -1,14 +1,15 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
+pragma experimental ABIEncoderV2; | |
-import "Engine.sol"; | |
-import "Hub.sol"; | |
-import "Trading.sol"; | |
-import "Vault.sol"; | |
-import "math.sol"; | |
-import "Weth.sol"; | |
-import "ERC20.i.sol"; | |
-import "ExchangeAdapter.sol"; | |
-import "TokenUser.sol"; | |
+import "../engine/Engine.sol"; | |
+import "../fund/hub/Hub.sol"; | |
+import "../fund/trading/Trading.sol"; | |
+import "../fund/vault/Vault.sol"; | |
+import "../dependencies/DSMath.sol"; | |
+import "../dependencies/WETH.sol"; | |
+import "../dependencies/token/IERC20.sol"; | |
+import "./ExchangeAdapter.sol"; | |
+import "../dependencies/TokenUser.sol"; | |
/// @notice Trading adapter to Melon Engine | |
contract EngineAdapter is DSMath, TokenUser, ExchangeAdapter { | |
@@ -22,12 +23,11 @@ contract EngineAdapter is DSMath, TokenUser, ExchangeAdapter { | |
/// @param orderAddresses [3] MLN token | |
function takeOrder ( | |
address targetExchange, | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
+ address[8] memory orderAddresses, | |
+ uint[8] memory orderValues, | |
+ bytes[4] memory orderData, | |
bytes32 identifier, | |
- bytes makerAssetData, | |
- bytes takerAssetData, | |
- bytes signature | |
+ bytes memory signature | |
) public onlyManager notShutDown { | |
Hub hub = getHub(); | |
@@ -36,40 +36,40 @@ contract EngineAdapter is DSMath, TokenUser, ExchangeAdapter { | |
uint minEthToReceive = orderValues[0]; | |
uint mlnQuantity = orderValues[1]; | |
- require( | |
- wethAddress == Registry(hub.registry()).nativeAsset(), | |
- "maker asset doesnt match nativeAsset on registry" | |
+ require( | |
+ wethAddress == Registry(hub.registry()).nativeAsset(), | |
+ "maker asset doesnt match nativeAsset on registry" | |
); | |
- require( | |
- orderValues[1] == orderValues[6], | |
- "fillTakerQuantity must equal takerAssetQuantity" | |
+ require( | |
+ orderValues[1] == orderValues[6], | |
+ "fillTakerQuantity must equal takerAssetQuantity" | |
); | |
Vault vault = Vault(hub.vault()); | |
vault.withdraw(mlnAddress, mlnQuantity); | |
require( | |
- ERC20(mlnAddress).approve(targetExchange, mlnQuantity), | |
+ IERC20(mlnAddress).approve(targetExchange, mlnQuantity), | |
"MLN could not be approved" | |
); | |
uint ethToReceive = Engine(targetExchange).ethPayoutForMlnAmount(mlnQuantity); | |
- | |
+ | |
require( | |
ethToReceive >= minEthToReceive, | |
"Expected ETH to receive is less than takerQuantity (minEthToReceive)" | |
); | |
- | |
+ | |
Engine(targetExchange).sellAndBurnMln(mlnQuantity); | |
- WETH(wethAddress).deposit.value(ethToReceive)(); | |
+ WETH(address(uint160(wethAddress))).deposit.value(ethToReceive)(); | |
safeTransfer(wethAddress, address(vault), ethToReceive); | |
- | |
+ | |
getAccounting().addAssetToOwnedAssets(wethAddress); | |
getAccounting().updateOwnedAssets(); | |
getTrading().orderUpdateHook( | |
targetExchange, | |
bytes32(0), | |
Trading.UpdateType.take, | |
- [wethAddress, mlnAddress], | |
+ [address(uint160(wethAddress)), address(uint160(mlnAddress))], | |
[ethToReceive, mlnQuantity, mlnQuantity] | |
); | |
} | |
diff --git a/src/contracts/exchanges/EthfinexAdapter.sol b/src/exchanges/EthfinexAdapter.sol | |
similarity index 66% | |
rename from src/contracts/exchanges/EthfinexAdapter.sol | |
rename to src/exchanges/EthfinexAdapter.sol | |
index b1144a1f..19a40818 100644 | |
--- a/src/contracts/exchanges/EthfinexAdapter.sol | |
+++ b/src/exchanges/EthfinexAdapter.sol | |
@@ -1,19 +1,17 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
pragma experimental ABIEncoderV2; | |
-import "ERC20.i.sol"; | |
-import "Trading.sol"; | |
-import "Hub.sol"; | |
-import "Vault.sol"; | |
-import "Accounting.sol"; | |
-import "Registry.sol"; | |
-import "Weth.sol"; | |
-import "math.sol"; | |
-import "Exchange.sol"; | |
-import "WrapperLock.sol"; | |
-import "WrapperLockEth.sol"; | |
-import "ExchangeAdapter.sol"; | |
-import "WrapperRegistryEFX.sol"; | |
+import "../dependencies/token/IERC20.sol"; | |
+import "../fund/trading/Trading.sol"; | |
+import "../fund/hub/Hub.sol"; | |
+import "../fund/vault/Vault.sol"; | |
+import "../fund/accounting/Accounting.sol"; | |
+import "../version/Registry.sol"; | |
+import "../dependencies/WETH.sol"; | |
+import "../dependencies/DSMath.sol"; | |
+import "./interfaces/IZeroExV2.sol"; | |
+import "./interfaces/IEthfinex.sol"; | |
+import "./ExchangeAdapter.sol"; | |
/// @title EthfinexAdapter Contract | |
/// @author Melonport AG <team@melonport.com> | |
@@ -27,17 +25,17 @@ contract EthfinexAdapter is DSMath, ExchangeAdapter { | |
/// @notice Make order by pre-approving signatures | |
function makeOrder( | |
address targetExchange, | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
+ address[8] memory orderAddresses, | |
+ uint[8] memory orderValues, | |
+ bytes[4] memory orderData, | |
bytes32 identifier, | |
- bytes wrappedMakerAssetData, | |
- bytes takerAssetData, | |
- bytes signature | |
+ bytes memory signature | |
) public onlyManager notShutDown { | |
ensureCanMakeOrder(orderAddresses[2]); | |
Hub hub = getHub(); | |
- | |
- LibOrder.Order memory order = constructOrderStruct(orderAddresses, orderValues, wrappedMakerAssetData, takerAssetData); | |
+ IZeroExV2.Order memory order = constructOrderStruct(orderAddresses, orderValues, orderData); | |
+ bytes memory wrappedMakerAssetData = orderData[0]; | |
+ bytes memory takerAssetData = orderData[1]; | |
address makerAsset = orderAddresses[2]; | |
address takerAsset = getAssetAddress(takerAssetData); | |
require( | |
@@ -49,11 +47,11 @@ contract EthfinexAdapter is DSMath, ExchangeAdapter { | |
ensureNotInOpenMakeOrder(makerAsset); | |
wrapMakerAsset(targetExchange, makerAsset, wrappedMakerAssetData, order.makerAssetAmount, order.expirationTimeSeconds); | |
- LibOrder.OrderInfo memory orderInfo = Exchange(targetExchange).getOrderInfo(order); | |
- Exchange(targetExchange).preSign(orderInfo.orderHash, address(this), signature); | |
+ IZeroExV2.OrderInfo memory orderInfo = IZeroExV2(targetExchange).getOrderInfo(order); | |
+ IZeroExV2(targetExchange).preSign(orderInfo.orderHash, address(this), signature); | |
require( | |
- Exchange(targetExchange).isValidSignature( | |
+ IZeroExV2(targetExchange).isValidSignature( | |
orderInfo.orderHash, | |
address(this), | |
signature | |
@@ -65,33 +63,32 @@ contract EthfinexAdapter is DSMath, ExchangeAdapter { | |
targetExchange, | |
orderInfo.orderHash, | |
Trading.UpdateType.make, | |
- [address(makerAsset), address(takerAsset)], | |
+ [address(uint160(makerAsset)), address(uint160(takerAsset))], | |
[order.makerAssetAmount, order.takerAssetAmount, uint(0)] | |
); | |
getTrading().addOpenMakeOrder( | |
- targetExchange, | |
+ targetExchange, | |
makerAsset, | |
takerAsset, | |
- uint256(orderInfo.orderHash), | |
+ uint256(orderInfo.orderHash), | |
order.expirationTimeSeconds | |
); | |
- getTrading().addZeroExOrderData(orderInfo.orderHash, order); | |
+ getTrading().addZeroExV2OrderData(orderInfo.orderHash, order); | |
} | |
/// @notice Cancel the 0x make order | |
function cancelOrder( | |
address targetExchange, | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
+ address[8] memory orderAddresses, | |
+ uint[8] memory orderValues, | |
+ bytes[4] memory orderData, | |
bytes32 identifier, | |
- bytes wrappedMakerAssetData, | |
- bytes takerAssetData, | |
- bytes signature | |
+ bytes memory signature | |
) public onlyCancelPermitted(targetExchange, orderAddresses[2]) { | |
Hub hub = getHub(); | |
- LibOrder.Order memory order = getTrading().getZeroExOrderDetails(identifier); | |
- Exchange(targetExchange).cancelOrder(order); | |
+ IZeroExV2.Order memory order = getTrading().getZeroExV2OrderDetails(identifier); | |
+ IZeroExV2(targetExchange).cancelOrder(order); | |
getAccounting().updateOwnedAssets(); | |
// Order is not removed from OpenMakeOrder mapping as it's needed for accounting (wrapped tokens) | |
@@ -107,12 +104,11 @@ contract EthfinexAdapter is DSMath, ExchangeAdapter { | |
/// @notice Unwrap (withdraw) tokens, uses orderAddresses for input list of tokens to be unwrapped | |
function withdrawTokens( | |
address targetExchange, | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
+ address[8] memory orderAddresses, | |
+ uint[8] memory orderValues, | |
+ bytes[4] memory orderData, | |
bytes32 identifier, | |
- bytes makerAssetData, | |
- bytes takerAssetData, | |
- bytes signature | |
+ bytes memory signature | |
) public { | |
Hub hub = getHub(); | |
address nativeAsset = Accounting(hub.accounting()).NATIVE_ASSET(); | |
@@ -121,11 +117,11 @@ contract EthfinexAdapter is DSMath, ExchangeAdapter { | |
// Check if the input token address is null address | |
if (orderAddresses[i] == address(0)) continue; | |
address wrappedToken = getWrapperToken(orderAddresses[i]); | |
- uint balance = WrapperLock(wrappedToken).balanceOf(address(this)); | |
+ uint balance = IWrapperLock(wrappedToken).balanceOf(address(this)); | |
require(balance > 0, "Insufficient balance"); | |
- WrapperLock(wrappedToken).withdraw(balance, 0, bytes32(0), bytes32(0), 0); | |
+ IWrapperLock(wrappedToken).withdraw(balance, 0, bytes32(0), bytes32(0), 0); | |
if (orderAddresses[i] == nativeAsset) { | |
- WETH(nativeAsset).deposit.value(balance)(); | |
+ WETH(address(uint160(nativeAsset))).deposit.value(balance)(); | |
} | |
getTrading().removeOpenMakeOrder(targetExchange, orderAddresses[i]); | |
getTrading().returnAssetToVault(orderAddresses[i]); | |
@@ -133,10 +129,10 @@ contract EthfinexAdapter is DSMath, ExchangeAdapter { | |
} | |
/// @notice Minor: Wrapped tokens directly sent to the fund are not accounted. To be called by Trading spoke | |
- function getOrder(address targetExchange, uint id, address makerAsset) | |
+ function getOrder(address targetExchange, uint256 id, address makerAsset) | |
public | |
view | |
- returns (address, address, uint, uint) | |
+ returns (address, address, uint256, uint256) | |
{ | |
uint orderId; | |
uint orderIndex; | |
@@ -147,13 +143,13 @@ contract EthfinexAdapter is DSMath, ExchangeAdapter { | |
(, takerAsset, makerQuantity, takerQuantity) = Trading(msg.sender).getOrderDetails(orderIndex); | |
// Check if order has been completely filled | |
- uint takerAssetFilledAmount = Exchange(targetExchange).filled(bytes32(orderId)); | |
+ uint takerAssetFilledAmount = IZeroExV2(targetExchange).filled(bytes32(orderId)); | |
if (sub(takerQuantity, takerAssetFilledAmount) == 0) { | |
return (makerAsset, takerAsset, 0, 0); | |
} | |
// Check if tokens have been withdrawn (cancelled order may still need to be accounted if there is balance) | |
- uint balance = WrapperLock(getWrapperTokenFromAdapterContext(makerAsset)).balanceOf(msg.sender); | |
+ uint balance = IWrapperLock(getWrapperTokenFromAdapterContext(makerAsset)).balanceOf(msg.sender); | |
if (balance == 0) { | |
return (makerAsset, takerAsset, 0, 0); | |
} | |
@@ -164,7 +160,7 @@ contract EthfinexAdapter is DSMath, ExchangeAdapter { | |
/// @notice needed to avoid stack too deep error | |
/// @dev deposit time should be greater than 1 hour | |
- function wrapMakerAsset(address targetExchange, address makerAsset, bytes wrappedMakerAssetData, uint makerQuantity, uint orderExpirationTime) | |
+ function wrapMakerAsset(address targetExchange, address makerAsset, bytes memory wrappedMakerAssetData, uint makerQuantity, uint orderExpirationTime) | |
internal | |
{ | |
Hub hub = getHub(); | |
@@ -183,27 +179,26 @@ contract EthfinexAdapter is DSMath, ExchangeAdapter { | |
// Handle case for WETH | |
address nativeAsset = Accounting(hub.accounting()).NATIVE_ASSET(); | |
if (makerAsset == nativeAsset) { | |
- WETH(nativeAsset).withdraw(makerQuantity); | |
- WrapperLockEth(wrappedToken).deposit.value(makerQuantity)(makerQuantity, depositTime); | |
+ WETH(address(uint160(nativeAsset))).withdraw(makerQuantity); | |
+ IWrapperLockEth(wrappedToken).deposit.value(makerQuantity)(makerQuantity, depositTime); | |
} else { | |
- ERC20(makerAsset).approve(wrappedToken, makerQuantity); | |
- WrapperLock(wrappedToken).deposit(makerQuantity, depositTime); | |
+ IERC20(makerAsset).approve(wrappedToken, makerQuantity); | |
+ IWrapperLock(wrappedToken).deposit(makerQuantity, depositTime); | |
} | |
} | |
// VIEW METHODS | |
function constructOrderStruct( | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
- bytes makerAssetData, | |
- bytes takerAssetData | |
+ address[8] memory orderAddresses, | |
+ uint[8] memory orderValues, | |
+ bytes[4] memory orderData | |
) | |
internal | |
view | |
- returns (LibOrder.Order memory order) | |
+ returns (IZeroExV2.Order memory order) | |
{ | |
- order = LibOrder.Order({ | |
+ order = IZeroExV2.Order({ | |
makerAddress: orderAddresses[0], | |
takerAddress: orderAddresses[1], | |
feeRecipientAddress: orderAddresses[4], | |
@@ -214,12 +209,12 @@ contract EthfinexAdapter is DSMath, ExchangeAdapter { | |
takerFee: orderValues[3], | |
expirationTimeSeconds: orderValues[4], | |
salt: orderValues[5], | |
- makerAssetData: makerAssetData, | |
- takerAssetData: takerAssetData | |
+ makerAssetData: orderData[0], | |
+ takerAssetData: orderData[1] | |
}); | |
} | |
- function getAssetProxy(address targetExchange, bytes assetData) | |
+ function getAssetProxy(address targetExchange, bytes memory assetData) | |
internal | |
view | |
returns (address assetProxy) | |
@@ -231,10 +226,10 @@ contract EthfinexAdapter is DSMath, ExchangeAdapter { | |
0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 | |
) | |
} | |
- assetProxy = Exchange(targetExchange).getAssetProxy(assetProxyId); | |
+ assetProxy = IZeroExV2(targetExchange).getAssetProxy(assetProxyId); | |
} | |
- function getAssetAddress(bytes assetData) | |
+ function getAssetAddress(bytes memory assetData) | |
internal | |
view | |
returns (address assetAddress) | |
@@ -250,8 +245,8 @@ contract EthfinexAdapter is DSMath, ExchangeAdapter { | |
view | |
returns (address wrapperToken) | |
{ | |
- address wrapperRegistry = Registry(Trading(address(this)).registry()).ethfinexWrapperRegistry(); | |
- return WrapperRegistryEFX(wrapperRegistry).token2WrapperLookup(token); | |
+ address wrapperRegistry = Registry(getTrading().registry()).ethfinexWrapperRegistry(); | |
+ return IWrapperRegistryEFX(wrapperRegistry).token2WrapperLookup(token); | |
} | |
/// @dev Function to be called by Trading spoke without change of context (Non delegate call) | |
@@ -261,6 +256,6 @@ contract EthfinexAdapter is DSMath, ExchangeAdapter { | |
returns (address wrapperToken) | |
{ | |
address wrapperRegistry = Registry(Trading(msg.sender).registry()).ethfinexWrapperRegistry(); | |
- return WrapperRegistryEFX(wrapperRegistry).token2WrapperLookup(token); | |
+ return IWrapperRegistryEFX(wrapperRegistry).token2WrapperLookup(token); | |
} | |
} | |
diff --git a/src/contracts/exchanges/ExchangeAdapter.sol b/src/exchanges/ExchangeAdapter.sol | |
similarity index 81% | |
rename from src/contracts/exchanges/ExchangeAdapter.sol | |
rename to src/exchanges/ExchangeAdapter.sol | |
index f0c56137..5571fce9 100644 | |
--- a/src/contracts/exchanges/ExchangeAdapter.sol | |
+++ b/src/exchanges/ExchangeAdapter.sol | |
@@ -1,8 +1,9 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
+pragma experimental ABIEncoderV2; | |
-import "Accounting.sol"; | |
-import "Hub.sol"; | |
-import "Trading.sol"; | |
+import "../fund/accounting/Accounting.sol"; | |
+import "../fund/hub/Hub.sol"; | |
+import "../fund/trading/Trading.sol"; | |
/// @title Exchange Adapter base contract | |
/// @author Melonport AG <team@melonport.com> | |
@@ -37,7 +38,7 @@ contract ExchangeAdapter { | |
} | |
function getTrading() internal view returns (Trading) { | |
- return Trading(address(this)); | |
+ return Trading(address(uint160(address(this)))); | |
} | |
function getHub() internal view returns (Hub) { | |
@@ -76,6 +77,8 @@ contract ExchangeAdapter { | |
/// @param orderAddresses [3] Order taker asset | |
/// @param orderAddresses [4] feeRecipientAddress | |
/// @param orderAddresses [5] senderAddress | |
+ /// @param orderAddresses [6] maker fee asset | |
+ /// @param orderAddresses [7] taker fee asset | |
/// @param orderValues [0] makerAssetAmount | |
/// @param orderValues [1] takerAssetAmount | |
/// @param orderValues [2] Maker fee | |
@@ -84,9 +87,11 @@ contract ExchangeAdapter { | |
/// @param orderValues [5] Salt/nonce | |
/// @param orderValues [6] Fill amount: amount of taker token to be traded | |
/// @param orderValues [7] Dexy signature mode | |
+ /// @param orderData [0] Encoded data specific to maker asset | |
+ /// @param orderData [1] Encoded data specific to taker asset | |
+ /// @param orderData [2] Encoded data specific to maker asset fee | |
+ /// @param orderData [3] Encoded data specific to taker asset fee | |
/// @param identifier Order identifier | |
- /// @param makerAssetData Encoded data specific to makerAsset | |
- /// @param takerAssetData Encoded data specific to takerAsset | |
/// @param signature Signature of order maker | |
// Responsibilities of makeOrder are: | |
@@ -100,12 +105,11 @@ contract ExchangeAdapter { | |
// - place asset in ownedAssets if not already tracked | |
function makeOrder( | |
address targetExchange, | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
+ address[8] memory orderAddresses, | |
+ uint[8] memory orderValues, | |
+ bytes[4] memory orderData, | |
bytes32 identifier, | |
- bytes makerAssetData, | |
- bytes takerAssetData, | |
- bytes signature | |
+ bytes memory signature | |
) public { revert("Unimplemented"); } | |
// Responsibilities of takeOrder are: | |
@@ -121,12 +125,11 @@ contract ExchangeAdapter { | |
// - place asset in ownedAssets if not already tracked | |
function takeOrder( | |
address targetExchange, | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
+ address[8] memory orderAddresses, | |
+ uint[8] memory orderValues, | |
+ bytes[4] memory orderData, | |
bytes32 identifier, | |
- bytes makerAssetData, | |
- bytes takerAssetData, | |
- bytes signature | |
+ bytes memory signature | |
) public { revert("Unimplemented"); } | |
// responsibilities of cancelOrder are: | |
@@ -135,12 +138,11 @@ contract ExchangeAdapter { | |
// - cancel order on exchange | |
function cancelOrder( | |
address targetExchange, | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
+ address[8] memory orderAddresses, | |
+ uint[8] memory orderValues, | |
+ bytes[4] memory orderData, | |
bytes32 identifier, | |
- bytes makerAssetData, | |
- bytes takerAssetData, | |
- bytes signature | |
+ bytes memory signature | |
) public { revert("Unimplemented"); } | |
// PUBLIC METHODS | |
diff --git a/src/contracts/exchanges/KyberAdapter.sol b/src/exchanges/KyberAdapter.sol | |
similarity index 79% | |
rename from src/contracts/exchanges/KyberAdapter.sol | |
rename to src/exchanges/KyberAdapter.sol | |
index 16960739..330e7b3c 100644 | |
--- a/src/contracts/exchanges/KyberAdapter.sol | |
+++ b/src/exchanges/KyberAdapter.sol | |
@@ -1,17 +1,18 @@ | |
-pragma solidity ^0.4.25; | |
- | |
-import "Weth.sol"; | |
-import "Trading.sol"; | |
-import "Hub.sol"; | |
-import "Vault.sol"; | |
-import "Accounting.sol"; | |
-import "PriceSource.i.sol"; | |
-import "KyberNetworkProxy.sol"; | |
-import "ExchangeAdapter.sol"; | |
+pragma solidity 0.5.15; | |
+pragma experimental ABIEncoderV2; | |
+ | |
+import "../dependencies/WETH.sol"; | |
+import "../dependencies/token/IERC20.sol"; | |
+import "../fund/trading/Trading.sol"; | |
+import "../fund/hub/Hub.sol"; | |
+import "../fund/vault/Vault.sol"; | |
+import "../fund/accounting/Accounting.sol"; | |
+import "../prices/IPriceSource.sol"; | |
+import "./interfaces/IKyberNetworkProxy.sol"; | |
+import "./ExchangeAdapter.sol"; | |
contract KyberAdapter is DSMath, ExchangeAdapter { | |
- | |
- address public constant ETH_TOKEN_ADDRESS = 0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee; | |
+ address public constant ETH_TOKEN_ADDRESS = address(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee); | |
// NON-CONSTANT METHODS | |
@@ -31,18 +32,17 @@ contract KyberAdapter is DSMath, ExchangeAdapter { | |
/// @param orderValues [1] Taker asset quantity (Src token amount) | |
function takeOrder( | |
address targetExchange, | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
+ address[8] memory orderAddresses, | |
+ uint[8] memory orderValues, | |
+ bytes[4] memory orderData, | |
bytes32 identifier, | |
- bytes makerAssetData, | |
- bytes takerAssetData, | |
- bytes signature | |
+ bytes memory signature | |
) public onlyManager notShutDown { | |
Hub hub = getHub(); | |
- require( | |
- orderValues[1] == orderValues[6], | |
- "fillTakerQuantity must equal takerAssetQuantity" | |
+ require( | |
+ orderValues[1] == orderValues[6], | |
+ "fillTakerQuantity must equal takerAssetQuantity" | |
); | |
address makerAsset = orderAddresses[2]; | |
@@ -66,13 +66,13 @@ contract KyberAdapter is DSMath, ExchangeAdapter { | |
); | |
getAccounting().addAssetToOwnedAssets(makerAsset); | |
- getAccounting().updateOwnedAssets(); | |
+ getAccounting().updateOwnedAssets(); | |
getTrading().returnAssetToVault(makerAsset); | |
getTrading().orderUpdateHook( | |
targetExchange, | |
bytes32(0), | |
Trading.UpdateType.take, | |
- [makerAsset, takerAsset], | |
+ [address(uint160(makerAsset)), address(uint160(takerAsset))], | |
[actualReceiveAmount, takerAssetAmount, takerAssetAmount] | |
); | |
} | |
@@ -126,8 +126,8 @@ contract KyberAdapter is DSMath, ExchangeAdapter { | |
Hub hub = getHub(); | |
Vault vault = Vault(hub.vault()); | |
vault.withdraw(nativeAsset, srcAmount); | |
- WETH(nativeAsset).withdraw(srcAmount); | |
- receivedAmount = KyberNetworkProxy(targetExchange).swapEtherToToken.value(srcAmount)(ERC20Clone(destToken), minRate); | |
+ WETH(address(uint160(nativeAsset))).withdraw(srcAmount); | |
+ receivedAmount = IKyberNetworkProxy(targetExchange).swapEtherToToken.value(srcAmount)(destToken, minRate); | |
} | |
/// @dev If minRate is not defined, uses expected rate from the network | |
@@ -150,11 +150,11 @@ contract KyberAdapter is DSMath, ExchangeAdapter { | |
Hub hub = getHub(); | |
Vault vault = Vault(hub.vault()); | |
vault.withdraw(srcToken, srcAmount); | |
- ERC20Clone(srcToken).approve(targetExchange, srcAmount); | |
- receivedAmount = KyberNetworkProxy(targetExchange).swapTokenToEther(ERC20Clone(srcToken), srcAmount, minRate); | |
+ IERC20(srcToken).approve(targetExchange, srcAmount); | |
+ receivedAmount = IKyberNetworkProxy(targetExchange).swapTokenToEther(srcToken, srcAmount, minRate); | |
// Convert ETH to WETH | |
- WETH(nativeAsset).deposit.value(receivedAmount)(); | |
+ WETH(address(uint160(nativeAsset))).deposit.value(receivedAmount)(); | |
} | |
/// @dev If minRate is not defined, uses expected rate from the network | |
@@ -177,8 +177,8 @@ contract KyberAdapter is DSMath, ExchangeAdapter { | |
Hub hub = getHub(); | |
Vault vault = Vault(hub.vault()); | |
vault.withdraw(srcToken, srcAmount); | |
- ERC20Clone(srcToken).approve(targetExchange, srcAmount); | |
- receivedAmount = KyberNetworkProxy(targetExchange).swapTokenToToken(ERC20Clone(srcToken), srcAmount, ERC20Clone(destToken), minRate); | |
+ IERC20(srcToken).approve(targetExchange, srcAmount); | |
+ receivedAmount = IKyberNetworkProxy(targetExchange).swapTokenToToken(srcToken, srcAmount, destToken, minRate); | |
} | |
/// @dev Calculate min rate to be supplied to the network based on provided order parameters | |
@@ -196,7 +196,7 @@ contract KyberAdapter is DSMath, ExchangeAdapter { | |
view | |
returns (uint minRate) | |
{ | |
- PriceSourceInterface pricefeed = PriceSourceInterface(Hub(Trading(address(this)).hub()).priceSource()); | |
+ IPriceSource pricefeed = IPriceSource(getHub().priceSource()); | |
minRate = pricefeed.getOrderPriceInfo( | |
srcToken, | |
destToken, | |
diff --git a/src/contracts/exchanges/MatchingMarketAccessor.sol b/src/exchanges/OasisDexAccessor.sol | |
similarity index 81% | |
rename from src/contracts/exchanges/MatchingMarketAccessor.sol | |
rename to src/exchanges/OasisDexAccessor.sol | |
index 0286fc1a..6cf27740 100644 | |
--- a/src/contracts/exchanges/MatchingMarketAccessor.sol | |
+++ b/src/exchanges/OasisDexAccessor.sol | |
@@ -1,9 +1,8 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "ERC20.i.sol"; | |
-import "MatchingMarket.sol"; | |
+import "./interfaces/IOasisDex.sol"; | |
-contract MatchingMarketAccessor { | |
+contract OasisDexAccessor { | |
function getUnsortedOfferIds( | |
address targetExchange, | |
address sellAsset, | |
@@ -11,9 +10,9 @@ contract MatchingMarketAccessor { | |
) | |
public | |
view | |
- returns (uint[]) | |
+ returns (uint[] memory) | |
{ | |
- MatchingMarket market = MatchingMarket(targetExchange); | |
+ IOasisDex market = IOasisDex(targetExchange); | |
uint[] memory ids = new uint[](1000); | |
uint count = 0; | |
@@ -40,7 +39,7 @@ contract MatchingMarketAccessor { | |
// Create a new array of offers with the correct size. | |
uint[] memory copy = new uint[](count); | |
- for (i = 0; i < count; i++) { | |
+ for (uint i = 0; i < count; i++) { | |
copy[i] = ids[i]; | |
} | |
@@ -54,14 +53,14 @@ contract MatchingMarketAccessor { | |
) | |
public | |
view | |
- returns(uint[]) | |
+ returns(uint[] memory) | |
{ | |
- MatchingMarket market = MatchingMarket(targetExchange); | |
+ IOasisDex market = IOasisDex(targetExchange); | |
uint[] memory ids = new uint[](1000); | |
uint count = 0; | |
// Iterate over all sorted offers. | |
- uint id = market.getBestOffer(ERC20(sellAsset), ERC20(buyAsset)); | |
+ uint id = market.getBestOffer(sellAsset, buyAsset); | |
for (uint i = 0; i < 1000 ; i++ ) { | |
if (id == 0) { | |
break; | |
@@ -77,7 +76,7 @@ contract MatchingMarketAccessor { | |
// Create a new array of offers with the correct size. | |
uint[] memory copy = new uint[](count); | |
- for (i = 0; i < count; i++) { | |
+ for (uint i = 0; i < count; i++) { | |
copy[i] = ids[i]; | |
} | |
@@ -91,8 +90,8 @@ contract MatchingMarketAccessor { | |
) | |
public | |
view | |
- returns (uint[], uint[], uint[]) { | |
- MatchingMarket market = MatchingMarket(targetExchange); | |
+ returns (uint[] memory, uint[] memory, uint[] memory) { | |
+ IOasisDex market = IOasisDex(targetExchange); | |
uint[] memory sIds = getSortedOfferIds(targetExchange, sellAsset, buyAsset); | |
uint[] memory uIds = getUnsortedOfferIds(targetExchange, sellAsset, buyAsset); | |
uint[] memory ids = new uint[](uIds.length + sIds.length); | |
@@ -103,11 +102,11 @@ contract MatchingMarketAccessor { | |
ids[i] = sIds[i]; | |
} | |
- for (i = 0; i < uIds.length; i++) { | |
+ for (uint i = 0; i < uIds.length; i++) { | |
ids[i + sIds.length] = uIds[i]; | |
} | |
- for (i = 0; i < ids.length; i++) { | |
+ for (uint i = 0; i < ids.length; i++) { | |
uint sellQty; | |
uint buyQty; | |
(sellQty, , buyQty,) = market.getOffer(ids[i]); | |
diff --git a/src/contracts/exchanges/MatchingMarketAdapter.sol b/src/exchanges/OasisDexAdapter.sol | |
similarity index 65% | |
rename from src/contracts/exchanges/MatchingMarketAdapter.sol | |
rename to src/exchanges/OasisDexAdapter.sol | |
index b95fd573..d7523f48 100644 | |
--- a/src/contracts/exchanges/MatchingMarketAdapter.sol | |
+++ b/src/exchanges/OasisDexAdapter.sol | |
@@ -1,19 +1,20 @@ | |
-pragma solidity ^0.4.25; | |
- | |
-import "Hub.sol"; | |
-import "Trading.sol"; | |
-import "Vault.sol"; | |
-import "Accounting.sol"; | |
-import "math.sol"; | |
-import "MatchingMarket.sol"; | |
-import "ExchangeAdapter.sol"; | |
- | |
-/// @title MatchingMarketAdapter Contract | |
+pragma solidity 0.5.15; | |
+pragma experimental ABIEncoderV2; | |
+ | |
+import "../fund/hub/Hub.sol"; | |
+import "../fund/trading/Trading.sol"; | |
+import "../fund/vault/Vault.sol"; | |
+import "../fund/accounting/Accounting.sol"; | |
+import "../dependencies/DSMath.sol"; | |
+import "./interfaces/IOasisDex.sol"; | |
+import "./ExchangeAdapter.sol"; | |
+ | |
+/// @title OasisDexAdapter Contract | |
/// @author Melonport AG <team@melonport.com> | |
/// @notice Adapter between Melon and OasisDex Matching Market | |
-contract MatchingMarketAdapter is DSMath, ExchangeAdapter { | |
+contract OasisDexAdapter is DSMath, ExchangeAdapter { | |
- event OrderCreated(uint id); | |
+ event OrderCreated(uint256 id); | |
// METHODS | |
@@ -37,18 +38,17 @@ contract MatchingMarketAdapter is DSMath, ExchangeAdapter { | |
/// @param orderValues [1] Taker token quantity | |
function makeOrder( | |
address targetExchange, | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
+ address[8] memory orderAddresses, | |
+ uint[8] memory orderValues, | |
+ bytes[4] memory orderData, | |
bytes32 identifier, | |
- bytes makerAssetData, | |
- bytes takerAssetData, | |
- bytes signature | |
+ bytes memory signature | |
) public onlyManager notShutDown { | |
ensureCanMakeOrder(orderAddresses[2]); | |
- ERC20 makerAsset = ERC20(orderAddresses[2]); | |
- ERC20 takerAsset = ERC20(orderAddresses[3]); | |
- uint makerQuantity = orderValues[0]; | |
- uint takerQuantity = orderValues[1]; | |
+ address makerAsset = orderAddresses[2]; | |
+ address takerAsset = orderAddresses[3]; | |
+ uint256 makerQuantity = orderValues[0]; | |
+ uint256 takerQuantity = orderValues[1]; | |
// Order parameter checks | |
getTrading().updateAndGetQuantityBeingTraded(makerAsset); | |
@@ -56,11 +56,11 @@ contract MatchingMarketAdapter is DSMath, ExchangeAdapter { | |
Vault(Hub(getHub()).vault()).withdraw(makerAsset, makerQuantity); | |
require( | |
- makerAsset.approve(targetExchange, makerQuantity), | |
+ IERC20(makerAsset).approve(targetExchange, makerQuantity), | |
"Could not approve maker asset" | |
); | |
- uint orderId = MatchingMarket(targetExchange).offer(makerQuantity, makerAsset, takerQuantity, takerAsset); | |
+ uint256 orderId = IOasisDex(targetExchange).offer(makerQuantity, makerAsset, takerQuantity, takerAsset); | |
// defines success in MatchingMarket | |
require(orderId != 0, "Order ID should not be zero"); | |
@@ -70,8 +70,8 @@ contract MatchingMarketAdapter is DSMath, ExchangeAdapter { | |
targetExchange, | |
bytes32(orderId), | |
Trading.UpdateType.make, | |
- [address(makerAsset), address(takerAsset)], | |
- [makerQuantity, takerQuantity, uint(0)] | |
+ [address(uint160(makerAsset)), address(uint160(takerAsset))], | |
+ [makerQuantity, takerQuantity, uint256(0)] | |
); | |
getTrading().addOpenMakeOrder(targetExchange, makerAsset, takerAsset, orderId, orderValues[4]); | |
emit OrderCreated(orderId); | |
@@ -95,33 +95,32 @@ contract MatchingMarketAdapter is DSMath, ExchangeAdapter { | |
/// @param identifier Active order id | |
function takeOrder( | |
address targetExchange, | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
+ address[8] memory orderAddresses, | |
+ uint[8] memory orderValues, | |
+ bytes[4] memory orderData, | |
bytes32 identifier, | |
- bytes makerAssetData, | |
- bytes takerAssetData, | |
- bytes signature | |
+ bytes memory signature | |
) public onlyManager notShutDown { | |
Hub hub = getHub(); | |
- uint fillTakerQuantity = orderValues[6]; | |
- uint maxMakerQuantity; | |
- ERC20 makerAsset; | |
- uint maxTakerQuantity; | |
- ERC20 takerAsset; | |
+ uint256 fillTakerQuantity = orderValues[6]; | |
+ uint256 maxMakerQuantity; | |
+ address makerAsset; | |
+ uint256 maxTakerQuantity; | |
+ address takerAsset; | |
( | |
maxMakerQuantity, | |
makerAsset, | |
maxTakerQuantity, | |
takerAsset | |
- ) = MatchingMarket(targetExchange).getOffer(uint(identifier)); | |
- uint fillMakerQuantity = mul(fillTakerQuantity, maxMakerQuantity) / maxTakerQuantity; | |
+ ) = IOasisDex(targetExchange).getOffer(uint256(identifier)); | |
+ uint256 fillMakerQuantity = mul(fillTakerQuantity, maxMakerQuantity) / maxTakerQuantity; | |
require( | |
- address(makerAsset) == orderAddresses[2] && address(takerAsset) == orderAddresses[3], | |
+ makerAsset == orderAddresses[2] && takerAsset == orderAddresses[3], | |
"Maker and taker assets do not match the order addresses" | |
); | |
require( | |
- address(makerAsset) != address(takerAsset), | |
+ makerAsset != takerAsset, | |
"Maker and taker assets cannot be the same" | |
); | |
require(fillMakerQuantity <= maxMakerQuantity, "Maker amount to fill above max"); | |
@@ -129,11 +128,11 @@ contract MatchingMarketAdapter is DSMath, ExchangeAdapter { | |
Vault(hub.vault()).withdraw(takerAsset, fillTakerQuantity); | |
require( | |
- takerAsset.approve(targetExchange, fillTakerQuantity), | |
+ IERC20(takerAsset).approve(targetExchange, fillTakerQuantity), | |
"Taker asset could not be approved" | |
); | |
require( | |
- MatchingMarket(targetExchange).buy(uint(identifier), fillMakerQuantity), | |
+ IOasisDex(targetExchange).buy(uint256(identifier), fillMakerQuantity), | |
"Buy on matching market failed" | |
); | |
@@ -144,7 +143,7 @@ contract MatchingMarketAdapter is DSMath, ExchangeAdapter { | |
targetExchange, | |
bytes32(identifier), | |
Trading.UpdateType.take, | |
- [address(makerAsset), address(takerAsset)], | |
+ [address(uint160(makerAsset)), address(uint160(takerAsset))], | |
[maxMakerQuantity, maxTakerQuantity, fillTakerQuantity] | |
); | |
} | |
@@ -159,18 +158,17 @@ contract MatchingMarketAdapter is DSMath, ExchangeAdapter { | |
/// @param identifier Order ID on the exchange | |
function cancelOrder( | |
address targetExchange, | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
+ address[8] memory orderAddresses, | |
+ uint[8] memory orderValues, | |
+ bytes[4] memory orderData, | |
bytes32 identifier, | |
- bytes makerAssetData, | |
- bytes takerAssetData, | |
- bytes signature | |
+ bytes memory signature | |
) public onlyCancelPermitted(targetExchange, orderAddresses[2]) { | |
Hub hub = getHub(); | |
- require(uint(identifier) != 0, "ID cannot be zero"); | |
+ require(uint256(identifier) != 0, "ID cannot be zero"); | |
address makerAsset; | |
- (, makerAsset, ,) = MatchingMarket(targetExchange).getOffer(uint(identifier)); | |
+ (, makerAsset, ,) = IOasisDex(targetExchange).getOffer(uint256(identifier)); | |
require( | |
address(makerAsset) == orderAddresses[2], | |
@@ -178,8 +176,8 @@ contract MatchingMarketAdapter is DSMath, ExchangeAdapter { | |
); | |
getTrading().removeOpenMakeOrder(targetExchange, makerAsset); | |
- MatchingMarket(targetExchange).cancel( | |
- uint(identifier) | |
+ IOasisDex(targetExchange).cancel( | |
+ uint256(identifier) | |
); | |
getTrading().returnAssetToVault(makerAsset); | |
getAccounting().updateOwnedAssets(); | |
@@ -188,30 +186,30 @@ contract MatchingMarketAdapter is DSMath, ExchangeAdapter { | |
bytes32(identifier), | |
Trading.UpdateType.cancel, | |
[address(0), address(0)], | |
- [uint(0), uint(0), uint(0)] | |
+ [uint256(0), uint256(0), uint256(0)] | |
); | |
} | |
// VIEW METHODS | |
- function getOrder(address targetExchange, uint id, address makerAsset) | |
+ function getOrder(address targetExchange, uint256 id, address makerAsset) | |
public | |
view | |
- returns (address, address, uint, uint) | |
+ returns (address, address, uint256, uint256) | |
{ | |
- uint sellQuantity; | |
- ERC20 sellAsset; | |
- uint buyQuantity; | |
- ERC20 buyAsset; | |
+ uint256 sellQuantity; | |
+ address sellAsset; | |
+ uint256 buyQuantity; | |
+ address buyAsset; | |
( | |
sellQuantity, | |
sellAsset, | |
buyQuantity, | |
buyAsset | |
- ) = MatchingMarket(targetExchange).getOffer(id); | |
+ ) = IOasisDex(targetExchange).getOffer(id); | |
return ( | |
- address(sellAsset), | |
- address(buyAsset), | |
+ sellAsset, | |
+ buyAsset, | |
sellQuantity, | |
buyQuantity | |
); | |
diff --git a/src/exchanges/UniswapAdapter.sol b/src/exchanges/UniswapAdapter.sol | |
new file mode 100644 | |
index 00000000..143d78e0 | |
--- /dev/null | |
+++ b/src/exchanges/UniswapAdapter.sol | |
@@ -0,0 +1,258 @@ | |
+pragma solidity 0.5.15; | |
+pragma experimental ABIEncoderV2; | |
+ | |
+import "../dependencies/token/IERC20.sol"; | |
+import "../dependencies/WETH.sol"; | |
+import "../fund/accounting/Accounting.sol"; | |
+import "../fund/hub/Hub.sol"; | |
+import "../fund/trading/Trading.sol"; | |
+import "../fund/vault/Vault.sol"; | |
+import "./interfaces/IUniswapFactory.sol"; | |
+import "./interfaces/IUniswapExchange.sol"; | |
+import "./ExchangeAdapter.sol"; | |
+ | |
+contract UniswapAdapter is DSMath, ExchangeAdapter { | |
+ /// @notice Take order that uses a user-defined src token amount to trade for a dest token amount | |
+ /// @dev For the purpose of PriceTolerance, _orderValues [1] == _orderValues [6] = Dest token amount | |
+ /// @param _targetExchange Address of Uniswap factory contract | |
+ /// @param _orderAddresses [2] Maker asset (Dest token) | |
+ /// @param _orderAddresses [3] Taker asset (Src token) | |
+ /// @param _orderValues [0] Maker asset quantity (Dest token amount) | |
+ /// @param _orderValues [1] Taker asset quantity (Src token amount) | |
+ /// @param _orderValues [6] Taker asset fill amount | |
+ function takeOrder( | |
+ address _targetExchange, | |
+ address[8] memory _orderAddresses, | |
+ uint[8] memory _orderValues, | |
+ bytes[4] memory _orderData, | |
+ bytes32 _identifier, | |
+ bytes memory _signature | |
+ ) | |
+ public | |
+ onlyManager | |
+ notShutDown | |
+ { | |
+ Hub hub = getHub(); | |
+ | |
+ require( | |
+ _orderValues[1] == _orderValues[6], | |
+ "Taker asset amount must equal taker asset fill amount" | |
+ ); | |
+ | |
+ address makerAsset = _orderAddresses[2]; | |
+ address takerAsset = _orderAddresses[3]; | |
+ uint makerAssetAmount = _orderValues[0]; | |
+ uint takerAssetAmount = _orderValues[1]; | |
+ | |
+ uint actualReceiveAmount = dispatchSwap( | |
+ _targetExchange, takerAsset, takerAssetAmount, makerAsset, makerAssetAmount | |
+ ); | |
+ require( | |
+ actualReceiveAmount >= makerAssetAmount, | |
+ "Received less than expected from Uniswap exchange" | |
+ ); | |
+ | |
+ updateStateTakeOrder( | |
+ _targetExchange, | |
+ makerAsset, | |
+ takerAsset, | |
+ takerAssetAmount, | |
+ actualReceiveAmount | |
+ ); | |
+ } | |
+ | |
+ // INTERNAL FUNCTIONS | |
+ function approveAsset( | |
+ address _asset, | |
+ address _target, | |
+ uint256 _amount, | |
+ string memory _assetType | |
+ ) | |
+ internal | |
+ { | |
+ Hub hub = getHub(); | |
+ Vault vault = Vault(hub.vault()); | |
+ | |
+ require( | |
+ IERC20(_asset).balanceOf(address(vault)) >= _amount, | |
+ string(abi.encodePacked("Insufficient balance: ", _assetType)) | |
+ ); | |
+ | |
+ vault.withdraw(_asset, _amount); | |
+ uint256 allowance = IERC20(_asset).allowance(address(this), _target); | |
+ require( | |
+ IERC20(_asset).approve(_target, add(allowance, _amount)), | |
+ string(abi.encodePacked("Approval failed: ", _assetType)) | |
+ ); | |
+ } | |
+ | |
+ /// @notice Call different functions based on type of assets supplied | |
+ /// @param _targetExchange Address of Uniswap factory contract | |
+ /// @param _srcToken Address of src token | |
+ /// @param _srcAmount Amount of src token supplied | |
+ /// @param _destToken Address of dest token | |
+ /// @param _minDestAmount Minimum amount of dest token to receive | |
+ /// @return actualReceiveAmount_ Actual amount of _destToken received | |
+ function dispatchSwap( | |
+ address _targetExchange, | |
+ address _srcToken, | |
+ uint _srcAmount, | |
+ address _destToken, | |
+ uint _minDestAmount | |
+ ) | |
+ internal | |
+ returns (uint actualReceiveAmount_) | |
+ { | |
+ require( | |
+ _srcToken != _destToken, | |
+ "Src token cannot be the same as dest token" | |
+ ); | |
+ | |
+ Hub hub = getHub(); | |
+ address nativeAsset = Accounting(hub.accounting()).NATIVE_ASSET(); | |
+ | |
+ if (_srcToken == nativeAsset) { | |
+ actualReceiveAmount_ = swapNativeAssetToToken( | |
+ _targetExchange, | |
+ nativeAsset, | |
+ _srcAmount, | |
+ _destToken, | |
+ _minDestAmount | |
+ ); | |
+ } else if (_destToken == nativeAsset) { | |
+ actualReceiveAmount_ = swapTokenToNativeAsset( | |
+ _targetExchange, | |
+ _srcToken, | |
+ _srcAmount, | |
+ nativeAsset, | |
+ _minDestAmount | |
+ ); | |
+ } else { | |
+ actualReceiveAmount_ = swapTokenToToken( | |
+ _targetExchange, | |
+ _srcToken, | |
+ _srcAmount, | |
+ _destToken, | |
+ _minDestAmount | |
+ ); | |
+ } | |
+ } | |
+ | |
+ /// @param _targetExchange Address of Uniswap factory contract | |
+ /// @param _nativeAsset Native asset address as src token | |
+ /// @param _srcAmount Amount of native asset supplied | |
+ /// @param _destToken Address of dest token | |
+ /// @param _minDestAmount Minimum amount of dest token to get back | |
+ /// @return actualReceiveAmount_ Actual amount of _destToken received | |
+ function swapNativeAssetToToken( | |
+ address _targetExchange, | |
+ address _nativeAsset, | |
+ uint _srcAmount, | |
+ address _destToken, | |
+ uint _minDestAmount | |
+ ) | |
+ internal | |
+ returns (uint actualReceiveAmount_) | |
+ { | |
+ // Convert WETH to ETH | |
+ Hub hub = getHub(); | |
+ Vault vault = Vault(hub.vault()); | |
+ vault.withdraw(_nativeAsset, _srcAmount); | |
+ WETH(address(uint160(_nativeAsset))).withdraw(_srcAmount); | |
+ | |
+ address tokenExchange = IUniswapFactory(_targetExchange).getExchange(_destToken); | |
+ actualReceiveAmount_ = IUniswapExchange(tokenExchange).ethToTokenTransferInput.value( | |
+ _srcAmount | |
+ ) | |
+ ( | |
+ _minDestAmount, | |
+ add(block.timestamp, 1), | |
+ address(vault) | |
+ ); | |
+ } | |
+ | |
+ /// @param _targetExchange Address of Uniswap factory contract | |
+ /// @param _srcToken Address of src token | |
+ /// @param _srcAmount Amount of src token supplied | |
+ /// @param _nativeAsset Native asset address as dest token | |
+ /// @param _minDestAmount Minimum amount of dest token to get back | |
+ /// @return actualReceiveAmount_ Actual amount of _destToken received | |
+ function swapTokenToNativeAsset( | |
+ address _targetExchange, | |
+ address _srcToken, | |
+ uint _srcAmount, | |
+ address _nativeAsset, | |
+ uint _minDestAmount | |
+ ) | |
+ internal | |
+ returns (uint actualReceiveAmount_) | |
+ { | |
+ Hub hub = getHub(); | |
+ Vault vault = Vault(hub.vault()); | |
+ vault.withdraw(_srcToken, _srcAmount); | |
+ | |
+ address tokenExchange = IUniswapFactory(_targetExchange).getExchange(_srcToken); | |
+ approveAsset(_srcToken, tokenExchange, _srcAmount, "takerAsset"); | |
+ actualReceiveAmount_ = IUniswapExchange(tokenExchange).tokenToEthSwapInput( | |
+ _srcAmount, | |
+ _minDestAmount, | |
+ add(block.timestamp, 1) | |
+ ); | |
+ | |
+ // Convert ETH to WETH and move to Vault | |
+ WETH(address(uint160(_nativeAsset))).deposit.value(actualReceiveAmount_)(); | |
+ getTrading().returnAssetToVault(_nativeAsset); | |
+ } | |
+ | |
+ /// @param _targetExchange Address of Uniswap factory contract | |
+ /// @param _srcToken Address of src token | |
+ /// @param _srcAmount Amount of src token supplied | |
+ /// @param _destToken Address of dest token | |
+ /// @param _minDestAmount Minimum amount of dest token to get back | |
+ /// @return actualReceiveAmount_ Actual amount of _destToken received | |
+ function swapTokenToToken( | |
+ address _targetExchange, | |
+ address _srcToken, | |
+ uint _srcAmount, | |
+ address _destToken, | |
+ uint _minDestAmount | |
+ ) | |
+ internal | |
+ returns (uint actualReceiveAmount_) | |
+ { | |
+ Hub hub = getHub(); | |
+ Vault vault = Vault(hub.vault()); | |
+ vault.withdraw(_srcToken, _srcAmount); | |
+ | |
+ address tokenExchange = IUniswapFactory(_targetExchange).getExchange(_srcToken); | |
+ approveAsset(_srcToken, tokenExchange, _srcAmount, "takerAsset"); | |
+ actualReceiveAmount_ = IUniswapExchange(tokenExchange).tokenToTokenTransferInput( | |
+ _srcAmount, | |
+ _minDestAmount, | |
+ 1, | |
+ add(block.timestamp, 1), | |
+ address(vault), | |
+ _destToken | |
+ ); | |
+ } | |
+ | |
+ function updateStateTakeOrder( | |
+ address _targetExchange, | |
+ address _makerAsset, | |
+ address _takerAsset, | |
+ uint256 _takerAssetAmount, | |
+ uint256 _actualReceiveAmount | |
+ ) | |
+ internal | |
+ { | |
+ getAccounting().addAssetToOwnedAssets(_makerAsset); | |
+ getAccounting().updateOwnedAssets(); | |
+ getTrading().orderUpdateHook( | |
+ _targetExchange, | |
+ bytes32(0), | |
+ Trading.UpdateType.take, | |
+ [address(uint160(_makerAsset)), address(uint160(_takerAsset))], | |
+ [_actualReceiveAmount, _takerAssetAmount, _takerAssetAmount] | |
+ ); | |
+ } | |
+} | |
diff --git a/src/contracts/exchanges/ZeroExV2Adapter.sol b/src/exchanges/ZeroExV2Adapter.sol | |
similarity index 67% | |
rename from src/contracts/exchanges/ZeroExV2Adapter.sol | |
rename to src/exchanges/ZeroExV2Adapter.sol | |
index 98e013af..30eeff54 100644 | |
--- a/src/contracts/exchanges/ZeroExV2Adapter.sol | |
+++ b/src/exchanges/ZeroExV2Adapter.sol | |
@@ -1,14 +1,14 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
pragma experimental ABIEncoderV2; | |
-import "ERC20.i.sol"; | |
-import "Trading.sol"; | |
-import "Hub.sol"; | |
-import "Vault.sol"; | |
-import "Accounting.sol"; | |
-import "math.sol"; | |
-import "Exchange.sol"; | |
-import "ExchangeAdapter.sol"; | |
+import "../dependencies/token/IERC20.sol"; | |
+import "../fund/trading/Trading.sol"; | |
+import "../fund/hub/Hub.sol"; | |
+import "../fund/vault/Vault.sol"; | |
+import "../fund/accounting/Accounting.sol"; | |
+import "../dependencies/DSMath.sol"; | |
+import "./interfaces/IZeroExV2.sol"; | |
+import "./ExchangeAdapter.sol"; | |
/// @title ZeroExV2Adapter Contract | |
/// @author Melonport AG <team@melonport.com> | |
@@ -22,18 +22,18 @@ contract ZeroExV2Adapter is DSMath, ExchangeAdapter { | |
/// @notice Make order by pre-approving signatures | |
function makeOrder( | |
address targetExchange, | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
+ address[8] memory orderAddresses, | |
+ uint[8] memory orderValues, | |
+ bytes[4] memory orderData, | |
bytes32 identifier, | |
- bytes makerAssetData, | |
- bytes takerAssetData, | |
- bytes signature | |
+ bytes memory signature | |
) public onlyManager notShutDown { | |
ensureCanMakeOrder(orderAddresses[2]); | |
Hub hub = getHub(); | |
- LibOrder.Order memory order = constructOrderStruct(orderAddresses, orderValues, makerAssetData, takerAssetData); | |
- address makerAsset = getAssetAddress(makerAssetData); | |
- address takerAsset = getAssetAddress(takerAssetData); | |
+ | |
+ IZeroExV2.Order memory order = constructOrderStruct(orderAddresses, orderValues, orderData); | |
+ address makerAsset = getAssetAddress(orderData[0]); | |
+ address takerAsset = getAssetAddress(orderData[1]); | |
require( | |
makerAsset == orderAddresses[2], | |
"Maker asset data does not match order address in array" | |
@@ -47,12 +47,12 @@ contract ZeroExV2Adapter is DSMath, ExchangeAdapter { | |
getTrading().updateAndGetQuantityBeingTraded(makerAsset); | |
ensureNotInOpenMakeOrder(makerAsset); | |
- approveMakerAsset(targetExchange, makerAsset, makerAssetData, order.makerAssetAmount); | |
- LibOrder.OrderInfo memory orderInfo = Exchange(targetExchange).getOrderInfo(order); | |
- Exchange(targetExchange).preSign(orderInfo.orderHash, address(this), signature); | |
+ approveMakerAsset(targetExchange, makerAsset, orderData[0], order.makerAssetAmount); | |
+ IZeroExV2.OrderInfo memory orderInfo = IZeroExV2(targetExchange).getOrderInfo(order); | |
+ IZeroExV2(targetExchange).preSign(orderInfo.orderHash, address(this), signature); | |
require( | |
- Exchange(targetExchange).isValidSignature( | |
+ IZeroExV2(targetExchange).isValidSignature( | |
orderInfo.orderHash, | |
address(this), | |
signature | |
@@ -64,17 +64,17 @@ contract ZeroExV2Adapter is DSMath, ExchangeAdapter { | |
targetExchange, | |
orderInfo.orderHash, | |
Trading.UpdateType.make, | |
- [address(makerAsset), address(takerAsset)], | |
+ [address(uint160(makerAsset)), address(uint160(takerAsset))], | |
[order.makerAssetAmount, order.takerAssetAmount, uint(0)] | |
); | |
- Trading(address(this)).addOpenMakeOrder( | |
- targetExchange, | |
+ getTrading().addOpenMakeOrder( | |
+ targetExchange, | |
makerAsset, | |
takerAsset, | |
- uint256(orderInfo.orderHash), | |
+ uint256(orderInfo.orderHash), | |
order.expirationTimeSeconds | |
); | |
- Trading(address(this)).addZeroExOrderData(orderInfo.orderHash, order); | |
+ getTrading().addZeroExV2OrderData(orderInfo.orderHash, order); | |
} | |
// Responsibilities of takeOrder are: | |
@@ -105,25 +105,26 @@ contract ZeroExV2Adapter is DSMath, ExchangeAdapter { | |
/// @param orderValues [5] Salt/nonce | |
/// @param orderValues [6] Fill amount: amount of taker token to be traded | |
/// @param orderValues [7] Dexy signature mode | |
+ /// @param orderData [0] Encoded data specific to maker asset | |
+ /// @param orderData [1] Encoded data specific to taker asset | |
+ /// @param orderData [2] Encoded data specific to maker asset fee | |
+ /// @param orderData [3] Encoded data specific to taker asset fee | |
/// @param identifier Order identifier | |
- /// @param makerAssetData Encoded data specific to makerAsset. | |
- /// @param takerAssetData Encoded data specific to takerAsset. | |
/// @param signature Signature of the order. | |
function takeOrder( | |
address targetExchange, | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
+ address[8] memory orderAddresses, | |
+ uint[8] memory orderValues, | |
+ bytes[4] memory orderData, | |
bytes32 identifier, | |
- bytes makerAssetData, | |
- bytes takerAssetData, | |
- bytes signature | |
+ bytes memory signature | |
) public onlyManager notShutDown { | |
Hub hub = getHub(); | |
- LibOrder.Order memory order = constructOrderStruct(orderAddresses, orderValues, makerAssetData, takerAssetData); | |
+ IZeroExV2.Order memory order = constructOrderStruct(orderAddresses, orderValues, orderData); | |
uint fillTakerQuantity = orderValues[6]; | |
- address makerAsset = getAssetAddress(makerAssetData); | |
- address takerAsset = getAssetAddress(takerAssetData); | |
+ address makerAsset = getAssetAddress(orderData[0]); | |
+ address takerAsset = getAssetAddress(orderData[1]); | |
require( | |
makerAsset == orderAddresses[2], | |
"Maker asset data does not match order address in array" | |
@@ -132,8 +133,8 @@ contract ZeroExV2Adapter is DSMath, ExchangeAdapter { | |
takerAsset == orderAddresses[3], | |
"Taker asset data does not match order address in array" | |
); | |
- approveTakerAsset(targetExchange, takerAsset, takerAssetData, fillTakerQuantity); | |
- LibOrder.OrderInfo memory orderInfo = Exchange(targetExchange).getOrderInfo(order); | |
+ approveTakerAsset(targetExchange, takerAsset, orderData[1], fillTakerQuantity); | |
+ IZeroExV2.OrderInfo memory orderInfo = IZeroExV2(targetExchange).getOrderInfo(order); | |
uint takerAssetFilledAmount = executeFill(targetExchange, order, fillTakerQuantity, signature); | |
require( | |
@@ -147,7 +148,7 @@ contract ZeroExV2Adapter is DSMath, ExchangeAdapter { | |
targetExchange, | |
orderInfo.orderHash, | |
Trading.UpdateType.take, | |
- [makerAsset, takerAsset], | |
+ [address(uint160(makerAsset)), address(uint160(takerAsset))], | |
[order.makerAssetAmount, order.takerAssetAmount, fillTakerQuantity] | |
); | |
} | |
@@ -155,19 +156,18 @@ contract ZeroExV2Adapter is DSMath, ExchangeAdapter { | |
/// @notice Cancel the 0x make order | |
function cancelOrder( | |
address targetExchange, | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
+ address[8] memory orderAddresses, | |
+ uint[8] memory orderValues, | |
+ bytes[4] memory orderData, | |
bytes32 identifier, | |
- bytes makerAssetData, | |
- bytes takerAssetData, | |
- bytes signature | |
+ bytes memory signature | |
) public onlyCancelPermitted(targetExchange, orderAddresses[2]) { | |
Hub hub = getHub(); | |
- LibOrder.Order memory order = Trading(address(this)).getZeroExOrderDetails(identifier); | |
+ IZeroExV2.Order memory order = getTrading().getZeroExV2OrderDetails(identifier); | |
address makerAsset = getAssetAddress(order.makerAssetData); | |
if (order.expirationTimeSeconds > block.timestamp) { | |
- Exchange(targetExchange).cancelOrder(order); | |
+ IZeroExV2(targetExchange).cancelOrder(order); | |
} | |
// Set the approval back to 0 | |
@@ -185,10 +185,10 @@ contract ZeroExV2Adapter is DSMath, ExchangeAdapter { | |
} | |
/// @dev Get order details | |
- function getOrder(address targetExchange, uint id, address makerAsset) | |
+ function getOrder(address targetExchange, uint256 id, address makerAsset) | |
public | |
view | |
- returns (address, address, uint, uint) | |
+ returns (address, address, uint256, uint256) | |
{ | |
uint orderId; | |
uint orderIndex; | |
@@ -197,9 +197,9 @@ contract ZeroExV2Adapter is DSMath, ExchangeAdapter { | |
uint takerQuantity; | |
(orderId, , orderIndex) = Trading(msg.sender).getOpenOrderInfo(targetExchange, makerAsset); | |
(, takerAsset, makerQuantity, takerQuantity) = Trading(msg.sender).getOrderDetails(orderIndex); | |
- uint takerAssetFilledAmount = Exchange(targetExchange).filled(bytes32(orderId)); | |
+ uint takerAssetFilledAmount = IZeroExV2(targetExchange).filled(bytes32(orderId)); | |
uint makerAssetFilledAmount = mul(takerAssetFilledAmount, makerQuantity) / takerQuantity; | |
- if (Exchange(targetExchange).cancelled(bytes32(orderId)) || sub(takerQuantity, takerAssetFilledAmount) == 0) { | |
+ if (IZeroExV2(targetExchange).cancelled(bytes32(orderId)) || sub(takerQuantity, takerAssetFilledAmount) == 0) { | |
return (makerAsset, takerAsset, 0, 0); | |
} | |
return ( | |
@@ -214,7 +214,7 @@ contract ZeroExV2Adapter is DSMath, ExchangeAdapter { | |
/// @notice needed to avoid stack too deep error | |
- function approveTakerAsset(address targetExchange, address takerAsset, bytes takerAssetData, uint fillTakerQuantity) | |
+ function approveTakerAsset(address targetExchange, address takerAsset, bytes memory takerAssetData, uint256 fillTakerQuantity) | |
internal | |
{ | |
Hub hub = getHub(); | |
@@ -222,13 +222,13 @@ contract ZeroExV2Adapter is DSMath, ExchangeAdapter { | |
vault.withdraw(takerAsset, fillTakerQuantity); | |
address assetProxy = getAssetProxy(targetExchange, takerAssetData); | |
require( | |
- ERC20(takerAsset).approve(assetProxy, fillTakerQuantity), | |
+ IERC20(takerAsset).approve(assetProxy, fillTakerQuantity), | |
"Taker asset could not be approved" | |
); | |
} | |
/// @notice needed to avoid stack too deep error | |
- function approveMakerAsset(address targetExchange, address makerAsset, bytes makerAssetData, uint makerQuantity) | |
+ function approveMakerAsset(address targetExchange, address makerAsset, bytes memory makerAssetData, uint256 makerQuantity) | |
internal | |
{ | |
Hub hub = getHub(); | |
@@ -236,7 +236,7 @@ contract ZeroExV2Adapter is DSMath, ExchangeAdapter { | |
vault.withdraw(makerAsset, makerQuantity); | |
address assetProxy = getAssetProxy(targetExchange, makerAssetData); | |
require( | |
- ERC20(makerAsset).approve(assetProxy, makerQuantity), | |
+ IERC20(makerAsset).approve(assetProxy, makerQuantity), | |
"Maker asset could not be approved" | |
); | |
} | |
@@ -244,36 +244,36 @@ contract ZeroExV2Adapter is DSMath, ExchangeAdapter { | |
/// @dev needed to avoid stack too deep error | |
function executeFill( | |
address targetExchange, | |
- LibOrder.Order memory order, | |
+ IZeroExV2.Order memory order, | |
uint256 takerAssetFillAmount, | |
- bytes signature | |
+ bytes memory signature | |
) | |
internal | |
- returns (uint) | |
+ returns (uint256) | |
{ | |
uint takerFee = order.takerFee; | |
if (takerFee > 0) { | |
- bytes memory assetData = Exchange(targetExchange).ZRX_ASSET_DATA(); | |
+ bytes memory assetData = IZeroExV2(targetExchange).ZRX_ASSET_DATA(); | |
address zrxProxy = getAssetProxy(targetExchange, assetData); | |
Hub hub = getHub(); | |
Vault vault = Vault(hub.vault()); | |
vault.withdraw(getAssetAddress(assetData), takerFee); | |
require( | |
- ERC20(getAssetAddress(assetData)).approve(zrxProxy, takerFee), | |
+ IERC20(getAssetAddress(assetData)).approve(zrxProxy, takerFee), | |
"Fee asset could not be approved" | |
); | |
} | |
address makerAsset = getAssetAddress(order.makerAssetData); | |
- uint preMakerAssetBalance = ERC20(makerAsset).balanceOf(this); | |
+ uint preMakerAssetBalance = IERC20(makerAsset).balanceOf(address(this)); | |
- LibFillResults.FillResults memory fillResults = Exchange(targetExchange).fillOrder( | |
+ IZeroExV2.FillResults memory fillResults = IZeroExV2(targetExchange).fillOrder( | |
order, | |
takerAssetFillAmount, | |
signature | |
); | |
- uint postMakerAssetBalance = ERC20(makerAsset).balanceOf(this); | |
+ uint256 postMakerAssetBalance = IERC20(makerAsset).balanceOf(address(this)); | |
require( | |
postMakerAssetBalance == add(preMakerAssetBalance, fillResults.makerAssetFilledAmount), | |
"Maker asset balance different than expected" | |
@@ -285,16 +285,15 @@ contract ZeroExV2Adapter is DSMath, ExchangeAdapter { | |
// VIEW METHODS | |
function constructOrderStruct( | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
- bytes makerAssetData, | |
- bytes takerAssetData | |
+ address[8] memory orderAddresses, | |
+ uint[8] memory orderValues, | |
+ bytes[4] memory orderData | |
) | |
internal | |
view | |
- returns (LibOrder.Order memory order) | |
+ returns (IZeroExV2.Order memory order) | |
{ | |
- order = LibOrder.Order({ | |
+ order = IZeroExV2.Order({ | |
makerAddress: orderAddresses[0], | |
takerAddress: orderAddresses[1], | |
feeRecipientAddress: orderAddresses[4], | |
@@ -305,12 +304,12 @@ contract ZeroExV2Adapter is DSMath, ExchangeAdapter { | |
takerFee: orderValues[3], | |
expirationTimeSeconds: orderValues[4], | |
salt: orderValues[5], | |
- makerAssetData: makerAssetData, | |
- takerAssetData: takerAssetData | |
+ makerAssetData: orderData[0], | |
+ takerAssetData: orderData[1] | |
}); | |
} | |
- function getAssetProxy(address targetExchange, bytes assetData) | |
+ function getAssetProxy(address targetExchange, bytes memory assetData) | |
internal | |
view | |
returns (address assetProxy) | |
@@ -322,10 +321,10 @@ contract ZeroExV2Adapter is DSMath, ExchangeAdapter { | |
0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 | |
) | |
} | |
- assetProxy = Exchange(targetExchange).getAssetProxy(assetProxyId); | |
+ assetProxy = IZeroExV2(targetExchange).getAssetProxy(assetProxyId); | |
} | |
- function getAssetAddress(bytes assetData) | |
+ function getAssetAddress(bytes memory assetData) | |
internal | |
view | |
returns (address assetAddress) | |
diff --git a/src/exchanges/ZeroExV3Adapter.sol b/src/exchanges/ZeroExV3Adapter.sol | |
new file mode 100644 | |
index 00000000..d512f166 | |
--- /dev/null | |
+++ b/src/exchanges/ZeroExV3Adapter.sol | |
@@ -0,0 +1,466 @@ | |
+pragma solidity 0.5.15; | |
+pragma experimental ABIEncoderV2; | |
+ | |
+import "../dependencies/token/IERC20.sol"; | |
+import "../fund/trading/Trading.sol"; | |
+import "../fund/hub/Hub.sol"; | |
+import "../fund/vault/Vault.sol"; | |
+import "../fund/accounting/Accounting.sol"; | |
+import "../dependencies/DSMath.sol"; | |
+import "./interfaces/IZeroExV3.sol"; | |
+import "./ExchangeAdapter.sol"; | |
+ | |
+/// @title ZeroExV3Adapter Contract | |
+/// @author Melonport AG <team@melonport.com> | |
+/// @notice Adapter to 0xV3 Exchange Contract | |
+contract ZeroExV3Adapter is DSMath, ExchangeAdapter { | |
+ | |
+ /// @param _orderAddresses [2] Order maker asset | |
+ /// @param _orderAddresses [3] Order taker asset | |
+ /// @param _orderAddresses [6] Order maker fee asset | |
+ /// @param _orderAddresses [7] Order taker fee asset | |
+ /// @param _orderValues [2] Order maker fee amount | |
+ /// @param _orderValues [3] Order taker fee amount | |
+ modifier orderAddressesMatchOrderData( | |
+ address[8] memory _orderAddresses, | |
+ uint[8] memory _orderValues, | |
+ bytes[4] memory _orderData | |
+ ) | |
+ { | |
+ require( | |
+ getAssetAddress(_orderData[0]) == _orderAddresses[2], | |
+ "Maker asset data does not match order address in array" | |
+ ); | |
+ require( | |
+ getAssetAddress(_orderData[1]) == _orderAddresses[3], | |
+ "Taker asset data does not match order address in array" | |
+ ); | |
+ if (_orderValues[2] > 0) { | |
+ require( | |
+ getAssetAddress(_orderData[2]) == _orderAddresses[6], | |
+ "Maker fee asset data does not match order address in array" | |
+ ); | |
+ } | |
+ if (_orderValues[3] > 0) { | |
+ require( | |
+ getAssetAddress(_orderData[3]) == _orderAddresses[7], | |
+ "Taker fee asset data does not match order address in array" | |
+ ); | |
+ } | |
+ _; | |
+ } | |
+ | |
+ // METHODS | |
+ | |
+ // PUBLIC METHODS | |
+ | |
+ /// @notice Make order by pre-approving signatures | |
+ /// @param _targetExchange Address of the exchange | |
+ /// @param _orderAddresses [2] Maker asset (Dest token) | |
+ /// @param _orderAddresses [3] Taker asset (Src token) | |
+ /// @param _orderData [0] Encoded data specific to maker asset | |
+ /// @param _orderData [1] Encoded data specific to taker asset | |
+ /// @param _signature _signature of the order. | |
+ function makeOrder( | |
+ address _targetExchange, | |
+ address[8] memory _orderAddresses, | |
+ uint[8] memory _orderValues, | |
+ bytes[4] memory _orderData, | |
+ bytes32 _identifier, | |
+ bytes memory _signature | |
+ ) | |
+ public | |
+ onlyManager | |
+ notShutDown | |
+ orderAddressesMatchOrderData(_orderAddresses, _orderValues, _orderData) | |
+ { | |
+ ensureCanMakeOrder(_orderAddresses[2]); | |
+ Hub hub = getHub(); | |
+ | |
+ IZeroExV3.Order memory order = constructOrderStruct(_orderAddresses, _orderValues, _orderData); | |
+ address makerAsset = getAssetAddress(_orderData[0]); | |
+ address takerAsset = getAssetAddress(_orderData[1]); | |
+ | |
+ // Order parameter checks | |
+ getTrading().updateAndGetQuantityBeingTraded(makerAsset); | |
+ ensureNotInOpenMakeOrder(makerAsset); | |
+ | |
+ approveAssetsMakeOrder(_targetExchange, order); | |
+ | |
+ IZeroExV3.OrderInfo memory orderInfo = IZeroExV3(_targetExchange).getOrderInfo(order); | |
+ IZeroExV3(_targetExchange).preSign(orderInfo.orderHash); | |
+ | |
+ require( | |
+ IZeroExV3(_targetExchange).isValidOrderSignature(order, _signature), | |
+ "INVALID_ORDER_SIGNATURE" | |
+ ); | |
+ | |
+ updateStateMakeOrder(_targetExchange, order); | |
+ } | |
+ | |
+ /// @notice Takes an active order on the selected exchange | |
+ /// @dev These orders are expected to settle immediately | |
+ /// @param _targetExchange Address of the exchange | |
+ /// @param _orderAddresses [2] Order maker asset | |
+ /// @param _orderAddresses [3] Order taker asset | |
+ /// @param _orderValues [6] Fill amount: amount of taker token to be traded | |
+ /// @param _signature _signature of the order. | |
+ function takeOrder( | |
+ address _targetExchange, | |
+ address[8] memory _orderAddresses, | |
+ uint[8] memory _orderValues, | |
+ bytes[4] memory _orderData, | |
+ bytes32 _identifier, | |
+ bytes memory _signature | |
+ ) | |
+ public | |
+ onlyManager | |
+ notShutDown | |
+ orderAddressesMatchOrderData(_orderAddresses, _orderValues, _orderData) | |
+ { | |
+ IZeroExV3.Order memory order = constructOrderStruct(_orderAddresses, _orderValues, _orderData); | |
+ require(IZeroExV3(_targetExchange).isValidOrderSignature(order, _signature), "Order _signature is invalid"); | |
+ | |
+ uint256 fillTakerQuantity = _orderValues[6]; | |
+ | |
+ approveAssetsTakeOrder(_targetExchange, order); | |
+ | |
+ uint256 takerAssetFilledAmount = executeFill(_targetExchange, order, fillTakerQuantity, _signature); | |
+ require( | |
+ takerAssetFilledAmount == fillTakerQuantity, | |
+ "Filled amount does not match desired fill amount" | |
+ ); | |
+ | |
+ updateStateTakeOrder(_targetExchange, order, fillTakerQuantity); | |
+ } | |
+ | |
+ /// @notice Cancel the 0x make order | |
+ /// @param _targetExchange Address of the exchange | |
+ /// @param _orderAddresses [2] Order maker asset | |
+ /// @param _identifier Order _identifier | |
+ function cancelOrder( | |
+ address _targetExchange, | |
+ address[8] memory _orderAddresses, | |
+ uint[8] memory _orderValues, | |
+ bytes[4] memory _orderData, | |
+ bytes32 _identifier, | |
+ bytes memory _signature | |
+ ) | |
+ public | |
+ onlyCancelPermitted(_targetExchange, _orderAddresses[2]) | |
+ { | |
+ Hub hub = getHub(); | |
+ IZeroExV3.Order memory order = getTrading().getZeroExV3OrderDetails(_identifier); | |
+ address makerAsset = getAssetAddress(order.makerAssetData); | |
+ address makerFeeAsset = getAssetAddress(order.makerFeeAssetData); | |
+ | |
+ if (order.expirationTimeSeconds > block.timestamp) { | |
+ IZeroExV3(_targetExchange).cancelOrder(order); | |
+ } | |
+ | |
+ // Revoke asset approvals and return assets to vault | |
+ revokeApproveAsset( | |
+ makerAsset, | |
+ getAssetProxy(_targetExchange, order.takerAssetData), | |
+ order.makerAssetAmount, | |
+ "makerAsset" | |
+ ); | |
+ getTrading().returnAssetToVault(makerAsset); | |
+ | |
+ if (order.makerFee > 0) { | |
+ revokeApproveAsset( | |
+ makerFeeAsset, | |
+ getAssetProxy(_targetExchange, order.makerFeeAssetData), | |
+ order.makerFee, | |
+ "makerFeeAsset" | |
+ ); | |
+ if (makerFeeAsset != makerAsset) getTrading().returnAssetToVault(makerFeeAsset); | |
+ } | |
+ | |
+ updateStateCancelOrder(_targetExchange, order); | |
+ } | |
+ | |
+ /// @dev Get order details | |
+ function getOrder(address _targetExchange, uint256 _id, address _makerAsset) | |
+ public | |
+ view | |
+ returns (address, address, uint256, uint256) | |
+ { | |
+ uint orderId; | |
+ uint orderIndex; | |
+ address takerAsset; | |
+ uint makerQuantity; | |
+ uint takerQuantity; | |
+ (orderId, , orderIndex) = Trading(msg.sender).getOpenOrderInfo(_targetExchange, _makerAsset); | |
+ (, takerAsset, makerQuantity, takerQuantity) = Trading(msg.sender).getOrderDetails(orderIndex); | |
+ uint takerAssetFilledAmount = IZeroExV3(_targetExchange).filled(bytes32(orderId)); | |
+ uint makerAssetFilledAmount = mul(takerAssetFilledAmount, makerQuantity) / takerQuantity; | |
+ if (IZeroExV3(_targetExchange).cancelled(bytes32(orderId)) || sub(takerQuantity, takerAssetFilledAmount) == 0) { | |
+ return (_makerAsset, takerAsset, 0, 0); | |
+ } | |
+ return ( | |
+ _makerAsset, | |
+ takerAsset, | |
+ sub(makerQuantity, makerAssetFilledAmount), | |
+ sub(takerQuantity, takerAssetFilledAmount) | |
+ ); | |
+ } | |
+ | |
+ // INTERNAL METHODS | |
+ function approveAsset( | |
+ address _asset, | |
+ address _target, | |
+ uint256 _amount, | |
+ string memory _assetType | |
+ ) | |
+ internal | |
+ { | |
+ Hub hub = getHub(); | |
+ Vault vault = Vault(hub.vault()); | |
+ | |
+ require( | |
+ IERC20(_asset).balanceOf(address(vault)) >= _amount, | |
+ string(abi.encodePacked("Insufficient balance: ", _assetType)) | |
+ ); | |
+ | |
+ vault.withdraw(_asset, _amount); | |
+ uint256 allowance = IERC20(_asset).allowance(address(this), _target); | |
+ require( | |
+ IERC20(_asset).approve(_target, add(allowance, _amount)), | |
+ string(abi.encodePacked("Approval failed: ", _assetType)) | |
+ ); | |
+ } | |
+ | |
+ // @notice Approves makerAsset, makerFeeAsset | |
+ function approveAssetsMakeOrder(address _targetExchange, IZeroExV3.Order memory _order) | |
+ internal | |
+ { | |
+ approveAsset( | |
+ getAssetAddress(_order.makerAssetData), | |
+ getAssetProxy(_targetExchange, _order.makerAssetData), | |
+ _order.makerAssetAmount, | |
+ "makerAsset" | |
+ ); | |
+ if (_order.makerFee > 0) { | |
+ approveAsset( | |
+ getAssetAddress(_order.makerFeeAssetData), | |
+ getAssetProxy(_targetExchange, _order.makerFeeAssetData), | |
+ _order.makerFee, | |
+ "makerFeeAsset" | |
+ ); | |
+ } | |
+ } | |
+ | |
+ // @notice Approves takerAsset, takerFeeAsset, protocolFee | |
+ function approveAssetsTakeOrder(address _targetExchange, IZeroExV3.Order memory _order) | |
+ internal | |
+ { | |
+ approveProtocolFeeAsset(_targetExchange); | |
+ approveAsset( | |
+ getAssetAddress(_order.takerAssetData), | |
+ getAssetProxy(_targetExchange, _order.takerAssetData), | |
+ _order.takerAssetAmount, | |
+ "takerAsset" | |
+ ); | |
+ if (_order.takerFee > 0) { | |
+ approveAsset( | |
+ getAssetAddress(_order.takerFeeAssetData), | |
+ getAssetProxy(_targetExchange, _order.takerFeeAssetData), | |
+ _order.takerFee, | |
+ "takerFeeAsset" | |
+ ); | |
+ } | |
+ } | |
+ | |
+ function approveProtocolFeeAsset(address _targetExchange) internal { | |
+ address protocolFeeCollector = IZeroExV3(_targetExchange).protocolFeeCollector(); | |
+ uint256 protocolFeeAmount = calcProtocolFeeAmount(_targetExchange); | |
+ if (protocolFeeCollector == address(0) || protocolFeeAmount == 0) return; | |
+ | |
+ Hub hub = getHub(); | |
+ address nativeAsset = Accounting(hub.accounting()).NATIVE_ASSET(); | |
+ | |
+ approveAsset(nativeAsset, protocolFeeCollector, protocolFeeAmount, "protocolFee"); | |
+ } | |
+ | |
+ function executeFill( | |
+ address _targetExchange, | |
+ IZeroExV3.Order memory _order, | |
+ uint256 _takerAssetFillAmount, | |
+ bytes memory _signature | |
+ ) | |
+ internal | |
+ returns (uint256) | |
+ { | |
+ Hub hub = getHub(); | |
+ address makerAsset = getAssetAddress(_order.makerAssetData); | |
+ uint preMakerAssetBalance = IERC20(makerAsset).balanceOf(address(this)); | |
+ | |
+ IZeroExV3.FillResults memory fillResults = IZeroExV3(_targetExchange).fillOrder( | |
+ _order, | |
+ _takerAssetFillAmount, | |
+ _signature | |
+ ); | |
+ | |
+ uint256 postMakerAssetBalance = IERC20(makerAsset).balanceOf(address(this)); | |
+ | |
+ // Account for case where makerAsset, takerFee, protocolFee are the same | |
+ uint256 makerAssetFeesTotal; | |
+ if ( | |
+ makerAsset == Accounting(hub.accounting()).NATIVE_ASSET() && | |
+ IZeroExV3(_targetExchange).protocolFeeCollector() != address(0) | |
+ ) | |
+ { | |
+ makerAssetFeesTotal = calcProtocolFeeAmount(_targetExchange); | |
+ } | |
+ if (makerAsset == getAssetAddress(_order.takerFeeAssetData)) { | |
+ makerAssetFeesTotal = add(makerAssetFeesTotal, _order.takerFee); | |
+ } | |
+ | |
+ require( | |
+ postMakerAssetBalance == sub( | |
+ add(preMakerAssetBalance, fillResults.makerAssetFilledAmount), | |
+ makerAssetFeesTotal | |
+ ), | |
+ "Maker asset balance different than expected" | |
+ ); | |
+ | |
+ return fillResults.takerAssetFilledAmount; | |
+ } | |
+ | |
+ function revokeApproveAsset( | |
+ address _asset, | |
+ address _target, | |
+ uint256 _amount, | |
+ string memory _assetType | |
+ ) | |
+ internal | |
+ { | |
+ uint256 allowance = IERC20(_asset).allowance(address(this), _target); | |
+ require( | |
+ IERC20(_asset).approve(_target, sub(allowance, _amount)), | |
+ string(abi.encodePacked("Revoke approval failed: ", _assetType)) | |
+ ); | |
+ } | |
+ | |
+ function updateStateCancelOrder(address _targetExchange, IZeroExV3.Order memory _order) | |
+ internal | |
+ { | |
+ address makerAsset = getAssetAddress(_order.makerAssetData); | |
+ | |
+ getTrading().removeOpenMakeOrder(_targetExchange, makerAsset); | |
+ getAccounting().updateOwnedAssets(); | |
+ getTrading().orderUpdateHook( | |
+ _targetExchange, | |
+ IZeroExV3(_targetExchange).getOrderInfo(_order).orderHash, | |
+ Trading.UpdateType.cancel, | |
+ [address(0), address(0)], | |
+ [uint(0), uint(0), uint(0)] | |
+ ); | |
+ } | |
+ | |
+ function updateStateMakeOrder(address _targetExchange, IZeroExV3.Order memory _order) | |
+ internal | |
+ { | |
+ address makerAsset = getAssetAddress(_order.makerAssetData); | |
+ address takerAsset = getAssetAddress(_order.takerAssetData); | |
+ IZeroExV3.OrderInfo memory orderInfo = IZeroExV3(_targetExchange).getOrderInfo(_order); | |
+ | |
+ getAccounting().addAssetToOwnedAssets(takerAsset); | |
+ getTrading().orderUpdateHook( | |
+ _targetExchange, | |
+ orderInfo.orderHash, | |
+ Trading.UpdateType.make, | |
+ [address(uint160(makerAsset)), address(uint160(takerAsset))], | |
+ [_order.makerAssetAmount, _order.takerAssetAmount, uint(0)] | |
+ ); | |
+ getTrading().addOpenMakeOrder( | |
+ _targetExchange, | |
+ makerAsset, | |
+ takerAsset, | |
+ uint256(orderInfo.orderHash), | |
+ _order.expirationTimeSeconds | |
+ ); | |
+ getTrading().addZeroExV3OrderData(orderInfo.orderHash, _order); | |
+ } | |
+ | |
+ // @dev avoids stack too deep error | |
+ function updateStateTakeOrder( | |
+ address _targetExchange, | |
+ IZeroExV3.Order memory _order, | |
+ uint256 _fillTakerQuantity | |
+ ) | |
+ internal | |
+ { | |
+ address makerAsset = getAssetAddress(_order.makerAssetData); | |
+ address takerAsset = getAssetAddress(_order.takerAssetData); | |
+ | |
+ getAccounting().addAssetToOwnedAssets(makerAsset); | |
+ getAccounting().updateOwnedAssets(); | |
+ getTrading().returnAssetToVault(makerAsset); | |
+ getTrading().orderUpdateHook( | |
+ _targetExchange, | |
+ IZeroExV3(_targetExchange).getOrderInfo(_order).orderHash, | |
+ Trading.UpdateType.take, | |
+ [address(uint160(makerAsset)), address(uint160(takerAsset))], | |
+ [_order.makerAssetAmount, _order.takerAssetAmount, _fillTakerQuantity] | |
+ ); | |
+ } | |
+ | |
+ // VIEW METHODS | |
+ function calcProtocolFeeAmount(address _targetExchange) internal view returns (uint256) { | |
+ return mul(IZeroExV3(_targetExchange).protocolFeeMultiplier(), tx.gasprice); | |
+ } | |
+ | |
+ function constructOrderStruct( | |
+ address[8] memory _orderAddresses, | |
+ uint[8] memory _orderValues, | |
+ bytes[4] memory _orderData | |
+ ) | |
+ internal | |
+ view | |
+ returns (IZeroExV3.Order memory order_) | |
+ { | |
+ order_ = IZeroExV3.Order({ | |
+ makerAddress: _orderAddresses[0], | |
+ takerAddress: _orderAddresses[1], | |
+ feeRecipientAddress: _orderAddresses[4], | |
+ senderAddress: _orderAddresses[5], | |
+ makerAssetAmount: _orderValues[0], | |
+ takerAssetAmount: _orderValues[1], | |
+ makerFee: _orderValues[2], | |
+ takerFee: _orderValues[3], | |
+ expirationTimeSeconds: _orderValues[4], | |
+ salt: _orderValues[5], | |
+ makerAssetData: _orderData[0], | |
+ takerAssetData: _orderData[1], | |
+ makerFeeAssetData: _orderData[2], | |
+ takerFeeAssetData: _orderData[3] | |
+ }); | |
+ } | |
+ | |
+ function getAssetProxy(address _targetExchange, bytes memory _assetData) | |
+ internal | |
+ view | |
+ returns (address assetProxy_) | |
+ { | |
+ bytes4 assetProxyId; | |
+ assembly { | |
+ assetProxyId := and(mload( | |
+ add(_assetData, 32)), | |
+ 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 | |
+ ) | |
+ } | |
+ assetProxy_ = IZeroExV3(_targetExchange).getAssetProxy(assetProxyId); | |
+ } | |
+ | |
+ function getAssetAddress(bytes memory _assetData) | |
+ internal | |
+ view | |
+ returns (address assetAddress_) | |
+ { | |
+ assembly { | |
+ assetAddress_ := mload(add(_assetData, 36)) | |
+ } | |
+ } | |
+} | |
diff --git a/src/exchanges/interfaces/IEthfinex.sol b/src/exchanges/interfaces/IEthfinex.sol | |
new file mode 100644 | |
index 00000000..7c26b57d | |
--- /dev/null | |
+++ b/src/exchanges/interfaces/IEthfinex.sol | |
@@ -0,0 +1,20 @@ | |
+pragma solidity 0.5.15; | |
+// pragma experimental ABIEncoderV2; | |
+ | |
+/// @dev Minimal interface for our interactions with EthFinex WrapperLock | |
+interface IWrapperLock { | |
+ function balanceOf(address) external view returns (uint256); | |
+ function withdraw(uint256, uint8, bytes32, bytes32, uint256) external returns (bool); | |
+ function deposit(uint256, uint256) external returns (bool); | |
+} | |
+ | |
+/// @dev Minimal interface for our interactions with EthFinex WrapperLockEth | |
+interface IWrapperLockEth { | |
+ function balanceOf(address) external view returns (uint256); | |
+ function deposit(uint256, uint256) external payable returns (bool); | |
+} | |
+ | |
+/// @dev Minimal interface for our interactions with EthFinex WrapperRegistryEFX | |
+interface IWrapperRegistryEFX { | |
+ function token2WrapperLookup(address) external view returns (address); | |
+} | |
diff --git a/src/exchanges/interfaces/IKyberNetworkProxy.sol b/src/exchanges/interfaces/IKyberNetworkProxy.sol | |
new file mode 100644 | |
index 00000000..8c0750a4 | |
--- /dev/null | |
+++ b/src/exchanges/interfaces/IKyberNetworkProxy.sol | |
@@ -0,0 +1,18 @@ | |
+pragma solidity 0.5.15; | |
+ | |
+ | |
+/// @title Kyber Network interface | |
+interface IKyberNetworkProxy { | |
+ function maxGasPrice() external view returns(uint256); | |
+ function getUserCapInWei(address) external view returns(uint256); | |
+ function getUserCapInTokenWei(address, address) external view returns(uint256); | |
+ function enabled() external view returns(bool); | |
+ function info(bytes32) external view returns(uint256); | |
+ function swapEtherToToken(address, uint256) external payable returns(uint256); | |
+ function swapTokenToEther(address, uint256, uint256) external returns(uint256); | |
+ function swapTokenToToken(address, uint256, address, uint256) external returns(uint); | |
+ function getExpectedRate(address, address, uint256) external view returns (uint256, uint256); | |
+ function tradeWithHint( | |
+ address, uint256, address, address, uint256, uint256, address, bytes calldata | |
+ ) external payable returns(uint256); | |
+} | |
diff --git a/src/exchanges/interfaces/IOasisDex.sol b/src/exchanges/interfaces/IOasisDex.sol | |
new file mode 100644 | |
index 00000000..fca4b960 | |
--- /dev/null | |
+++ b/src/exchanges/interfaces/IOasisDex.sol | |
@@ -0,0 +1,14 @@ | |
+pragma solidity 0.5.15; | |
+ | |
+/// @dev Minimal interface for our interactions with OasisDex MatchingMarket | |
+interface IOasisDex { | |
+ function getFirstUnsortedOffer() external view returns(uint256); | |
+ function getNextUnsortedOffer(uint256) external view returns(uint256); | |
+ function getBestOffer(address, address) external view returns(uint256); | |
+ function getOffer(uint256) external view returns (uint256, address, uint256, address); | |
+ function getWorseOffer(uint256) external view returns(uint256); | |
+ function isActive(uint256) external view returns (bool); | |
+ function buy(uint256, uint256) external returns (bool); | |
+ function cancel(uint256) external returns (bool); | |
+ function offer(uint256, address, uint256, address) external returns (uint256); | |
+} | |
diff --git a/src/exchanges/interfaces/IUniswapExchange.sol b/src/exchanges/interfaces/IUniswapExchange.sol | |
new file mode 100644 | |
index 00000000..2fddff33 | |
--- /dev/null | |
+++ b/src/exchanges/interfaces/IUniswapExchange.sol | |
@@ -0,0 +1,41 @@ | |
+pragma solidity 0.5.15; | |
+ | |
+/// @dev Minimal interface for our interactions with UniswapExchange | |
+interface IUniswapExchange { | |
+ // Trade ETH to ERC20 | |
+ function ethToTokenTransferInput(uint256 min_tokens, uint256 deadline, address recipient) | |
+ external | |
+ payable | |
+ returns (uint256 tokens_bought); | |
+ // Trade ERC20 to ETH | |
+ function tokenToEthSwapInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline) | |
+ external | |
+ returns (uint256 eth_bought); | |
+ // Trade ERC20 to ERC20 | |
+ function tokenToTokenTransferInput( | |
+ uint256 tokens_sold, | |
+ uint256 min_tokens_bought, | |
+ uint256 min_eth_bought, | |
+ uint256 deadline, | |
+ address recipient, | |
+ address token_addr | |
+ ) | |
+ external | |
+ returns (uint256 tokens_bought); | |
+ | |
+ /// @dev The following functions are only used in tests | |
+ // Provide Liquidity | |
+ function addLiquidity(uint256 min_liquidity, uint256 max_tokens, uint256 deadline) | |
+ external | |
+ payable | |
+ returns (uint256); | |
+ // Get Prices | |
+ function getEthToTokenInputPrice(uint256 eth_sold) | |
+ external | |
+ view | |
+ returns (uint256 tokens_bought); | |
+ function getTokenToEthInputPrice(uint256 tokens_sold) | |
+ external | |
+ view | |
+ returns (uint256 eth_bought); | |
+} | |
diff --git a/src/exchanges/interfaces/IUniswapFactory.sol b/src/exchanges/interfaces/IUniswapFactory.sol | |
new file mode 100644 | |
index 00000000..dc1829af | |
--- /dev/null | |
+++ b/src/exchanges/interfaces/IUniswapFactory.sol | |
@@ -0,0 +1,6 @@ | |
+pragma solidity 0.5.15; | |
+ | |
+/// @dev Minimal interface for our interactions with UniswapFactory | |
+interface IUniswapFactory { | |
+ function getExchange(address token) external view returns (address exchange); | |
+} | |
diff --git a/src/exchanges/interfaces/IZeroExV2.sol b/src/exchanges/interfaces/IZeroExV2.sol | |
new file mode 100644 | |
index 00000000..56d70c9e | |
--- /dev/null | |
+++ b/src/exchanges/interfaces/IZeroExV2.sol | |
@@ -0,0 +1,43 @@ | |
+pragma solidity 0.5.15; | |
+pragma experimental ABIEncoderV2; | |
+ | |
+/// @dev Minimal interface for our interactions with the ZeroEx Exchange contract | |
+interface IZeroExV2 { | |
+ struct Order { | |
+ address makerAddress; | |
+ address takerAddress; | |
+ address feeRecipientAddress; | |
+ address senderAddress; | |
+ uint256 makerAssetAmount; | |
+ uint256 takerAssetAmount; | |
+ uint256 makerFee; | |
+ uint256 takerFee; | |
+ uint256 expirationTimeSeconds; | |
+ uint256 salt; | |
+ bytes makerAssetData; | |
+ bytes takerAssetData; | |
+ } | |
+ | |
+ struct OrderInfo { | |
+ uint8 orderStatus; | |
+ bytes32 orderHash; | |
+ uint256 orderTakerAssetFilledAmount; | |
+ } | |
+ | |
+ struct FillResults { | |
+ uint256 makerAssetFilledAmount; | |
+ uint256 takerAssetFilledAmount; | |
+ uint256 makerFeePaid; | |
+ uint256 takerFeePaid; | |
+ } | |
+ | |
+ function ZRX_ASSET_DATA() external view returns (bytes memory); | |
+ function filled(bytes32) external view returns (uint256); | |
+ function cancelled(bytes32) external view returns (bool); | |
+ function getOrderInfo(Order calldata) external view returns (OrderInfo memory); | |
+ function getAssetProxy(bytes4) external view returns (address); | |
+ function isValidSignature(bytes32, address, bytes calldata) external view returns (bool); | |
+ function preSign(bytes32, address, bytes calldata) external; | |
+ function cancelOrder(Order calldata) external; | |
+ function fillOrder(Order calldata, uint256, bytes calldata) external returns (FillResults memory); | |
+} | |
diff --git a/src/exchanges/interfaces/IZeroExV3.sol b/src/exchanges/interfaces/IZeroExV3.sol | |
new file mode 100644 | |
index 00000000..b858b458 | |
--- /dev/null | |
+++ b/src/exchanges/interfaces/IZeroExV3.sol | |
@@ -0,0 +1,47 @@ | |
+pragma solidity 0.5.15; | |
+pragma experimental ABIEncoderV2; | |
+ | |
+/// @dev Minimal interface for our interactions with the ZeroEx Exchange contract | |
+interface IZeroExV3 { | |
+ struct Order { | |
+ address makerAddress; | |
+ address takerAddress; | |
+ address feeRecipientAddress; | |
+ address senderAddress; | |
+ uint256 makerAssetAmount; | |
+ uint256 takerAssetAmount; | |
+ uint256 makerFee; | |
+ uint256 takerFee; | |
+ uint256 expirationTimeSeconds; | |
+ uint256 salt; | |
+ bytes makerAssetData; | |
+ bytes takerAssetData; | |
+ bytes makerFeeAssetData; | |
+ bytes takerFeeAssetData; | |
+ } | |
+ | |
+ struct OrderInfo { | |
+ uint8 orderStatus; | |
+ bytes32 orderHash; | |
+ uint256 orderTakerAssetFilledAmount; | |
+ } | |
+ | |
+ struct FillResults { | |
+ uint256 makerAssetFilledAmount; | |
+ uint256 takerAssetFilledAmount; | |
+ uint256 makerFeePaid; | |
+ uint256 takerFeePaid; | |
+ uint256 protocolFeePaid; | |
+ } | |
+ | |
+ function cancelled(bytes32) external view returns (bool); | |
+ function cancelOrder(Order calldata) external; | |
+ function filled(bytes32) external view returns (uint256); | |
+ function fillOrder(Order calldata, uint256, bytes calldata) external payable returns (FillResults memory); | |
+ function getAssetProxy(bytes4) external view returns (address); | |
+ function getOrderInfo(Order calldata) external view returns (OrderInfo memory); | |
+ function isValidOrderSignature(Order calldata, bytes calldata) external view returns (bool); | |
+ function preSign(bytes32) external; | |
+ function protocolFeeCollector() external view returns (address); | |
+ function protocolFeeMultiplier() external view returns (uint256); | |
+} | |
diff --git a/src/contracts/factory/Factory.sol b/src/factory/Factory.sol | |
similarity index 77% | |
rename from src/contracts/factory/Factory.sol | |
rename to src/factory/Factory.sol | |
index 2d37be6e..ec82dfce 100644 | |
--- a/src/contracts/factory/Factory.sol | |
+++ b/src/factory/Factory.sol | |
@@ -1,4 +1,4 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
contract Factory { | |
@@ -12,7 +12,4 @@ contract Factory { | |
function isInstance(address _child) public view returns (bool) { | |
return childExists[_child]; | |
} | |
- | |
- // function createInstance() returns (address); | |
} | |
- | |
diff --git a/src/contracts/factory/FundFactory.sol b/src/factory/FundFactory.sol | |
similarity index 73% | |
rename from src/contracts/factory/FundFactory.sol | |
rename to src/factory/FundFactory.sol | |
index 903abe91..c0abf6c1 100644 | |
--- a/src/contracts/factory/FundFactory.sol | |
+++ b/src/factory/FundFactory.sol | |
@@ -1,17 +1,17 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
pragma experimental ABIEncoderV2; | |
-import "Accounting.sol"; | |
-import "FeeManager.sol"; | |
-import "Hub.sol"; | |
-import "PolicyManager.sol"; | |
-import "Participation.sol"; | |
-import "Shares.sol"; | |
-import "Trading.sol"; | |
-import "Vault.sol"; | |
-import "Version.i.sol"; | |
-import "AmguConsumer.sol"; | |
-import "Factory.sol"; | |
+import "../fund/accounting/IAccounting.sol"; | |
+import "../fund/fees/IFeeManager.sol"; | |
+import "../fund/hub/Hub.sol"; | |
+import "../fund/policies/IPolicyManager.sol"; | |
+import "../fund/participation/IParticipation.sol"; | |
+import "../fund/shares/IShares.sol"; | |
+import "../fund/trading/ITrading.sol"; | |
+import "../fund/vault/IVault.sol"; | |
+import "../version/IVersion.sol"; | |
+import "../engine/AmguConsumer.sol"; | |
+import "./Factory.sol"; | |
/// @notice Creates fund routes and links them together | |
contract FundFactory is AmguConsumer, Factory { | |
@@ -22,15 +22,15 @@ contract FundFactory is AmguConsumer, Factory { | |
address[12] routes | |
); | |
- VersionInterface public version; | |
- address public registry; | |
- AccountingFactory public accountingFactory; | |
- FeeManagerFactory public feeManagerFactory; | |
- ParticipationFactory public participationFactory; | |
- PolicyManagerFactory public policyManagerFactory; | |
- SharesFactory public sharesFactory; | |
- TradingFactory public tradingFactory; | |
- VaultFactory public vaultFactory; | |
+ IVersion public version; | |
+ Registry public associatedRegistry; | |
+ IAccountingFactory public accountingFactory; | |
+ IFeeManagerFactory public feeManagerFactory; | |
+ IParticipationFactory public participationFactory; | |
+ IPolicyManagerFactory public policyManagerFactory; | |
+ ISharesFactory public sharesFactory; | |
+ ITradingFactory public tradingFactory; | |
+ IVaultFactory public vaultFactory; | |
address[] public funds; | |
mapping (address => address) public managersToHubs; | |
@@ -43,7 +43,7 @@ contract FundFactory is AmguConsumer, Factory { | |
address[] exchanges; | |
address[] adapters; | |
address denominationAsset; | |
- address[] defaultAssets; | |
+ address[] defaultInvestmentAssets; | |
address[] fees; | |
uint[] feeRates; | |
uint[] feePeriods; | |
@@ -74,56 +74,58 @@ contract FundFactory is AmguConsumer, Factory { | |
address _vaultFactory, | |
address _policyManagerFactory, | |
address _version | |
- ) { | |
- accountingFactory = AccountingFactory(_accountingFactory); | |
- feeManagerFactory = FeeManagerFactory(_feeManagerFactory); | |
- participationFactory = ParticipationFactory(_participationFactory); | |
- sharesFactory = SharesFactory(_sharesFactory); | |
- tradingFactory = TradingFactory(_tradingFactory); | |
- vaultFactory = VaultFactory(_vaultFactory); | |
- policyManagerFactory = PolicyManagerFactory(_policyManagerFactory); | |
- version = VersionInterface(_version); | |
+ ) | |
+ public | |
+ { | |
+ accountingFactory = IAccountingFactory(_accountingFactory); | |
+ feeManagerFactory = IFeeManagerFactory(_feeManagerFactory); | |
+ participationFactory = IParticipationFactory(_participationFactory); | |
+ sharesFactory = ISharesFactory(_sharesFactory); | |
+ tradingFactory = ITradingFactory(_tradingFactory); | |
+ vaultFactory = IVaultFactory(_vaultFactory); | |
+ policyManagerFactory = IPolicyManagerFactory(_policyManagerFactory); | |
+ version = IVersion(_version); | |
} | |
- function componentExists(address _component) internal returns (bool) { | |
+ function componentExists(address _component) internal pure returns (bool) { | |
return _component != address(0); | |
} | |
function beginSetup( | |
- string _name, | |
- address[] _fees, | |
- uint[] _feeRates, | |
- uint[] _feePeriods, | |
- address[] _exchanges, | |
- address[] _adapters, | |
+ string memory _name, | |
+ address[] memory _fees, | |
+ uint[] memory _feeRates, | |
+ uint[] memory _feePeriods, | |
+ address[] memory _exchanges, | |
+ address[] memory _adapters, | |
address _denominationAsset, | |
- address[] _defaultAssets | |
+ address[] memory _defaultInvestmentAssets | |
) | |
public | |
componentNotSet(managersToHubs[msg.sender]) | |
{ | |
- Registry(registry).reserveFundName( | |
+ associatedRegistry.reserveFundName( | |
msg.sender, | |
_name | |
); | |
require( | |
- Registry(registry).assetIsRegistered(_denominationAsset), | |
+ associatedRegistry.assetIsRegistered(_denominationAsset), | |
"Denomination asset must be registered" | |
); | |
- managersToHubs[msg.sender] = new Hub(msg.sender, _name); | |
+ managersToHubs[msg.sender] = address(new Hub(msg.sender, _name)); | |
managersToSettings[msg.sender] = Settings( | |
_name, | |
_exchanges, | |
_adapters, | |
_denominationAsset, | |
- _defaultAssets, | |
+ _defaultInvestmentAssets, | |
_fees, | |
_feeRates, | |
_feePeriods | |
); | |
managersToRoutes[msg.sender].priceSource = priceSource(); | |
- managersToRoutes[msg.sender].registry = registry; | |
+ managersToRoutes[msg.sender].registry = address(associatedRegistry); | |
managersToRoutes[msg.sender].version = address(version); | |
managersToRoutes[msg.sender].engine = engine(); | |
managersToRoutes[msg.sender].mlnToken = mlnToken(); | |
@@ -139,8 +141,7 @@ contract FundFactory is AmguConsumer, Factory { | |
managersToRoutes[msg.sender].accounting = accountingFactory.createInstance( | |
managersToHubs[msg.sender], | |
managersToSettings[msg.sender].denominationAsset, | |
- Registry(registry).nativeAsset(), | |
- managersToSettings[msg.sender].defaultAssets | |
+ associatedRegistry.nativeAsset() | |
); | |
} | |
@@ -157,7 +158,7 @@ contract FundFactory is AmguConsumer, Factory { | |
managersToSettings[msg.sender].fees, | |
managersToSettings[msg.sender].feeRates, | |
managersToSettings[msg.sender].feePeriods, | |
- registry | |
+ managersToRoutes[msg.sender].registry | |
); | |
} | |
@@ -170,7 +171,7 @@ contract FundFactory is AmguConsumer, Factory { | |
{ | |
managersToRoutes[msg.sender].participation = participationFactory.createInstance( | |
managersToHubs[msg.sender], | |
- managersToSettings[msg.sender].defaultAssets, | |
+ managersToSettings[msg.sender].defaultInvestmentAssets, | |
managersToRoutes[msg.sender].registry | |
); | |
} | |
@@ -227,11 +228,11 @@ contract FundFactory is AmguConsumer, Factory { | |
} | |
function completeSetup() external amguPayable(false) payable { | |
- Hub.Routes routes = managersToRoutes[msg.sender]; | |
+ Hub.Routes memory routes = managersToRoutes[msg.sender]; | |
Hub hub = Hub(managersToHubs[msg.sender]); | |
require(!childExists[address(hub)], "Setup already complete"); | |
require( | |
- componentExists(hub) && | |
+ componentExists(address(hub)) && | |
componentExists(routes.accounting) && | |
componentExists(routes.feeManager) && | |
componentExists(routes.participation) && | |
@@ -258,8 +259,8 @@ contract FundFactory is AmguConsumer, Factory { | |
]); | |
hub.setRouting(); | |
hub.setPermissions(); | |
- funds.push(hub); | |
- Registry(registry).registerFund( | |
+ funds.push(address(hub)); | |
+ associatedRegistry.registerFund( | |
address(hub), | |
msg.sender, | |
managersToSettings[msg.sender].name | |
@@ -267,7 +268,7 @@ contract FundFactory is AmguConsumer, Factory { | |
emit NewFund( | |
msg.sender, | |
- hub, | |
+ address(hub), | |
[ | |
routes.accounting, | |
routes.feeManager, | |
@@ -289,18 +290,17 @@ contract FundFactory is AmguConsumer, Factory { | |
function getLastFundId() external view returns (uint) { return funds.length - 1; } | |
function mlnToken() public view returns (address) { | |
- return address(Registry(registry).mlnToken()); | |
+ return address(associatedRegistry.mlnToken()); | |
} | |
function engine() public view returns (address) { | |
- return address(Registry(registry).engine()); | |
+ return address(associatedRegistry.engine()); | |
} | |
function priceSource() public view returns (address) { | |
- return address(Registry(registry).priceSource()); | |
+ return address(associatedRegistry.priceSource()); | |
} | |
- function version() public view returns (address) { return address(version); } | |
- function registry() public view returns (address) { return address(registry); } | |
- function getExchangesInfo(address user) public view returns (address[]) { | |
- return (managersToSettings[user].exchanges); | |
+ function registry() public view returns (address) { return address(associatedRegistry); } | |
+ function getExchangesInfo(address user) public view returns (address[] memory) { | |
+ return (managersToSettings[user].exchanges); | |
} | |
} | |
diff --git a/src/contracts/fund/accounting/Accounting.sol b/src/fund/accounting/Accounting.sol | |
similarity index 82% | |
rename from src/contracts/fund/accounting/Accounting.sol | |
rename to src/fund/accounting/Accounting.sol | |
index 04bc6a55..5a41afb0 100644 | |
--- a/src/contracts/fund/accounting/Accounting.sol | |
+++ b/src/fund/accounting/Accounting.sol | |
@@ -1,17 +1,19 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "StandardToken.sol"; | |
-import "Factory.sol"; | |
-import "PriceSource.i.sol"; | |
-import "FeeManager.sol"; | |
-import "Spoke.sol"; | |
-import "Shares.sol"; | |
-import "Trading.sol"; | |
-import "Vault.sol"; | |
-import "Accounting.i.sol"; | |
-import "AmguConsumer.sol"; | |
+import "../../dependencies/token/StandardToken.sol"; | |
+import "../../factory/Factory.sol"; | |
+import "../../prices/IPriceSource.sol"; | |
+import "../fees/FeeManager.sol"; | |
+import "../hub/Spoke.sol"; | |
+import "../shares/Shares.sol"; | |
+import "../trading/ITrading.sol"; | |
+import "../vault/Vault.sol"; | |
+import "../../engine/AmguConsumer.sol"; | |
-contract Accounting is AccountingInterface, AmguConsumer, Spoke { | |
+contract Accounting is AmguConsumer, Spoke { | |
+ | |
+ event AssetAddition(address indexed asset); | |
+ event AssetRemoval(address indexed asset); | |
struct Calculations { | |
uint gav; | |
@@ -31,12 +33,10 @@ contract Accounting is AccountingInterface, AmguConsumer, Spoke { | |
uint public DEFAULT_SHARE_PRICE; | |
Calculations public atLastAllocation; | |
- constructor(address _hub, address _denominationAsset, address _nativeAsset, address[] _defaultAssets) | |
+ constructor(address _hub, address _denominationAsset, address _nativeAsset) | |
+ public | |
Spoke(_hub) | |
{ | |
- for (uint i = 0; i < _defaultAssets.length; i++) { | |
- _addAssetToOwnedAssets(_defaultAssets[i]); | |
- } | |
DENOMINATION_ASSET = _denominationAsset; | |
NATIVE_ASSET = _nativeAsset; | |
DENOMINATION_ASSET_DECIMALS = ERC20WithFields(DENOMINATION_ASSET).decimals(); | |
@@ -47,15 +47,15 @@ contract Accounting is AccountingInterface, AmguConsumer, Spoke { | |
return ownedAssets.length; | |
} | |
- function assetHoldings(address _asset) public returns (uint) { | |
+ function assetHoldings(address _asset) public returns (uint256) { | |
return add( | |
- uint(ERC20WithFields(_asset).balanceOf(Vault(routes.vault))), | |
- Trading(routes.trading).updateAndGetQuantityBeingTraded(_asset) | |
+ uint256(ERC20WithFields(_asset).balanceOf(routes.vault)), | |
+ ITrading(routes.trading).updateAndGetQuantityBeingTraded(_asset) | |
); | |
} | |
/// @dev Returns sparse array | |
- function getFundHoldings() external returns (uint[], address[]) { | |
+ function getFundHoldings() external returns (uint[] memory, address[] memory) { | |
uint[] memory _quantities = new uint[](ownedAssets.length); | |
address[] memory _assets = new address[](ownedAssets.length); | |
for (uint i = 0; i < ownedAssets.length; i++) { | |
@@ -70,7 +70,7 @@ contract Accounting is AccountingInterface, AmguConsumer, Spoke { | |
function calcAssetGAV(address _queryAsset) external returns (uint) { | |
uint queryAssetQuantityHeld = assetHoldings(_queryAsset); | |
- return PriceSourceInterface(routes.priceSource).convertQuantity( | |
+ return IPriceSource(routes.priceSource).convertQuantity( | |
queryAssetQuantityHeld, _queryAsset, DENOMINATION_ASSET | |
); | |
} | |
@@ -88,7 +88,7 @@ contract Accounting is AccountingInterface, AmguConsumer, Spoke { | |
// gav as sum of mul(assetHoldings, assetPrice) with formatting: mul(mul(exchangeHoldings, exchangePrice), 10 ** shareDecimals) | |
gav = add( | |
gav, | |
- PriceSourceInterface(routes.priceSource).convertQuantity( | |
+ IPriceSource(routes.priceSource).convertQuantity( | |
quantityHeld, asset, DENOMINATION_ASSET | |
) | |
); | |
@@ -100,7 +100,7 @@ contract Accounting is AccountingInterface, AmguConsumer, Spoke { | |
return sub(gav, unclaimedFeesInDenominationAsset); | |
} | |
- function valuePerShare(uint totalValue, uint numShares) public view returns (uint) { | |
+ function valuePerShare(uint totalValue, uint numShares) public pure returns (uint) { | |
require(numShares > 0, "No shares to calculate value for"); | |
return (totalValue * 10 ** uint(SHARES_DECIMALS)) / numShares; | |
} | |
@@ -156,7 +156,7 @@ contract Accounting is AccountingInterface, AmguConsumer, Spoke { | |
_numShares, | |
calcGavPerShareNetManagementFee() | |
) / 10 ** uint(SHARES_DECIMALS); | |
- return PriceSourceInterface(routes.priceSource).convertQuantity( | |
+ return IPriceSource(routes.priceSource).convertQuantity( | |
denominationAssetQuantity, DENOMINATION_ASSET, _altAsset | |
); | |
} | |
@@ -169,12 +169,12 @@ contract Accounting is AccountingInterface, AmguConsumer, Spoke { | |
payable | |
{ | |
updateOwnedAssets(); | |
- uint gav; | |
- uint feesInDenomination; | |
- uint feesInShares; | |
- uint nav; | |
- (gav, feesInDenomination, feesInShares, nav, ) = performCalculations(); | |
- uint totalSupply = Shares(routes.shares).totalSupply(); | |
+ uint256 gav; | |
+ uint256 feesInDenomination; | |
+ uint256 feesInShares; | |
+ uint256 nav; | |
+ (gav, feesInDenomination, feesInShares, nav,,) = performCalculations(); | |
+ uint256 totalSupply = Shares(routes.shares).totalSupply(); | |
FeeManager(routes.feeManager).rewardAllFees(); | |
atLastAllocation = Calculations({ | |
gav: gav, | |
@@ -192,7 +192,7 @@ contract Accounting is AccountingInterface, AmguConsumer, Spoke { | |
if ( | |
assetHoldings(asset) == 0 && | |
!(asset == address(DENOMINATION_ASSET)) && | |
- Trading(routes.trading).openMakeOrdersAgainstAsset(asset) == 0 | |
+ ITrading(routes.trading).getOpenMakeOrdersAgainstAsset(asset) == 0 | |
) { | |
_removeFromOwnedAssets(asset); | |
} | |
@@ -241,14 +241,13 @@ contract AccountingFactory is Factory { | |
address indexed hub, | |
address indexed instance, | |
address denominationAsset, | |
- address nativeAsset, | |
- address[] defaultAssets | |
+ address nativeAsset | |
); | |
- function createInstance(address _hub, address _denominationAsset, address _nativeAsset, address[] _defaultAssets) external returns (address) { | |
- address accounting = new Accounting(_hub, _denominationAsset, _nativeAsset, _defaultAssets); | |
+ function createInstance(address _hub, address _denominationAsset, address _nativeAsset) external returns (address) { | |
+ address accounting = address(new Accounting(_hub, _denominationAsset, _nativeAsset)); | |
childExists[accounting] = true; | |
- emit NewInstance(_hub, accounting, _denominationAsset, _nativeAsset, _defaultAssets); | |
+ emit NewInstance(_hub, accounting, _denominationAsset, _nativeAsset); | |
return accounting; | |
} | |
} | |
diff --git a/src/fund/accounting/IAccounting.sol b/src/fund/accounting/IAccounting.sol | |
new file mode 100644 | |
index 00000000..de1e3804 | |
--- /dev/null | |
+++ b/src/fund/accounting/IAccounting.sol | |
@@ -0,0 +1,25 @@ | |
+pragma solidity 0.5.15; | |
+ | |
+/// @notice Gives metrics about a Fund | |
+interface IAccounting { | |
+ function getOwnedAssetsLength() external view returns (uint); | |
+ function getFundHoldings() external returns (uint[] memory, address[] memory); | |
+ function calcAssetGAV(address ofAsset) external returns (uint); | |
+ function calcGav() external returns (uint gav); | |
+ function calcNav(uint gav, uint unclaimedFees) external pure returns (uint); | |
+ function valuePerShare(uint totalValue, uint numShares) external view returns (uint); | |
+ function performCalculations() external returns ( | |
+ uint gav, | |
+ uint unclaimedFees, | |
+ uint feesInShares, | |
+ uint nav, | |
+ uint sharePrice, | |
+ uint gavPerShareNetManagementFee | |
+ ); | |
+ function calcSharePrice() external returns (uint); | |
+ function calcGavPerShareNetManagementFee() external returns (uint); | |
+} | |
+ | |
+interface IAccountingFactory { | |
+ function createInstance(address _hub, address _denominationAsset, address _nativeAsset) external returns (address); | |
+} | |
diff --git a/src/contracts/fund/fees/FeeManager.sol b/src/fund/fees/FeeManager.sol | |
similarity index 72% | |
rename from src/contracts/fund/fees/FeeManager.sol | |
rename to src/fund/fees/FeeManager.sol | |
index ce673753..2c1ac40f 100644 | |
--- a/src/contracts/fund/fees/FeeManager.sol | |
+++ b/src/fund/fees/FeeManager.sol | |
@@ -1,12 +1,13 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
pragma experimental ABIEncoderV2; | |
-import "Fee.i.sol"; | |
-import "Spoke.sol"; | |
-import "Shares.sol"; | |
-import "Factory.sol"; | |
-import "Registry.sol"; | |
-import "math.sol"; | |
+import "./IFee.sol"; | |
+import "../hub/Spoke.sol"; | |
+import "../shares/Shares.sol"; | |
+import "../../factory/Factory.sol"; | |
+import "../../version/Registry.sol"; | |
+import "../../dependencies/DSMath.sol"; | |
+import "./IFeeManager.sol"; | |
/// @notice Manages and allocates fees for a particular fund | |
contract FeeManager is DSMath, Spoke { | |
@@ -20,10 +21,10 @@ contract FeeManager is DSMath, Spoke { | |
uint feePeriod; | |
} | |
- Fee[] public fees; | |
+ IFee[] public fees; | |
mapping (address => bool) public feeIsRegistered; | |
- constructor(address _hub, address _denominationAsset, address[] _fees, uint[] _rates, uint[] _periods, address _registry) Spoke(_hub) public { | |
+ constructor(address _hub, address _denominationAsset, address[] memory _fees, uint[] memory _rates, uint[] memory _periods, address _registry) Spoke(_hub) public { | |
for (uint i = 0; i < _fees.length; i++) { | |
require( | |
Registry(_registry).isFeeRegistered(_fees[i]), | |
@@ -48,12 +49,12 @@ contract FeeManager is DSMath, Spoke { | |
function register(address feeAddress, uint feeRate, uint feePeriod, address denominationAsset) internal { | |
require(!feeIsRegistered[feeAddress], "Fee already registered"); | |
feeIsRegistered[feeAddress] = true; | |
- fees.push(Fee(feeAddress)); | |
- Fee(feeAddress).initializeForUser(feeRate, feePeriod, denominationAsset); // initialize state | |
+ fees.push(IFee(feeAddress)); | |
+ IFee(feeAddress).initializeForUser(feeRate, feePeriod, denominationAsset); // initialize state | |
emit FeeRegistration(feeAddress); | |
} | |
- function totalFeeAmount() public view returns (uint total) { | |
+ function totalFeeAmount() external returns (uint total) { | |
for (uint i = 0; i < fees.length; i++) { | |
total = add(total, fees[i].feeAmount()); | |
} | |
@@ -61,8 +62,8 @@ contract FeeManager is DSMath, Spoke { | |
} | |
/// @dev Shares to be inflated after update state | |
- function _rewardFee(Fee fee) internal { | |
- require(feeIsRegistered[fee], "Fee is not registered"); | |
+ function _rewardFee(IFee fee) internal { | |
+ require(feeIsRegistered[address(fee)], "Fee is not registered"); | |
uint rewardShares = fee.feeAmount(); | |
fee.updateState(); | |
Shares(routes.shares).createFor(hub.manager(), rewardShares); | |
@@ -86,14 +87,14 @@ contract FeeManager is DSMath, Spoke { | |
/// @dev Convenience function | |
/// @dev Convention that management fee is 0 | |
- function managementFeeAmount() public view returns (uint) { | |
+ function managementFeeAmount() external returns (uint) { | |
if (fees.length < 1) return 0; | |
return fees[0].feeAmount(); | |
} | |
/// @dev Convenience function | |
/// @dev Convention that performace fee is 1 | |
- function performanceFeeAmount() public view returns (uint) { | |
+ function performanceFeeAmount() external returns (uint) { | |
if (fees.length < 2) return 0; | |
return fees[1].feeAmount(); | |
} | |
@@ -103,15 +104,16 @@ contract FeeManagerFactory is Factory { | |
function createInstance( | |
address _hub, | |
address _denominationAsset, | |
- address[] _fees, | |
- uint[] _feeRates, | |
- uint[] _feePeriods, | |
+ address[] memory _fees, | |
+ uint[] memory _feeRates, | |
+ uint[] memory _feePeriods, | |
address _registry | |
) public returns (address) { | |
- address feeManager = new FeeManager(_hub, _denominationAsset, _fees, _feeRates, _feePeriods, _registry); | |
+ address feeManager = address( | |
+ new FeeManager(_hub, _denominationAsset, _fees, _feeRates, _feePeriods, _registry) | |
+ ); | |
childExists[feeManager] = true; | |
emit NewInstance(_hub, feeManager); | |
return feeManager; | |
} | |
} | |
- | |
diff --git a/src/contracts/fund/fees/Fee.i.sol b/src/fund/fees/IFee.sol | |
similarity index 71% | |
rename from src/contracts/fund/fees/Fee.i.sol | |
rename to src/fund/fees/IFee.sol | |
index 9ffdf5e6..cbe05183 100644 | |
--- a/src/contracts/fund/fees/Fee.i.sol | |
+++ b/src/fund/fees/IFee.sol | |
@@ -1,10 +1,11 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
/// @dev Exposes "feeAmount", which maps fund state and fee state to uint | |
+/// @dev Notice that "feeAmount" *may* change contract state | |
/// @dev Also exposes "updateState", which changes fee's internal state | |
-interface Fee { | |
- function feeAmount() public view returns (uint); | |
+interface IFee { | |
function initializeForUser(uint feeRate, uint feePeriod, address denominationAsset) external; | |
+ function feeAmount() external returns (uint); | |
function updateState() external; | |
/// @notice Used to enforce a convention | |
diff --git a/src/fund/fees/IFeeManager.sol b/src/fund/fees/IFeeManager.sol | |
new file mode 100644 | |
index 00000000..899c5203 | |
--- /dev/null | |
+++ b/src/fund/fees/IFeeManager.sol | |
@@ -0,0 +1,12 @@ | |
+pragma solidity 0.5.15; | |
+ | |
+interface IFeeManagerFactory { | |
+ function createInstance( | |
+ address _hub, | |
+ address _denominationAsset, | |
+ address[] calldata _fees, | |
+ uint[] calldata _feeRates, | |
+ uint[] calldata _feePeriods, | |
+ address _registry | |
+ ) external returns (address); | |
+} | |
diff --git a/src/contracts/fund/fees/ManagementFee.sol b/src/fund/fees/ManagementFee.sol | |
similarity index 74% | |
rename from src/contracts/fund/fees/ManagementFee.sol | |
rename to src/fund/fees/ManagementFee.sol | |
index c1cfeaff..a8cd684d 100644 | |
--- a/src/contracts/fund/fees/ManagementFee.sol | |
+++ b/src/fund/fees/ManagementFee.sol | |
@@ -1,26 +1,25 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "Fee.i.sol"; | |
-import "FeeManager.sol"; | |
-import "Hub.sol"; | |
-import "Shares.sol"; | |
-import "math.sol"; | |
+import "./FeeManager.sol"; | |
+import "../hub/Hub.sol"; | |
+import "../shares/Shares.sol"; | |
+import "../../dependencies/DSMath.sol"; | |
-contract ManagementFee is DSMath, Fee { | |
+contract ManagementFee is DSMath { | |
uint public DIVISOR = 10 ** 18; | |
mapping (address => uint) public managementFeeRate; | |
mapping (address => uint) public lastPayoutTime; | |
- function feeAmount() public view returns (uint feeInShares) { | |
+ function feeAmount() external view returns (uint feeInShares) { | |
Hub hub = FeeManager(msg.sender).hub(); | |
Shares shares = Shares(hub.shares()); | |
if (shares.totalSupply() == 0 || managementFeeRate[msg.sender] == 0) { | |
feeInShares = 0; | |
} else { | |
uint timePassed = sub(block.timestamp, lastPayoutTime[msg.sender]); | |
- uint preDilutionFeeShares = mul(mul(shares.totalSupply(), managementFeeRate[msg.sender]) / DIVISOR, timePassed) / 1 years; | |
+ uint preDilutionFeeShares = mul(mul(shares.totalSupply(), managementFeeRate[msg.sender]) / DIVISOR, timePassed) / 365 days; | |
feeInShares = | |
mul(preDilutionFeeShares, shares.totalSupply()) / | |
sub(shares.totalSupply(), preDilutionFeeShares); | |
@@ -38,7 +37,7 @@ contract ManagementFee is DSMath, Fee { | |
lastPayoutTime[msg.sender] = block.timestamp; | |
} | |
- function identifier() external view returns (uint) { | |
+ function identifier() external pure returns (uint) { | |
return 0; | |
} | |
} | |
diff --git a/src/contracts/fund/fees/PerformanceFee.sol b/src/fund/fees/PerformanceFee.sol | |
similarity index 91% | |
rename from src/contracts/fund/fees/PerformanceFee.sol | |
rename to src/fund/fees/PerformanceFee.sol | |
index 3bd1f344..b62b1323 100644 | |
--- a/src/contracts/fund/fees/PerformanceFee.sol | |
+++ b/src/fund/fees/PerformanceFee.sol | |
@@ -1,13 +1,12 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "Fee.i.sol"; | |
-import "FeeManager.sol"; | |
-import "Accounting.sol"; | |
-import "Hub.sol"; | |
-import "Shares.sol"; | |
-import "math.sol"; | |
+import "./FeeManager.sol"; | |
+import "../accounting/Accounting.sol"; | |
+import "../hub/Hub.sol"; | |
+import "../shares/Shares.sol"; | |
+import "../../dependencies/DSMath.sol"; | |
-contract PerformanceFee is DSMath, Fee { | |
+contract PerformanceFee is DSMath { | |
event HighWaterMarkUpdate(address indexed feeManager, uint indexed hwm); | |
@@ -31,7 +30,7 @@ contract PerformanceFee is DSMath, Fee { | |
} | |
/// @notice Assumes management fee is zero | |
- function feeAmount() public view returns (uint feeInShares) { | |
+ function feeAmount() external returns (uint feeInShares) { | |
Hub hub = FeeManager(msg.sender).hub(); | |
Accounting accounting = Accounting(hub.accounting()); | |
Shares shares = Shares(hub.shares()); | |
@@ -92,7 +91,7 @@ contract PerformanceFee is DSMath, Fee { | |
emit HighWaterMarkUpdate(msg.sender, currentGavPerShare); | |
} | |
- function identifier() external view returns (uint) { | |
+ function identifier() external pure returns (uint) { | |
return 1; | |
} | |
} | |
diff --git a/src/contracts/fund/hub/Hub.sol b/src/fund/hub/Hub.sol | |
similarity index 94% | |
rename from src/contracts/fund/hub/Hub.sol | |
rename to src/fund/hub/Hub.sol | |
index bf85de42..0dff6cbb 100644 | |
--- a/src/contracts/fund/hub/Hub.sol | |
+++ b/src/fund/hub/Hub.sol | |
@@ -1,7 +1,7 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "guard.sol"; | |
-import "Spoke.sol"; | |
+import "../../dependencies/DSGuard.sol"; | |
+import "./Spoke.sol"; | |
/// @notice Router for communication between components | |
/// @notice Has one or more Spokes | |
@@ -35,7 +35,7 @@ contract Hub is DSGuard { | |
uint public creationTime; | |
mapping (address => bool) public isSpoke; | |
- constructor(address _manager, string _name) { | |
+ constructor(address _manager, string memory _name) public { | |
creator = msg.sender; | |
manager = _manager; | |
name = _name; | |
@@ -53,7 +53,7 @@ contract Hub is DSGuard { | |
emit FundShutDown(); | |
} | |
- function setSpokes(address[12] _spokes) external onlyCreator { | |
+ function setSpokes(address[12] calldata _spokes) external onlyCreator { | |
require(!spokesSet, "Spokes already set"); | |
for (uint i = 0; i < _spokes.length; i++) { | |
isSpoke[_spokes[i]] = true; | |
@@ -109,6 +109,7 @@ contract Hub is DSGuard { | |
permit(manager, routes.policyManager, bytes4(keccak256('batchRegister(bytes4[],address[])'))); | |
permit(manager, routes.participation, bytes4(keccak256('enableInvestment(address[])'))); | |
permit(manager, routes.participation, bytes4(keccak256('disableInvestment(address[])'))); | |
+ permit(manager, routes.trading, bytes4(keccak256('addExchange(address,address)'))); | |
permissionsSet = true; | |
} | |
diff --git a/src/contracts/fund/hub/Spoke.sol b/src/fund/hub/Spoke.sol | |
similarity index 83% | |
rename from src/contracts/fund/hub/Spoke.sol | |
rename to src/fund/hub/Spoke.sol | |
index 79c55ada..1de598c5 100644 | |
--- a/src/contracts/fund/hub/Spoke.sol | |
+++ b/src/fund/hub/Spoke.sol | |
@@ -1,7 +1,7 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "Hub.sol"; | |
-import "auth.sol"; | |
+import "./Hub.sol"; | |
+import "../../dependencies/DSAuth.sol"; | |
/// @notice Has one Hub | |
contract Spoke is DSAuth { | |
@@ -19,13 +19,13 @@ contract Spoke is DSAuth { | |
_; | |
} | |
- constructor(address _hub) { | |
+ constructor(address _hub) public { | |
hub = Hub(_hub); | |
setAuthority(hub); | |
- setOwner(hub); // temporary, to allow initialization | |
+ setOwner(address(hub)); // temporary, to allow initialization | |
} | |
- function initialize(address[12] _spokes) external auth { | |
+ function initialize(address[12] calldata _spokes) external auth { | |
require(msg.sender == address(hub)); | |
require(!initialized, "Already initialized"); | |
routes = Hub.Routes( | |
diff --git a/src/fund/participation/IParticipation.sol b/src/fund/participation/IParticipation.sol | |
new file mode 100644 | |
index 00000000..ddcb7b24 | |
--- /dev/null | |
+++ b/src/fund/participation/IParticipation.sol | |
@@ -0,0 +1,20 @@ | |
+pragma solidity 0.5.15; | |
+ | |
+/// @notice Investor Fund interactions | |
+/// @notice Handles redemptions and requests for investment | |
+interface IParticipation { | |
+ function requestInvestment( | |
+ uint requestedShares, | |
+ uint investmentAmount, | |
+ address investmentAsset | |
+ ) external payable; | |
+ function hasRequest(address) external view returns (bool); | |
+ function cancelRequest() external payable; | |
+ function executeRequestFor(address requestOwner) external payable; | |
+ function redeem() external; | |
+ function redeemWithConstraints(uint shareQuantity, address[] calldata requestedAssets) external; | |
+} | |
+ | |
+interface IParticipationFactory { | |
+ function createInstance(address _hub, address[] calldata _defaultAssets, address _registry) external returns (address); | |
+} | |
diff --git a/src/contracts/fund/participation/Participation.sol b/src/fund/participation/Participation.sol | |
similarity index 79% | |
rename from src/contracts/fund/participation/Participation.sol | |
rename to src/fund/participation/Participation.sol | |
index e371d3c1..f4de9fb8 100644 | |
--- a/src/contracts/fund/participation/Participation.sol | |
+++ b/src/fund/participation/Participation.sol | |
@@ -1,19 +1,48 @@ | |
-pragma solidity ^0.4.25; | |
- | |
-import "Spoke.sol"; | |
-import "Shares.sol"; | |
-import "Accounting.sol"; | |
-import "Vault.sol"; | |
-import "ERC20.i.sol"; | |
-import "Factory.sol"; | |
-import "math.sol"; | |
-import "PriceSource.i.sol"; | |
-import "AmguConsumer.sol"; | |
-import "Participation.i.sol"; | |
-import "TokenUser.sol"; | |
+pragma solidity 0.5.15; | |
+ | |
+import "../vault/Vault.sol"; | |
+import "../shares/Shares.sol"; | |
+import "../policies/PolicyManager.sol"; | |
+import "../hub/Spoke.sol"; | |
+import "../accounting/Accounting.sol"; | |
+import "../../prices/IPriceSource.sol"; | |
+import "../../factory/Factory.sol"; | |
+import "../../engine/AmguConsumer.sol"; | |
+import "../../dependencies/token/IERC20.sol"; | |
+import "../../dependencies/DSMath.sol"; | |
+import "../../dependencies/TokenUser.sol"; | |
/// @notice Entry and exit point for investors | |
-contract Participation is ParticipationInterface, TokenUser, AmguConsumer, Spoke { | |
+contract Participation is TokenUser, AmguConsumer, Spoke { | |
+ event EnableInvestment (address[] asset); | |
+ event DisableInvestment (address[] assets); | |
+ | |
+ event InvestmentRequest ( | |
+ address indexed requestOwner, | |
+ address indexed investmentAsset, | |
+ uint requestedShares, | |
+ uint investmentAmount | |
+ ); | |
+ | |
+ event RequestExecution ( | |
+ address indexed requestOwner, | |
+ address indexed executor, | |
+ address indexed investmentAsset, | |
+ uint investmentAmount, | |
+ uint requestedShares | |
+ ); | |
+ | |
+ event CancelRequest ( | |
+ address indexed requestOwner | |
+ ); | |
+ | |
+ event Redemption ( | |
+ address indexed redeemer, | |
+ address[] assets, | |
+ uint[] assetQuantities, | |
+ uint redeemedShares | |
+ ); | |
+ | |
struct Request { | |
address investmentAsset; | |
uint investmentAmount; | |
@@ -30,14 +59,17 @@ contract Participation is ParticipationInterface, TokenUser, AmguConsumer, Spoke | |
address[] public historicalInvestors; // for information purposes only (read) | |
- constructor(address _hub, address[] _defaultAssets, address _registry) Spoke(_hub) { | |
+ constructor(address _hub, address[] memory _defaultAssets, address _registry) | |
+ public | |
+ Spoke(_hub) | |
+ { | |
routes.registry = _registry; | |
_enableInvestment(_defaultAssets); | |
} | |
- function() public payable {} | |
+ function() external payable {} | |
- function _enableInvestment(address[] _assets) internal { | |
+ function _enableInvestment(address[] memory _assets) internal { | |
for (uint i = 0; i < _assets.length; i++) { | |
require( | |
Registry(routes.registry).assetIsRegistered(_assets[i]), | |
@@ -48,11 +80,11 @@ contract Participation is ParticipationInterface, TokenUser, AmguConsumer, Spoke | |
emit EnableInvestment(_assets); | |
} | |
- function enableInvestment(address[] _assets) external auth { | |
+ function enableInvestment(address[] calldata _assets) external auth { | |
_enableInvestment(_assets); | |
} | |
- function disableInvestment(address[] _assets) external auth { | |
+ function disableInvestment(address[] calldata _assets) external auth { | |
for (uint i = 0; i < _assets.length; i++) { | |
investAllowed[_assets[i]] = false; | |
} | |
@@ -71,7 +103,7 @@ contract Participation is ParticipationInterface, TokenUser, AmguConsumer, Spoke | |
/// @dev Request valid if price update happened since request and not expired | |
/// @dev If no shares exist and not expired, request can be executed immediately | |
function hasValidRequest(address _who) public view returns (bool) { | |
- PriceSourceInterface priceSource = PriceSourceInterface(routes.priceSource); | |
+ IPriceSource priceSource = IPriceSource(routes.priceSource); | |
bool delayRespectedOrNoShares = requests[_who].timestamp < priceSource.getLastUpdate() || | |
Shares(routes.shares).totalSupply() == 0; | |
@@ -131,25 +163,37 @@ contract Participation is ParticipationInterface, TokenUser, AmguConsumer, Spoke | |
); | |
} | |
- /// @notice Can only cancel when no price, request expired or fund shut down | |
- /// @dev Only request owner can cancel their request | |
- function cancelRequest() external payable amguPayable(false) { | |
- require(hasRequest(msg.sender), "No request to cancel"); | |
- PriceSourceInterface priceSource = PriceSourceInterface(routes.priceSource); | |
- Request request = requests[msg.sender]; | |
+ function _cancelRequestFor(address requestOwner) internal { | |
+ require(hasRequest(requestOwner), "No request to cancel"); | |
+ IPriceSource priceSource = IPriceSource(routes.priceSource); | |
+ Request memory request = requests[requestOwner]; | |
require( | |
!priceSource.hasValidPrice(request.investmentAsset) || | |
- hasExpiredRequest(msg.sender) || | |
+ hasExpiredRequest(requestOwner) || | |
hub.isShutDown(), | |
"No cancellation condition was met" | |
); | |
- ERC20 investmentAsset = ERC20(request.investmentAsset); | |
+ IERC20 investmentAsset = IERC20(request.investmentAsset); | |
uint investmentAmount = request.investmentAmount; | |
- delete requests[msg.sender]; | |
+ delete requests[requestOwner]; | |
msg.sender.transfer(Registry(routes.registry).incentive()); | |
- safeTransfer(investmentAsset, msg.sender, investmentAmount); | |
+ safeTransfer(address(investmentAsset), requestOwner, investmentAmount); | |
+ | |
+ emit CancelRequest(requestOwner); | |
+ } | |
- emit CancelRequest(msg.sender); | |
+ /// @notice Can only cancel when no price, request expired or fund shut down | |
+ /// @dev Only request owner can cancel their request | |
+ function cancelRequest() external payable amguPayable(false) { | |
+ _cancelRequestFor(msg.sender); | |
+ } | |
+ | |
+ function cancelRequestFor(address requestOwner) | |
+ external | |
+ payable | |
+ amguPayable(false) | |
+ { | |
+ _cancelRequestFor(requestOwner); | |
} | |
function executeRequestFor(address requestOwner) | |
@@ -164,7 +208,7 @@ contract Participation is ParticipationInterface, TokenUser, AmguConsumer, Spoke | |
"No valid request for this address" | |
); | |
require( | |
- PriceSourceInterface(routes.priceSource).hasValidPrice(request.investmentAsset), | |
+ IPriceSource(routes.priceSource).hasValidPrice(request.investmentAsset), | |
"Price not valid" | |
); | |
@@ -223,7 +267,6 @@ contract Participation is ParticipationInterface, TokenUser, AmguConsumer, Spoke | |
function getOwedPerformanceFees(uint shareQuantity) | |
public | |
- view | |
returns (uint remainingShareQuantity) | |
{ | |
Shares shares = Shares(routes.shares); | |
@@ -258,7 +301,7 @@ contract Participation is ParticipationInterface, TokenUser, AmguConsumer, Spoke | |
// TODO: reconsider the scenario where the user has enough funds to force shutdown on a large trade (any way around this?) | |
/// @dev Redeem only selected assets (used only when an asset throws) | |
- function redeemWithConstraints(uint shareQuantity, address[] requestedAssets) public { | |
+ function redeemWithConstraints(uint shareQuantity, address[] memory requestedAssets) public { | |
Shares shares = Shares(routes.shares); | |
require( | |
shares.balanceOf(msg.sender) >= shareQuantity && | |
@@ -268,7 +311,8 @@ contract Participation is ParticipationInterface, TokenUser, AmguConsumer, Spoke | |
uint owedPerformanceFees = 0; | |
if ( | |
- PriceSourceInterface(routes.priceSource).hasValidPrices(requestedAssets) | |
+ IPriceSource(routes.priceSource).hasValidPrices(requestedAssets) && | |
+ msg.sender != hub.manager() | |
) { | |
FeeManager(routes.feeManager).rewardManagementFee(); | |
owedPerformanceFees = getOwedPerformanceFees(shareQuantity); | |
@@ -323,7 +367,7 @@ contract Participation is ParticipationInterface, TokenUser, AmguConsumer, Spoke | |
); | |
} | |
- function getHistoricalInvestors() external view returns (address[]) { | |
+ function getHistoricalInvestors() external view returns (address[] memory) { | |
return historicalInvestors; | |
} | |
} | |
@@ -336,11 +380,13 @@ contract ParticipationFactory is Factory { | |
address registry | |
); | |
- function createInstance(address _hub, address[] _defaultAssets, address _registry) | |
+ function createInstance(address _hub, address[] calldata _defaultAssets, address _registry) | |
external | |
returns (address) | |
{ | |
- address participation = new Participation(_hub, _defaultAssets, _registry); | |
+ address participation = address( | |
+ new Participation(_hub, _defaultAssets, _registry) | |
+ ); | |
childExists[participation] = true; | |
emit NewInstance(_hub, participation, _defaultAssets, _registry); | |
return participation; | |
diff --git a/src/contracts/fund/policies/AddressList.sol b/src/fund/policies/AddressList.sol | |
similarity index 80% | |
rename from src/contracts/fund/policies/AddressList.sol | |
rename to src/fund/policies/AddressList.sol | |
index 308d2b91..a905171a 100644 | |
--- a/src/contracts/fund/policies/AddressList.sol | |
+++ b/src/fund/policies/AddressList.sol | |
@@ -1,6 +1,6 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "auth.sol"; | |
+import "../../dependencies/DSAuth.sol"; | |
/// @notice Generic AddressList | |
contract AddressList is DSAuth { | |
@@ -10,7 +10,7 @@ contract AddressList is DSAuth { | |
mapping(address => bool) internal list; | |
address[] internal mirror; | |
- constructor(address[] _assets) { | |
+ constructor(address[] memory _assets) public { | |
for (uint i = 0; i < _assets.length; i++) { | |
if (!isMember(_assets[i])) { // filter duplicates in _assets | |
list[_assets[i]] = true; | |
@@ -31,5 +31,5 @@ contract AddressList is DSAuth { | |
} | |
/// @return array of all listed asset addresses | |
- function getMembers() external view returns (address[]) { return mirror; } | |
+ function getMembers() external view returns (address[] memory) { return mirror; } | |
} | |
diff --git a/src/contracts/fund/policies/Policy.sol b/src/fund/policies/IPolicy.sol | |
similarity index 62% | |
rename from src/contracts/fund/policies/Policy.sol | |
rename to src/fund/policies/IPolicy.sol | |
index 500df7f7..1dce5b3e 100644 | |
--- a/src/contracts/fund/policies/Policy.sol | |
+++ b/src/fund/policies/IPolicy.sol | |
@@ -1,6 +1,6 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-contract Policy { | |
+interface IPolicy { | |
enum Applied { pre, post } | |
// In Trading context: | |
@@ -9,8 +9,8 @@ contract Policy { | |
// In Participation context: | |
// address[0]: Investor address, address[3]: Investment asset | |
- function rule(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) external view returns (bool); | |
+ function rule(bytes4 sig, address[5] calldata addresses, uint[3] calldata values, bytes32 identifier) external returns (bool); | |
function position() external view returns (Applied); | |
- function identifier() external view returns (string); | |
+ function identifier() external view returns (string memory); | |
} | |
diff --git a/src/fund/policies/IPolicyManager.sol b/src/fund/policies/IPolicyManager.sol | |
new file mode 100644 | |
index 00000000..37a0bc81 | |
--- /dev/null | |
+++ b/src/fund/policies/IPolicyManager.sol | |
@@ -0,0 +1,7 @@ | |
+pragma solidity 0.5.15; | |
+ | |
+ | |
+interface IPolicyManagerFactory { | |
+ function createInstance(address _hub) external returns (address); | |
+} | |
+ | |
diff --git a/src/contracts/fund/policies/PolicyManager.sol b/src/fund/policies/PolicyManager.sol | |
similarity index 54% | |
rename from src/contracts/fund/policies/PolicyManager.sol | |
rename to src/fund/policies/PolicyManager.sol | |
index 13167818..a5d1400d 100644 | |
--- a/src/contracts/fund/policies/PolicyManager.sol | |
+++ b/src/fund/policies/PolicyManager.sol | |
@@ -1,46 +1,46 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "Factory.sol"; | |
-import "Spoke.sol"; | |
-import "Policy.sol"; | |
+import "../../factory/Factory.sol"; | |
+import "../hub/Spoke.sol"; | |
+import "./IPolicy.sol"; | |
contract PolicyManager is Spoke { | |
event Registration( | |
bytes4 indexed sig, | |
- Policy.Applied position, | |
+ IPolicy.Applied position, | |
address indexed policy | |
); | |
struct Entry { | |
- Policy[] pre; | |
- Policy[] post; | |
+ IPolicy[] pre; | |
+ IPolicy[] post; | |
} | |
mapping(bytes4 => Entry) policies; | |
- constructor (address _hub) Spoke(_hub) {} | |
+ constructor (address _hub) public Spoke(_hub) {} | |
function register(bytes4 sig, address _policy) public auth { | |
- Policy.Applied position = Policy(_policy).position(); | |
- if (position == Policy.Applied.pre) { | |
- policies[sig].pre.push(Policy(_policy)); | |
- } else if (position == Policy.Applied.post) { | |
- policies[sig].post.push(Policy(_policy)); | |
+ IPolicy.Applied position = IPolicy(_policy).position(); | |
+ if (position == IPolicy.Applied.pre) { | |
+ policies[sig].pre.push(IPolicy(_policy)); | |
+ } else if (position == IPolicy.Applied.post) { | |
+ policies[sig].post.push(IPolicy(_policy)); | |
} else { | |
revert("Only pre and post allowed"); | |
} | |
emit Registration(sig, position, _policy); | |
} | |
- function batchRegister(bytes4[] sig, address[] _policies) public auth { | |
+ function batchRegister(bytes4[] memory sig, address[] memory _policies) public auth { | |
require(sig.length == _policies.length, "Arrays lengths unequal"); | |
for (uint i = 0; i < sig.length; i++) { | |
register(sig[i], _policies[i]); | |
} | |
} | |
- function PoliciesToAddresses(Policy[] storage _policies) internal view returns (address[]) { | |
+ function PoliciesToAddresses(IPolicy[] storage _policies) internal view returns (address[] memory) { | |
address[] memory res = new address[](_policies.length); | |
for(uint i = 0; i < _policies.length; i++) { | |
res[i] = address(_policies[i]); | |
@@ -48,31 +48,31 @@ contract PolicyManager is Spoke { | |
return res; | |
} | |
- function getPoliciesBySig(bytes4 sig) public view returns (address[], address[]) { | |
+ function getPoliciesBySig(bytes4 sig) public view returns (address[] memory, address[] memory) { | |
return (PoliciesToAddresses(policies[sig].pre), PoliciesToAddresses(policies[sig].post)); | |
} | |
- modifier isValidPolicyBySig(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) { | |
+ modifier isValidPolicyBySig(bytes4 sig, address[5] memory addresses, uint[3] memory values, bytes32 identifier) { | |
preValidate(sig, addresses, values, identifier); | |
_; | |
postValidate(sig, addresses, values, identifier); | |
} | |
- modifier isValidPolicy(address[5] addresses, uint[3] values, bytes32 identifier) { | |
+ modifier isValidPolicy(address[5] memory addresses, uint[3] memory values, bytes32 identifier) { | |
preValidate(msg.sig, addresses, values, identifier); | |
_; | |
postValidate(msg.sig, addresses, values, identifier); | |
} | |
- function preValidate(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) view public { | |
+ function preValidate(bytes4 sig, address[5] memory addresses, uint[3] memory values, bytes32 identifier) public { | |
validate(policies[sig].pre, sig, addresses, values, identifier); | |
} | |
- function postValidate(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) view public { | |
+ function postValidate(bytes4 sig, address[5] memory addresses, uint[3] memory values, bytes32 identifier) public { | |
validate(policies[sig].post, sig, addresses, values, identifier); | |
} | |
- function validate(Policy[] storage aux, bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) view internal { | |
+ function validate(IPolicy[] storage aux, bytes4 sig, address[5] memory addresses, uint[3] memory values, bytes32 identifier) internal { | |
for(uint i = 0; i < aux.length; i++) { | |
require( | |
aux[i].rule(sig, addresses, values, identifier), | |
@@ -83,8 +83,8 @@ contract PolicyManager is Spoke { | |
} | |
contract PolicyManagerFactory is Factory { | |
- function createInstance(address _hub) public returns (address) { | |
- address policyManager = new PolicyManager(_hub); | |
+ function createInstance(address _hub) external returns (address) { | |
+ address policyManager = address(new PolicyManager(_hub)); | |
childExists[policyManager] = true; | |
emit NewInstance(_hub, policyManager); | |
return policyManager; | |
diff --git a/src/fund/policies/TradingSignatures.sol b/src/fund/policies/TradingSignatures.sol | |
new file mode 100644 | |
index 00000000..a509359f | |
--- /dev/null | |
+++ b/src/fund/policies/TradingSignatures.sol | |
@@ -0,0 +1,6 @@ | |
+pragma solidity 0.5.15; | |
+ | |
+contract TradingSignatures { | |
+ bytes4 constant public MAKE_ORDER = 0x5f08e909; // makeOrderSignature | |
+ bytes4 constant public TAKE_ORDER = 0x63b24ef1; // takeOrderSignature | |
+} | |
diff --git a/src/contracts/fund/policies/compliance/UserWhitelist.sol b/src/fund/policies/compliance/UserWhitelist.sol | |
similarity index 54% | |
rename from src/contracts/fund/policies/compliance/UserWhitelist.sol | |
rename to src/fund/policies/compliance/UserWhitelist.sol | |
index 8c870636..1573413b 100644 | |
--- a/src/contracts/fund/policies/compliance/UserWhitelist.sol | |
+++ b/src/fund/policies/compliance/UserWhitelist.sol | |
@@ -1,16 +1,16 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "auth.sol"; | |
-import "Policy.sol"; | |
+import "../../../dependencies/DSAuth.sol"; | |
-contract UserWhitelist is Policy, DSAuth { | |
+contract UserWhitelist is DSAuth { | |
+ enum Applied { pre, post } | |
event ListAddition(address indexed who); | |
event ListRemoval(address indexed who); | |
mapping (address => bool) public whitelisted; | |
- constructor(address[] _preApproved) public { | |
+ constructor(address[] memory _preApproved) public { | |
batchAddToWhitelist(_preApproved); | |
} | |
@@ -24,23 +24,23 @@ contract UserWhitelist is Policy, DSAuth { | |
emit ListRemoval(_who); | |
} | |
- function batchAddToWhitelist(address[] _members) public auth { | |
+ function batchAddToWhitelist(address[] memory _members) public auth { | |
for (uint i = 0; i < _members.length; i++) { | |
addToWhitelist(_members[i]); | |
} | |
} | |
- function batchRemoveFromWhitelist(address[] _members) public auth { | |
+ function batchRemoveFromWhitelist(address[] memory _members) public auth { | |
for (uint i = 0; i < _members.length; i++) { | |
removeFromWhitelist(_members[i]); | |
} | |
} | |
- function rule(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) external view returns (bool) { | |
+ function rule(bytes4 sig, address[5] calldata addresses, uint[3] calldata values, bytes32 identifier) external returns (bool) { | |
return whitelisted[addresses[0]]; | |
} | |
- function position() external view returns (Applied) { return Applied.pre; } | |
- function identifier() external view returns (string) { return 'UserWhitelist'; } | |
+ function position() external pure returns (Applied) { return Applied.pre; } | |
+ function identifier() external pure returns (string memory) { return 'UserWhitelist'; } | |
} | |
diff --git a/src/contracts/fund/policies/risk-management/AssetBlacklist.sol b/src/fund/policies/risk-management/AssetBlacklist.sol | |
similarity index 50% | |
rename from src/contracts/fund/policies/risk-management/AssetBlacklist.sol | |
rename to src/fund/policies/risk-management/AssetBlacklist.sol | |
index a32bf6a3..c41df600 100644 | |
--- a/src/contracts/fund/policies/risk-management/AssetBlacklist.sol | |
+++ b/src/fund/policies/risk-management/AssetBlacklist.sol | |
@@ -1,16 +1,16 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "Policy.sol"; | |
-import "AddressList.sol"; | |
-import "TradingSignatures.sol"; | |
+import "../AddressList.sol"; | |
+import "../TradingSignatures.sol"; | |
/// @notice Assets can be added but not removed from blacklist | |
-contract AssetBlacklist is TradingSignatures, AddressList, Policy { | |
+contract AssetBlacklist is TradingSignatures, AddressList { | |
+ enum Applied { pre, post } | |
// bytes4 constant public MAKE_ORDER = 0x79705be7; // makeOrderSignature | |
// bytes4 constant public TAKE_ORDER = 0xe51be6e8; // takeOrderSignature | |
- constructor(address[] _assets) AddressList(_assets) {} | |
+ constructor(address[] memory _assets) AddressList(_assets) public {} | |
function addToBlacklist(address _asset) external auth { | |
require(!isMember(_asset), "Asset already in blacklist"); | |
@@ -18,11 +18,11 @@ contract AssetBlacklist is TradingSignatures, AddressList, Policy { | |
mirror.push(_asset); | |
} | |
- function rule(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) external view returns (bool) { | |
+ function rule(bytes4 sig, address[5] calldata addresses, uint[3] calldata values, bytes32 identifier) external returns (bool) { | |
address incomingToken = (sig == TAKE_ORDER) ? addresses[2] : addresses[3]; | |
return !isMember(incomingToken); | |
} | |
- function position() external view returns (Applied) { return Applied.pre; } | |
- function identifier() external view returns (string) { return 'Asset blacklist'; } | |
+ function position() external pure returns (Applied) { return Applied.pre; } | |
+ function identifier() external pure returns (string memory) { return 'Asset blacklist'; } | |
} | |
diff --git a/src/contracts/fund/policies/risk-management/AssetWhitelist.sol b/src/fund/policies/risk-management/AssetWhitelist.sol | |
similarity index 57% | |
rename from src/contracts/fund/policies/risk-management/AssetWhitelist.sol | |
rename to src/fund/policies/risk-management/AssetWhitelist.sol | |
index 9fb280d8..96f3fc56 100644 | |
--- a/src/contracts/fund/policies/risk-management/AssetWhitelist.sol | |
+++ b/src/fund/policies/risk-management/AssetWhitelist.sol | |
@@ -1,12 +1,13 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "Policy.sol"; | |
-import "AddressList.sol"; | |
-import "TradingSignatures.sol"; | |
+import "../AddressList.sol"; | |
+import "../TradingSignatures.sol"; | |
/// @notice Assets can be removed from but not added to whitelist | |
-contract AssetWhitelist is TradingSignatures, AddressList, Policy { | |
- constructor(address[] _assets) AddressList(_assets) {} | |
+contract AssetWhitelist is TradingSignatures, AddressList { | |
+ enum Applied { pre, post } | |
+ | |
+ constructor(address[] memory _assets) public AddressList(_assets) {} | |
function removeFromWhitelist(address _asset) external auth { | |
require(isMember(_asset), "Asset not in whitelist"); | |
@@ -24,11 +25,11 @@ contract AssetWhitelist is TradingSignatures, AddressList, Policy { | |
} | |
} | |
- function rule(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) external view returns (bool) { | |
+ function rule(bytes4 sig, address[5] calldata addresses, uint[3] calldata values, bytes32 identifier) external returns (bool) { | |
address incomingToken = (sig == TAKE_ORDER) ? addresses[2] : addresses[3]; | |
return isMember(incomingToken); | |
} | |
- function position() external view returns (Applied) { return Applied.pre; } | |
- function identifier() external view returns (string) { return 'Asset whitelist'; } | |
+ function position() external pure returns (Applied) { return Applied.pre; } | |
+ function identifier() external pure returns (string memory) { return 'Asset whitelist'; } | |
} | |
diff --git a/src/contracts/fund/policies/risk-management/MaxConcentration.sol b/src/fund/policies/risk-management/MaxConcentration.sol | |
similarity index 59% | |
rename from src/contracts/fund/policies/risk-management/MaxConcentration.sol | |
rename to src/fund/policies/risk-management/MaxConcentration.sol | |
index bc121139..4a44b12e 100644 | |
--- a/src/contracts/fund/policies/risk-management/MaxConcentration.sol | |
+++ b/src/fund/policies/risk-management/MaxConcentration.sol | |
@@ -1,17 +1,18 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "math.sol"; | |
-import "PriceSource.i.sol"; | |
-import "Accounting.sol"; | |
-import "Trading.sol"; | |
-import "TradingSignatures.sol"; | |
-import "Policy.sol"; | |
+import "../../../dependencies/DSMath.sol"; | |
+import "../../../prices/IPriceSource.sol"; | |
+import "../../accounting/Accounting.sol"; | |
+import "../../trading/Trading.sol"; | |
+import "../TradingSignatures.sol"; | |
+ | |
+contract MaxConcentration is TradingSignatures, DSMath { | |
+ enum Applied { pre, post } | |
-contract MaxConcentration is TradingSignatures, DSMath, Policy { | |
uint internal constant ONE_HUNDRED_PERCENT = 10 ** 18; // 100% | |
uint public maxConcentration; | |
- constructor(uint _maxConcentration) { | |
+ constructor(uint _maxConcentration) public { | |
require( | |
_maxConcentration <= ONE_HUNDRED_PERCENT, | |
"Max concentration cannot exceed 100%" | |
@@ -19,9 +20,8 @@ contract MaxConcentration is TradingSignatures, DSMath, Policy { | |
maxConcentration = _maxConcentration; | |
} | |
- function rule(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) | |
+ function rule(bytes4 sig, address[5] calldata addresses, uint[3] calldata values, bytes32 identifier) | |
external | |
- view | |
returns (bool) | |
{ | |
Accounting accounting = Accounting(Hub(Trading(msg.sender).hub()).accounting()); | |
@@ -36,6 +36,6 @@ contract MaxConcentration is TradingSignatures, DSMath, Policy { | |
return concentration <= maxConcentration; | |
} | |
- function position() external view returns (Applied) { return Applied.post; } | |
- function identifier() external view returns (string) { return 'Max concentration'; } | |
+ function position() external pure returns (Applied) { return Applied.post; } | |
+ function identifier() external pure returns (string memory) { return 'Max concentration'; } | |
} | |
diff --git a/src/contracts/fund/policies/risk-management/MaxPositions.sol b/src/fund/policies/risk-management/MaxPositions.sol | |
similarity index 52% | |
rename from src/contracts/fund/policies/risk-management/MaxPositions.sol | |
rename to src/fund/policies/risk-management/MaxPositions.sol | |
index 4c4511b2..49873c5c 100644 | |
--- a/src/contracts/fund/policies/risk-management/MaxPositions.sol | |
+++ b/src/fund/policies/risk-management/MaxPositions.sol | |
@@ -1,21 +1,21 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "PriceSource.i.sol"; | |
-import "Accounting.sol"; | |
-import "Policy.sol"; | |
-import "Trading.sol"; | |
-import "TradingSignatures.sol"; | |
+import "../../../prices/IPriceSource.sol"; | |
+import "../../accounting/Accounting.sol"; | |
+import "../../trading/Trading.sol"; | |
+import "../TradingSignatures.sol"; | |
+ | |
+contract MaxPositions is TradingSignatures { | |
+ enum Applied { pre, post } | |
-contract MaxPositions is TradingSignatures, Policy { | |
uint public maxPositions; | |
/// @dev _maxPositions = 10 means max 10 different asset tokens | |
/// @dev _maxPositions = 0 means no asset tokens are investable | |
- constructor(uint _maxPositions) { maxPositions = _maxPositions; } | |
+ constructor(uint _maxPositions) public { maxPositions = _maxPositions; } | |
- function rule(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) | |
+ function rule(bytes4 sig, address[5] calldata addresses, uint[3] calldata values, bytes32 identifier) | |
external | |
- view | |
returns (bool) | |
{ | |
Accounting accounting = Accounting(Hub(Trading(msg.sender).hub()).accounting()); | |
@@ -26,6 +26,6 @@ contract MaxPositions is TradingSignatures, Policy { | |
return accounting.getOwnedAssetsLength() <= maxPositions; | |
} | |
- function position() external view returns (Applied) { return Applied.post; } | |
- function identifier() external view returns (string) { return 'Max positions'; } | |
+ function position() external pure returns (Applied) { return Applied.post; } | |
+ function identifier() external pure returns (string memory) { return 'Max positions'; } | |
} | |
diff --git a/src/contracts/fund/policies/risk-management/PriceTolerance.sol b/src/fund/policies/risk-management/PriceTolerance.sol | |
similarity index 68% | |
rename from src/contracts/fund/policies/risk-management/PriceTolerance.sol | |
rename to src/fund/policies/risk-management/PriceTolerance.sol | |
index 948cc615..30bfa82b 100644 | |
--- a/src/contracts/fund/policies/risk-management/PriceTolerance.sol | |
+++ b/src/fund/policies/risk-management/PriceTolerance.sol | |
@@ -1,20 +1,22 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "Hub.sol"; | |
-import "Policy.sol"; | |
-import "MatchingMarketAdapter.sol"; | |
-import "PriceSource.i.sol"; | |
-import "TradingSignatures.sol"; | |
-import "math.sol"; | |
+import "../../hub/Hub.sol"; | |
+import "../../../prices/IPriceSource.sol"; | |
+import "../TradingSignatures.sol"; | |
+import "../../../dependencies/DSMath.sol"; | |
+import "../../trading/Trading.sol"; | |
+import "../../../exchanges/interfaces/IOasisDex.sol"; | |
+ | |
+contract PriceTolerance is TradingSignatures, DSMath { | |
+ enum Applied { pre, post } | |
-contract PriceTolerance is TradingSignatures, DSMath, Policy { | |
uint public tolerance; | |
uint constant MULTIPLIER = 10 ** 16; // to give effect of a percentage | |
uint constant DIVISOR = 10 ** 18; | |
// _tolerance: 10 equals to 10% of tolerance | |
- constructor(uint _tolerancePercent) { | |
+ constructor(uint _tolerancePercent) public { | |
require(_tolerancePercent <= 100, "Tolerance range is 0% - 100%"); | |
tolerance = mul(_tolerancePercent, MULTIPLIER); | |
} | |
@@ -41,11 +43,11 @@ contract PriceTolerance is TradingSignatures, DSMath, Policy { | |
makerAsset, | |
maxTakerQuantity, | |
takerAsset | |
- ) = MatchingMarket(ofExchange).getOffer(uint(identifier)); | |
+ ) = IOasisDex(ofExchange).getOffer(uint(identifier)); | |
uint fillMakerQuantity = mul(fillTakerQuantity, maxMakerQuantity) / maxTakerQuantity; | |
- PriceSourceInterface pricefeed = PriceSourceInterface(Hub(Trading(address(msg.sender)).hub()).priceSource()); | |
+ IPriceSource pricefeed = IPriceSource(Hub(Trading(address(msg.sender)).hub()).priceSource()); | |
uint referencePrice; | |
(referencePrice,) = pricefeed.getReferencePriceInfo(takerAsset, makerAsset); | |
@@ -65,12 +67,12 @@ contract PriceTolerance is TradingSignatures, DSMath, Policy { | |
function takeGenericOrder( | |
address makerAsset, | |
address takerAsset, | |
- uint[3] values | |
+ uint[3] memory values | |
) public view returns (bool) { | |
uint fillTakerQuantity = values[2]; | |
uint fillMakerQuantity = mul(fillTakerQuantity, values[0]) / values[1]; | |
- PriceSourceInterface pricefeed = PriceSourceInterface(Hub(Trading(address(msg.sender)).hub()).priceSource()); | |
+ IPriceSource pricefeed = IPriceSource(Hub(Trading(address(msg.sender)).hub()).priceSource()); | |
uint referencePrice; | |
(referencePrice, ) = pricefeed.getReferencePriceInfo(takerAsset, makerAsset); | |
@@ -88,8 +90,8 @@ contract PriceTolerance is TradingSignatures, DSMath, Policy { | |
} | |
function takeOrder( | |
- address[5] addresses, | |
- uint[3] values, | |
+ address[5] memory addresses, | |
+ uint[3] memory values, | |
bytes32 identifier | |
) public view returns (bool) { | |
if (identifier == 0x0) { | |
@@ -100,15 +102,15 @@ contract PriceTolerance is TradingSignatures, DSMath, Policy { | |
} | |
function makeOrder( | |
- address[5] addresses, | |
- uint[3] values, | |
+ address[5] memory addresses, | |
+ uint[3] memory values, | |
bytes32 identifier | |
) public view returns (bool) { | |
- PriceSourceInterface pricefeed = PriceSourceInterface(Hub(Trading(address(msg.sender)).hub()).priceSource()); | |
+ IPriceSource pricefeed = IPriceSource(Hub(Trading(address(msg.sender)).hub()).priceSource()); | |
uint ratio; | |
- (ratio,) = PriceSourceInterface(pricefeed).getReferencePriceInfo(addresses[2], addresses[3]); | |
- uint _value = PriceSourceInterface(pricefeed).getOrderPriceInfo(addresses[2], addresses[3], values[0], values[1]); | |
+ (ratio,) = IPriceSource(pricefeed).getReferencePriceInfo(addresses[2], addresses[3]); | |
+ uint _value = IPriceSource(pricefeed).getOrderPriceInfo(addresses[2], addresses[3], values[0], values[1]); | |
int res = signedSafeSub(int(ratio), int(_value)); | |
if (res < 0) { | |
@@ -120,10 +122,10 @@ contract PriceTolerance is TradingSignatures, DSMath, Policy { | |
function rule( | |
bytes4 sig, | |
- address[5] addresses, | |
- uint[3] values, | |
+ address[5] calldata addresses, | |
+ uint[3] calldata values, | |
bytes32 identifier | |
- ) external view returns (bool) { | |
+ ) external returns (bool) { | |
if (sig == MAKE_ORDER) { | |
return makeOrder(addresses, values, identifier); | |
} else if (sig == TAKE_ORDER) { | |
@@ -132,6 +134,6 @@ contract PriceTolerance is TradingSignatures, DSMath, Policy { | |
revert("Signature was neither MakeOrder nor TakeOrder"); | |
} | |
- function position() external view returns (Applied) { return Applied.pre; } | |
- function identifier() external view returns (string) { return 'Price tolerance'; } | |
+ function position() external pure returns (Applied) { return Applied.pre; } | |
+ function identifier() external pure returns (string memory) { return 'Price tolerance'; } | |
} | |
diff --git a/src/fund/shares/IShares.sol b/src/fund/shares/IShares.sol | |
new file mode 100644 | |
index 00000000..a5b06862 | |
--- /dev/null | |
+++ b/src/fund/shares/IShares.sol | |
@@ -0,0 +1,12 @@ | |
+pragma solidity 0.5.15; | |
+ | |
+/// @notice Token representing ownership of the Fund | |
+interface IShares { | |
+ function createFor(address who, uint amount) external; | |
+ function destroyFor(address who, uint amount) external; | |
+} | |
+ | |
+interface ISharesFactory { | |
+ function createInstance(address _hub) external returns (address); | |
+} | |
+ | |
diff --git a/src/contracts/fund/shares/Shares.sol b/src/fund/shares/Shares.sol | |
similarity index 82% | |
rename from src/contracts/fund/shares/Shares.sol | |
rename to src/fund/shares/Shares.sol | |
index c4fa4108..9e712b57 100644 | |
--- a/src/contracts/fund/shares/Shares.sol | |
+++ b/src/fund/shares/Shares.sol | |
@@ -1,16 +1,15 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "Shares.i.sol"; | |
-import "Spoke.sol"; | |
-import "StandardToken.sol"; | |
-import "Factory.sol"; | |
+import "../hub/Spoke.sol"; | |
+import "../../dependencies/token/StandardToken.sol"; | |
+import "../../factory/Factory.sol"; | |
-contract Shares is Spoke, StandardToken, SharesInterface { | |
+contract Shares is Spoke, StandardToken { | |
string public symbol; | |
string public name; | |
uint8 public decimals; | |
- constructor(address _hub) Spoke(_hub) { | |
+ constructor(address _hub) public Spoke(_hub) { | |
name = hub.name(); | |
symbol = "MLNF"; | |
decimals = 18; | |
@@ -66,7 +65,7 @@ contract Shares is Spoke, StandardToken, SharesInterface { | |
contract SharesFactory is Factory { | |
function createInstance(address _hub) external returns (address) { | |
- address shares = new Shares(_hub); | |
+ address shares = address(new Shares(_hub)); | |
childExists[shares] = true; | |
emit NewInstance(_hub, shares); | |
return shares; | |
diff --git a/src/fund/trading/ITrading.sol b/src/fund/trading/ITrading.sol | |
new file mode 100644 | |
index 00000000..34e9808d | |
--- /dev/null | |
+++ b/src/fund/trading/ITrading.sol | |
@@ -0,0 +1,43 @@ | |
+pragma solidity 0.5.15; | |
+ | |
+pragma experimental ABIEncoderV2; | |
+ | |
+// TODO: Restore indexed params | |
+ | |
+/// @notice Mediation between a Fund and exchanges | |
+interface ITrading { | |
+ function callOnExchange( | |
+ uint exchangeIndex, | |
+ string calldata methodSignature, | |
+ address[8] calldata orderAddresses, | |
+ uint[8] calldata orderValues, | |
+ bytes[4] calldata orderData, | |
+ bytes32 identifier, | |
+ bytes calldata signature | |
+ ) external; | |
+ | |
+ function addOpenMakeOrder( | |
+ address ofExchange, | |
+ address ofSellAsset, | |
+ address ofBuyAsset, | |
+ uint orderId, | |
+ uint expiryTime | |
+ ) external; | |
+ | |
+ function removeOpenMakeOrder( | |
+ address ofExchange, | |
+ address ofSellAsset | |
+ ) external; | |
+ | |
+ function updateAndGetQuantityBeingTraded(address _asset) external returns (uint256); | |
+ function getOpenMakeOrdersAgainstAsset(address _asset) external view returns (uint256); | |
+} | |
+ | |
+interface ITradingFactory { | |
+ function createInstance( | |
+ address _hub, | |
+ address[] calldata _exchanges, | |
+ address[] calldata _adapters, | |
+ address _registry | |
+ ) external returns (address); | |
+} | |
diff --git a/src/contracts/fund/trading/Trading.sol b/src/fund/trading/Trading.sol | |
similarity index 71% | |
rename from src/contracts/fund/trading/Trading.sol | |
rename to src/fund/trading/Trading.sol | |
index 0e67aa0e..36793e4e 100644 | |
--- a/src/contracts/fund/trading/Trading.sol | |
+++ b/src/fund/trading/Trading.sol | |
@@ -1,18 +1,28 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
pragma experimental ABIEncoderV2; | |
-import "Trading.i.sol"; | |
-import "Spoke.sol"; | |
-import "Vault.sol"; | |
-import "PolicyManager.sol"; | |
-import "Factory.sol"; | |
-import "math.sol"; | |
-import "ExchangeAdapter.sol"; | |
-import "LibOrder.sol"; | |
-import "Registry.sol"; | |
-import "TokenUser.sol"; | |
- | |
-contract Trading is DSMath, TokenUser, Spoke, TradingInterface { | |
+import "../hub/Spoke.sol"; | |
+import "../vault/Vault.sol"; | |
+import "../policies/PolicyManager.sol"; | |
+import "../policies/TradingSignatures.sol"; | |
+import "../../factory/Factory.sol"; | |
+import "../../dependencies/DSMath.sol"; | |
+import "../../exchanges/ExchangeAdapter.sol"; | |
+import "../../exchanges/interfaces/IZeroExV2.sol"; | |
+import "../../exchanges/interfaces/IZeroExV3.sol"; | |
+import "../../version/Registry.sol"; | |
+import "../../dependencies/TokenUser.sol"; | |
+ | |
+contract Trading is DSMath, TokenUser, Spoke, TradingSignatures { | |
+ event ExchangeMethodCall( | |
+ address indexed exchangeAddress, | |
+ string indexed methodSignature, | |
+ address[8] orderAddresses, | |
+ uint[8] orderValues, | |
+ bytes[4] orderData, | |
+ bytes32 identifier, | |
+ bytes signature | |
+ ); | |
struct Exchange { | |
address exchange; | |
@@ -47,8 +57,9 @@ contract Trading is DSMath, TokenUser, Spoke, TradingInterface { | |
mapping (address => mapping(address => OpenMakeOrder)) public exchangesToOpenMakeOrders; | |
mapping (address => uint) public openMakeOrdersAgainstAsset; | |
mapping (address => bool) public isInOpenMakeOrder; | |
- mapping (address => uint) public makerAssetCooldown; | |
- mapping (bytes32 => LibOrder.Order) public orderIdToZeroExOrder; | |
+ mapping (address => uint) public makerAssetCooldown; | |
+ mapping (bytes32 => IZeroExV2.Order) internal orderIdToZeroExV2Order; | |
+ mapping (bytes32 => IZeroExV3.Order) internal orderIdToZeroExV3Order; | |
uint public constant ORDER_LIFESPAN = 1 days; | |
uint public constant MAKE_ORDER_COOLDOWN = 30 minutes; | |
@@ -60,10 +71,13 @@ contract Trading is DSMath, TokenUser, Spoke, TradingInterface { | |
constructor( | |
address _hub, | |
- address[] _exchanges, | |
- address[] _adapters, | |
+ address[] memory _exchanges, | |
+ address[] memory _adapters, | |
address _registry | |
- ) Spoke(_hub) { | |
+ ) | |
+ public | |
+ Spoke(_hub) | |
+ { | |
routes.registry = _registry; | |
require(_exchanges.length == _adapters.length, "Array lengths unequal"); | |
for (uint i = 0; i < _exchanges.length; i++) { | |
@@ -72,7 +86,11 @@ contract Trading is DSMath, TokenUser, Spoke, TradingInterface { | |
} | |
/// @notice Fallback function to receive ETH from WETH | |
- function() public payable {} | |
+ function() external payable {} | |
+ | |
+ function addExchange(address _exchange, address _adapter) external auth { | |
+ _addExchange(_exchange, _adapter); | |
+ } | |
function _addExchange( | |
address _exchange, | |
@@ -106,6 +124,8 @@ contract Trading is DSMath, TokenUser, Spoke, TradingInterface { | |
/// @param orderAddresses [3] Order taker asset | |
/// @param orderAddresses [4] feeRecipientAddress | |
/// @param orderAddresses [5] senderAddress | |
+ /// @param orderAddresses [6] maker fee asset | |
+ /// @param orderAddresses [7] taker fee asset | |
/// @param orderValues [0] makerAssetAmount | |
/// @param orderValues [1] takerAssetAmount | |
/// @param orderValues [2] Maker fee | |
@@ -114,34 +134,36 @@ contract Trading is DSMath, TokenUser, Spoke, TradingInterface { | |
/// @param orderValues [5] Salt/nonce | |
/// @param orderValues [6] Fill amount: amount of taker token to be traded | |
/// @param orderValues [7] Dexy signature mode | |
+ /// @param orderData [0] Encoded data specific to maker asset | |
+ /// @param orderData [1] Encoded data specific to taker asset | |
+ /// @param orderData [2] Encoded data specific to maker asset fee | |
+ /// @param orderData [3] Encoded data specific to taker asset fee | |
/// @param identifier Order identifier | |
- /// @param makerAssetData Encoded data specific to makerAsset. | |
- /// @param takerAssetData Encoded data specific to takerAsset. | |
- /// @param signature Signature of order maker. | |
+ /// @param signature Signature of order maker | |
function callOnExchange( | |
uint exchangeIndex, | |
- string methodSignature, | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
+ string memory methodSignature, | |
+ address[8] memory orderAddresses, | |
+ uint[8] memory orderValues, | |
+ bytes[4] memory orderData, | |
bytes32 identifier, | |
- bytes makerAssetData, | |
- bytes takerAssetData, | |
- bytes signature | |
+ bytes memory signature | |
) | |
public | |
onlyInitialized | |
{ | |
- bytes4 methodSelector = bytes4(keccak256(methodSignature)); | |
+ bytes4 methodSelector = bytes4(keccak256(bytes(methodSignature))); | |
require( | |
Registry(routes.registry).adapterMethodIsAllowed( | |
exchanges[exchangeIndex].adapter, | |
methodSelector | |
- ) | |
+ ), | |
+ "Adapter method not allowed" | |
); | |
PolicyManager(routes.policyManager).preValidate(methodSelector, [orderAddresses[0], orderAddresses[1], orderAddresses[2], orderAddresses[3], exchanges[exchangeIndex].exchange], [orderValues[0], orderValues[1], orderValues[6]], identifier); | |
if ( | |
- methodSelector == bytes4(hex'79705be7') || // make | |
- methodSelector == bytes4(hex'e51be6e8') // take | |
+ methodSelector == MAKE_ORDER || | |
+ methodSelector == TAKE_ORDER | |
) { | |
require(Registry(routes.registry).assetIsRegistered( | |
orderAddresses[2]), 'Maker asset not registered' | |
@@ -149,31 +171,39 @@ contract Trading is DSMath, TokenUser, Spoke, TradingInterface { | |
require(Registry(routes.registry).assetIsRegistered( | |
orderAddresses[3]), 'Taker asset not registered' | |
); | |
+ if (orderAddresses[6] != address(0) && methodSelector == MAKE_ORDER) { | |
+ require( | |
+ Registry(routes.registry).assetIsRegistered(orderAddresses[6]), | |
+ 'Maker fee asset not registered' | |
+ ); | |
+ } | |
+ if (orderAddresses[7] != address(0) && methodSelector == TAKE_ORDER) { | |
+ require( | |
+ Registry(routes.registry).assetIsRegistered(orderAddresses[7]), | |
+ 'Taker fee asset not registered' | |
+ ); | |
+ } | |
} | |
- require( | |
- exchanges[exchangeIndex].adapter.delegatecall( | |
- abi.encodeWithSignature( | |
- methodSignature, | |
- exchanges[exchangeIndex].exchange, | |
- orderAddresses, | |
- orderValues, | |
- identifier, | |
- makerAssetData, | |
- takerAssetData, | |
- signature | |
- ) | |
- ), | |
- "Delegated call to exchange failed" | |
+ (bool success, bytes memory returnData) = exchanges[exchangeIndex].adapter.delegatecall( | |
+ abi.encodeWithSignature( | |
+ methodSignature, | |
+ exchanges[exchangeIndex].exchange, | |
+ orderAddresses, | |
+ orderValues, | |
+ orderData, | |
+ identifier, | |
+ signature | |
+ ) | |
); | |
+ require(success, string(returnData)); | |
PolicyManager(routes.policyManager).postValidate(methodSelector, [orderAddresses[0], orderAddresses[1], orderAddresses[2], orderAddresses[3], exchanges[exchangeIndex].exchange], [orderValues[0], orderValues[1], orderValues[6]], identifier); | |
emit ExchangeMethodCall( | |
exchanges[exchangeIndex].exchange, | |
methodSignature, | |
orderAddresses, | |
orderValues, | |
+ orderData, | |
identifier, | |
- makerAssetData, | |
- takerAssetData, | |
signature | |
); | |
} | |
@@ -213,7 +243,7 @@ contract Trading is DSMath, TokenUser, Spoke, TradingInterface { | |
) internal { | |
if (isInOpenMakeOrder[sellAsset]) { | |
- makerAssetCooldown[sellAsset] = add(block.timestamp, MAKE_ORDER_COOLDOWN); | |
+ makerAssetCooldown[sellAsset] = add(block.timestamp, MAKE_ORDER_COOLDOWN); | |
address buyAsset = exchangesToOpenMakeOrders[exchange][sellAsset].buyAsset; | |
delete exchangesToOpenMakeOrders[exchange][sellAsset]; | |
openMakeOrdersAgainstAsset[buyAsset] = sub(openMakeOrdersAgainstAsset[buyAsset], 1); | |
@@ -228,19 +258,25 @@ contract Trading is DSMath, TokenUser, Spoke, TradingInterface { | |
} | |
/// @dev Bit of Redundancy for now | |
- function addZeroExOrderData( | |
+ function addZeroExV2OrderData( | |
bytes32 orderId, | |
- LibOrder.Order zeroExOrderData | |
+ IZeroExV2.Order memory zeroExOrderData | |
) public delegateInternal { | |
- orderIdToZeroExOrder[orderId] = zeroExOrderData; | |
+ orderIdToZeroExV2Order[orderId] = zeroExOrderData; | |
+ } | |
+ function addZeroExV3OrderData( | |
+ bytes32 orderId, | |
+ IZeroExV3.Order memory zeroExOrderData | |
+ ) public delegateInternal { | |
+ orderIdToZeroExV3Order[orderId] = zeroExOrderData; | |
} | |
function orderUpdateHook( | |
address ofExchange, | |
bytes32 orderId, | |
UpdateType updateType, | |
- address[2] orderAddresses, | |
- uint[3] orderValues | |
+ address payable[2] memory orderAddresses, | |
+ uint[3] memory orderValues | |
) public delegateInternal { | |
// only save make/take | |
if (updateType == UpdateType.make || updateType == UpdateType.take) { | |
@@ -258,8 +294,8 @@ contract Trading is DSMath, TokenUser, Spoke, TradingInterface { | |
} | |
} | |
- function updateAndGetQuantityBeingTraded(address _asset) public returns (uint) { | |
- uint quantityHere = ERC20(_asset).balanceOf(this); | |
+ function updateAndGetQuantityBeingTraded(address _asset) external returns (uint) { | |
+ uint quantityHere = IERC20(_asset).balanceOf(address(this)); | |
return add(updateAndGetQuantityHeldInExchange(_asset), quantityHere); | |
} | |
@@ -293,7 +329,7 @@ contract Trading is DSMath, TokenUser, Spoke, TradingInterface { | |
return sub(totalSellQuantity, totalSellQuantityInApprove); // Since quantity in approve is not actually in custody | |
} | |
- function returnBatchToVault(address[] _tokens) public { | |
+ function returnBatchToVault(address[] memory _tokens) public { | |
for (uint i = 0; i < _tokens.length; i++) { | |
returnAssetToVault(_tokens[i]); | |
} | |
@@ -304,10 +340,10 @@ contract Trading is DSMath, TokenUser, Spoke, TradingInterface { | |
msg.sender == address(this) || msg.sender == hub.manager() || hub.isShutDown(), | |
"Sender is not this contract or manager" | |
); | |
- safeTransfer(_token, routes.vault, ERC20(_token).balanceOf(this)); | |
+ safeTransfer(_token, routes.vault, IERC20(_token).balanceOf(address(this))); | |
} | |
- function getExchangeInfo() public view returns (address[], address[], bool[]) { | |
+ function getExchangeInfo() public view returns (address[] memory, address[] memory, bool[] memory) { | |
address[] memory ofExchanges = new address[](exchanges.length); | |
address[] memory ofAdapters = new address[](exchanges.length); | |
bool[] memory takesCustody = new bool[](exchanges.length); | |
@@ -320,7 +356,7 @@ contract Trading is DSMath, TokenUser, Spoke, TradingInterface { | |
} | |
function getOpenOrderInfo(address ofExchange, address ofAsset) public view returns (uint, uint, uint) { | |
- OpenMakeOrder order = exchangesToOpenMakeOrders[ofExchange][ofAsset]; | |
+ OpenMakeOrder memory order = exchangesToOpenMakeOrders[ofExchange][ofAsset]; | |
return (order.id, order.expiresAt, order.orderIndex); | |
} | |
@@ -333,8 +369,16 @@ contract Trading is DSMath, TokenUser, Spoke, TradingInterface { | |
return (order.makerAsset, order.takerAsset, order.makerQuantity, order.takerQuantity); | |
} | |
- function getZeroExOrderDetails(bytes32 orderId) public view returns (LibOrder.Order) { | |
- return orderIdToZeroExOrder[orderId]; | |
+ function getZeroExV2OrderDetails(bytes32 orderId) public view returns (IZeroExV2.Order memory) { | |
+ return orderIdToZeroExV2Order[orderId]; | |
+ } | |
+ | |
+ function getZeroExV3OrderDetails(bytes32 orderId) public view returns (IZeroExV3.Order memory) { | |
+ return orderIdToZeroExV3Order[orderId]; | |
+ } | |
+ | |
+ function getOpenMakeOrdersAgainstAsset(address _asset) external view returns (uint256) { | |
+ return openMakeOrdersAgainstAsset[_asset]; | |
} | |
} | |
@@ -349,11 +393,11 @@ contract TradingFactory is Factory { | |
function createInstance( | |
address _hub, | |
- address[] _exchanges, | |
- address[] _adapters, | |
+ address[] memory _exchanges, | |
+ address[] memory _adapters, | |
address _registry | |
) public returns (address) { | |
- address trading = new Trading(_hub, _exchanges, _adapters, _registry); | |
+ address trading = address(new Trading(_hub, _exchanges, _adapters, _registry)); | |
childExists[trading] = true; | |
emit NewInstance( | |
_hub, | |
diff --git a/src/fund/vault/IVault.sol b/src/fund/vault/IVault.sol | |
new file mode 100644 | |
index 00000000..29d1fd4a | |
--- /dev/null | |
+++ b/src/fund/vault/IVault.sol | |
@@ -0,0 +1,10 @@ | |
+pragma solidity 0.5.15; | |
+ | |
+/// @notice Custody component | |
+interface IVault { | |
+ function withdraw(address token, uint amount) external; | |
+} | |
+ | |
+interface IVaultFactory { | |
+ function createInstance(address _hub) external returns (address); | |
+} | |
diff --git a/src/contracts/fund/vault/Vault.sol b/src/fund/vault/Vault.sol | |
similarity index 52% | |
rename from src/contracts/fund/vault/Vault.sol | |
rename to src/fund/vault/Vault.sol | |
index 5090db64..2158e14b 100644 | |
--- a/src/contracts/fund/vault/Vault.sol | |
+++ b/src/fund/vault/Vault.sol | |
@@ -1,14 +1,13 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "Vault.i.sol"; | |
-import "Spoke.sol"; | |
-import "Factory.sol"; | |
-import "TokenUser.sol"; | |
+import "../hub/Spoke.sol"; | |
+import "../../factory/Factory.sol"; | |
+import "../../dependencies/TokenUser.sol"; | |
/// @notice Dumb custody component | |
-contract Vault is VaultInterface, TokenUser, Spoke { | |
+contract Vault is TokenUser, Spoke { | |
- constructor(address _hub) Spoke(_hub) {} | |
+ constructor(address _hub) public Spoke(_hub) {} | |
function withdraw(address token, uint amount) external auth { | |
safeTransfer(token, msg.sender, amount); | |
@@ -17,9 +16,9 @@ contract Vault is VaultInterface, TokenUser, Spoke { | |
contract VaultFactory is Factory { | |
function createInstance(address _hub) external returns (address) { | |
- address vault = new Vault(_hub); | |
+ address vault = address(new Vault(_hub)); | |
childExists[vault] = true; | |
- NewInstance(_hub, vault); | |
+ emit NewInstance(_hub, vault); | |
return vault; | |
} | |
} | |
diff --git a/src/prices/IPriceSource.sol b/src/prices/IPriceSource.sol | |
new file mode 100644 | |
index 00000000..9386a279 | |
--- /dev/null | |
+++ b/src/prices/IPriceSource.sol | |
@@ -0,0 +1,28 @@ | |
+pragma solidity 0.5.15; | |
+ | |
+/// @notice Must return a value for an asset | |
+interface IPriceSource { | |
+ function getQuoteAsset() external view returns (address); | |
+ function getLastUpdate() external view returns (uint); | |
+ | |
+ /// @notice Returns false if asset not applicable, or price not recent | |
+ function hasValidPrice(address) external view returns (bool); | |
+ function hasValidPrices(address[] calldata) external view returns (bool); | |
+ | |
+ /// @notice Return the last known price, and when it was issued | |
+ function getPrice(address _asset) external view returns (uint price, uint timestamp); | |
+ function getPrices(address[] calldata _assets) external view returns (uint[] memory prices, uint[] memory timestamps); | |
+ | |
+ /// @notice Get price info, and revert if not valid | |
+ function getPriceInfo(address _asset) external view returns (uint price, uint decimals); | |
+ function getInvertedPriceInfo(address ofAsset) external view returns (uint price, uint decimals); | |
+ | |
+ function getReferencePriceInfo(address _base, address _quote) external view returns (uint referencePrice, uint decimal); | |
+ function getOrderPriceInfo(address sellAsset, address buyAsset, uint sellQuantity, uint buyQuantity) external view returns (uint orderPrice); | |
+ function existsPriceOnAssetPair(address sellAsset, address buyAsset) external view returns (bool isExistent); | |
+ function convertQuantity( | |
+ uint fromAssetQuantity, | |
+ address fromAsset, | |
+ address toAsset | |
+ ) external view returns (uint); | |
+} | |
diff --git a/src/contracts/prices/KyberPriceFeed.sol b/src/prices/KyberPriceFeed.sol | |
similarity index 78% | |
rename from src/contracts/prices/KyberPriceFeed.sol | |
rename to src/prices/KyberPriceFeed.sol | |
index 6d5ea322..6f13ab5a 100644 | |
--- a/src/contracts/prices/KyberPriceFeed.sol | |
+++ b/src/prices/KyberPriceFeed.sol | |
@@ -1,24 +1,25 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "PriceSource.i.sol"; | |
-import "ERC20.i.sol"; | |
-import "thing.sol"; | |
-import "KyberNetworkProxy.sol"; | |
-import "Registry.sol"; | |
+import "../dependencies/token/IERC20.sol"; | |
+import "../dependencies/DSMath.sol"; | |
+import "../dependencies/DSAuth.sol"; // TODO: remove? this may not be used at all | |
+import "../exchanges/interfaces/IKyberNetworkProxy.sol"; | |
+import "../version/Registry.sol"; | |
/// @title Price Feed Template | |
/// @author Melonport AG <team@melonport.com> | |
/// @notice Routes external data to smart contracts | |
/// @notice Where external data includes sharePrice of Melon funds | |
/// @notice PriceFeed operator could be staked and sharePrice input validated on chain | |
-contract KyberPriceFeed is PriceSourceInterface, DSThing { | |
+contract KyberPriceFeed is DSMath, DSAuth { | |
+ event PriceUpdate(address[] token, uint[] price); | |
address public KYBER_NETWORK_PROXY; | |
address public QUOTE_ASSET; | |
address public UPDATER; | |
Registry public REGISTRY; | |
uint public MAX_SPREAD; | |
- address public constant KYBER_ETH_TOKEN = 0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee; | |
+ address public constant KYBER_ETH_TOKEN = address(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee); | |
uint public constant KYBER_PRECISION = 18; | |
uint public constant VALIDITY_INTERVAL = 2 days; | |
uint public lastUpdate; | |
@@ -33,16 +34,19 @@ contract KyberPriceFeed is PriceSourceInterface, DSThing { | |
/// @dev Define and register a quote asset against which all prices are measured/based against | |
constructor( | |
- address ofRegistrar, | |
+ address ofRegistry, | |
address ofKyberNetworkProxy, | |
uint ofMaxSpread, | |
- address ofQuoteAsset | |
- ) { | |
+ address ofQuoteAsset, | |
+ address initialUpdater | |
+ ) | |
+ public | |
+ { | |
KYBER_NETWORK_PROXY = ofKyberNetworkProxy; | |
MAX_SPREAD = ofMaxSpread; | |
QUOTE_ASSET = ofQuoteAsset; | |
- REGISTRY = Registry(ofRegistrar); | |
- UPDATER = REGISTRY.owner(); | |
+ REGISTRY = Registry(ofRegistry); | |
+ UPDATER = initialUpdater; | |
} | |
/// @dev Stores zero as a convention for invalid price | |
@@ -56,12 +60,17 @@ contract KyberPriceFeed is PriceSourceInterface, DSThing { | |
for (uint i; i < assets.length; i++) { | |
bool isValid; | |
uint price; | |
- (isValid, price) = getKyberPrice(assets[i], QUOTE_ASSET); | |
+ if (assets[i] == QUOTE_ASSET) { | |
+ isValid = true; | |
+ price = 1 ether; | |
+ } else { | |
+ (isValid, price) = getKyberPrice(assets[i], QUOTE_ASSET); | |
+ } | |
newPrices[i] = isValid ? price : 0; | |
prices[assets[i]] = newPrices[i]; | |
} | |
lastUpdate = block.timestamp; | |
- PriceUpdate(assets, newPrices); | |
+ emit PriceUpdate(assets, newPrices); | |
} | |
function setUpdater(address _updater) external { | |
@@ -102,17 +111,17 @@ contract KyberPriceFeed is PriceSourceInterface, DSThing { | |
timestamp = now; | |
} | |
- function getPrices(address[] _assets) | |
+ function getPrices(address[] memory _assets) | |
public | |
view | |
- returns (uint[], uint[]) | |
+ returns (uint256[] memory, uint256[] memory) | |
{ | |
- uint[] memory prices = new uint[](_assets.length); | |
+ uint[] memory newPrices = new uint[](_assets.length); | |
uint[] memory timestamps = new uint[](_assets.length); | |
for (uint i; i < _assets.length; i++) { | |
- (prices[i], timestamps[i]) = getPrice(_assets[i]); | |
+ (newPrices[i], timestamps[i]) = getPrice(_assets[i]); | |
} | |
- return (prices, timestamps); | |
+ return (newPrices, timestamps); | |
} | |
function hasValidPrice(address _asset) | |
@@ -125,7 +134,7 @@ contract KyberPriceFeed is PriceSourceInterface, DSThing { | |
return prices[_asset] != 0 && isRegistered && isFresh; | |
} | |
- function hasValidPrices(address[] _assets) | |
+ function hasValidPrices(address[] memory _assets) | |
public | |
view | |
returns (bool) | |
@@ -167,7 +176,7 @@ contract KyberPriceFeed is PriceSourceInterface, DSThing { | |
returns (bool isValid, uint referencePrice, uint decimals) | |
{ | |
isValid = hasValidPrice(_baseAsset) && hasValidPrice(_quoteAsset); | |
- uint quoteDecimals = ERC20Clone(_quoteAsset).decimals(); | |
+ uint256 quoteDecimals = ERC20WithFields(_quoteAsset).decimals(); | |
if (prices[_quoteAsset] == 0) { | |
return (false, 0, 0); // return early and avoid revert | |
@@ -208,7 +217,7 @@ contract KyberPriceFeed is PriceSourceInterface, DSThing { | |
} | |
/// @dev Get Kyber representation of ETH if necessary | |
- function getKyberMaskAsset(address _asset) public returns (address) { | |
+ function getKyberMaskAsset(address _asset) public view returns (address) { | |
if (_asset == REGISTRY.nativeAsset()) { | |
return KYBER_ETH_TOKEN; | |
} | |
@@ -223,14 +232,14 @@ contract KyberPriceFeed is PriceSourceInterface, DSThing { | |
{ | |
uint bidRate; | |
uint bidRateOfReversePair; | |
- (bidRate,) = KyberNetworkProxy(KYBER_NETWORK_PROXY).getExpectedRate( | |
- ERC20Clone(getKyberMaskAsset(_baseAsset)), | |
- ERC20Clone(getKyberMaskAsset(_quoteAsset)), | |
+ (bidRate,) = IKyberNetworkProxy(KYBER_NETWORK_PROXY).getExpectedRate( | |
+ getKyberMaskAsset(_baseAsset), | |
+ getKyberMaskAsset(_quoteAsset), | |
REGISTRY.getReserveMin(_baseAsset) | |
); | |
- (bidRateOfReversePair,) = KyberNetworkProxy(KYBER_NETWORK_PROXY).getExpectedRate( | |
- ERC20Clone(getKyberMaskAsset(_quoteAsset)), | |
- ERC20Clone(getKyberMaskAsset(_baseAsset)), | |
+ (bidRateOfReversePair,) = IKyberNetworkProxy(KYBER_NETWORK_PROXY).getExpectedRate( | |
+ getKyberMaskAsset(_quoteAsset), | |
+ getKyberMaskAsset(_baseAsset), | |
REGISTRY.getReserveMin(_quoteAsset) | |
); | |
@@ -239,22 +248,29 @@ contract KyberPriceFeed is PriceSourceInterface, DSThing { | |
} | |
uint askRate = 10 ** (KYBER_PRECISION * 2) / bidRateOfReversePair; | |
- // Check the the spread and average the price on both sides | |
- uint spreadFromKyber = mul( | |
- sub(askRate, bidRate), | |
- 10 ** uint(KYBER_PRECISION) | |
- ) / bidRate; | |
/** | |
- avgPriceFromKyber = (bidRate + astRate) / 2 | |
+ Average the bid/ask prices: | |
+ avgPriceFromKyber = (bidRate + askRate) / 2 | |
kyberPrice = (avgPriceFromKyber * 10^quoteDecimals) / 10^kyberPrecision | |
or, rearranged: | |
kyberPrice = ((bidRate + askRate) * 10^quoteDecimals) / 2 * 10^kyberPrecision | |
*/ | |
uint kyberPrice = mul( | |
add(bidRate, askRate), | |
- 10 ** uint(ERC20Clone(_quoteAsset).decimals()) // use original quote decimals (not defined on mask) | |
+ 10 ** uint(ERC20WithFields(_quoteAsset).decimals()) // use original quote decimals (not defined on mask) | |
) / mul(2, 10 ** uint(KYBER_PRECISION)); | |
+ // Find the "quoted spread", to inform caller whether it is below maximum | |
+ uint spreadFromKyber; | |
+ if (bidRate > askRate) { | |
+ spreadFromKyber = 0; // crossed market condition | |
+ } else { | |
+ spreadFromKyber = mul( | |
+ sub(askRate, bidRate), | |
+ 10 ** uint(KYBER_PRECISION) | |
+ ) / kyberPrice; | |
+ } | |
+ | |
return ( | |
spreadFromKyber <= MAX_SPREAD && bidRate != 0 && askRate != 0, | |
kyberPrice | |
@@ -278,7 +294,7 @@ contract KyberPriceFeed is PriceSourceInterface, DSThing { | |
returns (uint orderPrice) | |
{ | |
// TODO: decimals | |
- return mul(buyQuantity, 10 ** uint(ERC20Clone(sellAsset).decimals())) / sellQuantity; | |
+ return mul(buyQuantity, 10 ** uint(ERC20WithFields(sellAsset).decimals())) / sellQuantity; | |
} | |
/// @notice Checks whether data exists for a given asset pair | |
diff --git a/src/version/IVersion.sol b/src/version/IVersion.sol | |
new file mode 100644 | |
index 00000000..5f51a6b3 | |
--- /dev/null | |
+++ b/src/version/IVersion.sol | |
@@ -0,0 +1,6 @@ | |
+pragma solidity 0.5.15; | |
+ | |
+interface IVersion { | |
+ function shutDownFund(address) external; | |
+} | |
+ | |
diff --git a/src/contracts/version/Registry.sol b/src/version/Registry.sol | |
similarity index 89% | |
rename from src/contracts/version/Registry.sol | |
rename to src/version/Registry.sol | |
index a86309d1..c19f9c12 100644 | |
--- a/src/contracts/version/Registry.sol | |
+++ b/src/version/Registry.sol | |
@@ -1,8 +1,8 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "auth.sol"; | |
-import "Hub.sol"; | |
-import "ERC20.i.sol"; | |
+import "../dependencies/DSAuth.sol"; | |
+import "../fund/hub/Hub.sol"; | |
+import "../dependencies/token/IERC20.sol"; | |
contract Registry is DSAuth { | |
@@ -100,14 +100,14 @@ contract Registry is DSAuth { | |
// METHODS | |
- constructor(address _postDeployOwner) { | |
+ constructor(address _postDeployOwner) public { | |
setOwner(_postDeployOwner); | |
} | |
// PUBLIC METHODS | |
/// @notice Whether _name has only valid characters | |
- function isValidFundName(string _name) public view returns (bool) { | |
+ function isValidFundName(string memory _name) public pure returns (bool) { | |
bytes memory b = bytes(_name); | |
if (b.length > MAX_FUND_NAME_BYTES) return false; | |
for (uint i; i < b.length; i++){ | |
@@ -127,8 +127,8 @@ contract Registry is DSAuth { | |
} | |
/// @notice Whether _user can use _name for their fund | |
- function canUseFundName(address _user, string _name) public view returns (bool) { | |
- bytes32 nameHash = keccak256(_name); | |
+ function canUseFundName(address _user, string memory _name) public view returns (bool) { | |
+ bytes32 nameHash = keccak256(bytes(_name)); | |
return ( | |
isValidFundName(_name) && | |
( | |
@@ -138,15 +138,15 @@ contract Registry is DSAuth { | |
); | |
} | |
- function reserveFundName(address _owner, string _name) | |
+ function reserveFundName(address _owner, string calldata _name) | |
external | |
onlyVersion | |
{ | |
require(canUseFundName(_owner, _name), "Fund name cannot be used"); | |
- fundNameHashToOwner[keccak256(_name)] = _owner; | |
+ fundNameHashToOwner[keccak256(bytes(_name))] = _owner; | |
} | |
- function registerFund(address _fund, address _owner, string _name) | |
+ function registerFund(address _fund, address _owner, string calldata _name) | |
external | |
onlyVersion | |
{ | |
@@ -165,12 +165,12 @@ contract Registry is DSAuth { | |
/// @param _sigs Function signatures for whitelisted asset functions | |
function registerAsset( | |
address _asset, | |
- string _name, | |
- string _symbol, | |
- string _url, | |
+ string calldata _name, | |
+ string calldata _symbol, | |
+ string calldata _url, | |
uint _reserveMin, | |
- uint[] _standards, | |
- bytes4[] _sigs | |
+ uint[] calldata _standards, | |
+ bytes4[] calldata _sigs | |
) external auth { | |
require(registeredAssets.length < MAX_REGISTERED_ENTITIES); | |
require(!assetInformation[_asset].exists); | |
@@ -199,7 +199,7 @@ contract Registry is DSAuth { | |
address _exchange, | |
address _adapter, | |
bool _takesCustody, | |
- bytes4[] _sigs | |
+ bytes4[] calldata _sigs | |
) external auth { | |
require(!exchangeInformation[_adapter].exists, "Adapter already exists"); | |
exchangeInformation[_adapter].exists = true; | |
@@ -273,15 +273,15 @@ contract Registry is DSAuth { | |
/// @param _url Url for extended information of the asset | |
function updateAsset( | |
address _asset, | |
- string _name, | |
- string _symbol, | |
- string _url, | |
+ string memory _name, | |
+ string memory _symbol, | |
+ string memory _url, | |
uint _reserveMin, | |
- uint[] _standards, | |
- bytes4[] _sigs | |
+ uint[] memory _standards, | |
+ bytes4[] memory _sigs | |
) public auth { | |
require(assetInformation[_asset].exists); | |
- Asset asset = assetInformation[_asset]; | |
+ Asset storage asset = assetInformation[_asset]; | |
asset.name = _name; | |
asset.symbol = _symbol; | |
asset.decimals = ERC20WithFields(_asset).decimals(); | |
@@ -305,10 +305,10 @@ contract Registry is DSAuth { | |
address _exchange, | |
address _adapter, | |
bool _takesCustody, | |
- bytes4[] _sigs | |
+ bytes4[] memory _sigs | |
) public auth { | |
require(exchangeInformation[_adapter].exists, "Exchange with adapter doesn't exist"); | |
- Exchange exchange = exchangeInformation[_adapter]; | |
+ Exchange storage exchange = exchangeInformation[_adapter]; | |
exchange.exchangeAddress = _exchange; | |
exchange.takesCustody = _takesCustody; | |
exchange.sigs = _sigs; | |
@@ -357,13 +357,13 @@ contract Registry is DSAuth { | |
emit ExchangeAdapterRemoval(_adapter); | |
} | |
- function registerFees(address[] _fees) external auth { | |
+ function registerFees(address[] calldata _fees) external auth { | |
for (uint i; i < _fees.length; i++) { | |
isFeeRegistered[_fees[i]] = true; | |
} | |
} | |
- function deregisterFees(address[] _fees) external auth { | |
+ function deregisterFees(address[] calldata _fees) external auth { | |
for (uint i; i < _fees.length; i++) { | |
delete isFeeRegistered[_fees[i]]; | |
} | |
@@ -372,10 +372,10 @@ contract Registry is DSAuth { | |
// PUBLIC VIEW METHODS | |
// get asset specific information | |
- function getName(address _asset) external view returns (string) { | |
+ function getName(address _asset) external view returns (string memory) { | |
return assetInformation[_asset].name; | |
} | |
- function getSymbol(address _asset) external view returns (string) { | |
+ function getSymbol(address _asset) external view returns (string memory) { | |
return assetInformation[_asset].symbol; | |
} | |
function getDecimals(address _asset) external view returns (uint) { | |
@@ -387,7 +387,7 @@ contract Registry is DSAuth { | |
function assetIsRegistered(address _asset) external view returns (bool) { | |
return assetInformation[_asset].exists; | |
} | |
- function getRegisteredAssets() external view returns (address[]) { | |
+ function getRegisteredAssets() external view returns (address[] memory) { | |
return registeredAssets; | |
} | |
function assetMethodIsAllowed(address _asset, bytes4 _sig) | |
@@ -408,7 +408,7 @@ contract Registry is DSAuth { | |
function exchangeAdapterIsRegistered(address _adapter) external view returns (bool) { | |
return exchangeInformation[_adapter].exists; | |
} | |
- function getRegisteredExchangeAdapters() external view returns (address[]) { | |
+ function getRegisteredExchangeAdapters() external view returns (address[] memory) { | |
return registeredExchangeAdapters; | |
} | |
function getExchangeInformation(address _adapter) | |
@@ -416,20 +416,20 @@ contract Registry is DSAuth { | |
view | |
returns (address, bool) | |
{ | |
- Exchange exchange = exchangeInformation[_adapter]; | |
+ Exchange memory exchange = exchangeInformation[_adapter]; | |
return ( | |
exchange.exchangeAddress, | |
exchange.takesCustody | |
); | |
} | |
function exchangeForAdapter(address _adapter) external view returns (address) { | |
- Exchange exchange = exchangeInformation[_adapter]; | |
+ Exchange memory exchange = exchangeInformation[_adapter]; | |
return exchange.exchangeAddress; | |
} | |
function getAdapterFunctionSignatures(address _adapter) | |
public | |
view | |
- returns (bytes4[]) | |
+ returns (bytes4[] memory) | |
{ | |
return exchangeInformation[_adapter].sigs; | |
} | |
@@ -450,7 +450,7 @@ contract Registry is DSAuth { | |
} | |
// get version and fund information | |
- function getRegisteredVersions() external view returns (address[]) { | |
+ function getRegisteredVersions() external view returns (address[] memory) { | |
return registeredVersions; | |
} | |
@@ -458,12 +458,12 @@ contract Registry is DSAuth { | |
if (fundsToVersions[_who] != address(0)) { | |
return true; // directly from a hub | |
} else { | |
- address hub = Hub(Spoke(_who).hub()); | |
+ Hub hub = Hub(Spoke(_who).hub()); | |
require( | |
- Hub(hub).isSpoke(_who), | |
+ hub.isSpoke(_who), | |
"Call from either a spoke or hub" | |
); | |
- return fundsToVersions[hub] != address(0); | |
+ return fundsToVersions[address(hub)] != address(0); | |
} | |
} | |
diff --git a/src/contracts/version/Version.sol b/src/version/Version.sol | |
similarity index 82% | |
rename from src/contracts/version/Version.sol | |
rename to src/version/Version.sol | |
index 8736bb39..1ac03bed 100644 | |
--- a/src/contracts/version/Version.sol | |
+++ b/src/version/Version.sol | |
@@ -1,12 +1,11 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
pragma experimental ABIEncoderV2; | |
-import "Version.i.sol"; | |
-import "FundFactory.sol"; | |
-import "Hub.sol"; | |
+import "../factory/FundFactory.sol"; | |
+import "../fund/hub/Hub.sol"; | |
/// @notice Controlled by governance | |
-contract Version is FundFactory, DSAuth, VersionInterface { | |
+contract Version is FundFactory, DSAuth { | |
constructor( | |
address _accountingFactory, | |
@@ -19,6 +18,7 @@ contract Version is FundFactory, DSAuth, VersionInterface { | |
address _registry, | |
address _postDeployOwner | |
) | |
+ public | |
FundFactory( | |
_accountingFactory, | |
_feeManagerFactory, | |
@@ -30,7 +30,7 @@ contract Version is FundFactory, DSAuth, VersionInterface { | |
address(this) | |
) | |
{ | |
- registry = _registry; | |
+ associatedRegistry = Registry(_registry); | |
setOwner(_postDeployOwner); | |
} | |
@@ -42,4 +42,3 @@ contract Version is FundFactory, DSAuth, VersionInterface { | |
Hub(_hub).shutDownFund(); | |
} | |
} | |
- | |
diff --git a/tests/contracts/BooleanPolicy.sol b/tests/contracts/BooleanPolicy.sol | |
new file mode 100644 | |
index 00000000..7e40258e | |
--- /dev/null | |
+++ b/tests/contracts/BooleanPolicy.sol | |
@@ -0,0 +1,23 @@ | |
+pragma solidity 0.5.15; | |
+ | |
+contract BooleanPolicy { | |
+ enum Applied { pre, post } | |
+ | |
+ bool allowed; | |
+ | |
+ function rule(bytes4 sig, address[5] calldata addresses, uint[3] calldata values, bytes32 identifier) external returns (bool) { | |
+ return allowed; | |
+ } | |
+ | |
+ function position() external pure returns (Applied) { return Applied.pre; } | |
+} | |
+ | |
+contract TruePolicy is BooleanPolicy { | |
+ constructor() public { allowed = true; } | |
+ function identifier() external pure returns (string memory) { return "TruePolicy"; } | |
+} | |
+ | |
+contract FalsePolicy is BooleanPolicy { | |
+ constructor() public { allowed = false; } | |
+ function identifier() external pure returns (string memory) { return "FalsePolicy"; } | |
+} | |
diff --git a/src/contracts/dependencies/token/MaliciousToken.sol b/tests/contracts/MaliciousToken.sol | |
similarity index 81% | |
rename from src/contracts/dependencies/token/MaliciousToken.sol | |
rename to tests/contracts/MaliciousToken.sol | |
index baf9bb20..db531d9d 100644 | |
--- a/src/contracts/dependencies/token/MaliciousToken.sol | |
+++ b/tests/contracts/MaliciousToken.sol | |
@@ -1,12 +1,12 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "PreminedToken.sol"; | |
+import "main/dependencies/token/PreminedToken.sol"; | |
contract MaliciousToken is PreminedToken { | |
bool public isReverting = false; | |
- constructor(string _symbol, uint8 _decimals, string _name) | |
+ constructor(string memory _symbol, uint8 _decimals, string memory _name) | |
public | |
PreminedToken(_symbol, _decimals, _name) | |
{} | |
diff --git a/src/contracts/fund/accounting/MockAccounting.sol b/tests/contracts/MockAccounting.sol | |
similarity index 71% | |
rename from src/contracts/fund/accounting/MockAccounting.sol | |
rename to tests/contracts/MockAccounting.sol | |
index 1eb13d2a..1c8e3b0b 100644 | |
--- a/src/contracts/fund/accounting/MockAccounting.sol | |
+++ b/tests/contracts/MockAccounting.sol | |
@@ -1,6 +1,6 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "Spoke.sol"; | |
+import "main/fund/hub/Spoke.sol"; | |
/// @dev Balances are fake and can be set by anyone (testing) | |
contract MockAccounting is Spoke { | |
@@ -8,7 +8,7 @@ contract MockAccounting is Spoke { | |
uint public gav; | |
uint public nav; | |
uint public unclaimedFees; | |
- uint public valuePerShare; | |
+ uint public mockValuePerShare; | |
address[] public ownedAssets; | |
mapping (address => bool) public isInAssetList; | |
@@ -19,28 +19,28 @@ contract MockAccounting is Spoke { | |
uint public DEFAULT_SHARE_PRICE; | |
uint public SHARES_DECIMALS; | |
- constructor(address _hub, address _denominationAsset, address _nativeAsset, address[] _defaultAssets) | |
+ constructor(address _hub, address _denominationAsset, address _nativeAsset) | |
+ public | |
Spoke(_hub) | |
{ | |
- setOwnedAssets(_defaultAssets); | |
DENOMINATION_ASSET = _denominationAsset; | |
NATIVE_ASSET = _nativeAsset; | |
SHARES_DECIMALS = 18; | |
DEFAULT_SHARE_PRICE = 10 ** uint(SHARES_DECIMALS); | |
} | |
- function setOwnedAssets(address[] _assets) public { ownedAssets = _assets; } | |
- function getOwnedAssetsLength() public returns (uint) { return ownedAssets.length; } | |
+ function setOwnedAssets(address[] memory _assets) public { ownedAssets = _assets; } | |
+ function getOwnedAssetsLength() public view returns (uint) { return ownedAssets.length; } | |
function setGav(uint _gav) public { gav = _gav; } | |
function setNav(uint _nav) public { nav = _nav; } | |
function setAssetGAV(address _asset, uint _amt) public { assetGav[_asset] = _amt; } | |
- function setFundHoldings(uint[] _amounts, address[] _assets) public { | |
+ function setFundHoldings(uint[] memory _amounts, address[] memory _assets) public { | |
for (uint i = 0; i < _assets.length; i++) { | |
held[_assets[i]] = _amounts[i]; | |
} | |
} | |
- function getFundHoldings() public returns (uint[], address[]) { | |
+ function getFundHoldings() public view returns (uint[] memory, address[] memory) { | |
uint[] memory _quantities = new uint[](ownedAssets.length); | |
address[] memory _assets = new address[](ownedAssets.length); | |
for (uint i = 0; i < ownedAssets.length; i++) { | |
@@ -56,17 +56,17 @@ contract MockAccounting is Spoke { | |
return (_quantities, _assets); | |
} | |
- function calcGav() public returns (uint) { return gav; } | |
- function calcNav() public returns (uint) { return nav; } | |
+ function calcGav() public view returns (uint) { return gav; } | |
+ function calcNav() public view returns (uint) { return nav; } | |
- function calcAssetGAV(address _a) public returns (uint) { return assetGav[_a]; } | |
+ function calcAssetGAV(address _a) public view returns (uint) { return assetGav[_a]; } | |
function valuePerShare(uint totalValue, uint numShares) public view returns (uint) { | |
- return valuePerShare; | |
+ return mockValuePerShare; | |
} | |
function performCalculations() public view returns (uint, uint, uint, uint, uint) { | |
- return (gav, unclaimedFees, 0, nav, valuePerShare); | |
+ return (gav, unclaimedFees, 0, nav, mockValuePerShare); | |
} | |
function calcSharePrice() public view returns (uint sharePrice) { | |
diff --git a/src/contracts/exchanges/MockAdapter.sol b/tests/contracts/MockAdapter.sol | |
similarity index 65% | |
rename from src/contracts/exchanges/MockAdapter.sol | |
rename to tests/contracts/MockAdapter.sol | |
index ad41adab..3523c83d 100644 | |
--- a/src/contracts/exchanges/MockAdapter.sol | |
+++ b/tests/contracts/MockAdapter.sol | |
@@ -1,9 +1,10 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
+pragma experimental ABIEncoderV2; | |
-import "Trading.sol"; | |
-import "Hub.sol"; | |
-import "Accounting.sol"; | |
-import "ExchangeAdapter.sol"; | |
+import "main/fund/trading/Trading.sol"; | |
+import "main/fund/hub/Hub.sol"; | |
+import "main/fund/accounting/Accounting.sol"; | |
+import "main/exchanges/ExchangeAdapter.sol"; | |
contract MockAdapter is ExchangeAdapter { | |
@@ -14,12 +15,11 @@ contract MockAdapter is ExchangeAdapter { | |
/// @notice Mock make order | |
function makeOrder( | |
address targetExchange, | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
+ address[8] memory orderAddresses, | |
+ uint[8] memory orderValues, | |
+ bytes[4] memory orderData, | |
bytes32 identifier, | |
- bytes makerAssetData, | |
- bytes takerAssetData, | |
- bytes signature | |
+ bytes memory signature | |
) public { | |
Hub hub = getHub(); | |
address makerAsset = orderAddresses[2]; | |
@@ -31,21 +31,20 @@ contract MockAdapter is ExchangeAdapter { | |
targetExchange, | |
identifier, | |
Trading.UpdateType.make, | |
- [address(makerAsset), address(takerAsset)], | |
+ [address(uint160(makerAsset)), address(uint160(takerAsset))], | |
[makerQuantity, takerQuantity, uint(0)] | |
); | |
- Trading(address(this)).addOpenMakeOrder(targetExchange, makerAsset, takerAsset, uint(identifier), 0); | |
+ getTrading().addOpenMakeOrder(targetExchange, makerAsset, takerAsset, uint(identifier), 0); | |
} | |
/// @notice Mock take order | |
function takeOrder( | |
address targetExchange, | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
+ address[8] memory orderAddresses, | |
+ uint[8] memory orderValues, | |
+ bytes[4] memory orderData, | |
bytes32 identifier, | |
- bytes makerAssetData, | |
- bytes takerAssetData, | |
- bytes signature | |
+ bytes memory signature | |
) public { | |
address makerAsset = orderAddresses[2]; | |
address takerAsset = orderAddresses[3]; | |
@@ -57,7 +56,7 @@ contract MockAdapter is ExchangeAdapter { | |
targetExchange, | |
bytes32(identifier), | |
Trading.UpdateType.take, | |
- [address(makerAsset), address(takerAsset)], | |
+ [address(uint160(makerAsset)), address(uint160(takerAsset))], | |
[makerQuantity, takerQuantity, fillTakerQuantity] | |
); | |
} | |
@@ -65,12 +64,11 @@ contract MockAdapter is ExchangeAdapter { | |
/// @notice Mock cancel order | |
function cancelOrder( | |
address targetExchange, | |
- address[6] orderAddresses, | |
- uint[8] orderValues, | |
+ address[8] memory orderAddresses, | |
+ uint[8] memory orderValues, | |
+ bytes[4] memory orderData, | |
bytes32 identifier, | |
- bytes makerAssetData, | |
- bytes takerAssetData, | |
- bytes signature | |
+ bytes memory signature | |
) public { | |
Hub hub = getHub(); | |
address makerAsset = orderAddresses[2]; | |
diff --git a/src/contracts/fund/fees/MockFee.sol b/tests/contracts/MockFee.sol | |
similarity index 77% | |
rename from src/contracts/fund/fees/MockFee.sol | |
rename to tests/contracts/MockFee.sol | |
index 4f071fe2..757b35a4 100644 | |
--- a/src/contracts/fund/fees/MockFee.sol | |
+++ b/tests/contracts/MockFee.sol | |
@@ -1,15 +1,13 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "Fee.i.sol"; | |
- | |
-contract MockFee is Fee { | |
+contract MockFee { | |
uint public fee; | |
uint public FEE_RATE; | |
uint public FEE_PERIOD; | |
uint public feeNumber; | |
- constructor(uint _feeNumber) { | |
+ constructor(uint _feeNumber) public { | |
feeNumber = _feeNumber; | |
} | |
@@ -17,7 +15,7 @@ contract MockFee is Fee { | |
fee = amount; | |
} | |
- function feeAmount() public view returns (uint feeInShares) { | |
+ function feeAmount() external returns (uint feeInShares) { | |
return fee; | |
} | |
diff --git a/src/contracts/fund/fees/MockFeeManager.sol b/tests/contracts/MockFeeManager.sol | |
similarity index 58% | |
rename from src/contracts/fund/fees/MockFeeManager.sol | |
rename to tests/contracts/MockFeeManager.sol | |
index 8c15c637..866cfe0f 100644 | |
--- a/src/contracts/fund/fees/MockFeeManager.sol | |
+++ b/tests/contracts/MockFeeManager.sol | |
@@ -1,12 +1,11 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
pragma experimental ABIEncoderV2; | |
-import "Fee.i.sol"; | |
-import "Spoke.sol"; | |
-import "Shares.sol"; | |
-import "Factory.sol"; | |
-import "math.sol"; | |
-import "AmguConsumer.sol"; | |
+import "main/fund/hub/Spoke.sol"; | |
+import "main/fund/shares/Shares.sol"; | |
+import "main/factory/Factory.sol"; | |
+import "main/dependencies/DSMath.sol"; | |
+import "main/engine/AmguConsumer.sol"; | |
contract MockFeeManager is DSMath, AmguConsumer, Spoke { | |
@@ -22,8 +21,8 @@ contract MockFeeManager is DSMath, AmguConsumer, Spoke { | |
constructor( | |
address _hub, | |
address _denominationAsset, | |
- address[] _fees, | |
- uint[] _periods, | |
+ address[] memory _fees, | |
+ uint[] memory _periods, | |
uint _rates, | |
address registry | |
) Spoke(_hub) public {} | |
@@ -32,6 +31,6 @@ contract MockFeeManager is DSMath, AmguConsumer, Spoke { | |
function setPerformanceFeeAmount(uint _amt) public { performanceFees = _amt; } | |
function rewardManagementFee() public { return; } | |
- function performanceFeeAmount() public view returns (uint) { return performanceFees; } | |
- function totalFeeAmount() public view returns (uint) { return totalFees; } | |
+ function performanceFeeAmount() external returns (uint) { return performanceFees; } | |
+ function totalFeeAmount() external returns (uint) { return totalFees; } | |
} | |
diff --git a/src/contracts/fund/hub/MockHub.sol b/tests/contracts/MockHub.sol | |
similarity index 95% | |
rename from src/contracts/fund/hub/MockHub.sol | |
rename to tests/contracts/MockHub.sol | |
index a3da104b..370f3566 100644 | |
--- a/src/contracts/fund/hub/MockHub.sol | |
+++ b/tests/contracts/MockHub.sol | |
@@ -1,7 +1,7 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "guard.sol"; | |
-import "Spoke.sol"; | |
+import "main/dependencies/DSGuard.sol"; | |
+import "main/fund/hub/Spoke.sol"; | |
/// @notice Hub used for testing | |
contract MockHub is DSGuard { | |
@@ -27,13 +27,13 @@ contract MockHub is DSGuard { | |
function setManager(address _manager) public { manager = _manager; } | |
- function setName(string _name) public { name = _name; } | |
+ function setName(string memory _name) public { name = _name; } | |
function shutDownFund() public { isShutDown = true; } | |
function setShutDownState(bool _state) public { isShutDown = _state; } | |
- function setSpokes(address[12] _spokes) public { | |
+ function setSpokes(address[12] memory _spokes) public { | |
routes.accounting = _spokes[0]; | |
routes.feeManager = _spokes[1]; | |
routes.participation = _spokes[2]; | |
diff --git a/src/contracts/version/MockRegistry.sol b/tests/contracts/MockRegistry.sol | |
similarity index 96% | |
rename from src/contracts/version/MockRegistry.sol | |
rename to tests/contracts/MockRegistry.sol | |
index f80a2a58..dda61c6b 100644 | |
--- a/src/contracts/version/MockRegistry.sol | |
+++ b/tests/contracts/MockRegistry.sol | |
@@ -1,6 +1,6 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "auth.sol"; | |
+import "main/dependencies/DSAuth.sol"; | |
/// @dev Simplified for testing, and by default rigged to always return true | |
contract MockRegistry is DSAuth { | |
@@ -62,7 +62,7 @@ contract MockRegistry is DSAuth { | |
function isFundFactory(address _who) public view returns (bool) { | |
return _who == fundFactory; | |
} | |
- function getRegisteredAssets() public view returns (address[]) { return assets; } | |
+ function getRegisteredAssets() public view returns (address[] memory) { return assets; } | |
function getReserveMin(address _asset) public view returns (uint) { return 0; } | |
function isFeeRegistered(address _fee) public view returns (bool) { | |
return alwaysRegistered; | |
diff --git a/src/contracts/fund/shares/MockShares.sol b/tests/contracts/MockShares.sol | |
similarity index 78% | |
rename from src/contracts/fund/shares/MockShares.sol | |
rename to tests/contracts/MockShares.sol | |
index bfdb2a1b..37f5f8cf 100644 | |
--- a/src/contracts/fund/shares/MockShares.sol | |
+++ b/tests/contracts/MockShares.sol | |
@@ -1,16 +1,15 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "Shares.i.sol"; | |
-import "Spoke.sol"; | |
-import "StandardToken.sol"; | |
+import "main/fund/hub/Spoke.sol"; | |
+import "main/dependencies/token/StandardToken.sol"; | |
/// @dev Shares can be destroyed and created by anyone (testing) | |
-contract MockShares is Spoke, StandardToken, SharesInterface { | |
+contract MockShares is Spoke, StandardToken { | |
string public symbol; | |
string public name; | |
uint8 public decimals; | |
- constructor(address _hub) Spoke(_hub) { | |
+ constructor(address _hub) public Spoke(_hub) { | |
name = hub.name(); | |
symbol = "MOCK"; | |
decimals = 18; | |
diff --git a/src/contracts/version/MockVersion.sol b/tests/contracts/MockVersion.sol | |
similarity index 67% | |
rename from src/contracts/version/MockVersion.sol | |
rename to tests/contracts/MockVersion.sol | |
index e03918e0..591ea881 100644 | |
--- a/src/contracts/version/MockVersion.sol | |
+++ b/tests/contracts/MockVersion.sol | |
@@ -1,16 +1,15 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "Version.i.sol"; | |
-import "Hub.sol"; | |
+import "main/fund/hub/Hub.sol"; | |
/// @notice Version contract useful for testing | |
-contract MockVersion is VersionInterface { | |
+contract MockVersion { | |
uint public amguPrice; | |
bool public isShutDown; | |
function setAmguPrice(uint _price) public { amguPrice = _price; } | |
function securityShutDown() external { isShutDown = true; } | |
function shutDownFund(address _hub) external { Hub(_hub).shutDownFund(); } | |
- function getShutDownStatus() external returns (bool) {return isShutDown;} | |
+ function getShutDownStatus() external view returns (bool) {return isShutDown;} | |
function getAmguPrice() public view returns (uint) { return amguPrice; } | |
} | |
diff --git a/src/contracts/dependencies/PermissiveAuthority.sol b/tests/contracts/PermissiveAuthority.sol | |
similarity index 75% | |
rename from src/contracts/dependencies/PermissiveAuthority.sol | |
rename to tests/contracts/PermissiveAuthority.sol | |
index 2f33f6b5..8e5e2c60 100644 | |
--- a/src/contracts/dependencies/PermissiveAuthority.sol | |
+++ b/tests/contracts/PermissiveAuthority.sol | |
@@ -1,6 +1,6 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "auth.sol"; | |
+import "main/dependencies/DSAuth.sol"; | |
contract PermissiveAuthority is DSAuthority { | |
function canCall(address src, address dst, bytes4 sig) | |
diff --git a/src/contracts/testing/SelfDestructing.sol b/tests/contracts/SelfDestructing.sol | |
similarity index 50% | |
rename from src/contracts/testing/SelfDestructing.sol | |
rename to tests/contracts/SelfDestructing.sol | |
index 22572ead..b1784888 100644 | |
--- a/src/contracts/testing/SelfDestructing.sol | |
+++ b/tests/contracts/SelfDestructing.sol | |
@@ -1,10 +1,10 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
/// @dev Useful for testing force-sending of funds | |
contract SelfDestructing { | |
- function bequeath(address _heir) public { | |
+ function bequeath(address payable _heir) public { | |
selfdestruct(_heir); | |
} | |
- function () public payable {} | |
+ function () external payable {} | |
} | |
diff --git a/src/contracts/prices/TestingPriceFeed.sol b/tests/contracts/TestingPriceFeed.sol | |
similarity index 89% | |
rename from src/contracts/prices/TestingPriceFeed.sol | |
rename to tests/contracts/TestingPriceFeed.sol | |
index 7ab7ada5..b1296a70 100644 | |
--- a/src/contracts/prices/TestingPriceFeed.sol | |
+++ b/tests/contracts/TestingPriceFeed.sol | |
@@ -1,13 +1,12 @@ | |
-pragma solidity ^0.4.25; | |
+pragma solidity 0.5.15; | |
-import "ERC20.i.sol"; | |
-import "PriceSource.i.sol"; | |
-import "UpdatableFeed.i.sol"; | |
-import "math.sol"; | |
+import "main/dependencies/token/IERC20.sol"; | |
+import "main/dependencies/DSMath.sol"; | |
/// @notice Intended for testing purposes only | |
/// @notice Updates and exposes price information | |
-contract TestingPriceFeed is UpdatableFeedInterface, PriceSourceInterface, DSMath { | |
+contract TestingPriceFeed is DSMath { | |
+ event PriceUpdate(address[] token, uint[] price); | |
struct Data { | |
uint price; | |
@@ -22,7 +21,7 @@ contract TestingPriceFeed is UpdatableFeedInterface, PriceSourceInterface, DSMat | |
bool mockIsRecent = true; | |
bool neverValid = false; | |
- constructor(address _quoteAsset, uint _quoteDecimals) { | |
+ constructor(address _quoteAsset, uint _quoteDecimals) public { | |
QUOTE_ASSET = _quoteAsset; | |
setDecimals(_quoteAsset, _quoteDecimals); | |
} | |
@@ -31,7 +30,7 @@ contract TestingPriceFeed is UpdatableFeedInterface, PriceSourceInterface, DSMat | |
Input price is how much quote asset you would get | |
for one unit of _asset (10**assetDecimals) | |
*/ | |
- function update(address[] _assets, uint[] _prices) external { | |
+ function update(address[] calldata _assets, uint[] calldata _prices) external { | |
require(_assets.length == _prices.length, "Array lengths unequal"); | |
updateId++; | |
for (uint i = 0; i < _assets.length; ++i) { | |
@@ -41,7 +40,7 @@ contract TestingPriceFeed is UpdatableFeedInterface, PriceSourceInterface, DSMat | |
}); | |
} | |
lastUpdate = block.timestamp; | |
- PriceUpdate(_assets, _prices); | |
+ emit PriceUpdate(_assets, _prices); | |
} | |
function getPrice(address ofAsset) | |
@@ -49,14 +48,14 @@ contract TestingPriceFeed is UpdatableFeedInterface, PriceSourceInterface, DSMat | |
view | |
returns (uint price, uint timestamp) | |
{ | |
- Data data = assetsToPrices[ofAsset]; | |
+ Data storage data = assetsToPrices[ofAsset]; | |
return (data.price, data.timestamp); | |
} | |
- function getPrices(address[] ofAssets) | |
+ function getPrices(address[] memory ofAssets) | |
public | |
view | |
- returns (uint[], uint[]) | |
+ returns (uint[] memory, uint[] memory) | |
{ | |
uint[] memory prices = new uint[](ofAssets.length); | |
uint[] memory timestamps = new uint[](ofAssets.length); | |
@@ -114,7 +113,7 @@ contract TestingPriceFeed is UpdatableFeedInterface, PriceSourceInterface, DSMat | |
} | |
// needed just to get decimals for prices | |
- function batchSetDecimals(address[] _assets, uint[] _decimals) public { | |
+ function batchSetDecimals(address[] memory _assets, uint[] memory _decimals) public { | |
require(_assets.length == _decimals.length, "Array lengths unequal"); | |
for (uint i = 0; i < _assets.length; i++) { | |
setDecimals(_assets[i], _decimals[i]); | |
@@ -168,7 +167,7 @@ contract TestingPriceFeed is UpdatableFeedInterface, PriceSourceInterface, DSMat | |
return !neverValid && price != 0; | |
} | |
- function hasValidPrices(address[] _assets) | |
+ function hasValidPrices(address[] memory _assets) | |
public | |
view | |
returns (bool) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment