Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save itsmehdiabdi/9295817646b7258b5167e77b2b4f15ca to your computer and use it in GitHub Desktop.
Save itsmehdiabdi/9295817646b7258b5167e77b2b4f15ca to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.5.17+commit.d19bba13.js&optimize=false&runs=200&gist=
/*
Copyright 2019 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.5.9;
import "@0x/contracts-utils/contracts/src/LibEIP712.sol";
contract LibEIP712ExchangeDomain {
// EIP712 Exchange Domain Name value
string constant internal _EIP712_EXCHANGE_DOMAIN_NAME = "0x Protocol";
// EIP712 Exchange Domain Version value
string constant internal _EIP712_EXCHANGE_DOMAIN_VERSION = "3.0.0";
// solhint-disable var-name-mixedcase
/// @dev Hash of the EIP712 Domain Separator data
/// @return 0 Domain hash.
bytes32 public EIP712_EXCHANGE_DOMAIN_HASH;
// solhint-enable var-name-mixedcase
/// @param chainId Chain ID of the network this contract is deployed on.
/// @param verifyingContractAddressIfExists Address of the verifying contract (null if the address of this contract)
constructor (
uint256 chainId,
address verifyingContractAddressIfExists
)
public
{
address verifyingContractAddress = verifyingContractAddressIfExists == address(0) ? address(this) : verifyingContractAddressIfExists;
EIP712_EXCHANGE_DOMAIN_HASH = LibEIP712.hashEIP712Domain(
_EIP712_EXCHANGE_DOMAIN_NAME,
_EIP712_EXCHANGE_DOMAIN_VERSION,
chainId,
verifyingContractAddress
);
}
}
/*
Copyright 2019 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.5.9;
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "./LibMath.sol";
import "./LibOrder.sol";
library LibFillResults {
using LibSafeMath for uint256;
struct BatchMatchedFillResults {
FillResults[] left; // Fill results for left orders
FillResults[] right; // Fill results for right orders
uint256 profitInLeftMakerAsset; // Profit taken from left makers
uint256 profitInRightMakerAsset; // Profit taken from right makers
}
struct FillResults {
uint256 makerAssetFilledAmount; // Total amount of makerAsset(s) filled.
uint256 takerAssetFilledAmount; // Total amount of takerAsset(s) filled.
uint256 makerFeePaid; // Total amount of fees paid by maker(s) to feeRecipient(s).
uint256 takerFeePaid; // Total amount of fees paid by taker to feeRecipients(s).
uint256 protocolFeePaid; // Total amount of fees paid by taker to the staking contract.
}
struct MatchedFillResults {
FillResults left; // Amounts filled and fees paid of left order.
FillResults right; // Amounts filled and fees paid of right order.
uint256 profitInLeftMakerAsset; // Profit taken from the left maker
uint256 profitInRightMakerAsset; // Profit taken from the right maker
}
/// @dev Calculates amounts filled and fees paid by maker and taker.
/// @param order to be filled.
/// @param takerAssetFilledAmount Amount of takerAsset that will be filled.
/// @param protocolFeeMultiplier The current protocol fee of the exchange contract.
/// @param gasPrice The gasprice of the transaction. This is provided so that the function call can continue
/// to be pure rather than view.
/// @return fillResults Amounts filled and fees paid by maker and taker.
function calculateFillResults(
LibOrder.Order memory order,
uint256 takerAssetFilledAmount,
uint256 protocolFeeMultiplier,
uint256 gasPrice
)
internal
pure
returns (FillResults memory fillResults)
{
// Compute proportional transfer amounts
fillResults.takerAssetFilledAmount = takerAssetFilledAmount;
fillResults.makerAssetFilledAmount = LibMath.safeGetPartialAmountFloor(
takerAssetFilledAmount,
order.takerAssetAmount,
order.makerAssetAmount
);
fillResults.makerFeePaid = LibMath.safeGetPartialAmountFloor(
takerAssetFilledAmount,
order.takerAssetAmount,
order.makerFee
);
fillResults.takerFeePaid = LibMath.safeGetPartialAmountFloor(
takerAssetFilledAmount,
order.takerAssetAmount,
order.takerFee
);
// Compute the protocol fee that should be paid for a single fill.
fillResults.protocolFeePaid = gasPrice.safeMul(protocolFeeMultiplier);
return fillResults;
}
/// @dev Calculates fill amounts for the matched orders.
/// Each order is filled at their respective price point. However, the calculations are
/// carried out as though the orders are both being filled at the right order's price point.
/// The profit made by the leftOrder order goes to the taker (who matched the two orders).
/// @param leftOrder First order to match.
/// @param rightOrder Second order to match.
/// @param leftOrderTakerAssetFilledAmount Amount of left order already filled.
/// @param rightOrderTakerAssetFilledAmount Amount of right order already filled.
/// @param protocolFeeMultiplier The current protocol fee of the exchange contract.
/// @param gasPrice The gasprice of the transaction. This is provided so that the function call can continue
/// to be pure rather than view.
/// @param shouldMaximallyFillOrders A value that indicates whether or not this calculation should use
/// the maximal fill order matching strategy.
/// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.
function calculateMatchedFillResults(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,
uint256 leftOrderTakerAssetFilledAmount,
uint256 rightOrderTakerAssetFilledAmount,
uint256 protocolFeeMultiplier,
uint256 gasPrice,
bool shouldMaximallyFillOrders
)
internal
pure
returns (MatchedFillResults memory matchedFillResults)
{
// Derive maker asset amounts for left & right orders, given store taker assert amounts
uint256 leftTakerAssetAmountRemaining = leftOrder.takerAssetAmount.safeSub(leftOrderTakerAssetFilledAmount);
uint256 leftMakerAssetAmountRemaining = LibMath.safeGetPartialAmountFloor(
leftOrder.makerAssetAmount,
leftOrder.takerAssetAmount,
leftTakerAssetAmountRemaining
);
uint256 rightTakerAssetAmountRemaining = rightOrder.takerAssetAmount.safeSub(rightOrderTakerAssetFilledAmount);
uint256 rightMakerAssetAmountRemaining = LibMath.safeGetPartialAmountFloor(
rightOrder.makerAssetAmount,
rightOrder.takerAssetAmount,
rightTakerAssetAmountRemaining
);
// Maximally fill the orders and pay out profits to the matcher in one or both of the maker assets.
if (shouldMaximallyFillOrders) {
matchedFillResults = _calculateMatchedFillResultsWithMaximalFill(
leftOrder,
rightOrder,
leftMakerAssetAmountRemaining,
leftTakerAssetAmountRemaining,
rightMakerAssetAmountRemaining,
rightTakerAssetAmountRemaining
);
} else {
matchedFillResults = _calculateMatchedFillResults(
leftOrder,
rightOrder,
leftMakerAssetAmountRemaining,
leftTakerAssetAmountRemaining,
rightMakerAssetAmountRemaining,
rightTakerAssetAmountRemaining
);
}
// Compute fees for left order
matchedFillResults.left.makerFeePaid = LibMath.safeGetPartialAmountFloor(
matchedFillResults.left.makerAssetFilledAmount,
leftOrder.makerAssetAmount,
leftOrder.makerFee
);
matchedFillResults.left.takerFeePaid = LibMath.safeGetPartialAmountFloor(
matchedFillResults.left.takerAssetFilledAmount,
leftOrder.takerAssetAmount,
leftOrder.takerFee
);
// Compute fees for right order
matchedFillResults.right.makerFeePaid = LibMath.safeGetPartialAmountFloor(
matchedFillResults.right.makerAssetFilledAmount,
rightOrder.makerAssetAmount,
rightOrder.makerFee
);
matchedFillResults.right.takerFeePaid = LibMath.safeGetPartialAmountFloor(
matchedFillResults.right.takerAssetFilledAmount,
rightOrder.takerAssetAmount,
rightOrder.takerFee
);
// Compute the protocol fee that should be paid for a single fill. In this
// case this should be made the protocol fee for both the left and right orders.
uint256 protocolFee = gasPrice.safeMul(protocolFeeMultiplier);
matchedFillResults.left.protocolFeePaid = protocolFee;
matchedFillResults.right.protocolFeePaid = protocolFee;
// Return fill results
return matchedFillResults;
}
/// @dev Adds properties of both FillResults instances.
/// @param fillResults1 The first FillResults.
/// @param fillResults2 The second FillResults.
/// @return The sum of both fill results.
function addFillResults(
FillResults memory fillResults1,
FillResults memory fillResults2
)
internal
pure
returns (FillResults memory totalFillResults)
{
totalFillResults.makerAssetFilledAmount = fillResults1.makerAssetFilledAmount.safeAdd(fillResults2.makerAssetFilledAmount);
totalFillResults.takerAssetFilledAmount = fillResults1.takerAssetFilledAmount.safeAdd(fillResults2.takerAssetFilledAmount);
totalFillResults.makerFeePaid = fillResults1.makerFeePaid.safeAdd(fillResults2.makerFeePaid);
totalFillResults.takerFeePaid = fillResults1.takerFeePaid.safeAdd(fillResults2.takerFeePaid);
totalFillResults.protocolFeePaid = fillResults1.protocolFeePaid.safeAdd(fillResults2.protocolFeePaid);
return totalFillResults;
}
/// @dev Calculates part of the matched fill results for a given situation using the fill strategy that only
/// awards profit denominated in the left maker asset.
/// @param leftOrder The left order in the order matching situation.
/// @param rightOrder The right order in the order matching situation.
/// @param leftMakerAssetAmountRemaining The amount of the left order maker asset that can still be filled.
/// @param leftTakerAssetAmountRemaining The amount of the left order taker asset that can still be filled.
/// @param rightMakerAssetAmountRemaining The amount of the right order maker asset that can still be filled.
/// @param rightTakerAssetAmountRemaining The amount of the right order taker asset that can still be filled.
/// @return MatchFillResults struct that does not include fees paid.
function _calculateMatchedFillResults(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,
uint256 leftMakerAssetAmountRemaining,
uint256 leftTakerAssetAmountRemaining,
uint256 rightMakerAssetAmountRemaining,
uint256 rightTakerAssetAmountRemaining
)
private
pure
returns (MatchedFillResults memory matchedFillResults)
{
// Calculate fill results for maker and taker assets: at least one order will be fully filled.
// The maximum amount the left maker can buy is `leftTakerAssetAmountRemaining`
// The maximum amount the right maker can sell is `rightMakerAssetAmountRemaining`
// We have two distinct cases for calculating the fill results:
// Case 1.
// If the left maker can buy more than the right maker can sell, then only the right order is fully filled.
// If the left maker can buy exactly what the right maker can sell, then both orders are fully filled.
// Case 2.
// If the left maker cannot buy more than the right maker can sell, then only the left order is fully filled.
// Case 3.
// If the left maker can buy exactly as much as the right maker can sell, then both orders are fully filled.
if (leftTakerAssetAmountRemaining > rightMakerAssetAmountRemaining) {
// Case 1: Right order is fully filled
matchedFillResults = _calculateCompleteRightFill(
leftOrder,
rightMakerAssetAmountRemaining,
rightTakerAssetAmountRemaining
);
} else if (leftTakerAssetAmountRemaining < rightMakerAssetAmountRemaining) {
// Case 2: Left order is fully filled
matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining;
matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining;
matchedFillResults.right.makerAssetFilledAmount = leftTakerAssetAmountRemaining;
// 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 = LibMath.safeGetPartialAmountCeil(
rightOrder.takerAssetAmount,
rightOrder.makerAssetAmount,
leftTakerAssetAmountRemaining // matchedFillResults.right.makerAssetFilledAmount
);
} else {
// leftTakerAssetAmountRemaining == rightMakerAssetAmountRemaining
// Case 3: Both orders are fully filled. Technically, this could be captured by the above cases, but
// this calculation will be more precise since it does not include rounding.
matchedFillResults = _calculateCompleteFillBoth(
leftMakerAssetAmountRemaining,
leftTakerAssetAmountRemaining,
rightMakerAssetAmountRemaining,
rightTakerAssetAmountRemaining
);
}
// Calculate amount given to taker
matchedFillResults.profitInLeftMakerAsset = matchedFillResults.left.makerAssetFilledAmount.safeSub(
matchedFillResults.right.takerAssetFilledAmount
);
return matchedFillResults;
}
/// @dev Calculates part of the matched fill results for a given situation using the maximal fill order matching
/// strategy.
/// @param leftOrder The left order in the order matching situation.
/// @param rightOrder The right order in the order matching situation.
/// @param leftMakerAssetAmountRemaining The amount of the left order maker asset that can still be filled.
/// @param leftTakerAssetAmountRemaining The amount of the left order taker asset that can still be filled.
/// @param rightMakerAssetAmountRemaining The amount of the right order maker asset that can still be filled.
/// @param rightTakerAssetAmountRemaining The amount of the right order taker asset that can still be filled.
/// @return MatchFillResults struct that does not include fees paid.
function _calculateMatchedFillResultsWithMaximalFill(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,
uint256 leftMakerAssetAmountRemaining,
uint256 leftTakerAssetAmountRemaining,
uint256 rightMakerAssetAmountRemaining,
uint256 rightTakerAssetAmountRemaining
)
private
pure
returns (MatchedFillResults memory matchedFillResults)
{
// If a maker asset is greater than the opposite taker asset, than there will be a spread denominated in that maker asset.
bool doesLeftMakerAssetProfitExist = leftMakerAssetAmountRemaining > rightTakerAssetAmountRemaining;
bool doesRightMakerAssetProfitExist = rightMakerAssetAmountRemaining > leftTakerAssetAmountRemaining;
// Calculate the maximum fill results for the maker and taker assets. At least one of the orders will be fully filled.
//
// The maximum that the left maker can possibly buy is the amount that the right order can sell.
// The maximum that the right maker can possibly buy is the amount that the left order can sell.
//
// If the left order is fully filled, profit will be paid out in the left maker asset. If the right order is fully filled,
// the profit will be out in the right maker asset.
//
// There are three cases to consider:
// Case 1.
// If the left maker can buy more than the right maker can sell, then only the right order is fully filled.
// Case 2.
// If the right maker can buy more than the left maker can sell, then only the right order is fully filled.
// Case 3.
// If the right maker can sell the max of what the left maker can buy and the left maker can sell the max of
// what the right maker can buy, then both orders are fully filled.
if (leftTakerAssetAmountRemaining > rightMakerAssetAmountRemaining) {
// Case 1: Right order is fully filled with the profit paid in the left makerAsset
matchedFillResults = _calculateCompleteRightFill(
leftOrder,
rightMakerAssetAmountRemaining,
rightTakerAssetAmountRemaining
);
} else if (rightTakerAssetAmountRemaining > leftMakerAssetAmountRemaining) {
// Case 2: Left order is fully filled with the profit paid in the right makerAsset.
matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining;
matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining;
// Round down to ensure the right maker's exchange rate does not exceed the price specified by the order.
// We favor the right maker when the exchange rate must be rounded and the profit is being paid in the
// right maker asset.
matchedFillResults.right.makerAssetFilledAmount = LibMath.safeGetPartialAmountFloor(
rightOrder.makerAssetAmount,
rightOrder.takerAssetAmount,
leftMakerAssetAmountRemaining
);
matchedFillResults.right.takerAssetFilledAmount = leftMakerAssetAmountRemaining;
} else {
// Case 3: The right and left orders are fully filled
matchedFillResults = _calculateCompleteFillBoth(
leftMakerAssetAmountRemaining,
leftTakerAssetAmountRemaining,
rightMakerAssetAmountRemaining,
rightTakerAssetAmountRemaining
);
}
// Calculate amount given to taker in the left order's maker asset if the left spread will be part of the profit.
if (doesLeftMakerAssetProfitExist) {
matchedFillResults.profitInLeftMakerAsset = matchedFillResults.left.makerAssetFilledAmount.safeSub(
matchedFillResults.right.takerAssetFilledAmount
);
}
// Calculate amount given to taker in the right order's maker asset if the right spread will be part of the profit.
if (doesRightMakerAssetProfitExist) {
matchedFillResults.profitInRightMakerAsset = matchedFillResults.right.makerAssetFilledAmount.safeSub(
matchedFillResults.left.takerAssetFilledAmount
);
}
return matchedFillResults;
}
/// @dev Calculates the fill results for the maker and taker in the order matching and writes the results
/// to the fillResults that are being collected on the order. Both orders will be fully filled in this
/// case.
/// @param leftMakerAssetAmountRemaining The amount of the left maker asset that is remaining to be filled.
/// @param leftTakerAssetAmountRemaining The amount of the left taker asset that is remaining to be filled.
/// @param rightMakerAssetAmountRemaining The amount of the right maker asset that is remaining to be filled.
/// @param rightTakerAssetAmountRemaining The amount of the right taker asset that is remaining to be filled.
/// @return MatchFillResults struct that does not include fees paid or spreads taken.
function _calculateCompleteFillBoth(
uint256 leftMakerAssetAmountRemaining,
uint256 leftTakerAssetAmountRemaining,
uint256 rightMakerAssetAmountRemaining,
uint256 rightTakerAssetAmountRemaining
)
private
pure
returns (MatchedFillResults memory matchedFillResults)
{
// Calculate the fully filled results for both orders.
matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining;
matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining;
matchedFillResults.right.makerAssetFilledAmount = rightMakerAssetAmountRemaining;
matchedFillResults.right.takerAssetFilledAmount = rightTakerAssetAmountRemaining;
return matchedFillResults;
}
/// @dev Calculates the fill results for the maker and taker in the order matching and writes the results
/// to the fillResults that are being collected on the order.
/// @param leftOrder The left order that is being maximally filled. All of the information about fill amounts
/// can be derived from this order and the right asset remaining fields.
/// @param rightMakerAssetAmountRemaining The amount of the right maker asset that is remaining to be filled.
/// @param rightTakerAssetAmountRemaining The amount of the right taker asset that is remaining to be filled.
/// @return MatchFillResults struct that does not include fees paid or spreads taken.
function _calculateCompleteRightFill(
LibOrder.Order memory leftOrder,
uint256 rightMakerAssetAmountRemaining,
uint256 rightTakerAssetAmountRemaining
)
private
pure
returns (MatchedFillResults memory matchedFillResults)
{
matchedFillResults.right.makerAssetFilledAmount = rightMakerAssetAmountRemaining;
matchedFillResults.right.takerAssetFilledAmount = rightTakerAssetAmountRemaining;
matchedFillResults.left.takerAssetFilledAmount = rightMakerAssetAmountRemaining;
// Round down to ensure the left maker's exchange rate does not exceed the price specified by the order.
// We favor the left maker when the exchange rate must be rounded and the profit is being paid in the
// left maker asset.
matchedFillResults.left.makerAssetFilledAmount = LibMath.safeGetPartialAmountFloor(
leftOrder.makerAssetAmount,
leftOrder.takerAssetAmount,
rightMakerAssetAmountRemaining
);
return matchedFillResults;
}
}
/*
Copyright 2019 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.5.9;
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "./LibMathRichErrors.sol";
library LibMath {
using LibSafeMath for uint256;
/// @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)
{
if (isRoundingErrorFloor(
numerator,
denominator,
target
)) {
LibRichErrors.rrevert(LibMathRichErrors.RoundingError(
numerator,
denominator,
target
));
}
partialAmount = numerator.safeMul(target).safeDiv(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)
{
if (isRoundingErrorCeil(
numerator,
denominator,
target
)) {
LibRichErrors.rrevert(LibMathRichErrors.RoundingError(
numerator,
denominator,
target
));
}
// 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 = numerator.safeMul(target)
.safeAdd(denominator.safeSub(1))
.safeDiv(denominator);
return partialAmount;
}
/// @dev Calculates partial value given a numerator and denominator rounded down.
/// @param numerator Numerator.
/// @param denominator Denominator.
/// @param target Value to calculate partial of.
/// @return Partial value of target rounded down.
function getPartialAmountFloor(
uint256 numerator,
uint256 denominator,
uint256 target
)
internal
pure
returns (uint256 partialAmount)
{
partialAmount = numerator.safeMul(target).safeDiv(denominator);
return partialAmount;
}
/// @dev Calculates partial value given a numerator and denominator rounded down.
/// @param numerator Numerator.
/// @param denominator Denominator.
/// @param target Value to calculate partial of.
/// @return Partial value of target rounded up.
function getPartialAmountCeil(
uint256 numerator,
uint256 denominator,
uint256 target
)
internal
pure
returns (uint256 partialAmount)
{
// 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 = numerator.safeMul(target)
.safeAdd(denominator.safeSub(1))
.safeDiv(denominator);
return partialAmount;
}
/// @dev Checks if rounding error >= 0.1% when rounding down.
/// @param numerator Numerator.
/// @param denominator Denominator.
/// @param target Value to multiply with numerator/denominator.
/// @return Rounding error is present.
function isRoundingErrorFloor(
uint256 numerator,
uint256 denominator,
uint256 target
)
internal
pure
returns (bool isError)
{
if (denominator == 0) {
LibRichErrors.rrevert(LibMathRichErrors.DivisionByZeroError());
}
// The absolute rounding error is the difference between the rounded
// value and the ideal value. The relative rounding error is the
// absolute rounding error divided by the absolute value of the
// ideal value. This is undefined when the ideal value is zero.
//
// The ideal value is `numerator * target / denominator`.
// Let's call `numerator * target % denominator` the remainder.
// The absolute error is `remainder / denominator`.
//
// When the ideal value is zero, we require the absolute error to
// be zero. Fortunately, this is always the case. The ideal value is
// zero iff `numerator == 0` and/or `target == 0`. In this case the
// remainder and absolute error are also zero.
if (target == 0 || numerator == 0) {
return false;
}
// Otherwise, we want the relative rounding error to be strictly
// less than 0.1%.
// The relative error is `remainder / (numerator * target)`.
// We want the relative error less than 1 / 1000:
// remainder / (numerator * denominator) < 1 / 1000
// or equivalently:
// 1000 * remainder < numerator * target
// so we have a rounding error iff:
// 1000 * remainder >= numerator * target
uint256 remainder = mulmod(
target,
numerator,
denominator
);
isError = remainder.safeMul(1000) >= numerator.safeMul(target);
return isError;
}
/// @dev Checks if rounding error >= 0.1% when rounding up.
/// @param numerator Numerator.
/// @param denominator Denominator.
/// @param target Value to multiply with numerator/denominator.
/// @return Rounding error is present.
function isRoundingErrorCeil(
uint256 numerator,
uint256 denominator,
uint256 target
)
internal
pure
returns (bool isError)
{
if (denominator == 0) {
LibRichErrors.rrevert(LibMathRichErrors.DivisionByZeroError());
}
// See the comments in `isRoundingError`.
if (target == 0 || numerator == 0) {
// When either is zero, the ideal value and rounded value are zero
// and there is no rounding error. (Although the relative error
// is undefined.)
return false;
}
// Compute remainder as before
uint256 remainder = mulmod(
target,
numerator,
denominator
);
remainder = denominator.safeSub(remainder) % denominator;
isError = remainder.safeMul(1000) >= numerator.safeMul(target);
return isError;
}
}
pragma solidity ^0.5.9;
library LibMathRichErrors {
// bytes4(keccak256("DivisionByZeroError()"))
bytes internal constant DIVISION_BY_ZERO_ERROR =
hex"a791837c";
// bytes4(keccak256("RoundingError(uint256,uint256,uint256)"))
bytes4 internal constant ROUNDING_ERROR_SELECTOR =
0x339f3de2;
// solhint-disable func-name-mixedcase
function DivisionByZeroError()
internal
pure
returns (bytes memory)
{
return DIVISION_BY_ZERO_ERROR;
}
function RoundingError(
uint256 numerator,
uint256 denominator,
uint256 target
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
ROUNDING_ERROR_SELECTOR,
numerator,
denominator,
target
);
}
}
/*
Copyright 2019 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.5.9;
import "@0x/contracts-utils/contracts/src/LibEIP712.sol";
library LibOrder {
using LibOrder for Order;
// Hash for the EIP712 Order Schema:
// keccak256(abi.encodePacked(
// "Order(",
// "address makerAddress,",
// "address takerAddress,",
// "address feeRecipientAddress,",
// "address senderAddress,",
// "uint256 makerAssetAmount,",
// "uint256 takerAssetAmount,",
// "uint256 makerFee,",
// "uint256 takerFee,",
// "uint256 expirationTimeSeconds,",
// "uint256 salt,",
// "bytes makerAssetData,",
// "bytes takerAssetData,",
// "bytes makerFeeAssetData,",
// "bytes takerFeeAssetData",
// ")"
// ))
bytes32 constant internal _EIP712_ORDER_SCHEMA_HASH =
0xf80322eb8376aafb64eadf8f0d7623f22130fd9491a221e902b713cb984a7534;
// A valid order remains fillable until it is expired, fully filled, or cancelled.
// An order's status is unaffected by external factors, like account balances.
enum OrderStatus {
INVALID, // Default value
INVALID_MAKER_ASSET_AMOUNT, // Order does not have a valid maker asset amount
INVALID_TAKER_ASSET_AMOUNT, // Order does not have a valid taker asset amount
FILLABLE, // Order is fillable
EXPIRED, // Order has already expired
FULLY_FILLED, // Order is fully filled
CANCELLED // Order has been cancelled
}
// solhint-disable max-line-length
/// @dev Canonical order structure.
struct Order {
address makerAddress; // Address that created the order.
address takerAddress; // Address that is allowed to fill the order. If set to 0, any address is allowed to fill the order.
address feeRecipientAddress; // Address that will recieve fees when order is filled.
address senderAddress; // Address that is allowed to call Exchange contract methods that affect this order. If set to 0, any address is allowed to call these methods.
uint256 makerAssetAmount; // Amount of makerAsset being offered by maker. Must be greater than 0.
uint256 takerAssetAmount; // Amount of takerAsset being bid on by maker. Must be greater than 0.
uint256 makerFee; // Fee paid to feeRecipient by maker when order is filled.
uint256 takerFee; // Fee paid to feeRecipient by taker when order is filled.
uint256 expirationTimeSeconds; // Timestamp in seconds at which order expires.
uint256 salt; // Arbitrary number to facilitate uniqueness of the order's hash.
bytes makerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring makerAsset. The leading bytes4 references the id of the asset proxy.
bytes takerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset. The leading bytes4 references the id of the asset proxy.
bytes makerFeeAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring makerFeeAsset. The leading bytes4 references the id of the asset proxy.
bytes takerFeeAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring takerFeeAsset. The leading bytes4 references the id of the asset proxy.
}
// solhint-enable max-line-length
/// @dev Order information returned by `getOrderInfo()`.
struct OrderInfo {
OrderStatus orderStatus; // Status that describes order's validity and fillability.
bytes32 orderHash; // EIP712 typed data hash of the order (see LibOrder.getTypedDataHash).
uint256 orderTakerAssetFilledAmount; // Amount of order that has already been filled.
}
/// @dev Calculates the EIP712 typed data hash of an order with a given domain separator.
/// @param order The order structure.
/// @return EIP712 typed data hash of the order.
function getTypedDataHash(Order memory order, bytes32 eip712ExchangeDomainHash)
internal
pure
returns (bytes32 orderHash)
{
orderHash = LibEIP712.hashEIP712Message(
eip712ExchangeDomainHash,
order.getStructHash()
);
return orderHash;
}
/// @dev Calculates EIP712 hash of the order struct.
/// @param order The order structure.
/// @return EIP712 hash of the order struct.
function getStructHash(Order memory order)
internal
pure
returns (bytes32 result)
{
bytes32 schemaHash = _EIP712_ORDER_SCHEMA_HASH;
bytes memory makerAssetData = order.makerAssetData;
bytes memory takerAssetData = order.takerAssetData;
bytes memory makerFeeAssetData = order.makerFeeAssetData;
bytes memory takerFeeAssetData = order.takerFeeAssetData;
// Assembly for more efficiently computing:
// keccak256(abi.encodePacked(
// EIP712_ORDER_SCHEMA_HASH,
// uint256(order.makerAddress),
// uint256(order.takerAddress),
// uint256(order.feeRecipientAddress),
// uint256(order.senderAddress),
// order.makerAssetAmount,
// order.takerAssetAmount,
// order.makerFee,
// order.takerFee,
// order.expirationTimeSeconds,
// order.salt,
// keccak256(order.makerAssetData),
// keccak256(order.takerAssetData),
// keccak256(order.makerFeeAssetData),
// keccak256(order.takerFeeAssetData)
// ));
assembly {
// Assert order offset (this is an internal error that should never be triggered)
if lt(order, 32) {
invalid()
}
// Calculate memory addresses that will be swapped out before hashing
let pos1 := sub(order, 32)
let pos2 := add(order, 320)
let pos3 := add(order, 352)
let pos4 := add(order, 384)
let pos5 := add(order, 416)
// Backup
let temp1 := mload(pos1)
let temp2 := mload(pos2)
let temp3 := mload(pos3)
let temp4 := mload(pos4)
let temp5 := mload(pos5)
// Hash in place
mstore(pos1, schemaHash)
mstore(pos2, keccak256(add(makerAssetData, 32), mload(makerAssetData))) // store hash of makerAssetData
mstore(pos3, keccak256(add(takerAssetData, 32), mload(takerAssetData))) // store hash of takerAssetData
mstore(pos4, keccak256(add(makerFeeAssetData, 32), mload(makerFeeAssetData))) // store hash of makerFeeAssetData
mstore(pos5, keccak256(add(takerFeeAssetData, 32), mload(takerFeeAssetData))) // store hash of takerFeeAssetData
result := keccak256(pos1, 480)
// Restore
mstore(pos1, temp1)
mstore(pos2, temp2)
mstore(pos3, temp3)
mstore(pos4, temp4)
mstore(pos5, temp5)
}
return result;
}
}
/*
Copyright 2019 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.5.9;
library LibEIP712 {
// Hash of the EIP712 Domain Separator Schema
// keccak256(abi.encodePacked(
// "EIP712Domain(",
// "string name,",
// "string version,",
// "uint256 chainId,",
// "address verifyingContract",
// ")"
// ))
bytes32 constant internal _EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
/// @dev Calculates a EIP712 domain separator.
/// @param name The EIP712 domain name.
/// @param version The EIP712 domain version.
/// @param verifyingContract The EIP712 verifying contract.
/// @return EIP712 domain separator.
function hashEIP712Domain(
string memory name,
string memory version,
uint256 chainId,
address verifyingContract
)
internal
pure
returns (bytes32 result)
{
bytes32 schemaHash = _EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH;
// Assembly for more efficient computing:
// keccak256(abi.encodePacked(
// _EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
// keccak256(bytes(name)),
// keccak256(bytes(version)),
// chainId,
// uint256(verifyingContract)
// ))
assembly {
// Calculate hashes of dynamic data
let nameHash := keccak256(add(name, 32), mload(name))
let versionHash := keccak256(add(version, 32), mload(version))
// Load free memory pointer
let memPtr := mload(64)
// Store params in memory
mstore(memPtr, schemaHash)
mstore(add(memPtr, 32), nameHash)
mstore(add(memPtr, 64), versionHash)
mstore(add(memPtr, 96), chainId)
mstore(add(memPtr, 128), verifyingContract)
// Compute hash
result := keccak256(memPtr, 160)
}
return result;
}
/// @dev Calculates EIP712 encoding for a hash struct with a given domain hash.
/// @param eip712DomainHash Hash of the domain domain separator data, computed
/// with getDomainHash().
/// @param hashStruct The EIP712 hash struct.
/// @return EIP712 hash applied to the given EIP712 Domain.
function hashEIP712Message(bytes32 eip712DomainHash, bytes32 hashStruct)
internal
pure
returns (bytes32 result)
{
// Assembly for more efficient computing:
// keccak256(abi.encodePacked(
// EIP191_HEADER,
// EIP712_DOMAIN_HASH,
// hashStruct
// ));
assembly {
// Load free memory pointer
let memPtr := mload(64)
mstore(memPtr, 0x1901000000000000000000000000000000000000000000000000000000000000) // EIP191 header
mstore(add(memPtr, 2), eip712DomainHash) // EIP712 domain hash
mstore(add(memPtr, 34), hashStruct) // Hash of struct
// Compute hash
result := keccak256(memPtr, 66)
}
return result;
}
}
/*
Copyright 2019 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.5.9;
library LibRichErrors {
// bytes4(keccak256("Error(string)"))
bytes4 internal constant STANDARD_ERROR_SELECTOR =
0x08c379a0;
// solhint-disable func-name-mixedcase
/// @dev ABI encode a standard, string revert error payload.
/// This is the same payload that would be included by a `revert(string)`
/// solidity statement. It has the function signature `Error(string)`.
/// @param message The error string.
/// @return The ABI encoded error.
function StandardError(
string memory message
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
STANDARD_ERROR_SELECTOR,
bytes(message)
);
}
// solhint-enable func-name-mixedcase
/// @dev Reverts an encoded rich revert reason `errorData`.
/// @param errorData ABI encoded error data.
function rrevert(bytes memory errorData)
internal
pure
{
assembly {
revert(add(errorData, 0x20), mload(errorData))
}
}
}
pragma solidity ^0.5.9;
import "./LibRichErrors.sol";
import "./LibSafeMathRichErrors.sol";
library LibSafeMath {
function safeMul(uint256 a, uint256 b)
internal
pure
returns (uint256)
{
if (a == 0) {
return 0;
}
uint256 c = a * b;
if (c / a != b) {
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.MULTIPLICATION_OVERFLOW,
a,
b
));
}
return c;
}
function safeDiv(uint256 a, uint256 b)
internal
pure
returns (uint256)
{
if (b == 0) {
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.DIVISION_BY_ZERO,
a,
b
));
}
uint256 c = a / b;
return c;
}
function safeSub(uint256 a, uint256 b)
internal
pure
returns (uint256)
{
if (b > a) {
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.SUBTRACTION_UNDERFLOW,
a,
b
));
}
return a - b;
}
function safeAdd(uint256 a, uint256 b)
internal
pure
returns (uint256)
{
uint256 c = a + b;
if (c < a) {
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.ADDITION_OVERFLOW,
a,
b
));
}
return c;
}
function max256(uint256 a, uint256 b)
internal
pure
returns (uint256)
{
return a >= b ? a : b;
}
function min256(uint256 a, uint256 b)
internal
pure
returns (uint256)
{
return a < b ? a : b;
}
}
pragma solidity ^0.5.9;
library LibSafeMathRichErrors {
// bytes4(keccak256("Uint256BinOpError(uint8,uint256,uint256)"))
bytes4 internal constant UINT256_BINOP_ERROR_SELECTOR =
0xe946c1bb;
// bytes4(keccak256("Uint256DowncastError(uint8,uint256)"))
bytes4 internal constant UINT256_DOWNCAST_ERROR_SELECTOR =
0xc996af7b;
enum BinOpErrorCodes {
ADDITION_OVERFLOW,
MULTIPLICATION_OVERFLOW,
SUBTRACTION_UNDERFLOW,
DIVISION_BY_ZERO
}
enum DowncastErrorCodes {
VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT32,
VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT64,
VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT96
}
// solhint-disable func-name-mixedcase
function Uint256BinOpError(
BinOpErrorCodes errorCode,
uint256 a,
uint256 b
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
UINT256_BINOP_ERROR_SELECTOR,
errorCode,
a,
b
);
}
function Uint256DowncastError(
DowncastErrorCodes errorCode,
uint256 a
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
UINT256_DOWNCAST_ERROR_SELECTOR,
errorCode,
a
);
}
}
REMIX EXAMPLE PROJECT
Remix example project is present when Remix loads very first time or there are no files existing in the File Explorer.
It contains 3 directories:
1. 'contracts': Holds three contracts with different complexity level, denoted with number prefix in file name.
2. 'scripts': Holds two scripts to deploy a contract. It is explained below.
3. 'tests': Contains one test file for 'Ballot' contract with unit tests in Solidity.
SCRIPTS
The 'scripts' folder contains example async/await scripts for deploying the 'Storage' contract.
For the deployment of any other contract, 'contractName' and 'constructorArgs' should be updated (along with other code if required).
Scripts have full access to the web3.js and ethers.js libraries.
To run a script, right click on file name in the file explorer and click 'Run'. Remember, Solidity file must already be compiled.
Output from script will appear in remix terminal.
{
"deploy": {
"VM:-": {
"linkReferences": {},
"autoDeployLib": true
},
"main:1": {
"linkReferences": {},
"autoDeployLib": true
},
"ropsten:3": {
"linkReferences": {},
"autoDeployLib": true
},
"rinkeby:4": {
"linkReferences": {},
"autoDeployLib": true
},
"kovan:42": {
"linkReferences": {},
"autoDeployLib": true
},
"görli:5": {
"linkReferences": {},
"autoDeployLib": true
},
"Custom": {
"linkReferences": {},
"autoDeployLib": true
}
},
"data": {
"bytecode": {
"linkReferences": {},
"object": "",
"opcodes": "",
"sourceMap": ""
},
"deployedBytecode": {
"linkReferences": {},
"object": "",
"opcodes": "",
"sourceMap": ""
},
"gasEstimates": null,
"methodIdentifiers": {
"getAssetProxy(bytes4)": "60704108",
"registerAssetProxy(address)": "c585bb93"
}
},
"abi": [
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "bytes4",
"name": "id",
"type": "bytes4"
},
{
"indexed": false,
"internalType": "address",
"name": "assetProxy",
"type": "address"
}
],
"name": "AssetProxyRegistered",
"type": "event"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes4",
"name": "assetProxyId",
"type": "bytes4"
}
],
"name": "getAssetProxy",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "assetProxy",
"type": "address"
}
],
"name": "registerAssetProxy",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
]
}
{
"compiler": {
"version": "0.5.17+commit.d19bba13"
},
"language": "Solidity",
"output": {
"abi": [
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "bytes4",
"name": "id",
"type": "bytes4"
},
{
"indexed": false,
"internalType": "address",
"name": "assetProxy",
"type": "address"
}
],
"name": "AssetProxyRegistered",
"type": "event"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes4",
"name": "assetProxyId",
"type": "bytes4"
}
],
"name": "getAssetProxy",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "assetProxy",
"type": "address"
}
],
"name": "registerAssetProxy",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
],
"devdoc": {
"methods": {
"getAssetProxy(bytes4)": {
"details": "Gets an asset proxy.",
"params": {
"assetProxyId": "Id of the asset proxy."
},
"return": "The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered."
},
"registerAssetProxy(address)": {
"details": "Registers an asset proxy to its asset proxy id. Once an asset proxy is registered, it cannot be unregistered.",
"params": {
"assetProxy": "Address of new asset proxy to register."
}
}
}
},
"userdoc": {
"methods": {}
}
},
"settings": {
"compilationTarget": {
"IAssetProxyDispatcher.sol": "IAssetProxyDispatcher"
},
"evmVersion": "istanbul",
"libraries": {},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
},
"sources": {
"IAssetProxyDispatcher.sol": {
"keccak256": "0x5aa3bcc134f9ff77da4f5ba42a17a62391f5adafd605658dcdcdd7c352d52e25",
"urls": [
"bzz-raw://92d355e026a2635f561621e7c835eac53ea15c40375bacc78f57c9c67b9793c4",
"dweb:/ipfs/QmeiYbPmb3yBQ66V1HRKfDUoPGof6AxWsPQfJLfau4j7d4"
]
}
},
"version": 1
}
{
"deploy": {
"VM:-": {
"linkReferences": {},
"autoDeployLib": true
},
"main:1": {
"linkReferences": {},
"autoDeployLib": true
},
"ropsten:3": {
"linkReferences": {},
"autoDeployLib": true
},
"rinkeby:4": {
"linkReferences": {},
"autoDeployLib": true
},
"kovan:42": {
"linkReferences": {},
"autoDeployLib": true
},
"görli:5": {
"linkReferences": {},
"autoDeployLib": true
},
"Custom": {
"linkReferences": {},
"autoDeployLib": true
}
},
"data": {
"bytecode": {
"linkReferences": {},
"object": "",
"opcodes": "",
"sourceMap": ""
},
"deployedBytecode": {
"linkReferences": {},
"object": "",
"opcodes": "",
"sourceMap": ""
},
"gasEstimates": null,
"methodIdentifiers": {
"batchMatchOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],bytes[],bytes[])": "6fcf3e9e",
"batchMatchOrdersWithMaximalFill((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],bytes[],bytes[])": "6a1a80fd",
"matchOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),bytes,bytes)": "88ec79fb",
"matchOrdersWithMaximalFill((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),bytes,bytes)": "b718e292"
}
},
"abi": [
{
"constant": false,
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "makerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "takerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipientAddress",
"type": "address"
},
{
"internalType": "address",
"name": "senderAddress",
"type": "address"
},
{
"internalType": "uint256",
"name": "makerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expirationTimeSeconds",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "salt",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "makerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "makerFeeAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerFeeAssetData",
"type": "bytes"
}
],
"internalType": "struct LibOrder.Order[]",
"name": "leftOrders",
"type": "tuple[]"
},
{
"components": [
{
"internalType": "address",
"name": "makerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "takerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipientAddress",
"type": "address"
},
{
"internalType": "address",
"name": "senderAddress",
"type": "address"
},
{
"internalType": "uint256",
"name": "makerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expirationTimeSeconds",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "salt",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "makerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "makerFeeAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerFeeAssetData",
"type": "bytes"
}
],
"internalType": "struct LibOrder.Order[]",
"name": "rightOrders",
"type": "tuple[]"
},
{
"internalType": "bytes[]",
"name": "leftSignatures",
"type": "bytes[]"
},
{
"internalType": "bytes[]",
"name": "rightSignatures",
"type": "bytes[]"
}
],
"name": "batchMatchOrders",
"outputs": [
{
"components": [
{
"components": [
{
"internalType": "uint256",
"name": "makerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "protocolFeePaid",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.FillResults[]",
"name": "left",
"type": "tuple[]"
},
{
"components": [
{
"internalType": "uint256",
"name": "makerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "protocolFeePaid",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.FillResults[]",
"name": "right",
"type": "tuple[]"
},
{
"internalType": "uint256",
"name": "profitInLeftMakerAsset",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "profitInRightMakerAsset",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.BatchMatchedFillResults",
"name": "batchMatchedFillResults",
"type": "tuple"
}
],
"payable": true,
"stateMutability": "payable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "makerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "takerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipientAddress",
"type": "address"
},
{
"internalType": "address",
"name": "senderAddress",
"type": "address"
},
{
"internalType": "uint256",
"name": "makerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expirationTimeSeconds",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "salt",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "makerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "makerFeeAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerFeeAssetData",
"type": "bytes"
}
],
"internalType": "struct LibOrder.Order[]",
"name": "leftOrders",
"type": "tuple[]"
},
{
"components": [
{
"internalType": "address",
"name": "makerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "takerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipientAddress",
"type": "address"
},
{
"internalType": "address",
"name": "senderAddress",
"type": "address"
},
{
"internalType": "uint256",
"name": "makerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expirationTimeSeconds",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "salt",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "makerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "makerFeeAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerFeeAssetData",
"type": "bytes"
}
],
"internalType": "struct LibOrder.Order[]",
"name": "rightOrders",
"type": "tuple[]"
},
{
"internalType": "bytes[]",
"name": "leftSignatures",
"type": "bytes[]"
},
{
"internalType": "bytes[]",
"name": "rightSignatures",
"type": "bytes[]"
}
],
"name": "batchMatchOrdersWithMaximalFill",
"outputs": [
{
"components": [
{
"components": [
{
"internalType": "uint256",
"name": "makerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "protocolFeePaid",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.FillResults[]",
"name": "left",
"type": "tuple[]"
},
{
"components": [
{
"internalType": "uint256",
"name": "makerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "protocolFeePaid",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.FillResults[]",
"name": "right",
"type": "tuple[]"
},
{
"internalType": "uint256",
"name": "profitInLeftMakerAsset",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "profitInRightMakerAsset",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.BatchMatchedFillResults",
"name": "batchMatchedFillResults",
"type": "tuple"
}
],
"payable": true,
"stateMutability": "payable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "makerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "takerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipientAddress",
"type": "address"
},
{
"internalType": "address",
"name": "senderAddress",
"type": "address"
},
{
"internalType": "uint256",
"name": "makerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expirationTimeSeconds",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "salt",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "makerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "makerFeeAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerFeeAssetData",
"type": "bytes"
}
],
"internalType": "struct LibOrder.Order",
"name": "leftOrder",
"type": "tuple"
},
{
"components": [
{
"internalType": "address",
"name": "makerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "takerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipientAddress",
"type": "address"
},
{
"internalType": "address",
"name": "senderAddress",
"type": "address"
},
{
"internalType": "uint256",
"name": "makerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expirationTimeSeconds",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "salt",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "makerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "makerFeeAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerFeeAssetData",
"type": "bytes"
}
],
"internalType": "struct LibOrder.Order",
"name": "rightOrder",
"type": "tuple"
},
{
"internalType": "bytes",
"name": "leftSignature",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "rightSignature",
"type": "bytes"
}
],
"name": "matchOrders",
"outputs": [
{
"components": [
{
"components": [
{
"internalType": "uint256",
"name": "makerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "protocolFeePaid",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.FillResults",
"name": "left",
"type": "tuple"
},
{
"components": [
{
"internalType": "uint256",
"name": "makerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "protocolFeePaid",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.FillResults",
"name": "right",
"type": "tuple"
},
{
"internalType": "uint256",
"name": "profitInLeftMakerAsset",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "profitInRightMakerAsset",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.MatchedFillResults",
"name": "matchedFillResults",
"type": "tuple"
}
],
"payable": true,
"stateMutability": "payable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "makerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "takerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipientAddress",
"type": "address"
},
{
"internalType": "address",
"name": "senderAddress",
"type": "address"
},
{
"internalType": "uint256",
"name": "makerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expirationTimeSeconds",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "salt",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "makerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "makerFeeAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerFeeAssetData",
"type": "bytes"
}
],
"internalType": "struct LibOrder.Order",
"name": "leftOrder",
"type": "tuple"
},
{
"components": [
{
"internalType": "address",
"name": "makerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "takerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipientAddress",
"type": "address"
},
{
"internalType": "address",
"name": "senderAddress",
"type": "address"
},
{
"internalType": "uint256",
"name": "makerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expirationTimeSeconds",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "salt",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "makerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "makerFeeAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerFeeAssetData",
"type": "bytes"
}
],
"internalType": "struct LibOrder.Order",
"name": "rightOrder",
"type": "tuple"
},
{
"internalType": "bytes",
"name": "leftSignature",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "rightSignature",
"type": "bytes"
}
],
"name": "matchOrdersWithMaximalFill",
"outputs": [
{
"components": [
{
"components": [
{
"internalType": "uint256",
"name": "makerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "protocolFeePaid",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.FillResults",
"name": "left",
"type": "tuple"
},
{
"components": [
{
"internalType": "uint256",
"name": "makerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "protocolFeePaid",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.FillResults",
"name": "right",
"type": "tuple"
},
{
"internalType": "uint256",
"name": "profitInLeftMakerAsset",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "profitInRightMakerAsset",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.MatchedFillResults",
"name": "matchedFillResults",
"type": "tuple"
}
],
"payable": true,
"stateMutability": "payable",
"type": "function"
}
]
}
{
"compiler": {
"version": "0.5.17+commit.d19bba13"
},
"language": "Solidity",
"output": {
"abi": [
{
"constant": false,
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "makerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "takerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipientAddress",
"type": "address"
},
{
"internalType": "address",
"name": "senderAddress",
"type": "address"
},
{
"internalType": "uint256",
"name": "makerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expirationTimeSeconds",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "salt",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "makerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "makerFeeAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerFeeAssetData",
"type": "bytes"
}
],
"internalType": "struct LibOrder.Order[]",
"name": "leftOrders",
"type": "tuple[]"
},
{
"components": [
{
"internalType": "address",
"name": "makerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "takerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipientAddress",
"type": "address"
},
{
"internalType": "address",
"name": "senderAddress",
"type": "address"
},
{
"internalType": "uint256",
"name": "makerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expirationTimeSeconds",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "salt",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "makerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "makerFeeAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerFeeAssetData",
"type": "bytes"
}
],
"internalType": "struct LibOrder.Order[]",
"name": "rightOrders",
"type": "tuple[]"
},
{
"internalType": "bytes[]",
"name": "leftSignatures",
"type": "bytes[]"
},
{
"internalType": "bytes[]",
"name": "rightSignatures",
"type": "bytes[]"
}
],
"name": "batchMatchOrders",
"outputs": [
{
"components": [
{
"components": [
{
"internalType": "uint256",
"name": "makerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "protocolFeePaid",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.FillResults[]",
"name": "left",
"type": "tuple[]"
},
{
"components": [
{
"internalType": "uint256",
"name": "makerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "protocolFeePaid",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.FillResults[]",
"name": "right",
"type": "tuple[]"
},
{
"internalType": "uint256",
"name": "profitInLeftMakerAsset",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "profitInRightMakerAsset",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.BatchMatchedFillResults",
"name": "batchMatchedFillResults",
"type": "tuple"
}
],
"payable": true,
"stateMutability": "payable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "makerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "takerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipientAddress",
"type": "address"
},
{
"internalType": "address",
"name": "senderAddress",
"type": "address"
},
{
"internalType": "uint256",
"name": "makerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expirationTimeSeconds",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "salt",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "makerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "makerFeeAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerFeeAssetData",
"type": "bytes"
}
],
"internalType": "struct LibOrder.Order[]",
"name": "leftOrders",
"type": "tuple[]"
},
{
"components": [
{
"internalType": "address",
"name": "makerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "takerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipientAddress",
"type": "address"
},
{
"internalType": "address",
"name": "senderAddress",
"type": "address"
},
{
"internalType": "uint256",
"name": "makerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expirationTimeSeconds",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "salt",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "makerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "makerFeeAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerFeeAssetData",
"type": "bytes"
}
],
"internalType": "struct LibOrder.Order[]",
"name": "rightOrders",
"type": "tuple[]"
},
{
"internalType": "bytes[]",
"name": "leftSignatures",
"type": "bytes[]"
},
{
"internalType": "bytes[]",
"name": "rightSignatures",
"type": "bytes[]"
}
],
"name": "batchMatchOrdersWithMaximalFill",
"outputs": [
{
"components": [
{
"components": [
{
"internalType": "uint256",
"name": "makerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "protocolFeePaid",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.FillResults[]",
"name": "left",
"type": "tuple[]"
},
{
"components": [
{
"internalType": "uint256",
"name": "makerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "protocolFeePaid",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.FillResults[]",
"name": "right",
"type": "tuple[]"
},
{
"internalType": "uint256",
"name": "profitInLeftMakerAsset",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "profitInRightMakerAsset",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.BatchMatchedFillResults",
"name": "batchMatchedFillResults",
"type": "tuple"
}
],
"payable": true,
"stateMutability": "payable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "makerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "takerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipientAddress",
"type": "address"
},
{
"internalType": "address",
"name": "senderAddress",
"type": "address"
},
{
"internalType": "uint256",
"name": "makerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expirationTimeSeconds",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "salt",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "makerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "makerFeeAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerFeeAssetData",
"type": "bytes"
}
],
"internalType": "struct LibOrder.Order",
"name": "leftOrder",
"type": "tuple"
},
{
"components": [
{
"internalType": "address",
"name": "makerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "takerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipientAddress",
"type": "address"
},
{
"internalType": "address",
"name": "senderAddress",
"type": "address"
},
{
"internalType": "uint256",
"name": "makerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expirationTimeSeconds",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "salt",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "makerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "makerFeeAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerFeeAssetData",
"type": "bytes"
}
],
"internalType": "struct LibOrder.Order",
"name": "rightOrder",
"type": "tuple"
},
{
"internalType": "bytes",
"name": "leftSignature",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "rightSignature",
"type": "bytes"
}
],
"name": "matchOrders",
"outputs": [
{
"components": [
{
"components": [
{
"internalType": "uint256",
"name": "makerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "protocolFeePaid",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.FillResults",
"name": "left",
"type": "tuple"
},
{
"components": [
{
"internalType": "uint256",
"name": "makerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "protocolFeePaid",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.FillResults",
"name": "right",
"type": "tuple"
},
{
"internalType": "uint256",
"name": "profitInLeftMakerAsset",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "profitInRightMakerAsset",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.MatchedFillResults",
"name": "matchedFillResults",
"type": "tuple"
}
],
"payable": true,
"stateMutability": "payable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "makerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "takerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipientAddress",
"type": "address"
},
{
"internalType": "address",
"name": "senderAddress",
"type": "address"
},
{
"internalType": "uint256",
"name": "makerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expirationTimeSeconds",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "salt",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "makerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "makerFeeAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerFeeAssetData",
"type": "bytes"
}
],
"internalType": "struct LibOrder.Order",
"name": "leftOrder",
"type": "tuple"
},
{
"components": [
{
"internalType": "address",
"name": "makerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "takerAddress",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipientAddress",
"type": "address"
},
{
"internalType": "address",
"name": "senderAddress",
"type": "address"
},
{
"internalType": "uint256",
"name": "makerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "expirationTimeSeconds",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "salt",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "makerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "makerFeeAssetData",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "takerFeeAssetData",
"type": "bytes"
}
],
"internalType": "struct LibOrder.Order",
"name": "rightOrder",
"type": "tuple"
},
{
"internalType": "bytes",
"name": "leftSignature",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "rightSignature",
"type": "bytes"
}
],
"name": "matchOrdersWithMaximalFill",
"outputs": [
{
"components": [
{
"components": [
{
"internalType": "uint256",
"name": "makerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "protocolFeePaid",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.FillResults",
"name": "left",
"type": "tuple"
},
{
"components": [
{
"internalType": "uint256",
"name": "makerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerAssetFilledAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "makerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "takerFeePaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "protocolFeePaid",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.FillResults",
"name": "right",
"type": "tuple"
},
{
"internalType": "uint256",
"name": "profitInLeftMakerAsset",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "profitInRightMakerAsset",
"type": "uint256"
}
],
"internalType": "struct LibFillResults.MatchedFillResults",
"name": "matchedFillResults",
"type": "tuple"
}
],
"payable": true,
"stateMutability": "payable",
"type": "function"
}
],
"devdoc": {
"methods": {
"batchMatchOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],bytes[],bytes[])": {
"details": "Match complementary orders that have a profitable spread. Each order is filled at their respective price point, and the matcher receives a profit denominated in the left maker asset.",
"params": {
"leftOrders": "Set of orders with the same maker / taker asset.",
"leftSignatures": "Proof that left orders were created by the left makers.",
"rightOrders": "Set of orders to match against `leftOrders`",
"rightSignatures": "Proof that right orders were created by the right makers."
},
"return": "batchMatchedFillResults Amounts filled and profit generated."
},
"batchMatchOrdersWithMaximalFill((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],bytes[],bytes[])": {
"details": "Match complementary orders that have a profitable spread. Each order is maximally filled at their respective price point, and the matcher receives a profit denominated in either the left maker asset, right maker asset, or a combination of both.",
"params": {
"leftOrders": "Set of orders with the same maker / taker asset.",
"leftSignatures": "Proof that left orders were created by the left makers.",
"rightOrders": "Set of orders to match against `leftOrders`",
"rightSignatures": "Proof that right orders were created by the right makers."
},
"return": "batchMatchedFillResults Amounts filled and profit generated."
},
"matchOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),bytes,bytes)": {
"details": "Match two complementary orders that have a profitable spread. Each order is filled at their respective price point. However, the calculations are carried out as though the orders are both being filled at the right order's price point. The profit made by the left order goes to the taker (who matched the two orders).",
"params": {
"leftOrder": "First order to match.",
"leftSignature": "Proof that order was created by the left maker.",
"rightOrder": "Second order to match.",
"rightSignature": "Proof that order was created by the right maker."
},
"return": "matchedFillResults Amounts filled and fees paid by maker and taker of matched orders."
},
"matchOrdersWithMaximalFill((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),bytes,bytes)": {
"details": "Match two complementary orders that have a profitable spread. Each order is maximally filled at their respective price point, and the matcher receives a profit denominated in either the left maker asset, right maker asset, or a combination of both.",
"params": {
"leftOrder": "First order to match.",
"leftSignature": "Proof that order was created by the left maker.",
"rightOrder": "Second order to match.",
"rightSignature": "Proof that order was created by the right maker."
},
"return": "matchedFillResults Amounts filled by maker and taker of matched orders."
}
}
},
"userdoc": {
"methods": {}
}
},
"settings": {
"compilationTarget": {
"IMatchOrders.sol": "IMatchOrders"
},
"evmVersion": "istanbul",
"libraries": {},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
},
"sources": {
"@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol": {
"keccak256": "0xedb2f1e66e7ece2302b4dd19d0ef29a0ffeefddae7e9a81f7104155ac3cbb75b",
"urls": [
"bzz-raw://9a490801ffde73bb98aa3de52ef2d615894d188820ebe4c8cd340f5cd33462ad",
"dweb:/ipfs/QmUTSaJ6GTvNoYCmpd9dv2mz25vakoLS9tQpiF1ohAUMFV"
]
},
"@0x/contracts-exchange-libs/contracts/src/LibMath.sol": {
"keccak256": "0xc331418f75cd832accf9dc439be811e10d99f1d2de3a9c0321fd3f127cdf1e54",
"urls": [
"bzz-raw://8a090ac0a55200b6f5624c41fff2ad21e5c673b7fcd0faf1bc88017087bbb1b2",
"dweb:/ipfs/QmWhxuazHstpSCYDQJscHnAh6ngW7SHKT79t6yQHaJHgmi"
]
},
"@0x/contracts-exchange-libs/contracts/src/LibMathRichErrors.sol": {
"keccak256": "0xd2d047534facd6051467dd7a22d7ee9fd4e54e63bb5a5fae2511eac7332fad3d",
"urls": [
"bzz-raw://5e0aeedd55c996fd72004a14f5c528ed60b46631ca9599bbb7925c8342ca3be4",
"dweb:/ipfs/QmNi1ozt4sYmn4pCobjU7FQ4KGUvK77pCyWUtbD18uQgVm"
]
},
"@0x/contracts-exchange-libs/contracts/src/LibOrder.sol": {
"keccak256": "0x1b7b1e1a2dff4249c6f6c500000342a805f372131797b6d7314435204a651ad4",
"urls": [
"bzz-raw://19c11b77022ccfa530af75f236ea00ecb92aa5476f96abbde268163660b3906b",
"dweb:/ipfs/Qmf1MtwPLpocsBArn8Npyzu4AtMqSagYTuee8JdbMXXwHM"
]
},
"@0x/contracts-utils/contracts/src/LibEIP712.sol": {
"keccak256": "0xe19c3cbc71fbea0fdb45c1763306550015d940bea9dbec5f3fd2edfe2165d673",
"urls": [
"bzz-raw://267d263f0453352a1ac8d8085a13c4976fb8c964eff8d3aab0a84173d1438efb",
"dweb:/ipfs/QmbaRw85MYYP3KTmQ68gYdEnFyMqD7frwjxxwsj4j4JCr1"
]
},
"@0x/contracts-utils/contracts/src/LibRichErrors.sol": {
"keccak256": "0xe3ce6ded2fea3cc80998ca4d364bda530e273af0ba1c78d22a6cdadb6f64186e",
"urls": [
"bzz-raw://8239629dbcee9797a8f8157f03c2b78827b446787b4d53b3033a3e1ab538ad0b",
"dweb:/ipfs/QmbttkX94CxMkS6t1K4He5bNwGKCRUhKqTt9pNsAL5ynst"
]
},
"@0x/contracts-utils/contracts/src/LibSafeMath.sol": {
"keccak256": "0xd0da9ac1905381832f6612774cb7eced3b4b60459d6bafa59f5424955b44c935",
"urls": [
"bzz-raw://ede4bbe2dac43c6cc4e3b7a22285e00d55b31fb8cc00b375c6ac4714298b7e6d",
"dweb:/ipfs/QmWbN5qbJSNYrMMZMugaH5itTVSexe9yPsCPuArQVCLUgi"
]
},
"@0x/contracts-utils/contracts/src/LibSafeMathRichErrors.sol": {
"keccak256": "0xcf6adae04bd7af8e41fcd40ad148cc3d1cd5596bd42b01961b735c9b416a6572",
"urls": [
"bzz-raw://103791140e631e9d670bbc99e5f8e546f56061c5d49de97754006ce2c252d61e",
"dweb:/ipfs/QmeRMyYFSk7n1sLzRSGqxcoX1sKQSdAQya6bRy6nseu6xX"
]
},
"IMatchOrders.sol": {
"keccak256": "0x57e052667a28152057a1ab25b958b55d9c79afe95ea5ab645692bad18e5504b3",
"urls": [
"bzz-raw://de35bf6f6ab53af3dcb6b09999840adbc161c1f59623940670f552b59a28b147",
"dweb:/ipfs/QmebunP1nLEWkMPqbFY8iQewkaUUZH5xVAs3erGerqf264"
]
}
},
"version": 1
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
/**
* @title Storage
* @dev Store & retrieve value in a variable
*/
contract Storage {
uint256 number;
/**
* @dev Store value in variable
* @param num value to store
*/
function store(uint256 num) public {
number = num;
}
/**
* @dev Return value
* @return value of 'number'
*/
function retrieve() public view returns (uint256){
return number;
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
/**
* @title Owner
* @dev Set & change owner
*/
contract Owner {
address private owner;
// event for EVM logging
event OwnerSet(address indexed oldOwner, address indexed newOwner);
// modifier to check if caller is owner
modifier isOwner() {
// If the first argument of 'require' evaluates to 'false', execution terminates and all
// changes to the state and to Ether balances are reverted.
// This used to consume all gas in old EVM versions, but not anymore.
// It is often a good idea to use 'require' to check if functions are called correctly.
// As a second argument, you can also provide an explanation about what went wrong.
require(msg.sender == owner, "Caller is not owner");
_;
}
/**
* @dev Set contract deployer as owner
*/
constructor() {
owner = msg.sender; // 'msg.sender' is sender of current call, contract deployer for a constructor
emit OwnerSet(address(0), owner);
}
/**
* @dev Change owner
* @param newOwner address of new owner
*/
function changeOwner(address newOwner) public isOwner {
emit OwnerSet(owner, newOwner);
owner = newOwner;
}
/**
* @dev Return owner address
* @return address of owner
*/
function getOwner() external view returns (address) {
return owner;
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
/**
* @title Ballot
* @dev Implements voting process along with vote delegation
*/
contract Ballot {
struct Voter {
uint weight; // weight is accumulated by delegation
bool voted; // if true, that person already voted
address delegate; // person delegated to
uint vote; // index of the voted proposal
}
struct Proposal {
// If you can limit the length to a certain number of bytes,
// always use one of bytes1 to bytes32 because they are much cheaper
bytes32 name; // short name (up to 32 bytes)
uint voteCount; // number of accumulated votes
}
address public chairperson;
mapping(address => Voter) public voters;
Proposal[] public proposals;
/**
* @dev Create a new ballot to choose one of 'proposalNames'.
* @param proposalNames names of proposals
*/
constructor(bytes32[] memory proposalNames) {
chairperson = msg.sender;
voters[chairperson].weight = 1;
for (uint i = 0; i < proposalNames.length; i++) {
// 'Proposal({...})' creates a temporary
// Proposal object and 'proposals.push(...)'
// appends it to the end of 'proposals'.
proposals.push(Proposal({
name: proposalNames[i],
voteCount: 0
}));
}
}
/**
* @dev Give 'voter' the right to vote on this ballot. May only be called by 'chairperson'.
* @param voter address of voter
*/
function giveRightToVote(address voter) public {
require(
msg.sender == chairperson,
"Only chairperson can give right to vote."
);
require(
!voters[voter].voted,
"The voter already voted."
);
require(voters[voter].weight == 0);
voters[voter].weight = 1;
}
/**
* @dev Delegate your vote to the voter 'to'.
* @param to address to which vote is delegated
*/
function delegate(address to) public {
Voter storage sender = voters[msg.sender];
require(!sender.voted, "You already voted.");
require(to != msg.sender, "Self-delegation is disallowed.");
while (voters[to].delegate != address(0)) {
to = voters[to].delegate;
// We found a loop in the delegation, not allowed.
require(to != msg.sender, "Found loop in delegation.");
}
sender.voted = true;
sender.delegate = to;
Voter storage delegate_ = voters[to];
if (delegate_.voted) {
// If the delegate already voted,
// directly add to the number of votes
proposals[delegate_.vote].voteCount += sender.weight;
} else {
// If the delegate did not vote yet,
// add to her weight.
delegate_.weight += sender.weight;
}
}
/**
* @dev Give your vote (including votes delegated to you) to proposal 'proposals[proposal].name'.
* @param proposal index of proposal in the proposals array
*/
function vote(uint proposal) public {
Voter storage sender = voters[msg.sender];
require(sender.weight != 0, "Has no right to vote");
require(!sender.voted, "Already voted.");
sender.voted = true;
sender.vote = proposal;
// If 'proposal' is out of the range of the array,
// this will throw automatically and revert all
// changes.
proposals[proposal].voteCount += sender.weight;
}
/**
* @dev Computes the winning proposal taking all previous votes into account.
* @return winningProposal_ index of winning proposal in the proposals array
*/
function winningProposal() public view
returns (uint winningProposal_)
{
uint winningVoteCount = 0;
for (uint p = 0; p < proposals.length; p++) {
if (proposals[p].voteCount > winningVoteCount) {
winningVoteCount = proposals[p].voteCount;
winningProposal_ = p;
}
}
}
/**
* @dev Calls winningProposal() function to get the index of the winner contained in the proposals array and then
* @return winnerName_ the name of the winner
*/
function winnerName() public view
returns (bytes32 winnerName_)
{
winnerName_ = proposals[winningProposal()].name;
}
}
/*
Copyright 2019 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.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
import "./MixinMatchOrders.sol";
import "./MixinWrapperFunctions.sol";
import "./MixinTransferSimulator.sol";
// solhint-disable no-empty-blocks
// MixinAssetProxyDispatcher, MixinExchangeCore, MixinSignatureValidator,
// and MixinTransactions are all inherited via the other Mixins that are
// used.
/// @dev The 0x Exchange contract.
contract Exchange is
LibEIP712ExchangeDomain,
MixinMatchOrders,
MixinWrapperFunctions,
MixinTransferSimulator
{
/// @dev Mixins are instantiated in the order they are inherited
/// @param chainId Chain ID of the network this contract is deployed on.
constructor (uint256 chainId)
public
LibEIP712ExchangeDomain(chainId, address(0))
{}
}
/*
Copyright 2019 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.5.9;
contract IAssetProxy {
/// @dev Transfers assets. Either succeeds or throws.
/// @param assetData Byte array encoded for the respective asset proxy.
/// @param from Address to transfer asset from.
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
function transferFrom(
bytes calldata assetData,
address from,
address to,
uint256 amount
)
external;
/// @dev Gets the proxy id associated with the proxy address.
/// @return Proxy id.
function getProxyId()
external
pure
returns (bytes4);
}
/*
Copyright 2019 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.5.9;
contract IAssetProxyDispatcher {
// Logs registration of new asset proxy
event AssetProxyRegistered(
bytes4 id, // Id of new registered AssetProxy.
address assetProxy // Address of new registered AssetProxy.
);
/// @dev Registers an asset proxy to its asset proxy id.
/// Once an asset proxy is registered, it cannot be unregistered.
/// @param assetProxy Address of new asset proxy to register.
function registerAssetProxy(address assetProxy)
external;
/// @dev Gets an asset proxy.
/// @param assetProxyId Id of the asset proxy.
/// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered.
function getAssetProxy(bytes4 assetProxyId)
external
view
returns (address);
}
/*
Copyright 2019 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.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
// solhint-disable
contract IEIP1271Data {
/// @dev This function's selector is used when ABI encoding the order
/// and hash into a byte array before calling `isValidSignature`.
/// This function serves no other purpose.
function OrderWithHash(
LibOrder.Order calldata order,
bytes32 orderHash
)
external
pure;
/// @dev This function's selector is used when ABI encoding the transaction
/// and hash into a byte array before calling `isValidSignature`.
/// This function serves no other purpose.
function ZeroExTransactionWithHash(
LibZeroExTransaction.ZeroExTransaction calldata transaction,
bytes32 transactionHash
)
external
pure;
}
/*
Copyright 2019 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.5.9;
import "@0x/contracts-utils/contracts/src/LibEIP1271.sol";
contract IEIP1271Wallet is
LibEIP1271
{
/// @dev Verifies that a signature is valid.
/// @param data Arbitrary signed data.
/// @param signature Proof that data has been signed.
/// @return magicValue bytes4(0x20c13b0b) if the signature check succeeds.
function isValidSignature(
bytes calldata data,
bytes calldata signature
)
external
view
returns (bytes4 magicValue);
}
/*
Copyright 2019 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.5.9;
pragma experimental ABIEncoderV2;
import "./IExchangeCore.sol";
import "./IProtocolFees.sol";
import "./IMatchOrders.sol";
import "./ISignatureValidator.sol";
import "./ITransactions.sol";
import "./IAssetProxyDispatcher.sol";
import "./IWrapperFunctions.sol";
import "./ITransferSimulator.sol";
// solhint-disable no-empty-blocks
contract IExchange is
IProtocolFees,
IExchangeCore,
IMatchOrders,
ISignatureValidator,
ITransactions,
IAssetProxyDispatcher,
ITransferSimulator,
IWrapperFunctions
{}
/*
Copyright 2019 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.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
contract IExchangeCore {
// Fill event is emitted whenever an order is filled.
event Fill(
address indexed makerAddress, // Address that created the order.
address indexed feeRecipientAddress, // Address that received fees.
bytes makerAssetData, // Encoded data specific to makerAsset.
bytes takerAssetData, // Encoded data specific to takerAsset.
bytes makerFeeAssetData, // Encoded data specific to makerFeeAsset.
bytes takerFeeAssetData, // Encoded data specific to takerFeeAsset.
bytes32 indexed orderHash, // EIP712 hash of order (see LibOrder.getTypedDataHash).
address takerAddress, // Address that filled the order.
address senderAddress, // Address that called the Exchange contract (msg.sender).
uint256 makerAssetFilledAmount, // Amount of makerAsset sold by maker and bought by taker.
uint256 takerAssetFilledAmount, // Amount of takerAsset sold by taker and bought by maker.
uint256 makerFeePaid, // Amount of makerFeeAssetData paid to feeRecipient by maker.
uint256 takerFeePaid, // Amount of takerFeeAssetData paid to feeRecipient by taker.
uint256 protocolFeePaid // Amount of eth or weth paid to the staking contract.
);
// Cancel event is emitted whenever an individual order is cancelled.
event Cancel(
address indexed makerAddress, // Address that created the order.
address indexed feeRecipientAddress, // Address that would have recieved fees if order was filled.
bytes makerAssetData, // Encoded data specific to makerAsset.
bytes takerAssetData, // Encoded data specific to takerAsset.
address senderAddress, // Address that called the Exchange contract (msg.sender).
bytes32 indexed orderHash // EIP712 hash of order (see LibOrder.getTypedDataHash).
);
// CancelUpTo event is emitted whenever `cancelOrdersUpTo` is executed succesfully.
event CancelUpTo(
address indexed makerAddress, // Orders cancelled must have been created by this address.
address indexed orderSenderAddress, // Orders cancelled must have a `senderAddress` equal to this address.
uint256 orderEpoch // Orders with specified makerAddress and senderAddress with a salt less than this value are considered cancelled.
);
/// @dev Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch
/// and senderAddress equal to msg.sender (or null address if msg.sender == makerAddress).
/// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled.
function cancelOrdersUpTo(uint256 targetOrderEpoch)
external
payable;
/// @dev Fills the input order.
/// @param order Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signature Proof that order has been created by maker.
/// @return Amounts filled and fees paid by maker and taker.
function fillOrder(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
public
payable
returns (LibFillResults.FillResults memory fillResults);
/// @dev After calling, the order can not be filled anymore.
/// @param order Order struct containing order specifications.
function cancelOrder(LibOrder.Order memory order)
public
payable;
/// @dev Gets information about an order: status, hash, and amount filled.
/// @param order Order to gather information on.
/// @return OrderInfo Information about the order and its state.
/// See LibOrder.OrderInfo for a complete description.
function getOrderInfo(LibOrder.Order memory order)
public
view
returns (LibOrder.OrderInfo memory orderInfo);
}
/*
Copyright 2019 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.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
contract IMatchOrders {
/// @dev Match complementary orders that have a profitable spread.
/// Each order is filled at their respective price point, and
/// the matcher receives a profit denominated in the left maker asset.
/// @param leftOrders Set of orders with the same maker / taker asset.
/// @param rightOrders Set of orders to match against `leftOrders`
/// @param leftSignatures Proof that left orders were created by the left makers.
/// @param rightSignatures Proof that right orders were created by the right makers.
/// @return batchMatchedFillResults Amounts filled and profit generated.
function batchMatchOrders(
LibOrder.Order[] memory leftOrders,
LibOrder.Order[] memory rightOrders,
bytes[] memory leftSignatures,
bytes[] memory rightSignatures
)
public
payable
returns (LibFillResults.BatchMatchedFillResults memory batchMatchedFillResults);
/// @dev Match complementary orders that have a profitable spread.
/// Each order is maximally filled at their respective price point, and
/// the matcher receives a profit denominated in either the left maker asset,
/// right maker asset, or a combination of both.
/// @param leftOrders Set of orders with the same maker / taker asset.
/// @param rightOrders Set of orders to match against `leftOrders`
/// @param leftSignatures Proof that left orders were created by the left makers.
/// @param rightSignatures Proof that right orders were created by the right makers.
/// @return batchMatchedFillResults Amounts filled and profit generated.
function batchMatchOrdersWithMaximalFill(
LibOrder.Order[] memory leftOrders,
LibOrder.Order[] memory rightOrders,
bytes[] memory leftSignatures,
bytes[] memory rightSignatures
)
public
payable
returns (LibFillResults.BatchMatchedFillResults memory batchMatchedFillResults);
/// @dev Match two complementary orders that have a profitable spread.
/// Each order is filled at their respective price point. However, the calculations are
/// carried out as though the orders are both being filled at the right order's price point.
/// The profit made by the left order goes to the taker (who matched the two orders).
/// @param leftOrder First order to match.
/// @param rightOrder Second order to match.
/// @param leftSignature Proof that order was created by the left maker.
/// @param rightSignature Proof that order was created by the right maker.
/// @return matchedFillResults Amounts filled and fees paid by maker and taker of matched orders.
function matchOrders(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,
bytes memory leftSignature,
bytes memory rightSignature
)
public
payable
returns (LibFillResults.MatchedFillResults memory matchedFillResults);
/// @dev Match two complementary orders that have a profitable spread.
/// Each order is maximally filled at their respective price point, and
/// the matcher receives a profit denominated in either the left maker asset,
/// right maker asset, or a combination of both.
/// @param leftOrder First order to match.
/// @param rightOrder Second order to match.
/// @param leftSignature Proof that order was created by the left maker.
/// @param rightSignature Proof that order was created by the right maker.
/// @return matchedFillResults Amounts filled by maker and taker of matched orders.
function matchOrdersWithMaximalFill(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,
bytes memory leftSignature,
bytes memory rightSignature
)
public
payable
returns (LibFillResults.MatchedFillResults memory matchedFillResults);
}
/*
Copyright 2019 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.5.9;
contract IProtocolFees {
// Logs updates to the protocol fee multiplier.
event ProtocolFeeMultiplier(uint256 oldProtocolFeeMultiplier, uint256 updatedProtocolFeeMultiplier);
// Logs updates to the protocolFeeCollector address.
event ProtocolFeeCollectorAddress(address oldProtocolFeeCollector, address updatedProtocolFeeCollector);
/// @dev Allows the owner to update the protocol fee multiplier.
/// @param updatedProtocolFeeMultiplier The updated protocol fee multiplier.
function setProtocolFeeMultiplier(uint256 updatedProtocolFeeMultiplier)
external;
/// @dev Allows the owner to update the protocolFeeCollector address.
/// @param updatedProtocolFeeCollector The updated protocolFeeCollector contract address.
function setProtocolFeeCollectorAddress(address updatedProtocolFeeCollector)
external;
/// @dev Returns the protocolFeeMultiplier
function protocolFeeMultiplier()
external
view
returns (uint256);
/// @dev Returns the protocolFeeCollector address
function protocolFeeCollector()
external
view
returns (address);
}
/*
Copyright 2019 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.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
contract ISignatureValidator {
// Allowed signature types.
enum SignatureType {
Illegal, // 0x00, default value
Invalid, // 0x01
EIP712, // 0x02
EthSign, // 0x03
Wallet, // 0x04
Validator, // 0x05
PreSigned, // 0x06
EIP1271Wallet, // 0x07
NSignatureTypes // 0x08, number of signature types. Always leave at end.
}
event SignatureValidatorApproval(
address indexed signerAddress, // Address that approves or disapproves a contract to verify signatures.
address indexed validatorAddress, // Address of signature validator contract.
bool isApproved // Approval or disapproval of validator contract.
);
/// @dev Approves a hash on-chain.
/// After presigning a hash, the preSign signature type will become valid for that hash and signer.
/// @param hash Any 32-byte hash.
function preSign(bytes32 hash)
external
payable;
/// @dev Approves/unnapproves a Validator contract to verify signatures on signer's behalf.
/// @param validatorAddress Address of Validator contract.
/// @param approval Approval or disapproval of Validator contract.
function setSignatureValidatorApproval(
address validatorAddress,
bool approval
)
external
payable;
/// @dev Verifies that a hash has been signed by the given signer.
/// @param hash Any 32-byte hash.
/// @param signature Proof that the hash has been signed by signer.
/// @return isValid `true` if the signature is valid for the given hash and signer.
function isValidHashSignature(
bytes32 hash,
address signerAddress,
bytes memory signature
)
public
view
returns (bool isValid);
/// @dev Verifies that a signature for an order is valid.
/// @param order The order.
/// @param signature Proof that the order has been signed by signer.
/// @return isValid true if the signature is valid for the given order and signer.
function isValidOrderSignature(
LibOrder.Order memory order,
bytes memory signature
)
public
view
returns (bool isValid);
/// @dev Verifies that a signature for a transaction is valid.
/// @param transaction The transaction.
/// @param signature Proof that the order has been signed by signer.
/// @return isValid true if the signature is valid for the given transaction and signer.
function isValidTransactionSignature(
LibZeroExTransaction.ZeroExTransaction memory transaction,
bytes memory signature
)
public
view
returns (bool isValid);
/// @dev Verifies that an order, with provided order hash, has been signed
/// by the given signer.
/// @param order The order.
/// @param orderHash The hash of the order.
/// @param signature Proof that the hash has been signed by signer.
/// @return isValid True if the signature is valid for the given order and signer.
function _isValidOrderWithHashSignature(
LibOrder.Order memory order,
bytes32 orderHash,
bytes memory signature
)
internal
view
returns (bool isValid);
/// @dev Verifies that a transaction, with provided order hash, has been signed
/// by the given signer.
/// @param transaction The transaction.
/// @param transactionHash The hash of the transaction.
/// @param signature Proof that the hash has been signed by signer.
/// @return isValid True if the signature is valid for the given transaction and signer.
function _isValidTransactionWithHashSignature(
LibZeroExTransaction.ZeroExTransaction memory transaction,
bytes32 transactionHash,
bytes memory signature
)
internal
view
returns (bool isValid);
}
/*
Copyright 2019 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.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
contract ITransactions {
// TransactionExecution event is emitted when a ZeroExTransaction is executed.
event TransactionExecution(bytes32 indexed transactionHash);
/// @dev Executes an Exchange method call in the context of signer.
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @param signature Proof that transaction has been signed by signer.
/// @return ABI encoded return data of the underlying Exchange function call.
function executeTransaction(
LibZeroExTransaction.ZeroExTransaction memory transaction,
bytes memory signature
)
public
payable
returns (bytes memory);
/// @dev Executes a batch of Exchange method calls in the context of signer(s).
/// @param transactions Array of 0x transactions containing salt, signerAddress, and data.
/// @param signatures Array of proofs that transactions have been signed by signer(s).
/// @return Array containing ABI encoded return data for each of the underlying Exchange function calls.
function batchExecuteTransactions(
LibZeroExTransaction.ZeroExTransaction[] memory transactions,
bytes[] memory signatures
)
public
payable
returns (bytes[] memory);
/// @dev The current function will be called in the context of this address (either 0x transaction signer or `msg.sender`).
/// If calling a fill function, this address will represent the taker.
/// If calling a cancel function, this address will represent the maker.
/// @return Signer of 0x transaction if entry point is `executeTransaction`.
/// `msg.sender` if entry point is any other function.
function _getCurrentContextAddress()
internal
view
returns (address);
}
/*
Copyright 2019 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.5.9;
pragma experimental ABIEncoderV2;
contract ITransferSimulator {
/// @dev This function may be used to simulate any amount of transfers
/// As they would occur through the Exchange contract. Note that this function
/// will always revert, even if all transfers are successful. However, it may
/// be used with eth_call or with a try/catch pattern in order to simulate
/// the results of the transfers.
/// @param assetData Array of asset details, each encoded per the AssetProxy contract specification.
/// @param fromAddresses Array containing the `from` addresses that correspond with each transfer.
/// @param toAddresses Array containing the `to` addresses that correspond with each transfer.
/// @param amounts Array containing the amounts that correspond to each transfer.
/// @return This function does not return a value. However, it will always revert with
/// `Error("TRANSFERS_SUCCESSFUL")` if all of the transfers were successful.
function simulateDispatchTransferFromCalls(
bytes[] memory assetData,
address[] memory fromAddresses,
address[] memory toAddresses,
uint256[] memory amounts
)
public;
}
/*
Copyright 2019 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.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
contract IWallet {
/// @dev Validates a hash with the `Wallet` signature type.
/// @param hash Message hash that is signed.
/// @param signature Proof of signing.
/// @return magicValue `bytes4(0xb0671381)` if the signature check succeeds.
function isValidSignature(
bytes32 hash,
bytes calldata signature
)
external
view
returns (bytes4 magicValue);
}
/*
Copyright 2019 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.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
contract IWrapperFunctions {
/// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.
/// @param order Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signature Proof that order has been created by maker.
function fillOrKillOrder(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
public
payable
returns (LibFillResults.FillResults memory fillResults);
/// @dev Executes multiple calls of fillOrder.
/// @param orders Array of order specifications.
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
/// @param signatures Proofs that orders have been created by makers.
/// @return Array of amounts filled and fees paid by makers and taker.
function batchFillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
public
payable
returns (LibFillResults.FillResults[] memory fillResults);
/// @dev Executes multiple calls of fillOrKillOrder.
/// @param orders Array of order specifications.
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
/// @param signatures Proofs that orders have been created by makers.
/// @return Array of amounts filled and fees paid by makers and taker.
function batchFillOrKillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
public
payable
returns (LibFillResults.FillResults[] memory fillResults);
/// @dev Executes multiple calls of fillOrder. If any fill reverts, the error is caught and ignored.
/// @param orders Array of order specifications.
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
/// @param signatures Proofs that orders have been created by makers.
/// @return Array of amounts filled and fees paid by makers and taker.
function batchFillOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
public
payable
returns (LibFillResults.FillResults[] memory fillResults);
/// @dev Executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.
/// If any fill reverts, the error is caught and ignored.
/// NOTE: This function does not enforce that the takerAsset is the same for each order.
/// @param orders Array of order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signatures Proofs that orders have been signed by makers.
/// @return Amounts filled and fees paid by makers and taker.
function marketSellOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256 takerAssetFillAmount,
bytes[] memory signatures
)
public
payable
returns (LibFillResults.FillResults memory fillResults);
/// @dev Executes multiple calls of fillOrder until total amount of makerAsset is bought by taker.
/// If any fill reverts, the error is caught and ignored.
/// NOTE: This function does not enforce that the makerAsset is the same for each order.
/// @param orders Array of order specifications.
/// @param makerAssetFillAmount Desired amount of makerAsset to buy.
/// @param signatures Proofs that orders have been signed by makers.
/// @return Amounts filled and fees paid by makers and taker.
function marketBuyOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256 makerAssetFillAmount,
bytes[] memory signatures
)
public
payable
returns (LibFillResults.FillResults memory fillResults);
/// @dev Calls marketSellOrdersNoThrow then reverts if < takerAssetFillAmount has been sold.
/// NOTE: This function does not enforce that the takerAsset is the same for each order.
/// @param orders Array of order specifications.
/// @param takerAssetFillAmount Minimum amount of takerAsset to sell.
/// @param signatures Proofs that orders have been signed by makers.
/// @return Amounts filled and fees paid by makers and taker.
function marketSellOrdersFillOrKill(
LibOrder.Order[] memory orders,
uint256 takerAssetFillAmount,
bytes[] memory signatures
)
public
payable
returns (LibFillResults.FillResults memory fillResults);
/// @dev Calls marketBuyOrdersNoThrow then reverts if < makerAssetFillAmount has been bought.
/// NOTE: This function does not enforce that the makerAsset is the same for each order.
/// @param orders Array of order specifications.
/// @param makerAssetFillAmount Minimum amount of makerAsset to buy.
/// @param signatures Proofs that orders have been signed by makers.
/// @return Amounts filled and fees paid by makers and taker.
function marketBuyOrdersFillOrKill(
LibOrder.Order[] memory orders,
uint256 makerAssetFillAmount,
bytes[] memory signatures
)
public
payable
returns (LibFillResults.FillResults memory fillResults);
/// @dev Executes multiple calls of cancelOrder.
/// @param orders Array of order specifications.
function batchCancelOrders(LibOrder.Order[] memory orders)
public
payable;
}
/*
Copyright 2019 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.5.9;
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeRichErrors.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
library LibExchangeRichErrorDecoder {
using LibBytes for bytes;
/// @dev Decompose an ABI-encoded SignatureError.
/// @param encoded ABI-encoded revert error.
/// @return errorCode The error code.
/// @return signerAddress The expected signer of the hash.
/// @return signature The full signature.
function decodeSignatureError(bytes memory encoded)
internal
pure
returns (
LibExchangeRichErrors.SignatureErrorCodes errorCode,
bytes32 hash,
address signerAddress,
bytes memory signature
)
{
_assertSelectorBytes(encoded, LibExchangeRichErrors.SignatureErrorSelector());
uint8 _errorCode;
(_errorCode, hash, signerAddress, signature) = abi.decode(
encoded.sliceDestructive(4, encoded.length),
(uint8, bytes32, address, bytes)
);
errorCode = LibExchangeRichErrors.SignatureErrorCodes(_errorCode);
}
/// @dev Decompose an ABI-encoded SignatureValidatorError.
/// @param encoded ABI-encoded revert error.
/// @return signerAddress The expected signer of the hash.
/// @return signature The full signature bytes.
/// @return errorData The revert data thrown by the validator contract.
function decodeEIP1271SignatureError(bytes memory encoded)
internal
pure
returns (
address verifyingContractAddress,
bytes memory data,
bytes memory signature,
bytes memory errorData
)
{
_assertSelectorBytes(encoded, LibExchangeRichErrors.EIP1271SignatureErrorSelector());
(verifyingContractAddress, data, signature, errorData) = abi.decode(
encoded.sliceDestructive(4, encoded.length),
(address, bytes, bytes, bytes)
);
}
/// @dev Decompose an ABI-encoded SignatureValidatorNotApprovedError.
/// @param encoded ABI-encoded revert error.
/// @return signerAddress The expected signer of the hash.
/// @return validatorAddress The expected validator.
function decodeSignatureValidatorNotApprovedError(bytes memory encoded)
internal
pure
returns (
address signerAddress,
address validatorAddress
)
{
_assertSelectorBytes(encoded, LibExchangeRichErrors.SignatureValidatorNotApprovedErrorSelector());
(signerAddress, validatorAddress) = abi.decode(
encoded.sliceDestructive(4, encoded.length),
(address, address)
);
}
/// @dev Decompose an ABI-encoded SignatureWalletError.
/// @param encoded ABI-encoded revert error.
/// @return errorCode The error code.
/// @return signerAddress The expected signer of the hash.
/// @return signature The full signature bytes.
/// @return errorData The revert data thrown by the validator contract.
function decodeSignatureWalletError(bytes memory encoded)
internal
pure
returns (
bytes32 hash,
address signerAddress,
bytes memory signature,
bytes memory errorData
)
{
_assertSelectorBytes(encoded, LibExchangeRichErrors.SignatureWalletErrorSelector());
(hash, signerAddress, signature, errorData) = abi.decode(
encoded.sliceDestructive(4, encoded.length),
(bytes32, address, bytes, bytes)
);
}
/// @dev Decompose an ABI-encoded OrderStatusError.
/// @param encoded ABI-encoded revert error.
/// @return orderHash The order hash.
/// @return orderStatus The order status.
function decodeOrderStatusError(bytes memory encoded)
internal
pure
returns (
bytes32 orderHash,
LibOrder.OrderStatus orderStatus
)
{
_assertSelectorBytes(encoded, LibExchangeRichErrors.OrderStatusErrorSelector());
uint8 _orderStatus;
(orderHash, _orderStatus) = abi.decode(
encoded.sliceDestructive(4, encoded.length),
(bytes32, uint8)
);
orderStatus = LibOrder.OrderStatus(_orderStatus);
}
/// @dev Decompose an ABI-encoded OrderStatusError.
/// @param encoded ABI-encoded revert error.
/// @return errorCode Error code that corresponds to invalid maker, taker, or sender.
/// @return orderHash The order hash.
/// @return contextAddress The maker, taker, or sender address
function decodeExchangeInvalidContextError(bytes memory encoded)
internal
pure
returns (
LibExchangeRichErrors.ExchangeContextErrorCodes errorCode,
bytes32 orderHash,
address contextAddress
)
{
_assertSelectorBytes(encoded, LibExchangeRichErrors.ExchangeInvalidContextErrorSelector());
uint8 _errorCode;
(_errorCode, orderHash, contextAddress) = abi.decode(
encoded.sliceDestructive(4, encoded.length),
(uint8, bytes32, address)
);
errorCode = LibExchangeRichErrors.ExchangeContextErrorCodes(_errorCode);
}
/// @dev Decompose an ABI-encoded FillError.
/// @param encoded ABI-encoded revert error.
/// @return errorCode The error code.
/// @return orderHash The order hash.
function decodeFillError(bytes memory encoded)
internal
pure
returns (
LibExchangeRichErrors.FillErrorCodes errorCode,
bytes32 orderHash
)
{
_assertSelectorBytes(encoded, LibExchangeRichErrors.FillErrorSelector());
uint8 _errorCode;
(_errorCode, orderHash) = abi.decode(
encoded.sliceDestructive(4, encoded.length),
(uint8, bytes32)
);
errorCode = LibExchangeRichErrors.FillErrorCodes(_errorCode);
}
/// @dev Decompose an ABI-encoded OrderEpochError.
/// @param encoded ABI-encoded revert error.
/// @return makerAddress The order maker.
/// @return orderSenderAddress The order sender.
/// @return currentEpoch The current epoch for the maker.
function decodeOrderEpochError(bytes memory encoded)
internal
pure
returns (
address makerAddress,
address orderSenderAddress,
uint256 currentEpoch
)
{
_assertSelectorBytes(encoded, LibExchangeRichErrors.OrderEpochErrorSelector());
(makerAddress, orderSenderAddress, currentEpoch) = abi.decode(
encoded.sliceDestructive(4, encoded.length),
(address, address, uint256)
);
}
/// @dev Decompose an ABI-encoded AssetProxyExistsError.
/// @param encoded ABI-encoded revert error.
/// @return assetProxyId Id of asset proxy.
/// @return assetProxyAddress The address of the asset proxy.
function decodeAssetProxyExistsError(bytes memory encoded)
internal
pure
returns (
bytes4 assetProxyId, address assetProxyAddress)
{
_assertSelectorBytes(encoded, LibExchangeRichErrors.AssetProxyExistsErrorSelector());
(assetProxyId, assetProxyAddress) = abi.decode(
encoded.sliceDestructive(4, encoded.length),
(bytes4, address)
);
}
/// @dev Decompose an ABI-encoded AssetProxyDispatchError.
/// @param encoded ABI-encoded revert error.
/// @return errorCode The error code.
/// @return orderHash Hash of the order being dispatched.
/// @return assetData Asset data of the order being dispatched.
function decodeAssetProxyDispatchError(bytes memory encoded)
internal
pure
returns (
LibExchangeRichErrors.AssetProxyDispatchErrorCodes errorCode,
bytes32 orderHash,
bytes memory assetData
)
{
_assertSelectorBytes(encoded, LibExchangeRichErrors.AssetProxyDispatchErrorSelector());
uint8 _errorCode;
(_errorCode, orderHash, assetData) = abi.decode(
encoded.sliceDestructive(4, encoded.length),
(uint8, bytes32, bytes)
);
errorCode = LibExchangeRichErrors.AssetProxyDispatchErrorCodes(_errorCode);
}
/// @dev Decompose an ABI-encoded AssetProxyTransferError.
/// @param encoded ABI-encoded revert error.
/// @return orderHash Hash of the order being dispatched.
/// @return assetData Asset data of the order being dispatched.
/// @return errorData ABI-encoded revert data from the asset proxy.
function decodeAssetProxyTransferError(bytes memory encoded)
internal
pure
returns (
bytes32 orderHash,
bytes memory assetData,
bytes memory errorData
)
{
_assertSelectorBytes(encoded, LibExchangeRichErrors.AssetProxyTransferErrorSelector());
(orderHash, assetData, errorData) = abi.decode(
encoded.sliceDestructive(4, encoded.length),
(bytes32, bytes, bytes)
);
}
/// @dev Decompose an ABI-encoded NegativeSpreadError.
/// @param encoded ABI-encoded revert error.
/// @return leftOrderHash Hash of the left order being matched.
/// @return rightOrderHash Hash of the right order being matched.
function decodeNegativeSpreadError(bytes memory encoded)
internal
pure
returns (
bytes32 leftOrderHash,
bytes32 rightOrderHash
)
{
_assertSelectorBytes(encoded, LibExchangeRichErrors.NegativeSpreadErrorSelector());
(leftOrderHash, rightOrderHash) = abi.decode(
encoded.sliceDestructive(4, encoded.length),
(bytes32, bytes32)
);
}
/// @dev Decompose an ABI-encoded TransactionError.
/// @param encoded ABI-encoded revert error.
/// @return errorCode The error code.
/// @return transactionHash Hash of the transaction.
function decodeTransactionError(bytes memory encoded)
internal
pure
returns (
LibExchangeRichErrors.TransactionErrorCodes errorCode,
bytes32 transactionHash
)
{
_assertSelectorBytes(encoded, LibExchangeRichErrors.TransactionErrorSelector());
uint8 _errorCode;
(_errorCode, transactionHash) = abi.decode(
encoded.sliceDestructive(4, encoded.length),
(uint8, bytes32)
);
errorCode = LibExchangeRichErrors.TransactionErrorCodes(_errorCode);
}
/// @dev Decompose an ABI-encoded TransactionExecutionError.
/// @param encoded ABI-encoded revert error.
/// @return transactionHash Hash of the transaction.
/// @return errorData Error thrown by exeucteTransaction().
function decodeTransactionExecutionError(bytes memory encoded)
internal
pure
returns (
bytes32 transactionHash,
bytes memory errorData
)
{
_assertSelectorBytes(encoded, LibExchangeRichErrors.TransactionExecutionErrorSelector());
(transactionHash, errorData) = abi.decode(
encoded.sliceDestructive(4, encoded.length),
(bytes32, bytes)
);
}
/// @dev Decompose an ABI-encoded IncompleteFillError.
/// @param encoded ABI-encoded revert error.
/// @return orderHash Hash of the order being filled.
function decodeIncompleteFillError(bytes memory encoded)
internal
pure
returns (
LibExchangeRichErrors.IncompleteFillErrorCode errorCode,
uint256 expectedAssetFillAmount,
uint256 actualAssetFillAmount
)
{
_assertSelectorBytes(encoded, LibExchangeRichErrors.IncompleteFillErrorSelector());
uint8 _errorCode;
(_errorCode, expectedAssetFillAmount, actualAssetFillAmount) = abi.decode(
encoded.sliceDestructive(4, encoded.length),
(uint8, uint256, uint256)
);
errorCode = LibExchangeRichErrors.IncompleteFillErrorCode(_errorCode);
}
/// @dev Revert if the leading 4 bytes of `encoded` is not `selector`.
function _assertSelectorBytes(bytes memory encoded, bytes4 selector)
private
pure
{
bytes4 actualSelector = LibBytes.readBytes4(encoded, 0);
require(
actualSelector == selector,
"BAD_SELECTOR"
);
}
}
/*
Copyright 2019 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.5.9;
import "@0x/contracts-utils/contracts/src/Ownable.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeRichErrors.sol";
import "./interfaces/IAssetProxy.sol";
import "./interfaces/IAssetProxyDispatcher.sol";
contract MixinAssetProxyDispatcher is
Ownable,
IAssetProxyDispatcher
{
using LibBytes for bytes;
// Mapping from Asset Proxy Id's to their respective Asset Proxy
mapping (bytes4 => address) internal _assetProxies;
/// @dev Registers an asset proxy to its asset proxy id.
/// Once an asset proxy is registered, it cannot be unregistered.
/// @param assetProxy Address of new asset proxy to register.
function registerAssetProxy(address assetProxy)
external
onlyOwner
{
// Ensure that no asset proxy exists with current id.
bytes4 assetProxyId = IAssetProxy(assetProxy).getProxyId();
address currentAssetProxy = _assetProxies[assetProxyId];
if (currentAssetProxy != address(0)) {
LibRichErrors.rrevert(LibExchangeRichErrors.AssetProxyExistsError(
assetProxyId,
currentAssetProxy
));
}
// Add asset proxy and log registration.
_assetProxies[assetProxyId] = assetProxy;
emit AssetProxyRegistered(
assetProxyId,
assetProxy
);
}
/// @dev Gets an asset proxy.
/// @param assetProxyId Id of the asset proxy.
/// @return assetProxy The asset proxy address registered to assetProxyId. Returns 0x0 if no proxy is registered.
function getAssetProxy(bytes4 assetProxyId)
external
view
returns (address assetProxy)
{
return _assetProxies[assetProxyId];
}
/// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws.
/// @param orderHash Hash of the order associated with this transfer.
/// @param assetData Byte array encoded for the asset.
/// @param from Address to transfer token from.
/// @param to Address to transfer token to.
/// @param amount Amount of token to transfer.
function _dispatchTransferFrom(
bytes32 orderHash,
bytes memory assetData,
address from,
address to,
uint256 amount
)
internal
{
// Do nothing if no amount should be transferred.
if (amount > 0) {
// Ensure assetData is padded to 32 bytes (excluding the id) and is at least 4 bytes long
if (assetData.length % 32 != 4) {
LibRichErrors.rrevert(LibExchangeRichErrors.AssetProxyDispatchError(
LibExchangeRichErrors.AssetProxyDispatchErrorCodes.INVALID_ASSET_DATA_LENGTH,
orderHash,
assetData
));
}
// Lookup assetProxy.
bytes4 assetProxyId = assetData.readBytes4(0);
address assetProxy = _assetProxies[assetProxyId];
// Ensure that assetProxy exists
if (assetProxy == address(0)) {
LibRichErrors.rrevert(LibExchangeRichErrors.AssetProxyDispatchError(
LibExchangeRichErrors.AssetProxyDispatchErrorCodes.UNKNOWN_ASSET_PROXY,
orderHash,
assetData
));
}
// Construct the calldata for the transferFrom call.
bytes memory proxyCalldata = abi.encodeWithSelector(
IAssetProxy(address(0)).transferFrom.selector,
assetData,
from,
to,
amount
);
// Call the asset proxy's transferFrom function with the constructed calldata.
(bool didSucceed, bytes memory returnData) = assetProxy.call(proxyCalldata);
// If the transaction did not succeed, revert with the returned data.
if (!didSucceed) {
LibRichErrors.rrevert(LibExchangeRichErrors.AssetProxyTransferError(
orderHash,
assetData,
returnData
));
}
}
}
}
/*
Copyright 2019 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.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "@0x/contracts-utils/contracts/src/Refundable.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeRichErrors.sol";
import "./interfaces/IExchangeCore.sol";
import "./MixinAssetProxyDispatcher.sol";
import "./MixinProtocolFees.sol";
import "./MixinSignatureValidator.sol";
contract MixinExchangeCore is
IExchangeCore,
Refundable,
LibEIP712ExchangeDomain,
MixinAssetProxyDispatcher,
MixinProtocolFees,
MixinSignatureValidator
{
using LibOrder for LibOrder.Order;
using LibSafeMath for uint256;
using LibBytes for bytes;
/// @dev Mapping of orderHash => amount of takerAsset already bought by maker
/// @param 0 Order hash.
/// @return 0 The amount of taker asset filled.
mapping (bytes32 => uint256) public filled;
/// @dev Mapping of orderHash => cancelled
/// @param 0 Order hash.
/// @return 0 Whether the order was cancelled.
mapping (bytes32 => bool) public cancelled;
/// @dev Mapping of makerAddress => senderAddress => lowest salt an order can have in order to be fillable
/// Orders with specified senderAddress and with a salt less than their epoch are considered cancelled
/// @param 0 Address of the order's maker.
/// @param 1 Address of the order's sender.
/// @return 0 Minimum valid order epoch.
mapping (address => mapping (address => uint256)) public orderEpoch;
/// @dev Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch
/// and senderAddress equal to msg.sender (or null address if msg.sender == makerAddress).
/// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled.
function cancelOrdersUpTo(uint256 targetOrderEpoch)
external
payable
refundFinalBalanceNoReentry
{
address makerAddress = _getCurrentContextAddress();
// If this function is called via `executeTransaction`, we only update the orderEpoch for the makerAddress/msg.sender combination.
// This allows external filter contracts to add rules to how orders are cancelled via this function.
address orderSenderAddress = makerAddress == msg.sender ? address(0) : msg.sender;
// orderEpoch is initialized to 0, so to cancelUpTo we need salt + 1
uint256 newOrderEpoch = targetOrderEpoch + 1;
uint256 oldOrderEpoch = orderEpoch[makerAddress][orderSenderAddress];
// Ensure orderEpoch is monotonically increasing
if (newOrderEpoch <= oldOrderEpoch) {
LibRichErrors.rrevert(LibExchangeRichErrors.OrderEpochError(
makerAddress,
orderSenderAddress,
oldOrderEpoch
));
}
// Update orderEpoch
orderEpoch[makerAddress][orderSenderAddress] = newOrderEpoch;
emit CancelUpTo(
makerAddress,
orderSenderAddress,
newOrderEpoch
);
}
/// @dev Fills the input order.
/// @param order Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signature Proof that order has been created by maker.
/// @return fillResults Amounts filled and fees paid by maker and taker.
function fillOrder(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
public
payable
refundFinalBalanceNoReentry
returns (LibFillResults.FillResults memory fillResults)
{
fillResults = _fillOrder(
order,
takerAssetFillAmount,
signature
);
return fillResults;
}
/// @dev After calling, the order can not be filled anymore.
/// @param order Order struct containing order specifications.
function cancelOrder(LibOrder.Order memory order)
public
payable
refundFinalBalanceNoReentry
{
_cancelOrder(order);
}
/// @dev Gets information about an order: status, hash, and amount filled.
/// @param order Order to gather information on.
/// @return orderInfo Information about the order and its state.
/// See LibOrder.OrderInfo for a complete description.
function getOrderInfo(LibOrder.Order memory order)
public
view
returns (LibOrder.OrderInfo memory orderInfo)
{
// Compute the order hash and fetch the amount of takerAsset that has already been filled
(orderInfo.orderHash, orderInfo.orderTakerAssetFilledAmount) = _getOrderHashAndFilledAmount(order);
// If order.makerAssetAmount is zero, we also reject the order.
// While the Exchange contract handles them correctly, they create
// edge cases in the supporting infrastructure because they have
// an 'infinite' price when computed by a simple division.
if (order.makerAssetAmount == 0) {
orderInfo.orderStatus = LibOrder.OrderStatus.INVALID_MAKER_ASSET_AMOUNT;
return orderInfo;
}
// If order.takerAssetAmount is zero, then the order will always
// be considered filled because 0 == takerAssetAmount == orderTakerAssetFilledAmount
// Instead of distinguishing between unfilled and filled zero taker
// amount orders, we choose not to support them.
if (order.takerAssetAmount == 0) {
orderInfo.orderStatus = LibOrder.OrderStatus.INVALID_TAKER_ASSET_AMOUNT;
return orderInfo;
}
// Validate order availability
if (orderInfo.orderTakerAssetFilledAmount >= order.takerAssetAmount) {
orderInfo.orderStatus = LibOrder.OrderStatus.FULLY_FILLED;
return orderInfo;
}
// Validate order expiration
// solhint-disable-next-line not-rely-on-time
if (block.timestamp >= order.expirationTimeSeconds) {
orderInfo.orderStatus = LibOrder.OrderStatus.EXPIRED;
return orderInfo;
}
// Check if order has been cancelled
if (cancelled[orderInfo.orderHash]) {
orderInfo.orderStatus = LibOrder.OrderStatus.CANCELLED;
return orderInfo;
}
if (orderEpoch[order.makerAddress][order.senderAddress] > order.salt) {
orderInfo.orderStatus = LibOrder.OrderStatus.CANCELLED;
return orderInfo;
}
// All other statuses are ruled out: order is Fillable
orderInfo.orderStatus = LibOrder.OrderStatus.FILLABLE;
return orderInfo;
}
/// @dev Fills the input order.
/// @param order Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signature Proof that order has been created by maker.
/// @return fillResults Amounts filled and fees paid by maker and taker.
function _fillOrder(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
internal
returns (LibFillResults.FillResults memory fillResults)
{
// Fetch order info
LibOrder.OrderInfo memory orderInfo = getOrderInfo(order);
// Fetch taker address
address takerAddress = _getCurrentContextAddress();
// Assert that the order is fillable by taker
_assertFillableOrder(
order,
orderInfo,
takerAddress,
signature
);
// Get amount of takerAsset to fill
uint256 remainingTakerAssetAmount = order.takerAssetAmount.safeSub(orderInfo.orderTakerAssetFilledAmount);
uint256 takerAssetFilledAmount = LibSafeMath.min256(takerAssetFillAmount, remainingTakerAssetAmount);
// Compute proportional fill amounts
fillResults = LibFillResults.calculateFillResults(
order,
takerAssetFilledAmount,
protocolFeeMultiplier,
tx.gasprice
);
bytes32 orderHash = orderInfo.orderHash;
// Update exchange internal state
_updateFilledState(
order,
takerAddress,
orderHash,
orderInfo.orderTakerAssetFilledAmount,
fillResults
);
// Settle order
_settleOrder(
orderHash,
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 _cancelOrder(LibOrder.Order memory order)
internal
{
// Fetch current order status
LibOrder.OrderInfo memory orderInfo = getOrderInfo(order);
// Validate context
_assertValidCancel(order, orderInfo);
// Noop if order is already unfillable
if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE) {
return;
}
// Perform cancel
_updateCancelledState(order, orderInfo.orderHash);
}
/// @dev Updates state with results of a fill order.
/// @param order that was filled.
/// @param takerAddress Address of taker who filled the order.
/// @param orderTakerAssetFilledAmount Amount of order already filled.
function _updateFilledState(
LibOrder.Order memory order,
address takerAddress,
bytes32 orderHash,
uint256 orderTakerAssetFilledAmount,
LibFillResults.FillResults memory fillResults
)
internal
{
// Update state
filled[orderHash] = orderTakerAssetFilledAmount.safeAdd(fillResults.takerAssetFilledAmount);
emit Fill(
order.makerAddress,
order.feeRecipientAddress,
order.makerAssetData,
order.takerAssetData,
order.makerFeeAssetData,
order.takerFeeAssetData,
orderHash,
takerAddress,
msg.sender,
fillResults.makerAssetFilledAmount,
fillResults.takerAssetFilledAmount,
fillResults.makerFeePaid,
fillResults.takerFeePaid,
fillResults.protocolFeePaid
);
}
/// @dev Updates state with results of cancelling an order.
/// State is only updated if the order is currently fillable.
/// Otherwise, updating state would have no effect.
/// @param order that was cancelled.
/// @param orderHash Hash of order that was cancelled.
function _updateCancelledState(
LibOrder.Order memory order,
bytes32 orderHash
)
internal
{
// Perform cancel
cancelled[orderHash] = true;
// Log cancel
emit Cancel(
order.makerAddress,
order.feeRecipientAddress,
order.makerAssetData,
order.takerAssetData,
msg.sender,
orderHash
);
}
/// @dev Validates context for fillOrder. Succeeds or throws.
/// @param order to be filled.
/// @param orderInfo OrderStatus, orderHash, and amount already filled of order.
/// @param takerAddress Address of order taker.
/// @param signature Proof that the orders was created by its maker.
function _assertFillableOrder(
LibOrder.Order memory order,
LibOrder.OrderInfo memory orderInfo,
address takerAddress,
bytes memory signature
)
internal
view
{
// An order can only be filled if its status is FILLABLE.
if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE) {
LibRichErrors.rrevert(LibExchangeRichErrors.OrderStatusError(
orderInfo.orderHash,
LibOrder.OrderStatus(orderInfo.orderStatus)
));
}
// Validate sender is allowed to fill this order
if (order.senderAddress != address(0)) {
if (order.senderAddress != msg.sender) {
LibRichErrors.rrevert(LibExchangeRichErrors.ExchangeInvalidContextError(
LibExchangeRichErrors.ExchangeContextErrorCodes.INVALID_SENDER,
orderInfo.orderHash,
msg.sender
));
}
}
// Validate taker is allowed to fill this order
if (order.takerAddress != address(0)) {
if (order.takerAddress != takerAddress) {
LibRichErrors.rrevert(LibExchangeRichErrors.ExchangeInvalidContextError(
LibExchangeRichErrors.ExchangeContextErrorCodes.INVALID_TAKER,
orderInfo.orderHash,
takerAddress
));
}
}
// Validate signature
if (!_isValidOrderWithHashSignature(
order,
orderInfo.orderHash,
signature
)
) {
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
LibExchangeRichErrors.SignatureErrorCodes.BAD_ORDER_SIGNATURE,
orderInfo.orderHash,
order.makerAddress,
signature
));
}
}
/// @dev Validates context for cancelOrder. Succeeds or throws.
/// @param order to be cancelled.
/// @param orderInfo OrderStatus, orderHash, and amount already filled of order.
function _assertValidCancel(
LibOrder.Order memory order,
LibOrder.OrderInfo memory orderInfo
)
internal
view
{
// Validate sender is allowed to cancel this order
if (order.senderAddress != address(0)) {
if (order.senderAddress != msg.sender) {
LibRichErrors.rrevert(LibExchangeRichErrors.ExchangeInvalidContextError(
LibExchangeRichErrors.ExchangeContextErrorCodes.INVALID_SENDER,
orderInfo.orderHash,
msg.sender
));
}
}
// Validate transaction signed by maker
address makerAddress = _getCurrentContextAddress();
if (order.makerAddress != makerAddress) {
LibRichErrors.rrevert(LibExchangeRichErrors.ExchangeInvalidContextError(
LibExchangeRichErrors.ExchangeContextErrorCodes.INVALID_MAKER,
orderInfo.orderHash,
makerAddress
));
}
}
/// @dev Settles an order by transferring assets between counterparties.
/// @param orderHash The order hash.
/// @param order Order struct containing order specifications.
/// @param takerAddress Address selling takerAsset and buying makerAsset.
/// @param fillResults Amounts to be filled and fees paid by maker and taker.
function _settleOrder(
bytes32 orderHash,
LibOrder.Order memory order,
address takerAddress,
LibFillResults.FillResults memory fillResults
)
internal
{
// Transfer taker -> maker
_dispatchTransferFrom(
orderHash,
order.takerAssetData,
takerAddress,
order.makerAddress,
fillResults.takerAssetFilledAmount
);
// Transfer maker -> taker
_dispatchTransferFrom(
orderHash,
order.makerAssetData,
order.makerAddress,
takerAddress,
fillResults.makerAssetFilledAmount
);
// Transfer taker fee -> feeRecipient
_dispatchTransferFrom(
orderHash,
order.takerFeeAssetData,
takerAddress,
order.feeRecipientAddress,
fillResults.takerFeePaid
);
// Transfer maker fee -> feeRecipient
_dispatchTransferFrom(
orderHash,
order.makerFeeAssetData,
order.makerAddress,
order.feeRecipientAddress,
fillResults.makerFeePaid
);
// Pay protocol fee
bool didPayProtocolFee = _paySingleProtocolFee(
orderHash,
fillResults.protocolFeePaid,
order.makerAddress,
takerAddress
);
// Protocol fees are not paid if the protocolFeeCollector contract is not set
if (!didPayProtocolFee) {
fillResults.protocolFeePaid = 0;
}
}
/// @dev Gets the order's hash and amount of takerAsset that has already been filled.
/// @param order Order struct containing order specifications.
/// @return The typed data hash and amount filled of the order.
function _getOrderHashAndFilledAmount(LibOrder.Order memory order)
internal
view
returns (bytes32 orderHash, uint256 orderTakerAssetFilledAmount)
{
orderHash = order.getTypedDataHash(EIP712_EXCHANGE_DOMAIN_HASH);
orderTakerAssetFilledAmount = filled[orderHash];
return (orderHash, orderTakerAssetFilledAmount);
}
}
/*
Copyright 2019 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.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeRichErrors.sol";
import "./interfaces/IMatchOrders.sol";
import "./MixinExchangeCore.sol";
contract MixinMatchOrders is
MixinExchangeCore,
IMatchOrders
{
using LibBytes for bytes;
using LibSafeMath for uint256;
using LibOrder for LibOrder.Order;
/// @dev Match complementary orders that have a profitable spread.
/// Each order is filled at their respective price point, and
/// the matcher receives a profit denominated in the left maker asset.
/// @param leftOrders Set of orders with the same maker / taker asset.
/// @param rightOrders Set of orders to match against `leftOrders`
/// @param leftSignatures Proof that left orders were created by the left makers.
/// @param rightSignatures Proof that right orders were created by the right makers.
/// @return batchMatchedFillResults Amounts filled and profit generated.
function batchMatchOrders(
LibOrder.Order[] memory leftOrders,
LibOrder.Order[] memory rightOrders,
bytes[] memory leftSignatures,
bytes[] memory rightSignatures
)
public
payable
refundFinalBalanceNoReentry
returns (LibFillResults.BatchMatchedFillResults memory batchMatchedFillResults)
{
return _batchMatchOrders(
leftOrders,
rightOrders,
leftSignatures,
rightSignatures,
false
);
}
/// @dev Match complementary orders that have a profitable spread.
/// Each order is maximally filled at their respective price point, and
/// the matcher receives a profit denominated in either the left maker asset,
/// right maker asset, or a combination of both.
/// @param leftOrders Set of orders with the same maker / taker asset.
/// @param rightOrders Set of orders to match against `leftOrders`
/// @param leftSignatures Proof that left orders were created by the left makers.
/// @param rightSignatures Proof that right orders were created by the right makers.
/// @return batchMatchedFillResults Amounts filled and profit generated.
function batchMatchOrdersWithMaximalFill(
LibOrder.Order[] memory leftOrders,
LibOrder.Order[] memory rightOrders,
bytes[] memory leftSignatures,
bytes[] memory rightSignatures
)
public
payable
refundFinalBalanceNoReentry
returns (LibFillResults.BatchMatchedFillResults memory batchMatchedFillResults)
{
return _batchMatchOrders(
leftOrders,
rightOrders,
leftSignatures,
rightSignatures,
true
);
}
/// @dev Match two complementary orders that have a profitable spread.
/// Each order is filled at their respective price point. However, the calculations are
/// carried out as though the orders are both being filled at the right order's price point.
/// The profit made by the left order goes to the taker (who matched the two orders).
/// @param leftOrder First order to match.
/// @param rightOrder Second order to match.
/// @param leftSignature Proof that order was created by the left maker.
/// @param rightSignature Proof that order was created by the right maker.
/// @return matchedFillResults Amounts filled and fees paid by maker and taker of matched orders.
function matchOrders(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,
bytes memory leftSignature,
bytes memory rightSignature
)
public
payable
refundFinalBalanceNoReentry
returns (LibFillResults.MatchedFillResults memory matchedFillResults)
{
return _matchOrders(
leftOrder,
rightOrder,
leftSignature,
rightSignature,
false
);
}
/// @dev Match two complementary orders that have a profitable spread.
/// Each order is maximally filled at their respective price point, and
/// the matcher receives a profit denominated in either the left maker asset,
/// right maker asset, or a combination of both.
/// @param leftOrder First order to match.
/// @param rightOrder Second order to match.
/// @param leftSignature Proof that order was created by the left maker.
/// @param rightSignature Proof that order was created by the right maker.
/// @return matchedFillResults Amounts filled by maker and taker of matched orders.
function matchOrdersWithMaximalFill(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,
bytes memory leftSignature,
bytes memory rightSignature
)
public
payable
refundFinalBalanceNoReentry
returns (LibFillResults.MatchedFillResults memory matchedFillResults)
{
return _matchOrders(
leftOrder,
rightOrder,
leftSignature,
rightSignature,
true
);
}
/// @dev Validates context for matchOrders. Succeeds or throws.
/// @param leftOrder First order to match.
/// @param rightOrder Second order to match.
/// @param leftOrderHash First matched order hash.
/// @param rightOrderHash Second matched order hash.
function _assertValidMatch(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,
bytes32 leftOrderHash,
bytes32 rightOrderHash
)
internal
pure
{
// Make sure there is a profitable spread.
// There is a profitable spread iff the cost per unit bought (OrderA.MakerAmount/OrderA.TakerAmount) for each order is greater
// than the profit per unit sold of the matched order (OrderB.TakerAmount/OrderB.MakerAmount).
// This is satisfied by the equations below:
// <leftOrder.makerAssetAmount> / <leftOrder.takerAssetAmount> >= <rightOrder.takerAssetAmount> / <rightOrder.makerAssetAmount>
// AND
// <rightOrder.makerAssetAmount> / <rightOrder.takerAssetAmount> >= <leftOrder.takerAssetAmount> / <leftOrder.makerAssetAmount>
// These equations can be combined to get the following:
if (leftOrder.makerAssetAmount.safeMul(rightOrder.makerAssetAmount) <
leftOrder.takerAssetAmount.safeMul(rightOrder.takerAssetAmount)) {
LibRichErrors.rrevert(LibExchangeRichErrors.NegativeSpreadError(
leftOrderHash,
rightOrderHash
));
}
}
/// @dev Match complementary orders that have a profitable spread.
/// Each order is filled at their respective price point, and
/// the matcher receives a profit denominated in the left maker asset.
/// This is the reentrant version of `batchMatchOrders` and `batchMatchOrdersWithMaximalFill`.
/// @param leftOrders Set of orders with the same maker / taker asset.
/// @param rightOrders Set of orders to match against `leftOrders`
/// @param leftSignatures Proof that left orders were created by the left makers.
/// @param rightSignatures Proof that right orders were created by the right makers.
/// @param shouldMaximallyFillOrders A value that indicates whether or not the order matching
/// should be done with maximal fill.
/// @return batchMatchedFillResults Amounts filled and profit generated.
function _batchMatchOrders(
LibOrder.Order[] memory leftOrders,
LibOrder.Order[] memory rightOrders,
bytes[] memory leftSignatures,
bytes[] memory rightSignatures,
bool shouldMaximallyFillOrders
)
internal
returns (LibFillResults.BatchMatchedFillResults memory batchMatchedFillResults)
{
// Ensure that the left and right orders have nonzero lengths.
if (leftOrders.length == 0) {
LibRichErrors.rrevert(LibExchangeRichErrors.BatchMatchOrdersError(
LibExchangeRichErrors.BatchMatchOrdersErrorCodes.ZERO_LEFT_ORDERS
));
}
if (rightOrders.length == 0) {
LibRichErrors.rrevert(LibExchangeRichErrors.BatchMatchOrdersError(
LibExchangeRichErrors.BatchMatchOrdersErrorCodes.ZERO_RIGHT_ORDERS
));
}
// Ensure that the left and right arrays are compatible.
if (leftOrders.length != leftSignatures.length) {
LibRichErrors.rrevert(LibExchangeRichErrors.BatchMatchOrdersError(
LibExchangeRichErrors.BatchMatchOrdersErrorCodes.INVALID_LENGTH_LEFT_SIGNATURES
));
}
if (rightOrders.length != rightSignatures.length) {
LibRichErrors.rrevert(LibExchangeRichErrors.BatchMatchOrdersError(
LibExchangeRichErrors.BatchMatchOrdersErrorCodes.INVALID_LENGTH_RIGHT_SIGNATURES
));
}
batchMatchedFillResults.left = new LibFillResults.FillResults[](leftOrders.length);
batchMatchedFillResults.right = new LibFillResults.FillResults[](rightOrders.length);
// Set up initial indices.
uint256 leftIdx = 0;
uint256 rightIdx = 0;
// Keep local variables for orders, order filled amounts, and signatures for efficiency.
LibOrder.Order memory leftOrder = leftOrders[0];
LibOrder.Order memory rightOrder = rightOrders[0];
(, uint256 leftOrderTakerAssetFilledAmount) = _getOrderHashAndFilledAmount(leftOrder);
(, uint256 rightOrderTakerAssetFilledAmount) = _getOrderHashAndFilledAmount(rightOrder);
LibFillResults.FillResults memory leftFillResults;
LibFillResults.FillResults memory rightFillResults;
// Loop infinitely (until broken inside of the loop), but keep a counter of how
// many orders have been matched.
for (;;) {
// Match the two orders that are pointed to by the left and right indices
LibFillResults.MatchedFillResults memory matchResults = _matchOrders(
leftOrder,
rightOrder,
leftSignatures[leftIdx],
rightSignatures[rightIdx],
shouldMaximallyFillOrders
);
// Update the order filled amounts with the updated takerAssetFilledAmount
leftOrderTakerAssetFilledAmount = leftOrderTakerAssetFilledAmount.safeAdd(matchResults.left.takerAssetFilledAmount);
rightOrderTakerAssetFilledAmount = rightOrderTakerAssetFilledAmount.safeAdd(matchResults.right.takerAssetFilledAmount);
// Aggregate the new fill results with the previous fill results for the current orders.
leftFillResults = LibFillResults.addFillResults(
leftFillResults,
matchResults.left
);
rightFillResults = LibFillResults.addFillResults(
rightFillResults,
matchResults.right
);
// Update the profit in the left and right maker assets using the profits from
// the match.
batchMatchedFillResults.profitInLeftMakerAsset = batchMatchedFillResults.profitInLeftMakerAsset.safeAdd(
matchResults.profitInLeftMakerAsset
);
batchMatchedFillResults.profitInRightMakerAsset = batchMatchedFillResults.profitInRightMakerAsset.safeAdd(
matchResults.profitInRightMakerAsset
);
// If the leftOrder is filled, update the leftIdx, leftOrder, and leftSignature,
// or break out of the loop if there are no more leftOrders to match.
if (leftOrderTakerAssetFilledAmount >= leftOrder.takerAssetAmount) {
// Update the batched fill results once the leftIdx is updated.
batchMatchedFillResults.left[leftIdx++] = leftFillResults;
// Clear the intermediate fill results value.
leftFillResults = LibFillResults.FillResults(0, 0, 0, 0, 0);
// If all of the left orders have been filled, break out of the loop.
// Otherwise, update the current right order.
if (leftIdx == leftOrders.length) {
// Update the right batched fill results
batchMatchedFillResults.right[rightIdx] = rightFillResults;
break;
} else {
leftOrder = leftOrders[leftIdx];
(, leftOrderTakerAssetFilledAmount) = _getOrderHashAndFilledAmount(leftOrder);
}
}
// If the rightOrder is filled, update the rightIdx, rightOrder, and rightSignature,
// or break out of the loop if there are no more rightOrders to match.
if (rightOrderTakerAssetFilledAmount >= rightOrder.takerAssetAmount) {
// Update the batched fill results once the rightIdx is updated.
batchMatchedFillResults.right[rightIdx++] = rightFillResults;
// Clear the intermediate fill results value.
rightFillResults = LibFillResults.FillResults(0, 0, 0, 0, 0);
// If all of the right orders have been filled, break out of the loop.
// Otherwise, update the current right order.
if (rightIdx == rightOrders.length) {
// Update the left batched fill results
batchMatchedFillResults.left[leftIdx] = leftFillResults;
break;
} else {
rightOrder = rightOrders[rightIdx];
(, rightOrderTakerAssetFilledAmount) = _getOrderHashAndFilledAmount(rightOrder);
}
}
}
// Return the fill results from the batch match
return batchMatchedFillResults;
}
/// @dev Match two complementary orders that have a profitable spread.
/// Each order is filled at their respective price point. However, the calculations are
/// carried out as though the orders are both being filled at the right order's price point.
/// The profit made by the left order goes to the taker (who matched the two orders). This
/// function is needed to allow for reentrant order matching (used by `batchMatchOrders` and
/// `batchMatchOrdersWithMaximalFill`).
/// @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.
/// @param shouldMaximallyFillOrders Indicates whether or not the maximal fill matching strategy should be used
/// @return matchedFillResults Amounts filled and fees paid by maker and taker of matched orders.
function _matchOrders(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,
bytes memory leftSignature,
bytes memory rightSignature,
bool shouldMaximallyFillOrders
)
internal
returns (LibFillResults.MatchedFillResults memory matchedFillResults)
{
// We assume that rightOrder.takerAssetData == leftOrder.makerAssetData and rightOrder.makerAssetData == leftOrder.takerAssetData
// by pointing these values to the same location in memory. This is cheaper than checking equality.
// If this assumption isn't true, the match will fail at signature validation.
rightOrder.makerAssetData = leftOrder.takerAssetData;
rightOrder.takerAssetData = leftOrder.makerAssetData;
// Get left & right order info
LibOrder.OrderInfo memory leftOrderInfo = getOrderInfo(leftOrder);
LibOrder.OrderInfo memory rightOrderInfo = getOrderInfo(rightOrder);
// Fetch taker address
address takerAddress = _getCurrentContextAddress();
// Either our context is valid or we revert
_assertFillableOrder(
leftOrder,
leftOrderInfo,
takerAddress,
leftSignature
);
_assertFillableOrder(
rightOrder,
rightOrderInfo,
takerAddress,
rightSignature
);
_assertValidMatch(
leftOrder,
rightOrder,
leftOrderInfo.orderHash,
rightOrderInfo.orderHash
);
// Compute proportional fill amounts
matchedFillResults = LibFillResults.calculateMatchedFillResults(
leftOrder,
rightOrder,
leftOrderInfo.orderTakerAssetFilledAmount,
rightOrderInfo.orderTakerAssetFilledAmount,
protocolFeeMultiplier,
tx.gasprice,
shouldMaximallyFillOrders
);
// Update exchange state
_updateFilledState(
leftOrder,
takerAddress,
leftOrderInfo.orderHash,
leftOrderInfo.orderTakerAssetFilledAmount,
matchedFillResults.left
);
_updateFilledState(
rightOrder,
takerAddress,
rightOrderInfo.orderHash,
rightOrderInfo.orderTakerAssetFilledAmount,
matchedFillResults.right
);
// Settle matched orders. Succeeds or throws.
_settleMatchedOrders(
leftOrderInfo.orderHash,
rightOrderInfo.orderHash,
leftOrder,
rightOrder,
takerAddress,
matchedFillResults
);
return matchedFillResults;
}
/// @dev Settles matched order by transferring appropriate funds between order makers, taker, and fee recipient.
/// @param leftOrderHash First matched order hash.
/// @param rightOrderHash Second matched order hash.
/// @param leftOrder First matched order.
/// @param rightOrder Second matched order.
/// @param takerAddress Address that matched the orders. The taker receives the spread between orders as profit.
/// @param matchedFillResults Struct holding amounts to transfer between makers, taker, and fee recipients.
function _settleMatchedOrders(
bytes32 leftOrderHash,
bytes32 rightOrderHash,
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,
address takerAddress,
LibFillResults.MatchedFillResults memory matchedFillResults
)
internal
{
address leftMakerAddress = leftOrder.makerAddress;
address rightMakerAddress = rightOrder.makerAddress;
address leftFeeRecipientAddress = leftOrder.feeRecipientAddress;
address rightFeeRecipientAddress = rightOrder.feeRecipientAddress;
// Right maker asset -> left maker
_dispatchTransferFrom(
rightOrderHash,
rightOrder.makerAssetData,
rightMakerAddress,
leftMakerAddress,
matchedFillResults.left.takerAssetFilledAmount
);
// Left maker asset -> right maker
_dispatchTransferFrom(
leftOrderHash,
leftOrder.makerAssetData,
leftMakerAddress,
rightMakerAddress,
matchedFillResults.right.takerAssetFilledAmount
);
// Right maker fee -> right fee recipient
_dispatchTransferFrom(
rightOrderHash,
rightOrder.makerFeeAssetData,
rightMakerAddress,
rightFeeRecipientAddress,
matchedFillResults.right.makerFeePaid
);
// Left maker fee -> left fee recipient
_dispatchTransferFrom(
leftOrderHash,
leftOrder.makerFeeAssetData,
leftMakerAddress,
leftFeeRecipientAddress,
matchedFillResults.left.makerFeePaid
);
// Settle taker profits.
_dispatchTransferFrom(
leftOrderHash,
leftOrder.makerAssetData,
leftMakerAddress,
takerAddress,
matchedFillResults.profitInLeftMakerAsset
);
_dispatchTransferFrom(
rightOrderHash,
rightOrder.makerAssetData,
rightMakerAddress,
takerAddress,
matchedFillResults.profitInRightMakerAsset
);
// Pay protocol fees for each maker
bool didPayProtocolFees = _payTwoProtocolFees(
leftOrderHash,
rightOrderHash,
matchedFillResults.left.protocolFeePaid,
leftMakerAddress,
rightMakerAddress,
takerAddress
);
// Protocol fees are not paid if the protocolFeeCollector contract is not set
if (!didPayProtocolFees) {
matchedFillResults.left.protocolFeePaid = 0;
matchedFillResults.right.protocolFeePaid = 0;
}
// Settle taker fees.
if (
leftFeeRecipientAddress == rightFeeRecipientAddress &&
leftOrder.takerFeeAssetData.equals(rightOrder.takerFeeAssetData)
) {
// Fee recipients and taker fee assets are identical, so we can
// transfer them in one go.
_dispatchTransferFrom(
leftOrderHash,
leftOrder.takerFeeAssetData,
takerAddress,
leftFeeRecipientAddress,
matchedFillResults.left.takerFeePaid.safeAdd(matchedFillResults.right.takerFeePaid)
);
} else {
// Right taker fee -> right fee recipient
_dispatchTransferFrom(
rightOrderHash,
rightOrder.takerFeeAssetData,
takerAddress,
rightFeeRecipientAddress,
matchedFillResults.right.takerFeePaid
);
// Left taker fee -> left fee recipient
_dispatchTransferFrom(
leftOrderHash,
leftOrder.takerFeeAssetData,
takerAddress,
leftFeeRecipientAddress,
matchedFillResults.left.takerFeePaid
);
}
}
}
/*
Copyright 2019 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.5.9;
import "@0x/contracts-utils/contracts/src/Ownable.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeRichErrors.sol";
import "@0x/contracts-staking/contracts/src/interfaces/IStaking.sol";
import "./interfaces/IProtocolFees.sol";
contract MixinProtocolFees is
IProtocolFees,
Ownable
{
/// @dev The protocol fee multiplier -- the owner can update this field.
/// @return 0 Gas multplier.
uint256 public protocolFeeMultiplier;
/// @dev The address of the registered protocolFeeCollector contract -- the owner can update this field.
/// @return 0 Contract to forward protocol fees to.
address public protocolFeeCollector;
/// @dev Allows the owner to update the protocol fee multiplier.
/// @param updatedProtocolFeeMultiplier The updated protocol fee multiplier.
function setProtocolFeeMultiplier(uint256 updatedProtocolFeeMultiplier)
external
onlyOwner
{
emit ProtocolFeeMultiplier(protocolFeeMultiplier, updatedProtocolFeeMultiplier);
protocolFeeMultiplier = updatedProtocolFeeMultiplier;
}
/// @dev Allows the owner to update the protocolFeeCollector address.
/// @param updatedProtocolFeeCollector The updated protocolFeeCollector contract address.
function setProtocolFeeCollectorAddress(address updatedProtocolFeeCollector)
external
onlyOwner
{
_setProtocolFeeCollectorAddress(updatedProtocolFeeCollector);
}
/// @dev Sets the protocolFeeCollector contract address to 0.
/// Only callable by owner.
function detachProtocolFeeCollector()
external
onlyOwner
{
_setProtocolFeeCollectorAddress(address(0));
}
/// @dev Sets the protocolFeeCollector address and emits an event.
/// @param updatedProtocolFeeCollector The updated protocolFeeCollector contract address.
function _setProtocolFeeCollectorAddress(address updatedProtocolFeeCollector)
internal
{
emit ProtocolFeeCollectorAddress(protocolFeeCollector, updatedProtocolFeeCollector);
protocolFeeCollector = updatedProtocolFeeCollector;
}
/// @dev Pays a protocol fee for a single fill.
/// @param orderHash Hash of the order being filled.
/// @param protocolFee Value of the fee being paid (equal to protocolFeeMultiplier * tx.gasPrice).
/// @param makerAddress Address of maker of order being filled.
/// @param takerAddress Address filling order.
function _paySingleProtocolFee(
bytes32 orderHash,
uint256 protocolFee,
address makerAddress,
address takerAddress
)
internal
returns (bool)
{
address feeCollector = protocolFeeCollector;
if (feeCollector != address(0)) {
_payProtocolFeeToFeeCollector(
orderHash,
feeCollector,
address(this).balance,
protocolFee,
makerAddress,
takerAddress
);
return true;
} else {
return false;
}
}
/// @dev Pays a protocol fee for two orders (used when settling functions in MixinMatchOrders)
/// @param orderHash1 Hash of the first order being filled.
/// @param orderHash2 Hash of the second order being filled.
/// @param protocolFee Value of the fee being paid (equal to protocolFeeMultiplier * tx.gasPrice).
/// @param makerAddress1 Address of maker of first order being filled.
/// @param makerAddress2 Address of maker of second order being filled.
/// @param takerAddress Address filling orders.
function _payTwoProtocolFees(
bytes32 orderHash1,
bytes32 orderHash2,
uint256 protocolFee,
address makerAddress1,
address makerAddress2,
address takerAddress
)
internal
returns (bool)
{
address feeCollector = protocolFeeCollector;
if (feeCollector != address(0)) {
// Since the `BALANCE` opcode costs 400 gas, we choose to calculate this value by hand rather than calling it twice.
uint256 exchangeBalance = address(this).balance;
// Pay protocol fee and attribute to first maker
uint256 valuePaid = _payProtocolFeeToFeeCollector(
orderHash1,
feeCollector,
exchangeBalance,
protocolFee,
makerAddress1,
takerAddress
);
// Pay protocol fee and attribute to second maker
_payProtocolFeeToFeeCollector(
orderHash2,
feeCollector,
exchangeBalance - valuePaid,
protocolFee,
makerAddress2,
takerAddress
);
return true;
} else {
return false;
}
}
/// @dev Pays a single protocol fee.
/// @param orderHash Hash of the order being filled.
/// @param feeCollector Address of protocolFeeCollector contract.
/// @param exchangeBalance Assumed ETH balance of Exchange contract (in wei).
/// @param protocolFee Value of the fee being paid (equal to protocolFeeMultiplier * tx.gasPrice).
/// @param makerAddress Address of maker of order being filled.
/// @param takerAddress Address filling order.
function _payProtocolFeeToFeeCollector(
bytes32 orderHash,
address feeCollector,
uint256 exchangeBalance,
uint256 protocolFee,
address makerAddress,
address takerAddress
)
internal
returns (uint256 valuePaid)
{
// Do not send a value with the call if the exchange has an insufficient balance
// The protocolFeeCollector contract will fallback to charging WETH
if (exchangeBalance >= protocolFee) {
valuePaid = protocolFee;
}
bytes memory payProtocolFeeData = abi.encodeWithSelector(
IStaking(address(0)).payProtocolFee.selector,
makerAddress,
takerAddress,
protocolFee
);
// solhint-disable-next-line avoid-call-value
(bool didSucceed, bytes memory returnData) = feeCollector.call.value(valuePaid)(payProtocolFeeData);
if (!didSucceed) {
LibRichErrors.rrevert(LibExchangeRichErrors.PayProtocolFeeError(
orderHash,
protocolFee,
makerAddress,
takerAddress,
returnData
));
}
return valuePaid;
}
}
/*
Copyright 2019 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.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibEIP1271.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeRichErrors.sol";
import "./interfaces/IWallet.sol";
import "./interfaces/IEIP1271Wallet.sol";
import "./interfaces/ISignatureValidator.sol";
import "./interfaces/IEIP1271Data.sol";
import "./MixinTransactions.sol";
contract MixinSignatureValidator is
LibEIP712ExchangeDomain,
LibEIP1271,
ISignatureValidator,
MixinTransactions
{
using LibBytes for bytes;
using LibOrder for LibOrder.Order;
using LibZeroExTransaction for LibZeroExTransaction.ZeroExTransaction;
// Magic bytes to be returned by `Wallet` signature type validators.
// bytes4(keccak256("isValidWalletSignature(bytes32,address,bytes)"))
bytes4 private constant LEGACY_WALLET_MAGIC_VALUE = 0xb0671381;
/// @dev Mapping of hash => signer => signed
/// @param 0 Order hash.
/// @param 1 Signer address.
/// @return 0 Whether the hash is presigned.
mapping (bytes32 => mapping (address => bool)) public preSigned;
/// @dev Mapping of signer => validator => approved
/// @param 0 Signer address.
/// @param 1 Signature validator address.
/// @return 0 Whether the validator is allowed to validate on behalf of the signer.
mapping (address => mapping (address => bool)) public allowedValidators;
/// @dev Approves a hash on-chain.
/// After presigning a hash, the preSign signature type will become valid for that hash and signer.
/// @param hash Any 32-byte hash.
function preSign(bytes32 hash)
external
payable
refundFinalBalanceNoReentry
{
address signerAddress = _getCurrentContextAddress();
preSigned[hash][signerAddress] = true;
}
/// @dev Approves/unnapproves a Validator contract to verify signatures on signer's behalf
/// using the `Validator` signature type.
/// @param validatorAddress Address of Validator contract.
/// @param approval Approval or disapproval of Validator contract.
function setSignatureValidatorApproval(
address validatorAddress,
bool approval
)
external
payable
refundFinalBalanceNoReentry
{
address signerAddress = _getCurrentContextAddress();
allowedValidators[signerAddress][validatorAddress] = approval;
emit SignatureValidatorApproval(
signerAddress,
validatorAddress,
approval
);
}
/// @dev Verifies that a hash has been signed by the given signer.
/// @param hash Any 32-byte hash.
/// @param signerAddress Address that should have signed the given hash.
/// @param signature Proof that the hash has been signed by signer.
/// @return isValid `true` if the signature is valid for the given hash and signer.
function isValidHashSignature(
bytes32 hash,
address signerAddress,
bytes memory signature
)
public
view
returns (bool isValid)
{
SignatureType signatureType = _readValidSignatureType(
hash,
signerAddress,
signature
);
// Only hash-compatible signature types can be handled by this
// function.
if (
signatureType == SignatureType.Validator ||
signatureType == SignatureType.EIP1271Wallet
) {
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
LibExchangeRichErrors.SignatureErrorCodes.INAPPROPRIATE_SIGNATURE_TYPE,
hash,
signerAddress,
signature
));
}
isValid = _validateHashSignatureTypes(
signatureType,
hash,
signerAddress,
signature
);
return isValid;
}
/// @dev Verifies that a signature for an order is valid.
/// @param order The order.
/// @param signature Proof that the order has been signed by signer.
/// @return isValid `true` if the signature is valid for the given order and signer.
function isValidOrderSignature(
LibOrder.Order memory order,
bytes memory signature
)
public
view
returns (bool isValid)
{
bytes32 orderHash = order.getTypedDataHash(EIP712_EXCHANGE_DOMAIN_HASH);
isValid = _isValidOrderWithHashSignature(
order,
orderHash,
signature
);
return isValid;
}
/// @dev Verifies that a signature for a transaction is valid.
/// @param transaction The transaction.
/// @param signature Proof that the order has been signed by signer.
/// @return isValid `true` if the signature is valid for the given transaction and signer.
function isValidTransactionSignature(
LibZeroExTransaction.ZeroExTransaction memory transaction,
bytes memory signature
)
public
view
returns (bool isValid)
{
bytes32 transactionHash = transaction.getTypedDataHash(EIP712_EXCHANGE_DOMAIN_HASH);
isValid = _isValidTransactionWithHashSignature(
transaction,
transactionHash,
signature
);
return isValid;
}
/// @dev Verifies that an order, with provided order hash, has been signed
/// by the given signer.
/// @param order The order.
/// @param orderHash The hash of the order.
/// @param signature Proof that the hash has been signed by signer.
/// @return isValid True if the signature is valid for the given order and signer.
function _isValidOrderWithHashSignature(
LibOrder.Order memory order,
bytes32 orderHash,
bytes memory signature
)
internal
view
returns (bool isValid)
{
address signerAddress = order.makerAddress;
SignatureType signatureType = _readValidSignatureType(
orderHash,
signerAddress,
signature
);
if (signatureType == SignatureType.Validator) {
// The entire order is verified by a validator contract.
isValid = _validateBytesWithValidator(
_encodeEIP1271OrderWithHash(order, orderHash),
orderHash,
signerAddress,
signature
);
} else if (signatureType == SignatureType.EIP1271Wallet) {
// The entire order is verified by a wallet contract.
isValid = _validateBytesWithWallet(
_encodeEIP1271OrderWithHash(order, orderHash),
signerAddress,
signature
);
} else {
// Otherwise, it's one of the hash-only signature types.
isValid = _validateHashSignatureTypes(
signatureType,
orderHash,
signerAddress,
signature
);
}
return isValid;
}
/// @dev Verifies that a transaction, with provided order hash, has been signed
/// by the given signer.
/// @param transaction The transaction.
/// @param transactionHash The hash of the transaction.
/// @param signature Proof that the hash has been signed by signer.
/// @return isValid True if the signature is valid for the given transaction and signer.
function _isValidTransactionWithHashSignature(
LibZeroExTransaction.ZeroExTransaction memory transaction,
bytes32 transactionHash,
bytes memory signature
)
internal
view
returns (bool isValid)
{
address signerAddress = transaction.signerAddress;
SignatureType signatureType = _readValidSignatureType(
transactionHash,
signerAddress,
signature
);
if (signatureType == SignatureType.Validator) {
// The entire transaction is verified by a validator contract.
isValid = _validateBytesWithValidator(
_encodeEIP1271TransactionWithHash(transaction, transactionHash),
transactionHash,
signerAddress,
signature
);
} else if (signatureType == SignatureType.EIP1271Wallet) {
// The entire transaction is verified by a wallet contract.
isValid = _validateBytesWithWallet(
_encodeEIP1271TransactionWithHash(transaction, transactionHash),
signerAddress,
signature
);
} else {
// Otherwise, it's one of the hash-only signature types.
isValid = _validateHashSignatureTypes(
signatureType,
transactionHash,
signerAddress,
signature
);
}
return isValid;
}
/// Validates a hash-only signature type
/// (anything but `Validator` and `EIP1271Wallet`).
function _validateHashSignatureTypes(
SignatureType signatureType,
bytes32 hash,
address signerAddress,
bytes memory signature
)
private
view
returns (bool isValid)
{
// Always invalid signature.
// Like Illegal, this is always implicitly available and therefore
// offered explicitly. It can be implicitly created by providing
// a correctly formatted but incorrect signature.
if (signatureType == SignatureType.Invalid) {
if (signature.length != 1) {
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
LibExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
hash,
signerAddress,
signature
));
}
isValid = false;
// Signature using EIP712
} else if (signatureType == SignatureType.EIP712) {
if (signature.length != 66) {
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
LibExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
hash,
signerAddress,
signature
));
}
uint8 v = uint8(signature[0]);
bytes32 r = signature.readBytes32(1);
bytes32 s = signature.readBytes32(33);
address recovered = ecrecover(
hash,
v,
r,
s
);
isValid = signerAddress == recovered;
// Signed using web3.eth_sign
} else if (signatureType == SignatureType.EthSign) {
if (signature.length != 66) {
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
LibExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
hash,
signerAddress,
signature
));
}
uint8 v = uint8(signature[0]);
bytes32 r = signature.readBytes32(1);
bytes32 s = signature.readBytes32(33);
address recovered = ecrecover(
keccak256(abi.encodePacked(
"\x19Ethereum Signed Message:\n32",
hash
)),
v,
r,
s
);
isValid = signerAddress == recovered;
// Signature verified by wallet contract.
} else if (signatureType == SignatureType.Wallet) {
isValid = _validateHashWithWallet(
hash,
signerAddress,
signature
);
// Otherwise, signatureType == SignatureType.PreSigned
} else {
assert(signatureType == SignatureType.PreSigned);
// Signer signed hash previously using the preSign function.
isValid = preSigned[hash][signerAddress];
}
return isValid;
}
/// @dev Reads the `SignatureType` from a signature with minimal validation.
function _readSignatureType(
bytes32 hash,
address signerAddress,
bytes memory signature
)
private
pure
returns (SignatureType)
{
if (signature.length == 0) {
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
LibExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
hash,
signerAddress,
signature
));
}
return SignatureType(uint8(signature[signature.length - 1]));
}
/// @dev Reads the `SignatureType` from the end of a signature and validates it.
function _readValidSignatureType(
bytes32 hash,
address signerAddress,
bytes memory signature
)
private
pure
returns (SignatureType signatureType)
{
// Read the signatureType from the signature
signatureType = _readSignatureType(
hash,
signerAddress,
signature
);
// Disallow address zero because ecrecover() returns zero on failure.
if (signerAddress == address(0)) {
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
LibExchangeRichErrors.SignatureErrorCodes.INVALID_SIGNER,
hash,
signerAddress,
signature
));
}
// Ensure signature is supported
if (uint8(signatureType) >= uint8(SignatureType.NSignatureTypes)) {
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
LibExchangeRichErrors.SignatureErrorCodes.UNSUPPORTED,
hash,
signerAddress,
signature
));
}
// Always illegal signature.
// This is always an implicit option since a signer can create a
// signature array with invalid type or length. We may as well make
// it an explicit option. This aids testing and analysis. It is
// also the initialization value for the enum type.
if (signatureType == SignatureType.Illegal) {
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
LibExchangeRichErrors.SignatureErrorCodes.ILLEGAL,
hash,
signerAddress,
signature
));
}
return signatureType;
}
/// @dev ABI encodes an order and hash with a selector to be passed into
/// an EIP1271 compliant `isValidSignature` function.
function _encodeEIP1271OrderWithHash(
LibOrder.Order memory order,
bytes32 orderHash
)
private
pure
returns (bytes memory encoded)
{
return abi.encodeWithSelector(
IEIP1271Data(address(0)).OrderWithHash.selector,
order,
orderHash
);
}
/// @dev ABI encodes a transaction and hash with a selector to be passed into
/// an EIP1271 compliant `isValidSignature` function.
function _encodeEIP1271TransactionWithHash(
LibZeroExTransaction.ZeroExTransaction memory transaction,
bytes32 transactionHash
)
private
pure
returns (bytes memory encoded)
{
return abi.encodeWithSelector(
IEIP1271Data(address(0)).ZeroExTransactionWithHash.selector,
transaction,
transactionHash
);
}
/// @dev Verifies a hash and signature using logic defined by Wallet contract.
/// @param hash Any 32 byte hash.
/// @param walletAddress Address that should have signed the given hash
/// and defines its own signature verification method.
/// @param signature Proof that the hash has been signed by signer.
/// @return True if the signature is validated by the Wallet.
function _validateHashWithWallet(
bytes32 hash,
address walletAddress,
bytes memory signature
)
private
view
returns (bool)
{
// Backup length of signature
uint256 signatureLength = signature.length;
// Temporarily remove signatureType byte from end of signature
signature.writeLength(signatureLength - 1);
// Encode the call data.
bytes memory callData = abi.encodeWithSelector(
IWallet(address(0)).isValidSignature.selector,
hash,
signature
);
// Restore the original signature length
signature.writeLength(signatureLength);
// Static call the verification function.
(bool didSucceed, bytes memory returnData) = walletAddress.staticcall(callData);
// Return the validity of the signature if the call was successful
if (didSucceed && returnData.length == 32) {
return returnData.readBytes4(0) == LEGACY_WALLET_MAGIC_VALUE;
}
// Revert if the call was unsuccessful
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureWalletError(
hash,
walletAddress,
signature,
returnData
));
}
/// @dev Verifies arbitrary data and a signature via an EIP1271 Wallet
/// contract, where the wallet address is also the signer address.
/// @param data Arbitrary signed data.
/// @param walletAddress Contract that will verify the data and signature.
/// @param signature Proof that the data has been signed by signer.
/// @return isValid True if the signature is validated by the Wallet.
function _validateBytesWithWallet(
bytes memory data,
address walletAddress,
bytes memory signature
)
private
view
returns (bool isValid)
{
isValid = _staticCallEIP1271WalletWithReducedSignatureLength(
walletAddress,
data,
signature,
1 // The last byte of the signature (signatureType) is removed before making the staticcall
);
return isValid;
}
/// @dev Verifies arbitrary data and a signature via an EIP1271 contract
/// whose address is encoded in the signature.
/// @param data Arbitrary signed data.
/// @param hash The hash associated with the data.
/// @param signerAddress Address that should have signed the given hash.
/// @param signature Proof that the data has been signed by signer.
/// @return isValid True if the signature is validated by the validator contract.
function _validateBytesWithValidator(
bytes memory data,
bytes32 hash,
address signerAddress,
bytes memory signature
)
private
view
returns (bool isValid)
{
uint256 signatureLength = signature.length;
if (signatureLength < 21) {
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
LibExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
hash,
signerAddress,
signature
));
}
// The validator address is appended to the signature before the signatureType.
// Read the validator address from the signature.
address validatorAddress = signature.readAddress(signatureLength - 21);
// Ensure signer has approved validator.
if (!allowedValidators[signerAddress][validatorAddress]) {
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureValidatorNotApprovedError(
signerAddress,
validatorAddress
));
}
isValid = _staticCallEIP1271WalletWithReducedSignatureLength(
validatorAddress,
data,
signature,
21 // The last 21 bytes of the signature (validatorAddress + signatureType) are removed before making the staticcall
);
return isValid;
}
/// @dev Performs a staticcall to an EIP1271 compiant `isValidSignature` function and validates the output.
/// @param verifyingContractAddress Address of EIP1271Wallet or Validator contract.
/// @param data Arbitrary signed data.
/// @param signature Proof that the hash has been signed by signer. Bytes will be temporarily be popped
/// off of the signature before calling `isValidSignature`.
/// @param ignoredSignatureBytesLen The amount of bytes that will be temporarily popped off the the signature.
/// @return The validity of the signature.
function _staticCallEIP1271WalletWithReducedSignatureLength(
address verifyingContractAddress,
bytes memory data,
bytes memory signature,
uint256 ignoredSignatureBytesLen
)
private
view
returns (bool)
{
// Backup length of the signature
uint256 signatureLength = signature.length;
// Temporarily remove bytes from signature end
signature.writeLength(signatureLength - ignoredSignatureBytesLen);
bytes memory callData = abi.encodeWithSelector(
IEIP1271Wallet(address(0)).isValidSignature.selector,
data,
signature
);
// Restore original signature length
signature.writeLength(signatureLength);
// Static call the verification function
(bool didSucceed, bytes memory returnData) = verifyingContractAddress.staticcall(callData);
// Return the validity of the signature if the call was successful
if (didSucceed && returnData.length == 32) {
return returnData.readBytes4(0) == EIP1271_MAGIC_VALUE;
}
// Revert if the call was unsuccessful
LibRichErrors.rrevert(LibExchangeRichErrors.EIP1271SignatureError(
verifyingContractAddress,
data,
signature,
returnData
));
}
}
/*
Copyright 2019 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.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeRichErrors.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-utils/contracts/src/Refundable.sol";
import "./interfaces/ITransactions.sol";
import "./interfaces/ISignatureValidator.sol";
contract MixinTransactions is
Refundable,
LibEIP712ExchangeDomain,
ISignatureValidator,
ITransactions
{
using LibZeroExTransaction for LibZeroExTransaction.ZeroExTransaction;
/// @dev Mapping of transaction hash => executed
/// This prevents transactions from being executed more than once.
/// @param 0 The transaction hash.
/// @return 0 Whether the transation was executed.
mapping (bytes32 => bool) public transactionsExecuted;
/// @dev Address of current transaction signer.
/// @return 0 The address associated with the the current transaction.
address public currentContextAddress;
/// @dev Executes an Exchange method call in the context of signer.
/// @param transaction 0x transaction structure.
/// @param signature Proof that transaction has been signed by signer.
/// @return ABI encoded return data of the underlying Exchange function call.
function executeTransaction(
LibZeroExTransaction.ZeroExTransaction memory transaction,
bytes memory signature
)
public
payable
disableRefundUntilEnd
returns (bytes memory)
{
return _executeTransaction(transaction, signature);
}
/// @dev Executes a batch of Exchange method calls in the context of signer(s).
/// @param transactions Array of 0x transaction structures.
/// @param signatures Array of proofs that transactions have been signed by signer(s).
/// @return returnData Array containing ABI encoded return data for each of the underlying Exchange function calls.
function batchExecuteTransactions(
LibZeroExTransaction.ZeroExTransaction[] memory transactions,
bytes[] memory signatures
)
public
payable
disableRefundUntilEnd
returns (bytes[] memory returnData)
{
uint256 length = transactions.length;
returnData = new bytes[](length);
for (uint256 i = 0; i != length; i++) {
returnData[i] = _executeTransaction(transactions[i], signatures[i]);
}
return returnData;
}
/// @dev Executes an Exchange method call in the context of signer.
/// @param transaction 0x transaction structure.
/// @param signature Proof that transaction has been signed by signer.
/// @return ABI encoded return data of the underlying Exchange function call.
function _executeTransaction(
LibZeroExTransaction.ZeroExTransaction memory transaction,
bytes memory signature
)
internal
returns (bytes memory)
{
bytes32 transactionHash = transaction.getTypedDataHash(EIP712_EXCHANGE_DOMAIN_HASH);
_assertExecutableTransaction(
transaction,
signature,
transactionHash
);
// Set the current transaction signer
address signerAddress = transaction.signerAddress;
_setCurrentContextAddressIfRequired(signerAddress, signerAddress);
// Execute transaction
transactionsExecuted[transactionHash] = true;
(bool didSucceed, bytes memory returnData) = address(this).delegatecall(transaction.data);
if (!didSucceed) {
LibRichErrors.rrevert(LibExchangeRichErrors.TransactionExecutionError(
transactionHash,
returnData
));
}
// Reset current transaction signer if it was previously updated
_setCurrentContextAddressIfRequired(signerAddress, address(0));
emit TransactionExecution(transactionHash);
return returnData;
}
/// @dev Validates context for executeTransaction. Succeeds or throws.
/// @param transaction 0x transaction structure.
/// @param signature Proof that transaction has been signed by signer.
/// @param transactionHash EIP712 typed data hash of 0x transaction.
function _assertExecutableTransaction(
LibZeroExTransaction.ZeroExTransaction memory transaction,
bytes memory signature,
bytes32 transactionHash
)
internal
view
{
// Check transaction is not expired
// solhint-disable-next-line not-rely-on-time
if (block.timestamp >= transaction.expirationTimeSeconds) {
LibRichErrors.rrevert(LibExchangeRichErrors.TransactionError(
LibExchangeRichErrors.TransactionErrorCodes.EXPIRED,
transactionHash
));
}
// Validate that transaction is executed with the correct gasPrice
uint256 requiredGasPrice = transaction.gasPrice;
if (tx.gasprice != requiredGasPrice) {
LibRichErrors.rrevert(LibExchangeRichErrors.TransactionGasPriceError(
transactionHash,
tx.gasprice,
requiredGasPrice
));
}
// Prevent `executeTransaction` from being called when context is already set
address currentContextAddress_ = currentContextAddress;
if (currentContextAddress_ != address(0)) {
LibRichErrors.rrevert(LibExchangeRichErrors.TransactionInvalidContextError(
transactionHash,
currentContextAddress_
));
}
// Validate transaction has not been executed
if (transactionsExecuted[transactionHash]) {
LibRichErrors.rrevert(LibExchangeRichErrors.TransactionError(
LibExchangeRichErrors.TransactionErrorCodes.ALREADY_EXECUTED,
transactionHash
));
}
// Validate signature
// Transaction always valid if signer is sender of transaction
address signerAddress = transaction.signerAddress;
if (signerAddress != msg.sender && !_isValidTransactionWithHashSignature(
transaction,
transactionHash,
signature
)
) {
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
LibExchangeRichErrors.SignatureErrorCodes.BAD_TRANSACTION_SIGNATURE,
transactionHash,
signerAddress,
signature
));
}
}
/// @dev Sets the currentContextAddress if the current context is not msg.sender.
/// @param signerAddress Address of the transaction signer.
/// @param contextAddress The current context address.
function _setCurrentContextAddressIfRequired(
address signerAddress,
address contextAddress
)
internal
{
if (signerAddress != msg.sender) {
currentContextAddress = contextAddress;
}
}
/// @dev The current function will be called in the context of this address (either 0x transaction signer or `msg.sender`).
/// If calling a fill function, this address will represent the taker.
/// If calling a cancel function, this address will represent the maker.
/// @return Signer of 0x transaction if entry point is `executeTransaction`.
/// `msg.sender` if entry point is any other function.
function _getCurrentContextAddress()
internal
view
returns (address)
{
address currentContextAddress_ = currentContextAddress;
address contextAddress = currentContextAddress_ == address(0) ? msg.sender : currentContextAddress_;
return contextAddress;
}
}
/*
Copyright 2019 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.5.9;
pragma experimental ABIEncoderV2;
import "./MixinAssetProxyDispatcher.sol";
contract MixinTransferSimulator is
MixinAssetProxyDispatcher
{
/// @dev This function may be used to simulate any amount of transfers
/// As they would occur through the Exchange contract. Note that this function
/// will always revert, even if all transfers are successful. However, it may
/// be used with eth_call or with a try/catch pattern in order to simulate
/// the results of the transfers.
/// @param assetData Array of asset details, each encoded per the AssetProxy contract specification.
/// @param fromAddresses Array containing the `from` addresses that correspond with each transfer.
/// @param toAddresses Array containing the `to` addresses that correspond with each transfer.
/// @param amounts Array containing the amounts that correspond to each transfer.
/// @return This function does not return a value. However, it will always revert with
/// `Error("TRANSFERS_SUCCESSFUL")` if all of the transfers were successful.
function simulateDispatchTransferFromCalls(
bytes[] memory assetData,
address[] memory fromAddresses,
address[] memory toAddresses,
uint256[] memory amounts
)
public
{
uint256 length = assetData.length;
for (uint256 i = 0; i != length; i++) {
_dispatchTransferFrom(
// The index is passed in as `orderHash` so that a failed transfer can be quickly identified when catching the error
bytes32(i),
assetData[i],
fromAddresses[i],
toAddresses[i],
amounts[i]
);
}
revert("TRANSFERS_SUCCESSFUL");
}
}
/*
Copyright 2019 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.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeRichErrors.sol";
import "./interfaces/IExchangeCore.sol";
import "./interfaces/IWrapperFunctions.sol";
import "./MixinExchangeCore.sol";
contract MixinWrapperFunctions is
IWrapperFunctions,
MixinExchangeCore
{
using LibSafeMath for uint256;
/// @dev Fills the input order. Reverts if exact `takerAssetFillAmount` not filled.
/// @param order Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signature Proof that order has been created by maker.
/// @return fillResults Amounts filled and fees paid.
function fillOrKillOrder(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
public
payable
refundFinalBalanceNoReentry
returns (LibFillResults.FillResults memory fillResults)
{
fillResults = _fillOrKillOrder(
order,
takerAssetFillAmount,
signature
);
return fillResults;
}
/// @dev Executes multiple calls of fillOrder.
/// @param orders Array of order specifications.
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
/// @param signatures Proofs that orders have been created by makers.
/// @return fillResults Array of amounts filled and fees paid by makers and taker.
function batchFillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
public
payable
refundFinalBalanceNoReentry
returns (LibFillResults.FillResults[] memory fillResults)
{
uint256 ordersLength = orders.length;
fillResults = new LibFillResults.FillResults[](ordersLength);
for (uint256 i = 0; i != ordersLength; i++) {
fillResults[i] = _fillOrder(
orders[i],
takerAssetFillAmounts[i],
signatures[i]
);
}
return fillResults;
}
/// @dev Executes multiple calls of fillOrKillOrder.
/// @param orders Array of order specifications.
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
/// @param signatures Proofs that orders have been created by makers.
/// @return fillResults Array of amounts filled and fees paid by makers and taker.
function batchFillOrKillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
public
payable
refundFinalBalanceNoReentry
returns (LibFillResults.FillResults[] memory fillResults)
{
uint256 ordersLength = orders.length;
fillResults = new LibFillResults.FillResults[](ordersLength);
for (uint256 i = 0; i != ordersLength; i++) {
fillResults[i] = _fillOrKillOrder(
orders[i],
takerAssetFillAmounts[i],
signatures[i]
);
}
return fillResults;
}
/// @dev Executes multiple calls of fillOrder. If any fill reverts, the error is caught and ignored.
/// @param orders Array of order specifications.
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
/// @param signatures Proofs that orders have been created by makers.
/// @return fillResults Array of amounts filled and fees paid by makers and taker.
function batchFillOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
public
payable
disableRefundUntilEnd
returns (LibFillResults.FillResults[] memory fillResults)
{
uint256 ordersLength = orders.length;
fillResults = new LibFillResults.FillResults[](ordersLength);
for (uint256 i = 0; i != ordersLength; i++) {
fillResults[i] = _fillOrderNoThrow(
orders[i],
takerAssetFillAmounts[i],
signatures[i]
);
}
return fillResults;
}
/// @dev Executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.
/// If any fill reverts, the error is caught and ignored.
/// NOTE: This function does not enforce that the takerAsset is the same for each order.
/// @param orders Array of order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signatures Proofs that orders have been signed by makers.
/// @return fillResults Amounts filled and fees paid by makers and taker.
function marketSellOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256 takerAssetFillAmount,
bytes[] memory signatures
)
public
payable
disableRefundUntilEnd
returns (LibFillResults.FillResults memory fillResults)
{
uint256 ordersLength = orders.length;
for (uint256 i = 0; i != ordersLength; i++) {
// Calculate the remaining amount of takerAsset to sell
uint256 remainingTakerAssetFillAmount = takerAssetFillAmount.safeSub(fillResults.takerAssetFilledAmount);
// Attempt to sell the remaining amount of takerAsset
LibFillResults.FillResults memory singleFillResults = _fillOrderNoThrow(
orders[i],
remainingTakerAssetFillAmount,
signatures[i]
);
// Update amounts filled and fees paid by maker and taker
fillResults = LibFillResults.addFillResults(fillResults, singleFillResults);
// Stop execution if the entire amount of takerAsset has been sold
if (fillResults.takerAssetFilledAmount >= takerAssetFillAmount) {
break;
}
}
return fillResults;
}
/// @dev Executes multiple calls of fillOrder until total amount of makerAsset is bought by taker.
/// If any fill reverts, the error is caught and ignored.
/// NOTE: This function does not enforce that the makerAsset is the same for each order.
/// @param orders Array of order specifications.
/// @param makerAssetFillAmount Desired amount of makerAsset to buy.
/// @param signatures Proofs that orders have been signed by makers.
/// @return fillResults Amounts filled and fees paid by makers and taker.
function marketBuyOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256 makerAssetFillAmount,
bytes[] memory signatures
)
public
payable
disableRefundUntilEnd
returns (LibFillResults.FillResults memory fillResults)
{
uint256 ordersLength = orders.length;
for (uint256 i = 0; i != ordersLength; i++) {
// Calculate the remaining amount of makerAsset to buy
uint256 remainingMakerAssetFillAmount = makerAssetFillAmount.safeSub(fillResults.makerAssetFilledAmount);
// Convert the remaining amount of makerAsset to buy into remaining amount
// of takerAsset to sell, assuming entire amount can be sold in the current order
uint256 remainingTakerAssetFillAmount = LibMath.getPartialAmountCeil(
orders[i].takerAssetAmount,
orders[i].makerAssetAmount,
remainingMakerAssetFillAmount
);
// Attempt to sell the remaining amount of takerAsset
LibFillResults.FillResults memory singleFillResults = _fillOrderNoThrow(
orders[i],
remainingTakerAssetFillAmount,
signatures[i]
);
// Update amounts filled and fees paid by maker and taker
fillResults = LibFillResults.addFillResults(fillResults, singleFillResults);
// Stop execution if the entire amount of makerAsset has been bought
if (fillResults.makerAssetFilledAmount >= makerAssetFillAmount) {
break;
}
}
return fillResults;
}
/// @dev Calls marketSellOrdersNoThrow then reverts if < takerAssetFillAmount has been sold.
/// NOTE: This function does not enforce that the takerAsset is the same for each order.
/// @param orders Array of order specifications.
/// @param takerAssetFillAmount Minimum amount of takerAsset to sell.
/// @param signatures Proofs that orders have been signed by makers.
/// @return fillResults Amounts filled and fees paid by makers and taker.
function marketSellOrdersFillOrKill(
LibOrder.Order[] memory orders,
uint256 takerAssetFillAmount,
bytes[] memory signatures
)
public
payable
returns (LibFillResults.FillResults memory fillResults)
{
fillResults = marketSellOrdersNoThrow(orders, takerAssetFillAmount, signatures);
if (fillResults.takerAssetFilledAmount < takerAssetFillAmount) {
LibRichErrors.rrevert(LibExchangeRichErrors.IncompleteFillError(
LibExchangeRichErrors.IncompleteFillErrorCode.INCOMPLETE_MARKET_SELL_ORDERS,
takerAssetFillAmount,
fillResults.takerAssetFilledAmount
));
}
}
/// @dev Calls marketBuyOrdersNoThrow then reverts if < makerAssetFillAmount has been bought.
/// NOTE: This function does not enforce that the makerAsset is the same for each order.
/// @param orders Array of order specifications.
/// @param makerAssetFillAmount Minimum amount of makerAsset to buy.
/// @param signatures Proofs that orders have been signed by makers.
/// @return fillResults Amounts filled and fees paid by makers and taker.
function marketBuyOrdersFillOrKill(
LibOrder.Order[] memory orders,
uint256 makerAssetFillAmount,
bytes[] memory signatures
)
public
payable
returns (LibFillResults.FillResults memory fillResults)
{
fillResults = marketBuyOrdersNoThrow(orders, makerAssetFillAmount, signatures);
if (fillResults.makerAssetFilledAmount < makerAssetFillAmount) {
LibRichErrors.rrevert(LibExchangeRichErrors.IncompleteFillError(
LibExchangeRichErrors.IncompleteFillErrorCode.INCOMPLETE_MARKET_BUY_ORDERS,
makerAssetFillAmount,
fillResults.makerAssetFilledAmount
));
}
}
/// @dev Executes multiple calls of cancelOrder.
/// @param orders Array of order specifications.
function batchCancelOrders(LibOrder.Order[] memory orders)
public
payable
refundFinalBalanceNoReentry
{
uint256 ordersLength = orders.length;
for (uint256 i = 0; i != ordersLength; i++) {
_cancelOrder(orders[i]);
}
}
/// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.
/// @param order Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param fillResults ignature Proof that order has been created by maker.
function _fillOrKillOrder(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
internal
returns (LibFillResults.FillResults memory fillResults)
{
fillResults = _fillOrder(
order,
takerAssetFillAmount,
signature
);
if (fillResults.takerAssetFilledAmount != takerAssetFillAmount) {
LibRichErrors.rrevert(LibExchangeRichErrors.IncompleteFillError(
LibExchangeRichErrors.IncompleteFillErrorCode.INCOMPLETE_FILL_ORDER,
takerAssetFillAmount,
fillResults.takerAssetFilledAmount
));
}
return fillResults;
}
/// @dev Fills the input order.
/// Returns a null FillResults instance if the transaction would otherwise revert.
/// @param order Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signature Proof that order has been created by maker.
/// @return fillResults Amounts filled and fees paid by maker and taker.
function _fillOrderNoThrow(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
internal
returns (LibFillResults.FillResults memory fillResults)
{
// ABI encode calldata for `fillOrder`
bytes memory fillOrderCalldata = abi.encodeWithSelector(
IExchangeCore(address(0)).fillOrder.selector,
order,
takerAssetFillAmount,
signature
);
(bool didSucceed, bytes memory returnData) = address(this).delegatecall(fillOrderCalldata);
if (didSucceed) {
assert(returnData.length == 160);
fillResults = abi.decode(returnData, (LibFillResults.FillResults));
}
// fillResults values will be 0 by default if call was unsuccessful
return fillResults;
}
}
// Right click on the script name and hit "Run" to execute
(async () => {
try {
console.log('Running deployWithEthers script...')
const contractName = 'Storage' // Change this for other contract
const constructorArgs = [] // Put constructor args (if any) here for your contract
// Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated
const artifactsPath = `browser/contracts/artifacts/${contractName}.json` // Change this for different path
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))
// 'web3Provider' is a remix global variable object
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner()
let factory = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer);
let contract = await factory.deploy(...constructorArgs);
console.log('Contract Address: ', contract.address);
// The contract is NOT deployed yet; we must wait until it is mined
await contract.deployed()
console.log('Deployment successful.')
} catch (e) {
console.log(e.message)
}
})()
// Right click on the script name and hit "Run" to execute
(async () => {
try {
console.log('Running deployWithWeb3 script...')
const contractName = 'Storage' // Change this for other contract
const constructorArgs = [] // Put constructor args (if any) here for your contract
// Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated
const artifactsPath = `browser/contracts/artifacts/${contractName}.json` // Change this for different path
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))
const accounts = await web3.eth.getAccounts()
let contract = new web3.eth.Contract(metadata.abi)
contract = contract.deploy({
data: metadata.data.bytecode.object,
arguments: constructorArgs
})
const newContractInstance = await contract.send({
from: accounts[0],
gas: 1500000,
gasPrice: '30000000000'
})
console.log('Contract deployed at address: ', newContractInstance.options.address)
} catch (e) {
console.log(e.message)
}
})()
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
import "remix_tests.sol"; // this import is automatically injected by Remix.
import "../contracts/3_Ballot.sol";
contract BallotTest {
bytes32[] proposalNames;
Ballot ballotToTest;
function beforeAll () public {
proposalNames.push(bytes32("candidate1"));
ballotToTest = new Ballot(proposalNames);
}
function checkWinningProposal () public {
ballotToTest.vote(0);
Assert.equal(ballotToTest.winningProposal(), uint(0), "proposal at index 0 should be the winning proposal");
Assert.equal(ballotToTest.winnerName(), bytes32("candidate1"), "candidate1 should be the winner name");
}
function checkWinninProposalWithReturnValue () public view returns (bool) {
return ballotToTest.winningProposal() == 0;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment