diff options
author | Amir Bandeali <abandeali1@gmail.com> | 2018-10-03 07:22:35 +0800 |
---|---|---|
committer | Amir Bandeali <abandeali1@gmail.com> | 2018-12-21 01:13:36 +0800 |
commit | b786836db61de22fa290d94ac3f12fe5342aecc7 (patch) | |
tree | 214af7bf913a51ae7a3c1853ae3fa9bff49d96b1 /contracts | |
parent | ace968a4fc5281d6688ac6de931a46728455c693 (diff) | |
download | dexon-sol-tools-b786836db61de22fa290d94ac3f12fe5342aecc7.tar dexon-sol-tools-b786836db61de22fa290d94ac3f12fe5342aecc7.tar.gz dexon-sol-tools-b786836db61de22fa290d94ac3f12fe5342aecc7.tar.bz2 dexon-sol-tools-b786836db61de22fa290d94ac3f12fe5342aecc7.tar.lz dexon-sol-tools-b786836db61de22fa290d94ac3f12fe5342aecc7.tar.xz dexon-sol-tools-b786836db61de22fa290d94ac3f12fe5342aecc7.tar.zst dexon-sol-tools-b786836db61de22fa290d94ac3f12fe5342aecc7.zip |
feat: Add OrderMatcher contract that takes spread in multiple assets by calling `matchOrders` followed by `fillOrder`
Diffstat (limited to 'contracts')
10 files changed, 796 insertions, 1 deletions
diff --git a/contracts/extensions/compiler.json b/contracts/extensions/compiler.json index e6ed0c215..1e21e6e6a 100644 --- a/contracts/extensions/compiler.json +++ b/contracts/extensions/compiler.json @@ -18,5 +18,5 @@ } } }, - "contracts": ["BalanceThresholdFilter", "DutchAuction", "Forwarder"] + "contracts": ["BalanceThresholdFilter", "DutchAuction", "Forwarder", "OrderMatcher"] } diff --git a/contracts/extensions/contracts/OrderMatcher/MixinAssets.sol b/contracts/extensions/contracts/OrderMatcher/MixinAssets.sol new file mode 100644 index 000000000..a2bc95e4b --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/MixinAssets.sol @@ -0,0 +1,192 @@ +/* + + 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 "../../utils/LibBytes/LibBytes.sol"; +import "../../utils/Ownable/Ownable.sol"; +import "../../tokens/ERC20Token/IERC20Token.sol"; +import "../../tokens/ERC721Token/IERC721Token.sol"; +import "./mixins/MAssets.sol"; +import "./libs/LibConstants.sol"; + + +contract MixinAssets is + MAssets, + Ownable, + LibConstants +{ + using LibBytes for bytes; + + /// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to + /// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be + /// used to withdraw assets that were accidentally sent to this contract. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to withdraw. + function withdrawAsset( + bytes assetData, + uint256 amount + ) + external + onlyOwner + { + transferAssetToSender(assetData, amount); + } + + /// @dev Approves or disapproves an AssetProxy to spend asset. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to approve for respective proxy. + function approveAssetProxy( + bytes assetData, + uint256 amount + ) + external + onlyOwner + { + bytes4 proxyId = assetData.readBytes4(0); + + if (proxyId == ERC20_DATA_ID) { + approveERC20Token(assetData, amount); + } else if (proxyId == ERC721_DATA_ID) { + approveERC721Token(assetData, amount); + } else { + revert("UNSUPPORTED_ASSET_PROXY"); + } + } + + /// @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. + function transferAssetToSender( + bytes memory assetData, + uint256 amount + ) + internal + { + bytes4 proxyId = assetData.readBytes4(0); + + if (proxyId == ERC20_DATA_ID) { + transferERC20Token(assetData, amount); + } else if (proxyId == ERC721_DATA_ID) { + transferERC721Token(assetData, amount); + } else { + revert("UNSUPPORTED_ASSET_PROXY"); + } + } + + /// @dev Decodes ERC20 assetData and transfers given amount to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferERC20Token( + bytes memory assetData, + uint256 amount + ) + internal + { + address token = assetData.readAddress(16); + + // Transfer tokens. + // We do a raw call so we can check the success separate + // from the return data. + bool success = token.call(abi.encodeWithSelector( + ERC20_TRANSFER_SELECTOR, + msg.sender, + amount + )); + require( + success, + "TRANSFER_FAILED" + ); + + // 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 + // value that evaluates to true. + assembly { + if returndatasize { + success := 0 + if eq(returndatasize, 32) { + // First 64 bytes of memory are reserved scratch space + returndatacopy(0, 0, 32) + success := mload(0) + } + } + } + require( + success, + "TRANSFER_FAILED" + ); + } + + /// @dev Decodes ERC721 assetData and transfers given amount to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferERC721Token( + bytes memory assetData, + uint256 amount + ) + internal + { + require( + amount == 1, + "INVALID_AMOUNT" + ); + // Decode asset data. + address token = assetData.readAddress(16); + uint256 tokenId = assetData.readUint256(36); + + // Perform transfer. + IERC721Token(token).transferFrom( + address(this), + msg.sender, + tokenId + ); + } + + /// @dev Sets approval for ERC20 AssetProxy. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to approve for respective proxy. + function approveERC20Token( + bytes memory assetData, + uint256 amount + ) + internal + { + address token = assetData.readAddress(16); + require( + IERC20Token(token).approve(ERC20_PROXY_ADDRESS, amount), + "APPROVAL_FAILED" + ); + } + + /// @dev Sets approval for ERC721 AssetProxy. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to approve for respective proxy. + function approveERC721Token( + bytes memory assetData, + uint256 amount + ) + internal + { + address token = assetData.readAddress(16); + bool approval = amount >= 1; + IERC721Token(token).setApprovalForAll(ERC721_PROXY_ADDRESS, approval); + } +} diff --git a/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol b/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol new file mode 100644 index 000000000..1c7cce675 --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol @@ -0,0 +1,323 @@ +/* + + 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 "../../utils/Ownable/Ownable.sol"; +import "./libs/LibConstants.sol"; + + +contract MixinMatchOrders is + Ownable, + LibConstants +{ + // The below assembly in the fallback function is functionaly equivalent to the following Solidity code: + /* + /// @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 is then used to fill the right order as much as possible. + /// This results in a spread being taken in terms of both assets. The spread is held within this contract. + /// @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. + function matchOrders( + LibOrder.Order memory leftOrder, + LibOrder.Order memory rightOrder, + bytes memory leftSignature, + bytes memory rightSignature + ) + public + onlyOwner + { + // Match orders, maximally filling `leftOrder` + LibFillResults.MatchedFillResults memory matchedFillResults = EXCHANGE.matchOrders( + leftOrder, + rightOrder, + leftSignature, + rightSignature + ); + + // If a spread was taken, use the spread to fill remaining amount of `rightOrder` + // Only attempt to fill `rightOrder` if a spread was taken and if not already completely filled + uint256 leftMakerAssetSpreadAmount = matchedFillResults.leftMakerAssetSpreadAmount; + if (leftMakerAssetSpreadAmount > 0 && matchedFillResults.right.takerAssetFilledAmount < rightOrder.takerAssetAmount) { + // The `assetData` fields of the `rightOrder` could have been null for the `matchOrders` call. We reassign them before calling `fillOrder`. + rightOrder.makerAssetData = leftOrder.takerAssetData; + rightOrder.takerAssetData = leftOrder.makerAssetData; + + // We do not need to pass in a signature since it was already validated in the `matchOrders` call + EXCHANGE.fillOrder( + rightOrder, + leftMakerAssetSpreadAmount, + "" + ); + } + } + */ + // solhint-disable-next-line payable-fallback + function () + external + { + assembly { + // The first 4 bytes of calldata holds the function selector + // `matchOrders` selector = 0x3c28d861 + if eq( + and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000), + 0x3c28d86100000000000000000000000000000000000000000000000000000000 + ) { + + // Load address of `owner` + let owner := sload(owner_slot) + + // Revert if `msg.sender` != `owner` + if iszero(eq(owner, caller)) { + // Revert with `Error("ONLY_CONTRACT_OWNER")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x000000134f4e4c595f434f4e54524143545f4f574e4552000000000000000000) + mstore(96, 0) + revert(0, 100) + } + + // Load address of Exchange contract + let exchange := sload(EXCHANGE_slot) + + // Copy calldata to memory + // The calldata should be identical to the the ABIv2 encoded data required to call `EXCHANGE.matchOrders` + calldatacopy(0, 0, calldatasize()) + + // At this point, calldata and memory have the following layout: + + // | Offset | Length | Contents | + // |---------------------------|---------|-------------------------------------------- | + // | 0 | 4 | `matchOrders` function selector | + // | | 4 * 32 | function parameters: | + // | 4 | | 1. offset to leftOrder (*) | + // | 36 | | 2. offset to rightOrder (*) | + // | 68 | | 3. offset to leftSignature (*) | + // | 100 | | 4. offset to rightSignature (*) | + // | | 12 * 32 | leftOrder: | + // | 132 | | 1. senderAddress | + // | 164 | | 2. makerAddress | + // | 196 | | 3. takerAddress | + // | 228 | | 4. feeRecipientAddress | + // | 260 | | 5. makerAssetAmount | + // | 292 | | 6. takerAssetAmount | + // | 324 | | 7. makerFeeAmount | + // | 356 | | 8. takerFeeAmount | + // | 388 | | 9. expirationTimeSeconds | + // | 420 | | 10. salt | + // | 452 | | 11. offset to leftMakerAssetData (*) | + // | 484 | | 12. offset to leftTakerAssetData (*) | + // | | 12 * 32 | rightOrder: | + // | 516 | | 1. senderAddress | + // | 548 | | 2. makerAddress | + // | 580 | | 3. takerAddress | + // | 612 | | 4. feeRecipientAddress | + // | 644 | | 5. makerAssetAmount | + // | 676 | | 6. takerAssetAmount | + // | 708 | | 7. makerFeeAmount | + // | 740 | | 8. takerFeeAmount | + // | 772 | | 9. expirationTimeSeconds | + // | 804 | | 10. salt | + // | 836 | | 11. offset to rightMakerAssetData (*) | + // | 868 | | 12. offset to rightTakerAssetData (*) | + // | 900 | 32 | leftMakerAssetData Length | + // | 932 | a | leftMakerAssetData Contents | + // | 932 + a | 32 | leftTakerAssetData Length | + // | 964 + a | b | leftTakerAssetData Contents | + // | 964 + a + b | 32 | rightMakerAssetData Length | + // | 996 + a + b | c | rightMakerAssetData Contents | + // | 996 + a + b + c | 32 | rightTakerAssetData Length | + // | 1028 + a + b + c | d | rightTakerAssetData Contents | + // | 1028 + a + b + c + d | 32 | leftSignature Length | + // | 1060 + a + b + c + d | e | leftSignature Contents | + // | 1060 + a + b + c + d + e | 32 | rightSignature Length | + // | 1092 + a + b + c + d + e | f | rightSignature Contents | + + // Call `EXCHANGE.matchOrders` + let matchOrdersSuccess := call( + gas, // forward all gas + exchange, // call address of Exchange contract + 0, // transfer 0 wei + 0, // input starts at 0 + calldatasize(), // length of input + 0, // write output over output + 288 // length of output is 288 bytes + ) + + if iszero(matchOrdersSuccess) { + // Revert with reason if `matchOrders` call was unsuccessful + revert(0, returndatasize()) + } + + // After calling `matchOrders`, the relevant parts of memory are: + + // | Offset | Length | Contents | + // |---------------------------|---------|-------------------------------------------- | + // | | 9 * 32 | matchedFillResults | + // | 0 | | 1. left.makerAssetFilledAmount | + // | 32 | | 2. left.takerAssetFilledAmount | + // | 64 | | 3. left.makerFeePaid | + // | 96 | | 4. left.takerFeePaid | + // | 128 | | 5. right.makerAssetFilledAmount | + // | 160 | | 6. right.takerAssetFilledAmount | + // | 192 | | 7. right.makerFeePaid | + // | 224 | | 8. right.takerFeePaid | + // | 256 | | 9. leftMakerAssetSpreadAmount | + // | | 12 * 32 | rightOrder: | + // | 516 | | 1. senderAddress | + // | 548 | | 2. makerAddress | + // | 580 | | 3. takerAddress | + // | 612 | | 4. feeRecipientAddress | + // | 644 | | 5. makerAssetAmount | + // | 676 | | 6. takerAssetAmount | + // | 708 | | 7. makerFeeAmount | + // | 740 | | 8. takerFeeAmount | + // | 772 | | 9. expirationTimeSeconds | + // | 804 | | 10. salt | + // | 836 | | 11. offset to rightMakerAssetData (*) | + // | 868 | | 12. offset to rightTakerAssetData (*) | + + let rightOrderStart := add(calldataload(36), 4) + + // Only call `fillOrder` if a spread was taken and `rightOrder` has not been completely filled + if and( + gt(mload(256), 0), // gt(leftMakerAssetSpreadAmount, 0) + lt(mload(160), calldataload(add(rightOrderStart, 160))) // lt(rightOrderTakerAssetFilledAmount, rightOrderTakerAssetAmount) + ) { + + // We want the following layout in memory before calling `fillOrder`: + + // | Offset | Length | Contents | + // |---------------------------|---------|-------------------------------------------- | + // | 416 | 4 | `fillOrder` function selector | + // | | 3 * 32 | function parameters: | + // | 420 | | 1. offset to rightOrder (*) | + // | 452 | | 2. takerAssetFillAmount | + // | 484 | | 3. offset to rightSignature (*) | + // | | 12 * 32 | rightOrder: | + // | 516 | | 1. senderAddress | + // | 548 | | 2. makerAddress | + // | 580 | | 3. takerAddress | + // | 612 | | 4. feeRecipientAddress | + // | 644 | | 5. makerAssetAmount | + // | 676 | | 6. takerAssetAmount | + // | 708 | | 7. makerFeeAmount | + // | 740 | | 8. takerFeeAmount | + // | 772 | | 9. expirationTimeSeconds | + // | 804 | | 10. salt | + // | 836 | | 11. offset to rightMakerAssetData (*) | + // | 868 | | 12. offset to rightTakerAssetData (*) | + // | 900 | 32 | rightMakerAssetData Length | + // | 932 | a | rightMakerAssetData Contents | + // | 932 + a | 32 | rightTakerAssetData Length | + // | 964 | b | rightTakerAssetData Contents | + // | 964 + b | 32 | rightSigature Length (always 0) | + + // We assume that `leftOrder.makerAssetData == rightOrder.takerAssetData` and `leftOrder.takerAssetData == rightOrder.makerAssetData` + // `EXCHANGE.matchOrders` already makes this assumption, so it is likely + // that the `rightMakerAssetData` and `rightTakerAssetData` in calldata are empty + + let leftOrderStart := add(calldataload(4), 4) + + // Calculate locations of `leftMakerAssetData` and `leftTakerAssetData` in calldata + let leftMakerAssetDataStart := add( + leftOrderStart, + calldataload(add(leftOrderStart, 320)) // offset to `leftMakerAssetData` + ) + let leftTakerAssetDataStart := add( + leftOrderStart, + calldataload(add(leftOrderStart, 352)) // offset to `leftTakerAssetData` + ) + + // Load lengths of `leftMakerAssetData` and `leftTakerAssetData` + let leftMakerAssetDataLen := calldataload(leftMakerAssetDataStart) + let leftTakerAssetDataLen := calldataload(leftTakerAssetDataStart) + + // Write offset to `rightMakerAssetData` + mstore(add(rightOrderStart, 320), 384) + + // Write offset to `rightTakerAssetData` + let rightTakerAssetDataOffset := add(416, leftTakerAssetDataLen) + mstore(add(rightOrderStart, 352), rightTakerAssetDataOffset) + + // Copy `leftTakerAssetData` from calldata onto `rightMakerAssetData` in memory + calldatacopy( + add(rightOrderStart, 384), // `rightMakerAssetDataStart` + leftTakerAssetDataStart, + add(leftTakerAssetDataLen, 32) + ) + + // Copy `leftMakerAssetData` from calldata onto `rightTakerAssetData` in memory + calldatacopy( + add(rightOrderStart, rightTakerAssetDataOffset), // `rightTakerAssetDataStart` + leftMakerAssetDataStart, + add(leftMakerAssetDataLen, 32) + ) + + // Write length of signature (always 0 since signature was previously validated) + let rightSignatureStart := add( + add(rightOrderStart, rightTakerAssetDataOffset), // `rightTakerAssetDataStart` + add(leftMakerAssetDataLen, 32) + ) + mstore(rightSignatureStart, 0) + + let cdStart := sub(rightOrderStart, 100) + + // `fillOrder` selector = 0xb4be83d5 + mstore(cdStart, 0xb4be83d500000000000000000000000000000000000000000000000000000000) + + // Write offset to `rightOrder` + mstore(add(cdStart, 4), 96) + + // Write `takerAssetFillAmount` + mstore(add(cdStart, 36), mload(256)) + + // Write offset to `rightSignature` + mstore(add(cdStart, 68), sub(rightSignatureStart, add(cdStart, 4))) + + let fillOrderSuccess := call( + gas, // forward all gas + exchange, // call address of Exchange contract + 0, // transfer 0 wei + cdStart, // start of input + sub(add(rightSignatureStart, 32), cdStart), // length of input is end - start + 0, // write output over input + 128 // length of output is 128 bytes + ) + + if fillOrderSuccess { + return(0, 0) + } + + // Revert with reason if `fillOrder` call was unsuccessful + revert(0, returndatasize()) + } + + // Return if `matchOrders` call successful and `fillOrder` was not called + return(0, 0) + } + + // Revert if undefined function is called + revert(0, 0) + } + } +} diff --git a/contracts/extensions/contracts/OrderMatcher/OrderMatcher.sol b/contracts/extensions/contracts/OrderMatcher/OrderMatcher.sol new file mode 100644 index 000000000..b6f1566cf --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/OrderMatcher.sol @@ -0,0 +1,37 @@ +/* + + 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 "../../utils/Ownable/Ownable.sol"; +import "./libs/LibConstants.sol"; +import "./MixinMatchOrders.sol"; +import "./MixinAssets.sol"; + + +// solhint-disable no-empty-blocks +contract OrderMatcher is + MixinMatchOrders, + MixinAssets +{ + constructor (address _exchange) + public + LibConstants(_exchange) + Ownable() + {} +} diff --git a/contracts/extensions/contracts/OrderMatcher/interfaces/IAssets.sol b/contracts/extensions/contracts/OrderMatcher/interfaces/IAssets.sol new file mode 100644 index 000000000..a0b3aa4c7 --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/interfaces/IAssets.sol @@ -0,0 +1,43 @@ +/* + + 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; + + +contract IAssets { + + /// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to + /// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be + /// used to withdraw assets that were accidentally sent to this contract. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to withdraw. + function withdrawAsset( + bytes assetData, + uint256 amount + ) + external; + + /// @dev Approves or disapproves an AssetProxy to spend asset. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to approve for respective proxy. + function approveAssetProxy( + bytes assetData, + uint256 amount + ) + external; +} diff --git a/contracts/extensions/contracts/OrderMatcher/interfaces/IMatchOrders.sol b/contracts/extensions/contracts/OrderMatcher/interfaces/IMatchOrders.sol new file mode 100644 index 000000000..455259238 --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/interfaces/IMatchOrders.sol @@ -0,0 +1,43 @@ +/* + + 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; +pragma experimental ABIEncoderV2; + +import "../../../protocol/Exchange/libs/LibOrder.sol"; + + +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 is then used to fill the right order as much as possible. + /// This results in a spread being taken in terms of both assets. The spread is held within this contract. + /// @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. + function matchOrders( + LibOrder.Order memory leftOrder, + LibOrder.Order memory rightOrder, + bytes memory leftSignature, + bytes memory rightSignature + ) + public; +} diff --git a/contracts/extensions/contracts/OrderMatcher/interfaces/IOrderMatcher.sol b/contracts/extensions/contracts/OrderMatcher/interfaces/IOrderMatcher.sol new file mode 100644 index 000000000..ed4aee925 --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/interfaces/IOrderMatcher.sol @@ -0,0 +1,31 @@ +/* + + 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 "../../../utils/Ownable/IOwnable.sol"; +import "./IMatchOrders.sol"; +import "./IAssets.sol"; + + +// solhint-disable no-empty-blocks +contract IOrderMatcher is + IOwnable, + IMatchOrders, + IAssets +{} diff --git a/contracts/extensions/contracts/OrderMatcher/libs/LibConstants.sol b/contracts/extensions/contracts/OrderMatcher/libs/LibConstants.sol new file mode 100644 index 000000000..a7c170b42 --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/libs/LibConstants.sol @@ -0,0 +1,53 @@ +/* + + 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 "../../../protocol/Exchange/interfaces/IExchange.sol"; + + +contract LibConstants { + + bytes4 constant internal ERC20_TRANSFER_SELECTOR = bytes4(keccak256("transfer(address,uint256)")); + bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256("ERC20Token(address)")); + bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256("ERC721Token(address,uint256)")); + + // solhint-disable var-name-mixedcase + IExchange internal EXCHANGE; + address internal ERC20_PROXY_ADDRESS; + address internal ERC721_PROXY_ADDRESS; + // solhint-enable var-name-mixedcase + + constructor (address _exchange) + public + { + EXCHANGE = IExchange(_exchange); + + ERC20_PROXY_ADDRESS = EXCHANGE.getAssetProxy(ERC20_DATA_ID); + require( + ERC20_PROXY_ADDRESS != address(0), + "UNREGISTERED_ASSET_PROXY" + ); + + ERC721_PROXY_ADDRESS = EXCHANGE.getAssetProxy(ERC721_DATA_ID); + require( + ERC721_PROXY_ADDRESS != address(0), + "UNREGISTERED_ASSET_PROXY" + ); + } +} diff --git a/contracts/extensions/contracts/OrderMatcher/mixins/MAssets.sol b/contracts/extensions/contracts/OrderMatcher/mixins/MAssets.sol new file mode 100644 index 000000000..32cfddf1c --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/mixins/MAssets.sol @@ -0,0 +1,71 @@ +/* + + 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 "../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. + function transferAssetToSender( + bytes memory assetData, + uint256 amount + ) + internal; + + /// @dev Decodes ERC20 assetData and transfers given amount to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferERC20Token( + bytes memory assetData, + uint256 amount + ) + internal; + + /// @dev Decodes ERC721 assetData and transfers given amount to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferERC721Token( + bytes memory assetData, + uint256 amount + ) + internal; + + /// @dev Sets approval for ERC20 AssetProxy. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to approve for respective proxy. + function approveERC20Token( + bytes memory assetData, + uint256 amount + ) + internal; + + /// @dev Sets approval for ERC721 AssetProxy. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to approve for respective proxy. + function approveERC721Token( + bytes memory assetData, + uint256 amount + ) + internal; +} diff --git a/contracts/protocol/src/artifacts/index.ts b/contracts/protocol/src/artifacts/index.ts index 1d53ceb04..2a9de78d2 100644 --- a/contracts/protocol/src/artifacts/index.ts +++ b/contracts/protocol/src/artifacts/index.ts @@ -6,6 +6,7 @@ import * as ERC721Proxy from '../../generated-artifacts/ERC721Proxy.json'; import * as Exchange from '../../generated-artifacts/Exchange.json'; import * as MixinAuthorizable from '../../generated-artifacts/MixinAuthorizable.json'; import * as MultiAssetProxy from '../../generated-artifacts/MultiAssetProxy.json'; +import * as OrderMatcher from '../../generated-artifacts/OrderMatcher.json'; import * as OrderValidator from '../../generated-artifacts/OrderValidator.json'; import * as TestAssetProxyDispatcher from '../../generated-artifacts/TestAssetProxyDispatcher.json'; import * as TestAssetProxyOwner from '../../generated-artifacts/TestAssetProxyOwner.json'; @@ -20,6 +21,7 @@ export const artifacts = { Exchange: Exchange as ContractArtifact, MixinAuthorizable: MixinAuthorizable as ContractArtifact, MultiAssetProxy: MultiAssetProxy as ContractArtifact, + OrderMatcher: OrderMatcher as ContractArtifact, OrderValidator: OrderValidator as ContractArtifact, TestAssetProxyDispatcher: TestAssetProxyDispatcher as ContractArtifact, TestAssetProxyOwner: TestAssetProxyOwner as ContractArtifact, |