diff options
author | Fabio Berger <me@fabioberger.com> | 2018-09-28 19:08:12 +0800 |
---|---|---|
committer | Fabio Berger <me@fabioberger.com> | 2018-09-28 19:08:12 +0800 |
commit | ba7de7204d29d4004c347190be7a3b8c84951b82 (patch) | |
tree | 9dddbd1ded45484a6cb968cdf799bf5ce991477b /packages/contracts/src | |
parent | f3ad64aa1c2930affbfd074316b5f407580b7523 (diff) | |
parent | a737cfa004ee1dc18be935f61fb9c289ed5623fd (diff) | |
download | dexon-sol-tools-ba7de7204d29d4004c347190be7a3b8c84951b82.tar dexon-sol-tools-ba7de7204d29d4004c347190be7a3b8c84951b82.tar.gz dexon-sol-tools-ba7de7204d29d4004c347190be7a3b8c84951b82.tar.bz2 dexon-sol-tools-ba7de7204d29d4004c347190be7a3b8c84951b82.tar.lz dexon-sol-tools-ba7de7204d29d4004c347190be7a3b8c84951b82.tar.xz dexon-sol-tools-ba7de7204d29d4004c347190be7a3b8c84951b82.tar.zst dexon-sol-tools-ba7de7204d29d4004c347190be7a3b8c84951b82.zip |
merge development
Diffstat (limited to 'packages/contracts/src')
50 files changed, 501 insertions, 260 deletions
diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/Forwarder.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/Forwarder.sol index 6b17bb29b..94dec40ed 100644 --- a/packages/contracts/src/2.0.0/extensions/Forwarder/Forwarder.sol +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/Forwarder.sol @@ -34,7 +34,6 @@ contract Forwarder is MixinExchangeWrapper, MixinForwarderCore { - constructor ( address _exchange, bytes memory _zrxAssetData, diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinAssets.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinAssets.sol index d6a38aa6e..43efb5ff3 100644 --- a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinAssets.sol +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinAssets.sol @@ -31,7 +31,6 @@ contract MixinAssets is LibConstants, MAssets { - using LibBytes for bytes; bytes4 constant internal ERC20_TRANSFER_SELECTOR = bytes4(keccak256("transfer(address,uint256)")); diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol index 9e816716c..fea9a53c2 100644 --- a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol @@ -34,7 +34,6 @@ contract MixinExchangeWrapper is LibConstants, MExchangeWrapper { - /// @dev Fills the input order. /// Returns false if the transaction would otherwise revert. /// @param order Order struct containing order specifications. @@ -61,7 +60,7 @@ contract MixinExchangeWrapper is // Call `fillOrder` and handle any exceptions gracefully assembly { let success := call( - gas, // forward all gas, TODO: look into gas consumption of assert/throw + gas, // forward all gas exchange, // call address of Exchange contract 0, // transfer 0 wei add(fillOrderCalldata, 32), // pointer to start of input (skip array length in first 32 bytes) diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinForwarderCore.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinForwarderCore.sol index 14f191879..54487f726 100644 --- a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinForwarderCore.sol +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinForwarderCore.sol @@ -39,7 +39,6 @@ contract MixinForwarderCore is MExchangeWrapper, IForwarderCore { - using LibBytes for bytes; /// @dev Constructor approves ERC20 proxy to transfer ZRX and WETH on this contract's behalf. @@ -47,10 +46,12 @@ contract MixinForwarderCore is public { address proxyAddress = EXCHANGE.getAssetProxy(ERC20_DATA_ID); - if (proxyAddress != address(0)) { - ETHER_TOKEN.approve(proxyAddress, MAX_UINT); - ZRX_TOKEN.approve(proxyAddress, MAX_UINT); - } + require( + proxyAddress != address(0), + "UNREGISTERED_ASSET_PROXY" + ); + ETHER_TOKEN.approve(proxyAddress, MAX_UINT); + ZRX_TOKEN.approve(proxyAddress, MAX_UINT); } /// @dev Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value. diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinWeth.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinWeth.sol index 5863b522d..d2814a49b 100644 --- a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinWeth.sol +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinWeth.sol @@ -28,7 +28,6 @@ contract MixinWeth is LibConstants, MWeth { - /// @dev Default payabale function, this allows us to withdraw WETH function () public diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/mixins/MAssets.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/mixins/MAssets.sol index 83636432a..9e7f80d97 100644 --- a/packages/contracts/src/2.0.0/extensions/Forwarder/mixins/MAssets.sol +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/mixins/MAssets.sol @@ -24,7 +24,6 @@ import "../interfaces/IAssets.sol"; contract MAssets is IAssets { - /// @dev Transfers given amount of asset to sender. /// @param assetData Byte array encoded for the respective asset proxy. /// @param amount Amount of asset to transfer to sender. diff --git a/packages/contracts/src/2.0.0/extensions/OrderValidator/OrderValidator.sol b/packages/contracts/src/2.0.0/extensions/OrderValidator/OrderValidator.sol index a18345245..8bfde3847 100644 --- a/packages/contracts/src/2.0.0/extensions/OrderValidator/OrderValidator.sol +++ b/packages/contracts/src/2.0.0/extensions/OrderValidator/OrderValidator.sol @@ -28,11 +28,11 @@ import "../../utils/LibBytes/LibBytes.sol"; contract OrderValidator { + using LibBytes for bytes; + bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256("ERC20Token(address)")); bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256("ERC721Token(address,uint256)")); - using LibBytes for bytes; - struct TraderInfo { uint256 makerBalance; // Maker's balance of makerAsset uint256 makerAllowance; // Maker's allowance to corresponding AssetProxy diff --git a/packages/contracts/src/2.0.0/multisig/MultiSigWallet.sol b/packages/contracts/src/2.0.0/multisig/MultiSigWallet.sol index eb54fe047..516e7391c 100644 --- a/packages/contracts/src/2.0.0/multisig/MultiSigWallet.sol +++ b/packages/contracts/src/2.0.0/multisig/MultiSigWallet.sol @@ -1,13 +1,14 @@ // solhint-disable -pragma solidity ^0.4.10; +pragma solidity ^0.4.15; /// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution. /// @author Stefan George - <stefan.george@consensys.net> contract MultiSigWallet { - uint constant public MAX_OWNER_COUNT = 50; - + /* + * Events + */ event Confirmation(address indexed sender, uint indexed transactionId); event Revocation(address indexed sender, uint indexed transactionId); event Submission(uint indexed transactionId); @@ -18,6 +19,14 @@ contract MultiSigWallet { event OwnerRemoval(address indexed owner); event RequirementChange(uint required); + /* + * Constants + */ + uint constant public MAX_OWNER_COUNT = 50; + + /* + * Storage + */ mapping (uint => Transaction) public transactions; mapping (uint => mapping (address => bool)) public confirmations; mapping (address => bool) public isOwner; @@ -32,60 +41,54 @@ contract MultiSigWallet { bool executed; } + /* + * Modifiers + */ modifier onlyWallet() { - if (msg.sender != address(this)) - throw; + require(msg.sender == address(this)); _; } modifier ownerDoesNotExist(address owner) { - if (isOwner[owner]) - throw; + require(!isOwner[owner]); _; } modifier ownerExists(address owner) { - if (!isOwner[owner]) - throw; + require(isOwner[owner]); _; } modifier transactionExists(uint transactionId) { - if (transactions[transactionId].destination == 0) - throw; + require(transactions[transactionId].destination != 0); _; } modifier confirmed(uint transactionId, address owner) { - if (!confirmations[transactionId][owner]) - throw; + require(confirmations[transactionId][owner]); _; } modifier notConfirmed(uint transactionId, address owner) { - if (confirmations[transactionId][owner]) - throw; + require(!confirmations[transactionId][owner]); _; } modifier notExecuted(uint transactionId) { - if (transactions[transactionId].executed) - throw; + require(!transactions[transactionId].executed); _; } modifier notNull(address _address) { - if (_address == 0) - throw; + require(_address != 0); _; } modifier validRequirement(uint ownerCount, uint _required) { - if ( ownerCount > MAX_OWNER_COUNT - || _required > ownerCount - || _required == 0 - || ownerCount == 0) - throw; + require(ownerCount <= MAX_OWNER_COUNT + && _required <= ownerCount + && _required != 0 + && ownerCount != 0); _; } @@ -108,8 +111,7 @@ contract MultiSigWallet { validRequirement(_owners.length, _required) { for (uint i=0; i<_owners.length; i++) { - if (isOwner[_owners[i]] || _owners[i] == 0) - throw; + require(!isOwner[_owners[i]] && _owners[i] != 0); isOwner[_owners[i]] = true; } owners = _owners; @@ -151,7 +153,7 @@ contract MultiSigWallet { /// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet. /// @param owner Address of owner to be replaced. - /// @param owner Address of new owner. + /// @param newOwner Address of new owner. function replaceOwner(address owner, address newOwner) public onlyWallet @@ -222,20 +224,44 @@ contract MultiSigWallet { /// @param transactionId Transaction ID. function executeTransaction(uint transactionId) public + ownerExists(msg.sender) + confirmed(transactionId, msg.sender) notExecuted(transactionId) { if (isConfirmed(transactionId)) { - Transaction tx = transactions[transactionId]; - tx.executed = true; - if (tx.destination.call.value(tx.value)(tx.data)) + Transaction storage txn = transactions[transactionId]; + txn.executed = true; + if (external_call(txn.destination, txn.value, txn.data.length, txn.data)) Execution(transactionId); else { ExecutionFailure(transactionId); - tx.executed = false; + txn.executed = false; } } } + // call has been separated into its own function in order to take advantage + // of the Solidity's code generator to produce a loop that copies tx.data into memory. + function external_call(address destination, uint value, uint dataLength, bytes data) internal returns (bool) { + bool result; + assembly { + let x := mload(0x40) // "Allocate" memory for output (0x40 is where "free memory" pointer is stored by convention) + let d := add(data, 32) // First 32 bytes are the padded length of data, so exclude that + result := call( + sub(gas, 34710), // 34710 is the value that solidity is currently emitting + // It includes callGas (700) + callVeryLow (3, to pay for SUB) + callValueTransferGas (9000) + + // callNewAccountGas (25000, in case the destination address does not exist and needs creating) + destination, + value, + d, + dataLength, // Size of the input (in bytes) - this is what fixes the padding problem + x, + 0 // Output is ignored, therefore the output size is zero + ) + } + return result; + } + /// @dev Returns the confirmation status of a transaction. /// @param transactionId Transaction ID. /// @return Confirmation status. @@ -364,4 +390,4 @@ contract MultiSigWallet { for (i=from; i<to; i++) _transactionIds[i - from] = transactionIdsTemp[i]; } -} +}
\ No newline at end of file diff --git a/packages/contracts/src/2.0.0/multisig/MultiSigWalletWithTimeLock.sol b/packages/contracts/src/2.0.0/multisig/MultiSigWalletWithTimeLock.sol index 8c5e6e1e6..9513d3b30 100644 --- a/packages/contracts/src/2.0.0/multisig/MultiSigWalletWithTimeLock.sol +++ b/packages/contracts/src/2.0.0/multisig/MultiSigWalletWithTimeLock.sol @@ -16,47 +16,57 @@ */ -// solhint-disable -pragma solidity ^0.4.10; +pragma solidity 0.4.24; import "./MultiSigWallet.sol"; /// @title Multisignature wallet with time lock- Allows multiple parties to execute a transaction after a time lock has passed. /// @author Amir Bandeali - <amir@0xProject.com> -contract MultiSigWalletWithTimeLock is MultiSigWallet { - - event ConfirmationTimeSet(uint indexed transactionId, uint confirmationTime); - event TimeLockChange(uint secondsTimeLocked); - - uint public secondsTimeLocked; - - mapping (uint => uint) public confirmationTimes; - - modifier notFullyConfirmed(uint transactionId) { - require(!isConfirmed(transactionId)); +// solhint-disable not-rely-on-time +contract MultiSigWalletWithTimeLock is + MultiSigWallet +{ + event ConfirmationTimeSet(uint256 indexed transactionId, uint256 confirmationTime); + event TimeLockChange(uint256 secondsTimeLocked); + + uint256 public secondsTimeLocked; + + mapping (uint256 => uint256) public confirmationTimes; + + modifier notFullyConfirmed(uint256 transactionId) { + require( + !isConfirmed(transactionId), + "TX_FULLY_CONFIRMED" + ); _; } - modifier fullyConfirmed(uint transactionId) { - require(isConfirmed(transactionId)); + modifier fullyConfirmed(uint256 transactionId) { + require( + isConfirmed(transactionId), + "TX_NOT_FULLY_CONFIRMED" + ); _; } - modifier pastTimeLock(uint transactionId) { - require(block.timestamp >= confirmationTimes[transactionId] + secondsTimeLocked); + modifier pastTimeLock(uint256 transactionId) { + require( + block.timestamp >= confirmationTimes[transactionId] + secondsTimeLocked, + "TIME_LOCK_INCOMPLETE" + ); _; } - /* - * Public functions - */ - /// @dev Contract constructor sets initial owners, required number of confirmations, and time lock. /// @param _owners List of initial owners. /// @param _required Number of required confirmations. /// @param _secondsTimeLocked Duration needed after a transaction is confirmed and before it becomes executable, in seconds. - function MultiSigWalletWithTimeLock(address[] _owners, uint _required, uint _secondsTimeLocked) + constructor ( + address[] _owners, + uint256 _required, + uint256 _secondsTimeLocked + ) public MultiSigWallet(_owners, _required) { @@ -65,17 +75,17 @@ contract MultiSigWalletWithTimeLock is MultiSigWallet { /// @dev Changes the duration of the time lock for transactions. /// @param _secondsTimeLocked Duration needed after a transaction is confirmed and before it becomes executable, in seconds. - function changeTimeLock(uint _secondsTimeLocked) + function changeTimeLock(uint256 _secondsTimeLocked) public onlyWallet { secondsTimeLocked = _secondsTimeLocked; - TimeLockChange(_secondsTimeLocked); + emit TimeLockChange(_secondsTimeLocked); } /// @dev Allows an owner to confirm a transaction. /// @param transactionId Transaction ID. - function confirmTransaction(uint transactionId) + function confirmTransaction(uint256 transactionId) public ownerExists(msg.sender) transactionExists(transactionId) @@ -83,52 +93,35 @@ contract MultiSigWalletWithTimeLock is MultiSigWallet { notFullyConfirmed(transactionId) { confirmations[transactionId][msg.sender] = true; - Confirmation(msg.sender, transactionId); + emit Confirmation(msg.sender, transactionId); if (isConfirmed(transactionId)) { setConfirmationTime(transactionId, block.timestamp); } } - /// @dev Allows an owner to revoke a confirmation for a transaction. - /// @param transactionId Transaction ID. - function revokeConfirmation(uint transactionId) - public - ownerExists(msg.sender) - confirmed(transactionId, msg.sender) - notExecuted(transactionId) - notFullyConfirmed(transactionId) - { - confirmations[transactionId][msg.sender] = false; - Revocation(msg.sender, transactionId); - } - /// @dev Allows anyone to execute a confirmed transaction. /// @param transactionId Transaction ID. - function executeTransaction(uint transactionId) + function executeTransaction(uint256 transactionId) public notExecuted(transactionId) fullyConfirmed(transactionId) pastTimeLock(transactionId) { - Transaction storage tx = transactions[transactionId]; - tx.executed = true; - if (tx.destination.call.value(tx.value)(tx.data)) - Execution(transactionId); - else { - ExecutionFailure(transactionId); - tx.executed = false; + Transaction storage txn = transactions[transactionId]; + txn.executed = true; + if (external_call(txn.destination, txn.value, txn.data.length, txn.data)) { + emit Execution(transactionId); + } else { + emit ExecutionFailure(transactionId); + txn.executed = false; } } - /* - * Internal functions - */ - /// @dev Sets the time of when a submission first passed. - function setConfirmationTime(uint transactionId, uint confirmationTime) + function setConfirmationTime(uint256 transactionId, uint256 confirmationTime) internal { confirmationTimes[transactionId] = confirmationTime; - ConfirmationTimeSet(transactionId, confirmationTime); + emit ConfirmationTimeSet(transactionId, confirmationTime); } } diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC20Proxy.sol b/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC20Proxy.sol index 004c3892d..258443bca 100644 --- a/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC20Proxy.sol +++ b/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC20Proxy.sol @@ -18,7 +18,6 @@ pragma solidity 0.4.24; -import "../../utils/LibBytes/LibBytes.sol"; import "./MixinAuthorizable.sol"; @@ -59,15 +58,64 @@ contract ERC20Proxy is mstore(96, 0) revert(0, 100) } - - /////// Token contract address /////// - // The token address is found as follows: - // * It is stored at offset 4 in `assetData` contents. - // * This is stored at offset 32 from `assetData`. - // * The offset to `assetData` from Params is stored at offset - // 4 in calldata. - // * The offset of Params in calldata is 4. - // So we read location 4 and add 32 + 4 + 4 to it. + + // `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 /////// diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol b/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol index 9d0bc0f74..65b664b8b 100644 --- a/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol +++ b/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol @@ -18,7 +18,6 @@ pragma solidity 0.4.24; -import "../../utils/LibBytes/LibBytes.sol"; import "./MixinAuthorizable.sol"; @@ -80,6 +79,8 @@ contract ERC721Proxy is // (*): 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. diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxy/MixinAuthorizable.sol b/packages/contracts/src/2.0.0/protocol/AssetProxy/MixinAuthorizable.sol index ff4660a31..fe9bbf848 100644 --- a/packages/contracts/src/2.0.0/protocol/AssetProxy/MixinAuthorizable.sol +++ b/packages/contracts/src/2.0.0/protocol/AssetProxy/MixinAuthorizable.sol @@ -26,7 +26,6 @@ contract MixinAuthorizable is Ownable, MAuthorizable { - /// @dev Only authorized addresses can invoke functions with this modifier. modifier onlyAuthorized { require( diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxy/interfaces/IAssetProxy.sol b/packages/contracts/src/2.0.0/protocol/AssetProxy/interfaces/IAssetProxy.sol index 3651dd694..b25d2d75a 100644 --- a/packages/contracts/src/2.0.0/protocol/AssetProxy/interfaces/IAssetProxy.sol +++ b/packages/contracts/src/2.0.0/protocol/AssetProxy/interfaces/IAssetProxy.sol @@ -24,7 +24,6 @@ import "./IAuthorizable.sol"; 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. diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxy/interfaces/IAuthorizable.sol b/packages/contracts/src/2.0.0/protocol/AssetProxy/interfaces/IAuthorizable.sol index 8fac43a47..ba1d4aa77 100644 --- a/packages/contracts/src/2.0.0/protocol/AssetProxy/interfaces/IAuthorizable.sol +++ b/packages/contracts/src/2.0.0/protocol/AssetProxy/interfaces/IAuthorizable.sol @@ -24,7 +24,6 @@ import "../../../utils/Ownable/IOwnable.sol"; contract IAuthorizable is IOwnable { - /// @dev Authorizes an address. /// @param target Address to authorize. function addAuthorizedAddress(address target) diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxy/mixins/MAuthorizable.sol b/packages/contracts/src/2.0.0/protocol/AssetProxy/mixins/MAuthorizable.sol index 8afc8c8d8..d63fb7f6d 100644 --- a/packages/contracts/src/2.0.0/protocol/AssetProxy/mixins/MAuthorizable.sol +++ b/packages/contracts/src/2.0.0/protocol/AssetProxy/mixins/MAuthorizable.sol @@ -24,7 +24,6 @@ import "../interfaces/IAuthorizable.sol"; contract MAuthorizable is IAuthorizable { - // Event logged when a new address is authorized. event AuthorizedAddressAdded( address indexed target, diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxyOwner/AssetProxyOwner.sol b/packages/contracts/src/2.0.0/protocol/AssetProxyOwner/AssetProxyOwner.sol index 8b7333646..edb788fab 100644 --- a/packages/contracts/src/2.0.0/protocol/AssetProxyOwner/AssetProxyOwner.sol +++ b/packages/contracts/src/2.0.0/protocol/AssetProxyOwner/AssetProxyOwner.sol @@ -16,14 +16,16 @@ */ -pragma solidity 0.4.10; +pragma solidity 0.4.24; import "../../multisig/MultiSigWalletWithTimeLock.sol"; +import "../../utils/LibBytes/LibBytes.sol"; contract AssetProxyOwner is MultiSigWalletWithTimeLock { + using LibBytes for bytes; event AssetProxyRegistration(address assetProxyContract, bool isRegistered); @@ -36,9 +38,15 @@ contract AssetProxyOwner is /// @dev Function will revert if the transaction does not call `removeAuthorizedAddressAtIndex` /// on an approved AssetProxy contract. modifier validRemoveAuthorizedAddressAtIndexTx(uint256 transactionId) { - Transaction storage tx = transactions[transactionId]; - require(isAssetProxyRegistered[tx.destination]); - require(readBytes4(tx.data, 0) == REMOVE_AUTHORIZED_ADDRESS_AT_INDEX_SELECTOR); + Transaction storage txn = transactions[transactionId]; + require( + isAssetProxyRegistered[txn.destination], + "UNREGISTERED_ASSET_PROXY" + ); + require( + txn.data.readBytes4(0) == REMOVE_AUTHORIZED_ADDRESS_AT_INDEX_SELECTOR, + "INVALID_FUNCTION_SELECTOR" + ); _; } @@ -48,7 +56,7 @@ contract AssetProxyOwner is /// @param _assetProxyContracts Array of AssetProxy contract addresses. /// @param _required Number of required confirmations. /// @param _secondsTimeLocked Duration needed after a transaction is confirmed and before it becomes executable, in seconds. - function AssetProxyOwner( + constructor ( address[] memory _owners, address[] memory _assetProxyContracts, uint256 _required, @@ -59,7 +67,10 @@ contract AssetProxyOwner is { for (uint256 i = 0; i < _assetProxyContracts.length; i++) { address assetProxy = _assetProxyContracts[i]; - require(assetProxy != address(0)); + require( + assetProxy != address(0), + "INVALID_ASSET_PROXY" + ); isAssetProxyRegistered[assetProxy] = true; } } @@ -74,7 +85,7 @@ contract AssetProxyOwner is notNull(assetProxyContract) { isAssetProxyRegistered[assetProxyContract] = isRegistered; - AssetProxyRegistration(assetProxyContract, isRegistered); + emit AssetProxyRegistration(assetProxyContract, isRegistered); } /// @dev Allows execution of `removeAuthorizedAddressAtIndex` without time lock. @@ -85,35 +96,13 @@ contract AssetProxyOwner is fullyConfirmed(transactionId) validRemoveAuthorizedAddressAtIndexTx(transactionId) { - Transaction storage tx = transactions[transactionId]; - tx.executed = true; - // solhint-disable-next-line avoid-call-value - if (tx.destination.call.value(tx.value)(tx.data)) - Execution(transactionId); - else { - ExecutionFailure(transactionId); - tx.executed = false; + Transaction storage txn = transactions[transactionId]; + txn.executed = true; + if (external_call(txn.destination, txn.value, txn.data.length, txn.data)) { + emit Execution(transactionId); + } else { + emit ExecutionFailure(transactionId); + txn.executed = false; } } - - /// @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 - returns (bytes4 result) - { - require(b.length >= index + 4); - assembly { - result := mload(add(b, 32)) - // Solidity does not require us to clean the trailing bytes. - // We do it anyway - result := and(result, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000) - } - return result; - } } diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/Exchange.sol b/packages/contracts/src/2.0.0/protocol/Exchange/Exchange.sol index 7507d3da1..ead36009f 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/Exchange.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/Exchange.sol @@ -37,7 +37,6 @@ contract Exchange is MixinAssetProxyDispatcher, MixinWrapperFunctions { - string constant public VERSION = "2.0.1-alpha"; // Mixins are instantiated in the order they are inherited diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol index 80475e6e3..87b09b6b3 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol @@ -19,7 +19,6 @@ pragma solidity 0.4.24; import "../../utils/Ownable/Ownable.sol"; -import "../../utils/LibBytes/LibBytes.sol"; import "./mixins/MAssetProxyDispatcher.sol"; import "../AssetProxy/interfaces/IAssetProxy.sol"; @@ -28,8 +27,6 @@ contract MixinAssetProxyDispatcher is Ownable, MAssetProxyDispatcher { - using LibBytes for bytes; - // Mapping from Asset Proxy Id's to their respective Asset Proxy mapping (bytes4 => IAssetProxy) public assetProxies; @@ -90,7 +87,7 @@ contract MixinAssetProxyDispatcher is "LENGTH_GREATER_THAN_3_REQUIRED" ); - // Lookup assetProxy + // Lookup assetProxy. We do not use `LibBytes.readBytes4` for gas efficiency reasons. bytes4 assetProxyId; assembly { assetProxyId := and(mload( diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol index be163ec97..736dcd0b1 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol @@ -75,7 +75,11 @@ contract MixinExchangeCore is // Update orderEpoch orderEpoch[makerAddress][senderAddress] = newOrderEpoch; - emit CancelUpTo(makerAddress, senderAddress, newOrderEpoch); + emit CancelUpTo( + makerAddress, + senderAddress, + newOrderEpoch + ); } /// @dev Fills the input order. @@ -107,14 +111,7 @@ contract MixinExchangeCore is public nonReentrant { - // Fetch current order status - OrderInfo memory orderInfo = getOrderInfo(order); - - // Validate context - assertValidCancel(order, orderInfo); - - // Perform cancel - updateCancelledState(order, orderInfo.orderHash); + cancelOrderInternal(order); } /// @dev Gets information about an order: status, hash, and amount filled. @@ -231,11 +228,31 @@ contract MixinExchangeCore is ); // Settle order - settleOrder(order, takerAddress, fillResults); + 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. @@ -404,16 +421,6 @@ contract MixinExchangeCore is safeMul(order.makerAssetAmount, takerAssetFilledAmount), "INVALID_FILL_PRICE" ); - - // Validate fill order rounding - require( - !isRoundingErrorFloor( - takerAssetFilledAmount, - order.takerAssetAmount, - order.makerAssetAmount - ), - "ROUNDING_ERROR" - ); } /// @dev Validates context for cancelOrder. Succeeds or throws. @@ -463,17 +470,17 @@ contract MixinExchangeCore is { // Compute proportional transfer amounts fillResults.takerAssetFilledAmount = takerAssetFilledAmount; - fillResults.makerAssetFilledAmount = getPartialAmountFloor( + fillResults.makerAssetFilledAmount = safeGetPartialAmountFloor( takerAssetFilledAmount, order.takerAssetAmount, order.makerAssetAmount ); - fillResults.makerFeePaid = getPartialAmountFloor( - takerAssetFilledAmount, - order.takerAssetAmount, + fillResults.makerFeePaid = safeGetPartialAmountFloor( + fillResults.makerAssetFilledAmount, + order.makerAssetAmount, order.makerFee ); - fillResults.takerFeePaid = getPartialAmountFloor( + fillResults.takerFeePaid = safeGetPartialAmountFloor( takerAssetFilledAmount, order.takerAssetAmount, order.takerFee diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol index 075a610b5..b4f6bdb26 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol @@ -177,13 +177,13 @@ contract MixinMatchOrders is { // Derive maker asset amounts for left & right orders, given store taker assert amounts uint256 leftTakerAssetAmountRemaining = safeSub(leftOrder.takerAssetAmount, leftOrderTakerAssetFilledAmount); - uint256 leftMakerAssetAmountRemaining = getPartialAmountFloor( + uint256 leftMakerAssetAmountRemaining = safeGetPartialAmountFloor( leftOrder.makerAssetAmount, leftOrder.takerAssetAmount, leftTakerAssetAmountRemaining ); uint256 rightTakerAssetAmountRemaining = safeSub(rightOrder.takerAssetAmount, rightOrderTakerAssetFilledAmount); - uint256 rightMakerAssetAmountRemaining = getPartialAmountFloor( + uint256 rightMakerAssetAmountRemaining = safeGetPartialAmountFloor( rightOrder.makerAssetAmount, rightOrder.takerAssetAmount, rightTakerAssetAmountRemaining @@ -205,7 +205,7 @@ contract MixinMatchOrders is 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 = getPartialAmountFloor( + matchedFillResults.left.makerAssetFilledAmount = safeGetPartialAmountFloor( leftOrder.makerAssetAmount, leftOrder.takerAssetAmount, matchedFillResults.left.takerAssetFilledAmount @@ -217,7 +217,7 @@ contract MixinMatchOrders is 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 = getPartialAmountCeil( + matchedFillResults.right.takerAssetFilledAmount = safeGetPartialAmountCeil( rightOrder.takerAssetAmount, rightOrder.makerAssetAmount, matchedFillResults.right.makerAssetFilledAmount @@ -231,24 +231,24 @@ contract MixinMatchOrders is ); // Compute fees for left order - matchedFillResults.left.makerFeePaid = getPartialAmountFloor( + matchedFillResults.left.makerFeePaid = safeGetPartialAmountFloor( matchedFillResults.left.makerAssetFilledAmount, leftOrder.makerAssetAmount, leftOrder.makerFee ); - matchedFillResults.left.takerFeePaid = getPartialAmountFloor( + matchedFillResults.left.takerFeePaid = safeGetPartialAmountFloor( matchedFillResults.left.takerAssetFilledAmount, leftOrder.takerAssetAmount, leftOrder.takerFee ); // Compute fees for right order - matchedFillResults.right.makerFeePaid = getPartialAmountFloor( + matchedFillResults.right.makerFeePaid = safeGetPartialAmountFloor( matchedFillResults.right.makerAssetFilledAmount, rightOrder.makerAssetAmount, rightOrder.makerFee ); - matchedFillResults.right.takerFeePaid = getPartialAmountFloor( + matchedFillResults.right.takerFeePaid = safeGetPartialAmountFloor( matchedFillResults.right.takerAssetFilledAmount, rightOrder.takerAssetAmount, rightOrder.takerFee diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol index 4eb6a2fa6..176e28351 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol @@ -251,7 +251,7 @@ contract MixinSignatureValidator is walletAddress, // address of Wallet contract cdStart, // pointer to start of input mload(calldata), // length of input - cdStart, // write input over output + cdStart, // write output over input 32 // output size is 32 bytes ) @@ -301,7 +301,7 @@ contract MixinSignatureValidator is validatorAddress, // address of Validator contract cdStart, // pointer to start of input mload(calldata), // length of input - cdStart, // write input over output + cdStart, // write output over input 32 // output size is 32 bytes ) diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinTransactions.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinTransactions.sol index 4a59b6c0f..3a76ca202 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinTransactions.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinTransactions.sol @@ -28,7 +28,6 @@ contract MixinTransactions is MSignatureValidator, MTransactions { - // Mapping of transaction hash => executed // This prevents transactions from being executed more than once. mapping (bytes32 => bool) public transactions; @@ -36,15 +35,6 @@ contract MixinTransactions is // Address of current transaction signer address public currentContextAddress; - // 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 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. diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol index a5459a21e..cddff0e5f 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol @@ -36,7 +36,6 @@ contract MixinWrapperFunctions is 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. @@ -82,7 +81,7 @@ contract MixinWrapperFunctions is // Delegate to `fillOrder` and handle any exceptions gracefully assembly { let success := delegatecall( - gas, // forward all gas, TODO: look into gas consumption of assert/throw + 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 @@ -377,10 +376,11 @@ contract MixinWrapperFunctions is /// @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++) { - cancelOrder(orders[i]); + cancelOrderInternal(orders[i]); } } diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibEIP712.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibEIP712.sol index b02f7632e..203edc1fd 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibEIP712.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibEIP712.sol @@ -20,6 +20,7 @@ pragma solidity 0.4.24; contract LibEIP712 { + // EIP191 header for EIP712 prefix string constant internal EIP191_HEADER = "\x19\x01"; diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibFillResults.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibFillResults.sol index 1b4181d94..659ae9a69 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibFillResults.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibFillResults.sol @@ -24,7 +24,6 @@ import "../../../utils/SafeMath/SafeMath.sol"; contract LibFillResults is SafeMath { - struct FillResults { uint256 makerAssetFilledAmount; // Total amount of makerAsset(s) filled. uint256 takerAssetFilledAmount; // Total amount of takerAsset(s) filled. diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol index 0e0fba5d2..c0b85ea10 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol @@ -24,6 +24,83 @@ import "../../../utils/SafeMath/SafeMath.sol"; contract LibMath is SafeMath { + /// @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. @@ -43,7 +120,7 @@ contract LibMath is denominator > 0, "DIVISION_BY_ZERO" ); - + partialAmount = safeDiv( safeMul(numerator, target), denominator @@ -69,7 +146,7 @@ contract LibMath is 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. @@ -128,7 +205,11 @@ contract LibMath is // 1000 * remainder < numerator * target // so we have a rounding error iff: // 1000 * remainder >= numerator * target - uint256 remainder = mulmod(target, numerator, denominator); + uint256 remainder = mulmod( + target, + numerator, + denominator + ); isError = safeMul(1000, remainder) >= safeMul(numerator, target); return isError; } @@ -160,8 +241,11 @@ contract LibMath is return false; } // Compute remainder as before - uint256 remainder = mulmod(target, numerator, denominator); - // TODO: safeMod + uint256 remainder = mulmod( + target, + numerator, + denominator + ); remainder = safeSub(denominator, remainder) % denominator; isError = safeMul(1000, remainder) >= safeMul(numerator, target); return isError; diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibOrder.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibOrder.sol index 68f4f5f1b..0fe7c2161 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibOrder.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibOrder.sol @@ -24,7 +24,6 @@ import "./LibEIP712.sol"; contract LibOrder is LibEIP712 { - // Hash for the EIP712 Order Schema bytes32 constant internal EIP712_ORDER_SCHEMA_HASH = keccak256(abi.encodePacked( "Order(", diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MAssetProxyDispatcher.sol b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MAssetProxyDispatcher.sol index c6904300a..0ddfca270 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MAssetProxyDispatcher.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MAssetProxyDispatcher.sol @@ -24,7 +24,6 @@ import "../interfaces/IAssetProxyDispatcher.sol"; contract MAssetProxyDispatcher is IAssetProxyDispatcher { - // Logs registration of new asset proxy event AssetProxyRegistered( bytes4 id, // Id of new registered AssetProxy. diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol index d85913e0f..742499568 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol @@ -72,6 +72,11 @@ contract MExchangeCore is 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. diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MMatchOrders.sol b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MMatchOrders.sol index a31ec1585..96fa34bc0 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MMatchOrders.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MMatchOrders.sol @@ -26,7 +26,6 @@ import "../interfaces/IMatchOrders.sol"; contract MMatchOrders is IMatchOrders { - /// @dev Validates context for matchOrders. Succeeds or throws. /// @param leftOrder First order to match. /// @param rightOrder Second order to match. diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MTransactions.sol b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MTransactions.sol index f2b5e4b16..4f61a4945 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MTransactions.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MTransactions.sol @@ -23,6 +23,28 @@ import "../interfaces/ITransactions.sol"; 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. diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MWrapperFunctions.sol b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MWrapperFunctions.sol index e04d4a429..4adfbde01 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MWrapperFunctions.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MWrapperFunctions.sol @@ -24,8 +24,9 @@ import "../libs/LibFillResults.sol"; import "../interfaces/IWrapperFunctions.sol"; -contract MWrapperFunctions { - +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. diff --git a/packages/contracts/src/2.0.0/test/DummyERC20Token/DummyMultipleReturnERC20Token.sol b/packages/contracts/src/2.0.0/test/DummyERC20Token/DummyMultipleReturnERC20Token.sol new file mode 100644 index 000000000..733d4437e --- /dev/null +++ b/packages/contracts/src/2.0.0/test/DummyERC20Token/DummyMultipleReturnERC20Token.sol @@ -0,0 +1,69 @@ +/* + + 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.24; + +import "./DummyERC20Token.sol"; + + +// solhint-disable no-empty-blocks +contract DummyMultipleReturnERC20Token is + DummyERC20Token +{ + constructor ( + string _name, + string _symbol, + uint256 _decimals, + uint256 _totalSupply + ) + public + DummyERC20Token( + _name, + _symbol, + _decimals, + _totalSupply + ) + {} + + /// @dev send `value` token to `to` from `from` on the condition it is approved by `from` + /// @param _from The address of the sender + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + function transferFrom( + address _from, + address _to, + uint256 _value + ) + external + returns (bool) + { + emit Transfer( + _from, + _to, + _value + ); + + // HACK: This contract will not compile if we remove `returns (bool)`, so we manually return 64 bytes (equiavalent to true, true) + assembly { + mstore(0, 1) + mstore(32, 1) + return(0, 64) + } + } +} + diff --git a/packages/contracts/src/2.0.0/test/DummyERC20Token/DummyNoReturnERC20Token.sol b/packages/contracts/src/2.0.0/test/DummyERC20Token/DummyNoReturnERC20Token.sol index 79156d3dd..e16825a16 100644 --- a/packages/contracts/src/2.0.0/test/DummyERC20Token/DummyNoReturnERC20Token.sol +++ b/packages/contracts/src/2.0.0/test/DummyERC20Token/DummyNoReturnERC20Token.sol @@ -25,7 +25,6 @@ import "./DummyERC20Token.sol"; contract DummyNoReturnERC20Token is DummyERC20Token { - constructor ( string _name, string _symbol, diff --git a/packages/contracts/src/2.0.0/test/DummyERC721Receiver/DummyERC721Receiver.sol b/packages/contracts/src/2.0.0/test/DummyERC721Receiver/DummyERC721Receiver.sol index ac95e47bd..6c8371559 100644 --- a/packages/contracts/src/2.0.0/test/DummyERC721Receiver/DummyERC721Receiver.sol +++ b/packages/contracts/src/2.0.0/test/DummyERC721Receiver/DummyERC721Receiver.sol @@ -24,7 +24,6 @@ import "../../tokens/ERC721Token/IERC721Receiver.sol"; contract DummyERC721Receiver is IERC721Receiver { - // Function selector for ERC721Receiver.onERC721Received // 0x150b7a02 bytes4 constant internal ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")); diff --git a/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol b/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol index 8bfdd2e66..99dd47a78 100644 --- a/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol +++ b/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol @@ -25,10 +25,10 @@ import "../../protocol/Exchange/interfaces/IExchange.sol"; import "../../protocol/Exchange/libs/LibOrder.sol"; +// solhint-disable no-unused-vars contract ReentrantERC20Token is ERC20Token { - using LibBytes for bytes; // solhint-disable-next-line var-name-mixedcase @@ -50,6 +50,7 @@ contract ReentrantERC20Token is MARKET_SELL_ORDERS, MATCH_ORDERS, CANCEL_ORDER, + BATCH_CANCEL_ORDERS, CANCEL_ORDERS_UP_TO, SET_SIGNATURE_VALIDATOR_APPROVAL } @@ -149,6 +150,11 @@ contract ReentrantERC20Token is EXCHANGE.cancelOrder.selector, order ); + } else if (currentFunctionId == uint8(ExchangeFunction.BATCH_CANCEL_ORDERS)) { + calldata = abi.encodeWithSelector( + EXCHANGE.batchCancelOrders.selector, + orders + ); } else if (currentFunctionId == uint8(ExchangeFunction.CANCEL_ORDERS_UP_TO)) { calldata = abi.encodeWithSelector( EXCHANGE.cancelOrdersUpTo.selector, diff --git a/packages/contracts/src/2.0.0/test/TestAssetProxyOwner/TestAssetProxyOwner.sol b/packages/contracts/src/2.0.0/test/TestAssetProxyOwner/TestAssetProxyOwner.sol index 75e782d43..52c66cb56 100644 --- a/packages/contracts/src/2.0.0/test/TestAssetProxyOwner/TestAssetProxyOwner.sol +++ b/packages/contracts/src/2.0.0/test/TestAssetProxyOwner/TestAssetProxyOwner.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.10; +pragma solidity 0.4.24; import "../../protocol/AssetProxyOwner/AssetProxyOwner.sol"; @@ -25,8 +25,7 @@ import "../../protocol/AssetProxyOwner/AssetProxyOwner.sol"; contract TestAssetProxyOwner is AssetProxyOwner { - - function TestAssetProxyOwner( + constructor ( address[] memory _owners, address[] memory _assetProxyContracts, uint256 _required, @@ -38,6 +37,7 @@ contract TestAssetProxyOwner is function testValidRemoveAuthorizedAddressAtIndexTx(uint256 id) public + view validRemoveAuthorizedAddressAtIndexTx(id) returns (bool) { @@ -50,23 +50,9 @@ contract TestAssetProxyOwner is /// @return Successful if data is a call to `removeAuthorizedAddressAtIndex`. function isFunctionRemoveAuthorizedAddressAtIndex(bytes memory data) public + pure returns (bool) { - return readBytes4(data, 0) == REMOVE_AUTHORIZED_ADDRESS_AT_INDEX_SELECTOR; - } - - /// @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 publicReadBytes4( - bytes memory b, - uint256 index - ) - public - returns (bytes4 result) - { - result = readBytes4(b, index); - return result; + return data.readBytes4(0) == REMOVE_AUTHORIZED_ADDRESS_AT_INDEX_SELECTOR; } } diff --git a/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol b/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol index da9313e02..27187f8f8 100644 --- a/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol +++ b/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol @@ -63,6 +63,42 @@ contract TestExchangeInternals is } /// @dev Calculates partial value given a numerator and denominator. + /// 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. + function publicSafeGetPartialAmountFloor( + uint256 numerator, + uint256 denominator, + uint256 target + ) + public + pure + returns (uint256 partialAmount) + { + return safeGetPartialAmountFloor(numerator, denominator, target); + } + + /// @dev Calculates partial value given a numerator and denominator. + /// 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. + function publicSafeGetPartialAmountCeil( + uint256 numerator, + uint256 denominator, + uint256 target + ) + public + pure + returns (uint256 partialAmount) + { + return safeGetPartialAmountCeil(numerator, denominator, target); + } + + /// @dev Calculates partial value given a numerator and denominator. /// @param numerator Numerator. /// @param denominator Denominator. /// @param target Value to calculate partial of. diff --git a/packages/contracts/src/2.0.0/test/TestLibs/TestLibs.sol b/packages/contracts/src/2.0.0/test/TestLibs/TestLibs.sol index c8c58545f..a10f981fc 100644 --- a/packages/contracts/src/2.0.0/test/TestLibs/TestLibs.sol +++ b/packages/contracts/src/2.0.0/test/TestLibs/TestLibs.sol @@ -31,7 +31,6 @@ contract TestLibs is LibFillResults, LibAbiEncoder { - function publicAbiEncodeFillOrder( Order memory order, uint256 takerAssetFillAmount, diff --git a/packages/contracts/src/2.0.0/test/TestSignatureValidator/TestSignatureValidator.sol b/packages/contracts/src/2.0.0/test/TestSignatureValidator/TestSignatureValidator.sol index e1a610469..ea3e2de59 100644 --- a/packages/contracts/src/2.0.0/test/TestSignatureValidator/TestSignatureValidator.sol +++ b/packages/contracts/src/2.0.0/test/TestSignatureValidator/TestSignatureValidator.sol @@ -26,7 +26,6 @@ contract TestSignatureValidator is MixinSignatureValidator, MixinTransactions { - function publicIsValidSignature( bytes32 hash, address signer, diff --git a/packages/contracts/src/2.0.0/tokens/ERC20Token/ERC20Token.sol b/packages/contracts/src/2.0.0/tokens/ERC20Token/ERC20Token.sol index 5ef5ee7ce..725d304df 100644 --- a/packages/contracts/src/2.0.0/tokens/ERC20Token/ERC20Token.sol +++ b/packages/contracts/src/2.0.0/tokens/ERC20Token/ERC20Token.sol @@ -24,7 +24,6 @@ import "./IERC20Token.sol"; contract ERC20Token is IERC20Token { - mapping (address => uint256) internal balances; mapping (address => mapping (address => uint256)) internal allowed; diff --git a/packages/contracts/src/2.0.0/tokens/ERC20Token/MintableERC20Token.sol b/packages/contracts/src/2.0.0/tokens/ERC20Token/MintableERC20Token.sol index cd1c7b4bb..9dc924422 100644 --- a/packages/contracts/src/2.0.0/tokens/ERC20Token/MintableERC20Token.sol +++ b/packages/contracts/src/2.0.0/tokens/ERC20Token/MintableERC20Token.sol @@ -26,7 +26,6 @@ contract MintableERC20Token is SafeMath, UnlimitedAllowanceERC20Token { - /// @dev Mints new tokens /// @param _to Address of the beneficiary that will own the minted token /// @param _value Amount of tokens to mint diff --git a/packages/contracts/src/2.0.0/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol b/packages/contracts/src/2.0.0/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol index e6f7c063e..2e5bd4348 100644 --- a/packages/contracts/src/2.0.0/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol +++ b/packages/contracts/src/2.0.0/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol @@ -24,7 +24,6 @@ import "../ERC20Token/ERC20Token.sol"; contract UnlimitedAllowanceERC20Token is ERC20Token { - uint256 constant internal MAX_UINT = 2**256 - 1; /// @dev ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance. See https://github.com/ethereum/EIPs/issues/717 diff --git a/packages/contracts/src/2.0.0/tokens/ERC721Token/MintableERC721Token.sol b/packages/contracts/src/2.0.0/tokens/ERC721Token/MintableERC721Token.sol index 85d192779..bc5cd2cc2 100644 --- a/packages/contracts/src/2.0.0/tokens/ERC721Token/MintableERC721Token.sol +++ b/packages/contracts/src/2.0.0/tokens/ERC721Token/MintableERC721Token.sol @@ -24,7 +24,6 @@ import "./ERC721Token.sol"; contract MintableERC721Token is ERC721Token { - /// @dev Function to mint a new token /// Reverts if the given token ID already exists /// @param _to Address of the beneficiary that will own the minted token diff --git a/packages/contracts/src/2.0.0/tokens/ZRXToken/ZRXToken.sol b/packages/contracts/src/2.0.0/tokens/ZRXToken/ZRXToken.sol index 28c0b2fb3..f4855759c 100644 --- a/packages/contracts/src/2.0.0/tokens/ZRXToken/ZRXToken.sol +++ b/packages/contracts/src/2.0.0/tokens/ZRXToken/ZRXToken.sol @@ -22,11 +22,13 @@ pragma solidity 0.4.11; import { UnlimitedAllowanceToken_v1 as UnlimitedAllowanceToken } from "../../../1.0.0/UnlimitedAllowanceToken/UnlimitedAllowanceToken_v1.sol"; -contract ZRXToken is UnlimitedAllowanceToken { +contract ZRXToken is + UnlimitedAllowanceToken +{ // solhint-disable const-name-snakecase uint8 constant public decimals = 18; - uint public totalSupply = 10**27; // 1 billion tokens, 18 decimal places + uint256 public totalSupply = 10**27; // 1 billion tokens, 18 decimal places string constant public name = "0x Protocol Token"; string constant public symbol = "ZRX"; // solhint-enableconst-name-snakecase diff --git a/packages/contracts/src/2.0.0/utils/LibBytes/LibBytes.sol b/packages/contracts/src/2.0.0/utils/LibBytes/LibBytes.sol index 504e950a8..369f588ad 100644 --- a/packages/contracts/src/2.0.0/utils/LibBytes/LibBytes.sol +++ b/packages/contracts/src/2.0.0/utils/LibBytes/LibBytes.sol @@ -188,7 +188,8 @@ library LibBytes { memCopy( result.contentAddress(), b.contentAddress() + from, - result.length); + result.length + ); return result; } @@ -433,7 +434,8 @@ library LibBytes { pure returns (uint256 result) { - return uint256(readBytes32(b, index)); + result = uint256(readBytes32(b, index)); + return result; } /// @dev Writes a uint256 into a specific position in a byte array. @@ -467,8 +469,13 @@ library LibBytes { 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, 32)) + result := mload(add(b, index)) // Solidity does not require us to clean the trailing bytes. // We do it anyway result := and(result, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000) diff --git a/packages/contracts/src/2.0.0/utils/Ownable/IOwnable.sol b/packages/contracts/src/2.0.0/utils/Ownable/IOwnable.sol index 116b8dc89..5deb13497 100644 --- a/packages/contracts/src/2.0.0/utils/Ownable/IOwnable.sol +++ b/packages/contracts/src/2.0.0/utils/Ownable/IOwnable.sol @@ -1,13 +1,8 @@ pragma solidity 0.4.24; -/* - * Ownable - * - * Base contract with an owner. - * Provides onlyOwner modifier, which prevents function from running if it is called by anyone other than the owner. - */ contract IOwnable { + function transferOwnership(address newOwner) public; } diff --git a/packages/contracts/src/2.0.0/utils/Ownable/Ownable.sol b/packages/contracts/src/2.0.0/utils/Ownable/Ownable.sol index aca65aad2..0c830be68 100644 --- a/packages/contracts/src/2.0.0/utils/Ownable/Ownable.sol +++ b/packages/contracts/src/2.0.0/utils/Ownable/Ownable.sol @@ -1,16 +1,11 @@ pragma solidity 0.4.24; -/* - * Ownable - * - * Base contract with an owner. - * Provides onlyOwner modifier, which prevents function from running if it is called by anyone other than the owner. - */ - import "./IOwnable.sol"; -contract Ownable is IOwnable { +contract Ownable is + IOwnable +{ address public owner; constructor () diff --git a/packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol b/packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol index 1dee512d4..9f98a7a16 100644 --- a/packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol +++ b/packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol @@ -15,6 +15,7 @@ limitations under the License. */ + pragma solidity 0.4.24; diff --git a/packages/contracts/src/2.0.0/utils/SafeMath/SafeMath.sol b/packages/contracts/src/2.0.0/utils/SafeMath/SafeMath.sol index 63a2a085f..2855edb9d 100644 --- a/packages/contracts/src/2.0.0/utils/SafeMath/SafeMath.sol +++ b/packages/contracts/src/2.0.0/utils/SafeMath/SafeMath.sol @@ -2,6 +2,7 @@ pragma solidity 0.4.24; contract SafeMath { + function safeMul(uint256 a, uint256 b) internal pure |