Merge pull request #2055 from 0xProject/feat/3.0/optimizeConstants

Refactor library usage
This commit is contained in:
Amir Bandeali 2019-08-16 07:40:54 -07:00 committed by GitHub
commit 4c78b7d4bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
116 changed files with 3471 additions and 4076 deletions

View File

@ -90,13 +90,15 @@ jobs:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-coordinator @0x/contracts-dev-utils @0x/contracts-staking
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-dev-utils @0x/contracts-staking
# TODO(dorothy-zbornak): Re-enable after updating this package for 3.0.
# - run: yarn wsrun test:circleci @0x/contracts-extensions
# TODO(dorothy-zbornak): Re-enable after updating this package for 3.0.
# - run: yarn wsrun test:circleci @0x/contracts-exchange-forwarder
# TODO(dorothy-zbornak): Re-enable after this package is complete.
# - run: yarn wsrun test:circleci @0x/contracts-staking
# TODO(abandeali): Re-enable after this package is complete.
# - run: yarn wsrun test:circleci @0x/contracts-coordinator
test-contracts-geth:
docker:
- image: circleci/node:9-browsers

View File

@ -5,6 +5,14 @@
{
"note": "Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable",
"pr": 2019
},
{
"note": "Remove `LibAssetProxyIds` contract",
"pr": 2055
},
{
"note": "Compile and export all contracts, artifacts, and wrappers by default",
"pr": 2055
}
]
},

View File

@ -22,17 +22,5 @@
]
}
}
},
"contracts": [
"archive/MixinAuthorizable.sol",
"src/ERC1155Proxy.sol",
"src/ERC20Proxy.sol",
"src/ERC721Proxy.sol",
"src/MultiAssetProxy.sol",
"src/StaticCallProxy.sol",
"src/interfaces/IAssetData.sol",
"src/interfaces/IAssetProxy.sol",
"src/interfaces/IAuthorizable.sol",
"test/TestStaticCallTarget.sol"
]
}
}

View File

@ -1,40 +0,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.5;
contract LibAssetProxyIds {
// AssetProxy Ids are equiavalent the first 4 bytes of the keccak256 hash of the function signature assigned to each AssetProxy.
// ERC20Token(address)
bytes4 constant public ERC20_PROXY_ID = 0xf47261b0;
// ERC721Token(address,uint256)
bytes4 constant public ERC721_PROXY_ID = 0x02571792;
// ERC1155Assets(address,uint256[],uint256[],bytes)
bytes4 constant public ERC1155_PROXY_ID = 0xa7cb5fb7;
// MultiAsset(uint256[],bytes[])
bytes4 constant public MULTI_ASSET_PROXY_ID = 0x94cfcdd7;
// StaticCall(address,bytes,bytes32)
bytes4 constant public STATIC_CALL_PROXY_ID = 0xc339d10a;
}

View File

@ -12,7 +12,7 @@
"scripts": {
"build": "yarn pre_build && tsc -b",
"build:ci": "yarn build",
"pre_build": "run-s compile generate_contract_wrappers",
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
@ -34,7 +34,7 @@
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
},
"config": {
"abis": "./generated-artifacts/@(ERC1155Proxy|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|MixinAuthorizable|MultiAssetProxy|StaticCallProxy|TestStaticCallTarget).json",
"abis": "./generated-artifacts/@(ERC1155Proxy|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestStaticCallTarget).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {

View File

@ -10,20 +10,26 @@ import * as ERC20Proxy from '../generated-artifacts/ERC20Proxy.json';
import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.json';
import * as IAssetData from '../generated-artifacts/IAssetData.json';
import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
import * as IAssetProxyDispatcher from '../generated-artifacts/IAssetProxyDispatcher.json';
import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
import * as MixinAssetProxyDispatcher from '../generated-artifacts/MixinAssetProxyDispatcher.json';
import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json';
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
import * as Ownable from '../generated-artifacts/Ownable.json';
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
export const artifacts = {
MixinAssetProxyDispatcher: MixinAssetProxyDispatcher as ContractArtifact,
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
Ownable: Ownable as ContractArtifact,
ERC1155Proxy: ERC1155Proxy as ContractArtifact,
ERC20Proxy: ERC20Proxy as ContractArtifact,
ERC721Proxy: ERC721Proxy as ContractArtifact,
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
StaticCallProxy: StaticCallProxy as ContractArtifact,
IAssetData: IAssetData as ContractArtifact,
IAssetProxy: IAssetProxy as ContractArtifact,
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
IAuthorizable: IAuthorizable as ContractArtifact,
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
};

View File

@ -8,8 +8,11 @@ export * from '../generated-wrappers/erc20_proxy';
export * from '../generated-wrappers/erc721_proxy';
export * from '../generated-wrappers/i_asset_data';
export * from '../generated-wrappers/i_asset_proxy';
export * from '../generated-wrappers/i_asset_proxy_dispatcher';
export * from '../generated-wrappers/i_authorizable';
export * from '../generated-wrappers/mixin_asset_proxy_dispatcher';
export * from '../generated-wrappers/mixin_authorizable';
export * from '../generated-wrappers/multi_asset_proxy';
export * from '../generated-wrappers/ownable';
export * from '../generated-wrappers/static_call_proxy';
export * from '../generated-wrappers/test_static_call_target';

View File

@ -8,9 +8,12 @@
"generated-artifacts/ERC721Proxy.json",
"generated-artifacts/IAssetData.json",
"generated-artifacts/IAssetProxy.json",
"generated-artifacts/IAssetProxyDispatcher.json",
"generated-artifacts/IAuthorizable.json",
"generated-artifacts/MixinAssetProxyDispatcher.json",
"generated-artifacts/MixinAuthorizable.json",
"generated-artifacts/MultiAssetProxy.json",
"generated-artifacts/Ownable.json",
"generated-artifacts/StaticCallProxy.json",
"generated-artifacts/TestStaticCallTarget.json"
],

View File

@ -33,6 +33,14 @@
{
"note": "Update for new `marketXOrders` consolidation.",
"pr": 2042
},
{
"note": "Use built in selectors instead of hard coded constants",
"pr": 2055
},
{
"note": "Compile and export all contracts, artifacts, and wrappers by default",
"pr": 2055
}
]
},

View File

@ -21,6 +21,5 @@
]
}
}
},
"contracts": ["src/Coordinator.sol", "src/registry/CoordinatorRegistry.sol"]
}
}

View File

@ -19,11 +19,11 @@
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeSelectors.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibAddressArray.sol";
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "./libs/LibCoordinatorApproval.sol";
import "./interfaces/ISignatureValidator.sol";
import "./interfaces/ICoordinatorApprovalVerifier.sol";
@ -31,7 +31,6 @@ import "./interfaces/ICoordinatorApprovalVerifier.sol";
// solhint-disable avoid-tx-origin
contract MixinCoordinatorApprovalVerifier is
LibExchangeSelectors,
LibCoordinatorApproval,
LibZeroExTransaction,
ISignatureValidator,
@ -84,9 +83,9 @@ contract MixinCoordinatorApprovalVerifier is
{
bytes4 selector = data.readBytes4(0);
if (
selector == FILL_ORDER_SELECTOR ||
selector == FILL_ORDER_NO_THROW_SELECTOR ||
selector == FILL_OR_KILL_ORDER_SELECTOR
selector == IExchange(address(0)).fillOrder.selector ||
selector == IExchange(address(0)).fillOrderNoThrow.selector ||
selector == IExchange(address(0)).fillOrKillOrder.selector
) {
// Decode single order
(LibOrder.Order memory order) = abi.decode(
@ -96,11 +95,11 @@ contract MixinCoordinatorApprovalVerifier is
orders = new LibOrder.Order[](1);
orders[0] = order;
} else if (
selector == BATCH_FILL_ORDERS_SELECTOR ||
selector == BATCH_FILL_ORDERS_NO_THROW_SELECTOR ||
selector == BATCH_FILL_OR_KILL_ORDERS_SELECTOR ||
selector == MARKET_BUY_ORDERS_SELECTOR ||
selector == MARKET_SELL_ORDERS_SELECTOR
selector == IExchange(address(0)).batchFillOrders.selector ||
selector == IExchange(address(0)).batchFillOrdersNoThrow.selector ||
selector == IExchange(address(0)).batchFillOrKillOrders.selector ||
selector == IExchange(address(0)).marketBuyOrders.selector ||
selector == IExchange(address(0)).marketSellOrders.selector
) {
// Decode all orders
// solhint-disable indent
@ -108,7 +107,7 @@ contract MixinCoordinatorApprovalVerifier is
data.slice(4, data.length),
(LibOrder.Order[])
);
} else if (selector == MATCH_ORDERS_SELECTOR) {
} else if (selector == IExchange(address(0)).matchOrders.selector) {
// Decode left and right orders
(LibOrder.Order memory leftOrder, LibOrder.Order memory rightOrder) = abi.decode(
data.slice(4, data.length),

View File

@ -34,7 +34,7 @@ contract LibCoordinatorApproval is
// "uint256 approvalExpirationTimeSeconds",
// ")"
// ));
bytes32 constant internal EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH = 0x2fbcdbaa76bc7589916958ae919dfbef04d23f6bbf26de6ff317b32c6cc01e05;
bytes32 constant public EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH = 0x2fbcdbaa76bc7589916958ae919dfbef04d23f6bbf26de6ff317b32c6cc01e05;
struct CoordinatorApproval {
address txOrigin; // Required signer of Ethereum transaction that is submitting approval.

View File

@ -12,7 +12,7 @@
"scripts": {
"build": "yarn pre_build && tsc -b",
"build:ci": "yarn build",
"pre_build": "run-s compile generate_contract_wrappers",
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",

View File

@ -1,4 +1,17 @@
[
{
"version": "1.0.0",
"changes": [
{
"note": "Use built in selectors instead of hard coded constants",
"pr": 2055
},
{
"note": "Compile and export all contracts, artifacts, and wrappers by default",
"pr": 2055
}
]
},
{
"timestamp": 1563193019,
"version": "0.0.4",

View File

@ -22,12 +22,5 @@
]
}
}
},
"contracts": [
"src/DevUtils.sol",
"src/EthBalanceChecker.sol",
"src/LibAssetData.sol",
"src/LibTransactionDecoder.sol",
"src/OrderTransferSimulationUtils.sol"
]
}
}

View File

@ -20,32 +20,19 @@ pragma solidity ^0.5.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-asset-proxy/contracts/src/libs/LibAssetProxyIds.sol";
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetProxy.sol";
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
contract LibAssetData is
LibAssetProxyIds
{
contract LibAssetData {
// 2^256 - 1
uint256 constant internal _MAX_UINT256 = uint256(-1);
// ERC20 selectors
bytes4 constant internal _ERC20_BALANCE_OF_SELECTOR = 0x70a08231;
bytes4 constant internal _ERC20_ALLOWANCE_SELECTOR = 0xdd62ed3e;
// ERC721 selectors
bytes4 constant internal _ERC721_OWNER_OF_SELECTOR = 0x6352211e;
bytes4 constant internal _ERC721_IS_APPROVED_FOR_ALL_SELECTOR = 0xe985e9c5;
bytes4 constant internal _ERC721_GET_APPROVED_SELECTOR = 0x081812fc;
// ERC1155 selectors
bytes4 constant internal _ERC1155_BALANCE_OF_SELECTOR = 0x00fdd58e;
bytes4 constant internal _ERC1155_IS_APPROVED_FOR_ALL_SELECTOR = 0xe985e9c5;
// `transferFrom` selector for all AssetProxy contracts
bytes4 constant internal _ASSET_PROXY_TRANSFER_FROM_SELECTOR = 0xa85e59e4;
using LibBytes for bytes;
// solhint-disable var-name-mixedcase
@ -60,10 +47,10 @@ contract LibAssetData is
public
{
_EXCHANGE = IExchange(_exchange);
_ERC20_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC20_PROXY_ID);
_ERC721_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC721_PROXY_ID);
_ERC1155_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC1155_PROXY_ID);
_STATIC_CALL_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(STATIC_CALL_PROXY_ID);
_ERC20_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC20Token.selector);
_ERC721_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC721Token.selector);
_ERC1155_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC1155Assets.selector);
_STATIC_CALL_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).StaticCall.selector);
}
/// @dev Returns the owner's balance of the assets(s) specified in
@ -81,23 +68,33 @@ contract LibAssetData is
// Get id of AssetProxy contract
bytes4 assetProxyId = assetData.readBytes4(0);
if (assetProxyId == ERC20_PROXY_ID) {
if (assetProxyId == IAssetData(address(0)).ERC20Token.selector) {
// Get ERC20 token address
address tokenAddress = assetData.readAddress(16);
// Encode data for `balanceOf(ownerAddress)`
bytes memory balanceOfData = abi.encodeWithSelector(_ERC20_BALANCE_OF_SELECTOR, ownerAddress);
bytes memory balanceOfData = abi.encodeWithSelector(
IERC20Token(address(0)).balanceOf.selector,
ownerAddress
);
// Query balance
(bool success, bytes memory returnData) = tokenAddress.staticcall(balanceOfData);
balance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
} else if (assetProxyId == ERC721_PROXY_ID) {
} else if (assetProxyId == IAssetData(address(0)).ERC721Token.selector) {
// Get ERC721 token address and id
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
// Check if id is owned by ownerAddress
balance = getERC721TokenOwner(tokenAddress, tokenId) == ownerAddress ? 1 : 0;
} else if (assetProxyId == ERC1155_PROXY_ID) {
bytes memory ownerOfCalldata = abi.encodeWithSelector(
IERC721Token(address(0)).ownerOf.selector,
tokenId
);
(bool success, bytes memory returnData) = tokenAddress.staticcall(ownerOfCalldata);
address currentOwnerAddress = (success && returnData.length == 32) ? returnData.readAddress(12) : address(0);
balance = currentOwnerAddress == ownerAddress ? 1 : 0;
} else if (assetProxyId == IAssetData(address(0)).ERC1155Assets.selector) {
// Get ERC1155 token address, array of ids, and array of values
(, address tokenAddress, uint256[] memory tokenIds, uint256[] memory tokenValues,) = decodeERC1155AssetData(assetData);
@ -105,7 +102,7 @@ contract LibAssetData is
for (uint256 i = 0; i != length; i++) {
// Encode data for `balanceOf(ownerAddress, tokenIds[i])
bytes memory balanceOfData = abi.encodeWithSelector(
_ERC1155_BALANCE_OF_SELECTOR,
IERC1155(address(0)).balanceOf.selector,
ownerAddress,
tokenIds[i]
);
@ -120,10 +117,10 @@ contract LibAssetData is
balance = scaledBalance;
}
}
} else if (assetProxyId == STATIC_CALL_PROXY_ID) {
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
// Encode data for `staticCallProxy.transferFrom(assetData,...)`
bytes memory transferFromData = abi.encodeWithSelector(
_ASSET_PROXY_TRANSFER_FROM_SELECTOR,
IAssetProxy(address(0)).transferFrom.selector,
assetData,
address(0), // `from` address is not used
address(0), // `to` address is not used
@ -135,7 +132,7 @@ contract LibAssetData is
// Success means that the staticcall can be made an unlimited amount of times
balance = success ? _MAX_UINT256 : 0;
} else if (assetProxyId == MULTI_ASSET_PROXY_ID) {
} else if (assetProxyId == IAssetData(address(0)).MultiAsset.selector) {
// Get array of values and array of assetDatas
(, uint256[] memory assetAmounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
@ -190,7 +187,7 @@ contract LibAssetData is
// Get id of AssetProxy contract
bytes4 assetProxyId = assetData.readBytes4(0);
if (assetProxyId == MULTI_ASSET_PROXY_ID) {
if (assetProxyId == IAssetData(address(0)).MultiAsset.selector) {
// Get array of values and array of assetDatas
(, uint256[] memory amounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
@ -208,13 +205,13 @@ contract LibAssetData is
return allowance;
}
if (assetProxyId == ERC20_PROXY_ID) {
if (assetProxyId == IAssetData(address(0)).ERC20Token.selector) {
// Get ERC20 token address
address tokenAddress = assetData.readAddress(16);
// Encode data for `allowance(ownerAddress, _ERC20_PROXY_ADDRESS)`
bytes memory allowanceData = abi.encodeWithSelector(
_ERC20_ALLOWANCE_SELECTOR,
IERC20Token(address(0)).allowance.selector,
ownerAddress,
_ERC20_PROXY_ADDRESS
);
@ -222,13 +219,13 @@ contract LibAssetData is
// Query allowance
(bool success, bytes memory returnData) = tokenAddress.staticcall(allowanceData);
allowance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
} else if (assetProxyId == ERC721_PROXY_ID) {
} else if (assetProxyId == IAssetData(address(0)).ERC721Token.selector) {
// Get ERC721 token address and id
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
// Encode data for `isApprovedForAll(ownerAddress, _ERC721_PROXY_ADDRESS)`
bytes memory isApprovedForAllData = abi.encodeWithSelector(
_ERC721_IS_APPROVED_FOR_ALL_SELECTOR,
IERC721Token(address(0)).isApprovedForAll.selector,
ownerAddress,
_ERC721_PROXY_ADDRESS
);
@ -238,7 +235,7 @@ contract LibAssetData is
// If not approved for all, call `getApproved(tokenId)`
if (!success || returnData.length != 32 || returnData.readUint256(0) != 1) {
// Encode data for `getApproved(tokenId)`
bytes memory getApprovedData = abi.encodeWithSelector(_ERC721_GET_APPROVED_SELECTOR, tokenId);
bytes memory getApprovedData = abi.encodeWithSelector(IERC721Token(address(0)).getApproved.selector, tokenId);
(success, returnData) = tokenAddress.staticcall(getApprovedData);
// Allowance is 1 if successful and the approved address is the ERC721Proxy
@ -247,13 +244,13 @@ contract LibAssetData is
// Allowance is 2^256 - 1 if `isApprovedForAll` returned true
allowance = _MAX_UINT256;
}
} else if (assetProxyId == ERC1155_PROXY_ID) {
} else if (assetProxyId == IAssetData(address(0)).ERC1155Assets.selector) {
// Get ERC1155 token address
(, address tokenAddress, , , ) = decodeERC1155AssetData(assetData);
// Encode data for `isApprovedForAll(ownerAddress, _ERC1155_PROXY_ADDRESS)`
bytes memory isApprovedForAllData = abi.encodeWithSelector(
_ERC1155_IS_APPROVED_FOR_ALL_SELECTOR,
IERC1155(address(0)).isApprovedForAll.selector,
ownerAddress,
_ERC1155_PROXY_ADDRESS
);
@ -261,7 +258,7 @@ contract LibAssetData is
// Query allowance
(bool success, bytes memory returnData) = tokenAddress.staticcall(isApprovedForAllData);
allowance = success && returnData.length == 32 && returnData.readUint256(0) == 1 ? _MAX_UINT256 : 0;
} else if (assetProxyId == STATIC_CALL_PROXY_ID) {
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
// The StaticCallProxy does not require any approvals
allowance = _MAX_UINT256;
}
@ -327,7 +324,7 @@ contract LibAssetData is
pure
returns (bytes memory assetData)
{
assetData = abi.encodeWithSelector(ERC20_PROXY_ID, tokenAddress);
assetData = abi.encodeWithSelector(IAssetData(address(0)).ERC20Token.selector, tokenAddress);
return assetData;
}
@ -346,7 +343,7 @@ contract LibAssetData is
assetProxyId = assetData.readBytes4(0);
require(
assetProxyId == ERC20_PROXY_ID,
assetProxyId == IAssetData(address(0)).ERC20Token.selector,
"WRONG_PROXY_ID"
);
@ -364,7 +361,7 @@ contract LibAssetData is
returns (bytes memory assetData)
{
assetData = abi.encodeWithSelector(
ERC721_PROXY_ID,
IAssetData(address(0)).ERC721Token.selector,
tokenAddress,
tokenId
);
@ -388,7 +385,7 @@ contract LibAssetData is
assetProxyId = assetData.readBytes4(0);
require(
assetProxyId == ERC721_PROXY_ID,
assetProxyId == IAssetData(address(0)).ERC721Token.selector,
"WRONG_PROXY_ID"
);
@ -414,7 +411,7 @@ contract LibAssetData is
returns (bytes memory assetData)
{
assetData = abi.encodeWithSelector(
ERC1155_PROXY_ID,
IAssetData(address(0)).ERC1155Assets.selector,
tokenAddress,
tokenIds,
tokenValues,
@ -446,7 +443,7 @@ contract LibAssetData is
assetProxyId = assetData.readBytes4(0);
require(
assetProxyId == ERC1155_PROXY_ID,
assetProxyId == IAssetData(address(0)).ERC1155Assets.selector,
"WRONG_PROXY_ID"
);
@ -482,7 +479,7 @@ contract LibAssetData is
returns (bytes memory assetData)
{
assetData = abi.encodeWithSelector(
MULTI_ASSET_PROXY_ID,
IAssetData(address(0)).MultiAsset.selector,
amounts,
nestedAssetData
);
@ -507,7 +504,7 @@ contract LibAssetData is
assetProxyId = assetData.readBytes4(0);
require(
assetProxyId == MULTI_ASSET_PROXY_ID,
assetProxyId == IAssetData(address(0)).MultiAsset.selector,
"WRONG_PROXY_ID"
);
@ -518,24 +515,4 @@ contract LibAssetData is
);
// solhint-enable indent
}
/// @dev Calls `asset.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned asset.
/// @param tokenAddress Address of ERC721 asset.
/// @param tokenId The identifier for the specific NFT.
/// @return Owner of tokenId or null address if unowned.
function getERC721TokenOwner(address tokenAddress, uint256 tokenId)
public
view
returns (address ownerAddress)
{
bytes memory ownerOfCalldata = abi.encodeWithSelector(
_ERC721_OWNER_OF_SELECTOR,
tokenId
);
(bool success, bytes memory returnData) = tokenAddress.staticcall(ownerOfCalldata);
ownerAddress = (success && returnData.length == 32) ? returnData.readAddress(12) : address(0);
return ownerAddress;
}
}

View File

@ -19,14 +19,13 @@
pragma solidity ^0.5.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeSelectors.sol";
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
contract LibTransactionDecoder is
LibExchangeSelectors
{
contract LibTransactionDecoder {
using LibBytes for bytes;
/// @dev Decodes the call data for an Exchange contract method call.
@ -47,66 +46,65 @@ contract LibTransactionDecoder is
{
bytes4 functionSelector = transactionData.readBytes4(0);
if (functionSelector == BATCH_CANCEL_ORDERS_SELECTOR) {
if (functionSelector == IExchange(address(0)).batchCancelOrders.selector) {
functionName = "batchCancelOrders";
} else if (functionSelector == BATCH_FILL_ORDERS_SELECTOR) {
} else if (functionSelector == IExchange(address(0)).batchFillOrders.selector) {
functionName = "batchFillOrders";
} else if (functionSelector == BATCH_FILL_ORDERS_NO_THROW_SELECTOR) {
} else if (functionSelector == IExchange(address(0)).batchFillOrdersNoThrow.selector) {
functionName = "batchFillOrdersNoThrow";
} else if (functionSelector == BATCH_FILL_OR_KILL_ORDERS_SELECTOR) {
} else if (functionSelector == IExchange(address(0)).batchFillOrKillOrders.selector) {
functionName = "batchFillOrKillOrders";
} else if (functionSelector == CANCEL_ORDER_SELECTOR) {
} else if (functionSelector == IExchange(address(0)).cancelOrder.selector) {
functionName = "cancelOrder";
} else if (functionSelector == FILL_ORDER_SELECTOR) {
} else if (functionSelector == IExchange(address(0)).fillOrder.selector) {
functionName = "fillOrder";
} else if (functionSelector == FILL_ORDER_NO_THROW_SELECTOR) {
} else if (functionSelector == IExchange(address(0)).fillOrderNoThrow.selector) {
functionName = "fillOrderNoThrow";
} else if (functionSelector == FILL_OR_KILL_ORDER_SELECTOR) {
} else if (functionSelector == IExchange(address(0)).fillOrKillOrder.selector) {
functionName = "fillOrKillOrder";
} else if (functionSelector == MARKET_BUY_ORDERS_SELECTOR) {
} else if (functionSelector == IExchange(address(0)).marketBuyOrders.selector) {
functionName = "marketBuyOrders";
} else if (functionSelector == MARKET_SELL_ORDERS_SELECTOR) {
} else if (functionSelector == IExchange(address(0)).marketSellOrders.selector) {
functionName = "marketSellOrders";
} else if (functionSelector == MATCH_ORDERS_SELECTOR) {
} else if (functionSelector == IExchange(address(0)).matchOrders.selector) {
functionName = "matchOrders";
} else if (
functionSelector == CANCEL_ORDERS_UP_TO_SELECTOR ||
functionSelector == EXECUTE_TRANSACTION_SELECTOR
// TODO: add new noThrow cancel functions when https://github.com/0xProject/ZEIPs/issues/35 is merged.
functionSelector == IExchange(address(0)).cancelOrdersUpTo.selector ||
functionSelector == IExchange(address(0)).executeTransaction.selector
) {
revert("UNIMPLEMENTED");
} else {
revert("UNKNOWN_FUNCTION_SELECTOR");
}
if (functionSelector == BATCH_CANCEL_ORDERS_SELECTOR) {
if (functionSelector == IExchange(address(0)).batchCancelOrders.selector) {
// solhint-disable-next-line indent
orders = abi.decode(transactionData.slice(4, transactionData.length), (LibOrder.Order[]));
takerAssetFillAmounts = new uint256[](0);
signatures = new bytes[](0);
} else if (
functionSelector == BATCH_FILL_OR_KILL_ORDERS_SELECTOR ||
functionSelector == BATCH_FILL_ORDERS_NO_THROW_SELECTOR ||
functionSelector == BATCH_FILL_ORDERS_SELECTOR
functionSelector == IExchange(address(0)).batchFillOrKillOrders.selector ||
functionSelector == IExchange(address(0)).batchFillOrdersNoThrow.selector ||
functionSelector == IExchange(address(0)).batchFillOrders.selector
) {
(orders, takerAssetFillAmounts, signatures) = _makeReturnValuesForBatchFill(transactionData);
} else if (functionSelector == CANCEL_ORDER_SELECTOR) {
} else if (functionSelector == IExchange(address(0)).cancelOrder.selector) {
orders = new LibOrder.Order[](1);
orders[0] = abi.decode(transactionData.slice(4, transactionData.length), (LibOrder.Order));
takerAssetFillAmounts = new uint256[](0);
signatures = new bytes[](0);
} else if (
functionSelector == FILL_OR_KILL_ORDER_SELECTOR ||
functionSelector == FILL_ORDER_SELECTOR ||
functionSelector == FILL_ORDER_NO_THROW_SELECTOR
functionSelector == IExchange(address(0)).fillOrKillOrder.selector ||
functionSelector == IExchange(address(0)).fillOrder.selector ||
functionSelector == IExchange(address(0)).fillOrderNoThrow.selector
) {
(orders, takerAssetFillAmounts, signatures) = _makeReturnValuesForSingleOrderFill(transactionData);
} else if (
functionSelector == MARKET_BUY_ORDERS_SELECTOR ||
functionSelector == MARKET_SELL_ORDERS_SELECTOR
functionSelector == IExchange(address(0)).marketBuyOrders.selector ||
functionSelector == IExchange(address(0)).marketSellOrders.selector
) {
(orders, takerAssetFillAmounts, signatures) = _makeReturnValuesForMarketFill(transactionData);
} else if (functionSelector == MATCH_ORDERS_SELECTOR) {
} else if (functionSelector == IExchange(address(0)).matchOrders.selector) {
(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,

View File

@ -21,8 +21,8 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-exchange/contracts/src/LibExchangeRichErrors.sol";
import "@0x/contracts-exchange/contracts/src/libs/LibExchangeRichErrorDecoder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeRichErrors.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
@ -40,9 +40,6 @@ contract OrderTransferSimulationUtils is
TransfersSuccessful // All transfers in the order were successful
}
// simulateDispatchTransferFromCalls(bytes[],address[],address[],uint256[])
bytes4 constant internal _SIMULATE_DISPATCH_TRANSFER_FROM_CALLS_SELECTOR = 0xb04fbddd;
// keccak256(abi.encodeWithSignature("Error(string)", "TRANSFERS_SUCCESSFUL"));
bytes32 constant internal _TRANSFERS_SUCCESSFUL_RESULT_HASH = 0xf43f26ea5a94b478394a975e856464913dc1a8a1ca70939d974aa7c238aa0ce0;
@ -101,7 +98,7 @@ contract OrderTransferSimulationUtils is
// Encode data for `simulateDispatchTransferFromCalls(assetData, fromAddresses, toAddresses, amounts)`
bytes memory simulateDispatchTransferFromCallsData = abi.encodeWithSelector(
_SIMULATE_DISPATCH_TRANSFER_FROM_CALLS_SELECTOR,
IExchange(address(0)).simulateDispatchTransferFromCalls.selector,
assetData,
fromAddresses,
toAddresses,

View File

@ -23,14 +23,15 @@ import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "./LibAssetData.sol";
contract OrderValidationUtils is
LibAssetData,
LibMath
LibAssetData
{
using LibBytes for bytes;
using LibSafeMath for uint256;
constructor (address _exchange)
public
@ -79,9 +80,9 @@ contract OrderValidationUtils is
if (order.makerAssetData.equals(order.makerFeeAssetData)) {
// If `makerAsset` equals `makerFeeAsset`, the % that can be filled is
// transferableMakerAssetAmount / (makerAssetAmount + makerFee)
transferableTakerAssetAmount = _getPartialAmountFloor(
transferableTakerAssetAmount = LibMath.getPartialAmountFloor(
transferableMakerAssetAmount,
_safeAdd(order.makerAssetAmount, makerFee),
order.makerAssetAmount.safeAdd(makerFee),
takerAssetAmount
);
} else {
@ -90,7 +91,7 @@ contract OrderValidationUtils is
// If `makerFee` is 0, the % that can be filled is (transferableMakerAssetAmount / makerAssetAmount)
if (makerFee == 0) {
transferableTakerAssetAmount = _getPartialAmountFloor(
transferableTakerAssetAmount = LibMath.getPartialAmountFloor(
transferableMakerAssetAmount,
order.makerAssetAmount,
takerAssetAmount
@ -99,23 +100,23 @@ contract OrderValidationUtils is
// If `makerAsset` does not equal `makerFeeAsset`, the % that can be filled is the lower of
// (transferableMakerAssetAmount / makerAssetAmount) and (transferableMakerAssetFeeAmount / makerFee)
} else {
uint256 transferableMakerToTakerAmount = _getPartialAmountFloor(
uint256 transferableMakerToTakerAmount = LibMath.getPartialAmountFloor(
transferableMakerAssetAmount,
order.makerAssetAmount,
takerAssetAmount
);
uint256 transferableMakerFeeToTakerAmount = _getPartialAmountFloor(
uint256 transferableMakerFeeToTakerAmount = LibMath.getPartialAmountFloor(
transferableMakerFeeAssetAmount,
makerFee,
takerAssetAmount
);
transferableTakerAssetAmount = _min256(transferableMakerToTakerAmount, transferableMakerFeeToTakerAmount);
transferableTakerAssetAmount = LibSafeMath.min256(transferableMakerToTakerAmount, transferableMakerFeeToTakerAmount);
}
}
// `fillableTakerAssetAmount` is the lower of the order's remaining `takerAssetAmount` and the `transferableTakerAssetAmount`
fillableTakerAssetAmount = _min256(
_safeSub(takerAssetAmount, orderInfo.orderTakerAssetFilledAmount),
fillableTakerAssetAmount = LibSafeMath.min256(
takerAssetAmount.safeSub(orderInfo.orderTakerAssetFilledAmount),
transferableTakerAssetAmount
);
@ -170,7 +171,7 @@ contract OrderValidationUtils is
returns (uint256 transferableAssetAmount)
{
(uint256 balance, uint256 allowance) = getBalanceAndAssetProxyAllowance(ownerAddress, assetData);
transferableAssetAmount = _min256(balance, allowance);
transferableAssetAmount = LibSafeMath.min256(balance, allowance);
return transferableAssetAmount;
}
}

View File

@ -12,7 +12,7 @@
"scripts": {
"build": "yarn pre_build && tsc -b",
"build:ci": "yarn build",
"pre_build": "run-s compile generate_contract_wrappers",
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
@ -34,7 +34,7 @@
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
},
"config": {
"abis": "./generated-artifacts/@(DevUtils|EthBalanceChecker|LibAssetData|LibTransactionDecoder|OrderTransferSimulationUtils).json",
"abis": "./generated-artifacts/@(DevUtils|EthBalanceChecker|LibAssetData|LibTransactionDecoder|OrderTransferSimulationUtils|OrderValidationUtils).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {

View File

@ -10,10 +10,12 @@ import * as EthBalanceChecker from '../generated-artifacts/EthBalanceChecker.jso
import * as LibAssetData from '../generated-artifacts/LibAssetData.json';
import * as LibTransactionDecoder from '../generated-artifacts/LibTransactionDecoder.json';
import * as OrderTransferSimulationUtils from '../generated-artifacts/OrderTransferSimulationUtils.json';
import * as OrderValidationUtils from '../generated-artifacts/OrderValidationUtils.json';
export const artifacts = {
DevUtils: DevUtils as ContractArtifact,
EthBalanceChecker: EthBalanceChecker as ContractArtifact,
LibAssetData: LibAssetData as ContractArtifact,
LibTransactionDecoder: LibTransactionDecoder as ContractArtifact,
EthBalanceChecker: EthBalanceChecker as ContractArtifact,
OrderTransferSimulationUtils: OrderTransferSimulationUtils as ContractArtifact,
OrderValidationUtils: OrderValidationUtils as ContractArtifact,
};

View File

@ -8,3 +8,4 @@ export * from '../generated-wrappers/eth_balance_checker';
export * from '../generated-wrappers/lib_asset_data';
export * from '../generated-wrappers/lib_transaction_decoder';
export * from '../generated-wrappers/order_transfer_simulation_utils';
export * from '../generated-wrappers/order_validation_utils';

View File

@ -465,20 +465,6 @@ describe('LibAssetData', () => {
});
});
describe('getERC721TokenOwner', async () => {
it('should return the null address when tokenId is not owned', async () => {
const nonexistentTokenId = new BigNumber(1234567890);
expect(
await libAssetData.getERC721TokenOwner.callAsync(erc721Token.address, nonexistentTokenId),
).to.be.equal(constants.NULL_ADDRESS);
});
it('should return the owner address when tokenId is owned', async () => {
expect(
await libAssetData.getERC721TokenOwner.callAsync(erc721Token.address, firstERC721TokenId),
).to.be.equal(tokenOwnerAddress);
});
});
describe('getBalanceAndAllowance', () => {
it('should query balance and allowance together, from asset data', async () => {
const allowance = new BigNumber(1);

View File

@ -7,7 +7,8 @@
"generated-artifacts/EthBalanceChecker.json",
"generated-artifacts/LibAssetData.json",
"generated-artifacts/LibTransactionDecoder.json",
"generated-artifacts/OrderTransferSimulationUtils.json"
"generated-artifacts/OrderTransferSimulationUtils.json",
"generated-artifacts/OrderValidationUtils.json"
],
"exclude": ["./deploy/solc/solc_bin"]
}

View File

@ -22,14 +22,5 @@
]
}
}
},
"contracts": [
"src/ERC1155.sol",
"src/ERC1155Mintable.sol",
"src/MixinNonFungibleToken.sol",
"src/interfaces/IERC1155.sol",
"src/interfaces/IERC1155Mintable.sol",
"src/interfaces/IERC1155Receiver.sol",
"test/DummyERC1155Receiver.sol"
]
}
}

View File

@ -12,7 +12,7 @@
"scripts": {
"build": "yarn pre_build && tsc -b",
"build:ci": "yarn build",
"pre_build": "run-s compile generate_contract_wrappers",
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",

View File

@ -23,18 +23,5 @@
]
}
}
},
"contracts": [
"src/ERC20Token.sol",
"src/MintableERC20Token.sol",
"src/UnlimitedAllowanceERC20Token.sol",
"src/WETH9.sol",
"src/ZRXToken.sol",
"src/interfaces/IERC20Token.sol",
"src/interfaces/IEtherToken.sol",
"test/DummyERC20Token.sol",
"test/DummyMultipleReturnERC20Token.sol",
"test/DummyNoReturnERC20Token.sol",
"test/UntransferrableDummyERC20Token.sol"
]
}
}

View File

@ -12,7 +12,7 @@
"scripts": {
"build": "yarn pre_build && tsc -b",
"build:ci": "yarn build",
"pre_build": "run-s compile generate_contract_wrappers",
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",

View File

@ -22,14 +22,5 @@
]
}
}
},
"contracts": [
"src/ERC721Token.sol",
"src/MintableERC721Token.sol",
"src/interfaces/IERC721Receiver.sol",
"src/interfaces/IERC721Token.sol",
"test/DummyERC721Receiver.sol",
"test/DummyERC721Token.sol",
"test/InvalidERC721Receiver.sol"
]
}
}

View File

@ -12,7 +12,7 @@
"scripts": {
"build": "yarn pre_build && tsc -b",
"build:ci": "yarn build",
"pre_build": "run-s compile generate_contract_wrappers",
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",

View File

@ -22,6 +22,5 @@
]
}
}
},
"contracts": ["src/Forwarder.sol"]
}
}

View File

@ -23,14 +23,13 @@ import "./libs/LibConstants.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/LibMath.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeSelectors.sol";
import "@0x/contracts/exchange/contracts/src/interfaces/IExchange.sol";
contract MixinExchangeWrapper is
LibFillResults,
LibMath,
LibConstants,
LibExchangeSelectors
LibConstants
{
/// @dev Fills the input order.
/// Returns false if the transaction would otherwise revert.
@ -48,9 +47,7 @@ contract MixinExchangeWrapper is
{
// ABI encode calldata for `fillOrder`
bytes memory fillOrderCalldata = abi.encodeWithSelector(
// bytes4(keccak256("fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),uint256,bytes)"))
// = 0x9b44d556
0x9b44d556,
IExchange(address(0)).fillOrder.selector,
order,
takerAssetFillAmount,
signature

View File

@ -12,7 +12,7 @@
"scripts": {
"build": "yarn pre_build && tsc -b",
"build:ci": "yarn build",
"pre_build": "run-s compile generate_contract_wrappers",
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",

View File

@ -77,6 +77,30 @@
{
"note": "Regenerate selectors.",
"pr": 2042
},
{
"note": "Convert `LibFillResults`, `LibOrder`, `LibZeroExTransaction`, and `LibMath` to libraries",
"pr": 2055
},
{
"note": "Remove `LibExchangeSelectors`",
"pr": 2055
},
{
"note": "Add `LibExchangeRichErrors`",
"pr": 2055
},
{
"note": "Add `calculateFillResults` and `calculateMatchedFillResults` to `LibFillResults`",
"pr": 2055
},
{
"note": "Remove `_hashEIP712ExchangeMessage` from `LibEIP712ExchangeDomain`",
"pr": 2055
},
{
"note": "Compile and export all contracts, artifacts, and wrappers by default",
"pr": 2055
}
]
},

View File

@ -22,13 +22,5 @@
]
}
}
},
"contracts": [
"src/LibEIP712ExchangeDomain.sol",
"src/LibFillResults.sol",
"src/LibMath.sol",
"src/LibOrder.sol",
"src/LibZeroExTransaction.sol",
"test/TestLibs.sol"
]
}
}

View File

@ -21,9 +21,8 @@ pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibEIP712.sol";
contract LibEIP712ExchangeDomain is
LibEIP712
{
contract LibEIP712ExchangeDomain {
// EIP712 Exchange Domain Name value
string constant public EIP712_EXCHANGE_DOMAIN_NAME = "0x Protocol";
@ -43,23 +42,11 @@ contract LibEIP712ExchangeDomain is
public
{
address verifyingContractAddress = verifyingContractAddressIfExists == address(0) ? address(this) : verifyingContractAddressIfExists;
EIP712_EXCHANGE_DOMAIN_HASH = _hashEIP712Domain(
EIP712_EXCHANGE_DOMAIN_HASH = LibEIP712.hashEIP712Domain(
EIP712_EXCHANGE_DOMAIN_NAME,
EIP712_EXCHANGE_DOMAIN_VERSION,
chainId,
verifyingContractAddress
);
}
/// @dev Calculates EIP712 encoding for a hash struct in the EIP712 domain
/// of the Exchange contract.
/// @param hashStruct The EIP712 hash struct.
/// @return EIP712 hash applied to the Exchange EIP712 Domain.
function _hashEIP712ExchangeMessage(bytes32 hashStruct)
internal
view
returns (bytes32 result)
{
return _hashEIP712Message(EIP712_EXCHANGE_DOMAIN_HASH, hashStruct);
}
}

View File

@ -19,12 +19,45 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "./interfaces/IExchangeRichErrors.sol";
import "./LibOrder.sol";
library LibExchangeRichErrors {
enum AssetProxyDispatchErrorCodes {
INVALID_ASSET_DATA_LENGTH,
UNKNOWN_ASSET_PROXY
}
enum BatchMatchOrdersErrorCodes {
ZERO_LEFT_ORDERS,
ZERO_RIGHT_ORDERS,
INVALID_LENGTH_LEFT_SIGNATURES,
INVALID_LENGTH_RIGHT_SIGNATURES
}
enum FillErrorCodes {
INVALID_TAKER_AMOUNT,
TAKER_OVERPAY,
OVERFILL,
INVALID_FILL_PRICE
}
enum SignatureErrorCodes {
BAD_SIGNATURE,
INVALID_LENGTH,
UNSUPPORTED,
ILLEGAL,
INAPPROPRIATE_SIGNATURE_TYPE,
INVALID_SIGNER
}
enum TransactionErrorCodes {
NO_REENTRANCY,
ALREADY_EXECUTED,
EXPIRED
}
// bytes4(keccak256("SignatureError(uint8,bytes32,address,bytes)"))
bytes4 internal constant SIGNATURE_ERROR_SELECTOR =
0x7e5a2318;
@ -255,7 +288,7 @@ library LibExchangeRichErrors {
}
function BatchMatchOrdersError(
IExchangeRichErrors.BatchMatchOrdersErrorCodes errorCode
BatchMatchOrdersErrorCodes errorCode
)
internal
pure
@ -268,7 +301,7 @@ library LibExchangeRichErrors {
}
function SignatureError(
IExchangeRichErrors.SignatureErrorCodes errorCode,
SignatureErrorCodes errorCode,
bytes32 hash,
address signerAddress,
bytes memory signature
@ -387,7 +420,7 @@ library LibExchangeRichErrors {
}
function FillError(
IExchangeRichErrors.FillErrorCodes errorCode,
FillErrorCodes errorCode,
bytes32 orderHash
)
internal
@ -447,7 +480,7 @@ library LibExchangeRichErrors {
}
function AssetProxyDispatchError(
IExchangeRichErrors.AssetProxyDispatchErrorCodes errorCode,
AssetProxyDispatchErrorCodes errorCode,
bytes32 orderHash,
bytes memory assetData
)
@ -496,7 +529,7 @@ library LibExchangeRichErrors {
}
function TransactionError(
IExchangeRichErrors.TransactionErrorCodes errorCode,
TransactionErrorCodes errorCode,
bytes32 transactionHash
)
internal

View File

@ -1,186 +0,0 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
contract LibExchangeSelectors {
// solhint-disable max-line-length
// function allowedValidators(address,address)
bytes4 constant internal ALLOWED_VALIDATORS_SELECTOR = 0x7b8e3514;
// function assetProxies(bytes4)
bytes4 constant internal ASSET_PROXIES_SELECTOR = 0x3fd3c997;
// function batchCancelOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[])
bytes4 constant internal BATCH_CANCEL_ORDERS_SELECTOR = 0xdedfc1f1;
// function batchExecuteTransactions((uint256,uint256,address,bytes)[],bytes[])
bytes4 constant internal BATCH_EXECUTE_TRANSACTIONS_SELECTOR = 0x3f80f0ee;
// function batchFillOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],uint256[],bytes[])
bytes4 constant internal BATCH_FILL_ORDERS_SELECTOR = 0x9694a402;
// function batchFillOrdersNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],uint256[],bytes[])
bytes4 constant internal BATCH_FILL_ORDERS_NO_THROW_SELECTOR = 0x8ea8dfe4;
// function batchFillOrKillOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],uint256[],bytes[])
bytes4 constant internal BATCH_FILL_OR_KILL_ORDERS_SELECTOR = 0xbeee2e14;
// function 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[])
bytes4 constant internal BATCH_MATCH_ORDERS_SELECTOR = 0x6fcf3e9e;
// function 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[])
bytes4 constant internal BATCH_MATCH_ORDERS_WITH_MAXIMAL_FILL_SELECTOR = 0x6a1a80fd;
// function calculateMatchedFillResults((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),uint256,uint256,bool)
bytes4 constant internal CALCULATE_MATCHED_FILL_RESULTS_SELECTOR = 0x38f9eb3b;
// function cancelled(bytes32)
bytes4 constant internal CANCELLED_SELECTOR = 0x2ac12622;
// function cancelOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes))
bytes4 constant internal CANCEL_ORDER_SELECTOR = 0x2da62987;
// function cancelOrdersUpTo(uint256)
bytes4 constant internal CANCEL_ORDERS_UP_TO_SELECTOR = 0x4f9559b1;
// function currentContextAddress()
bytes4 constant internal CURRENT_CONTEXT_ADDRESS_SELECTOR = 0xeea086ba;
// function doesSignatureRequireRegularValidation(bytes32,address,bytes)
bytes4 constant internal DOES_SIGNATURE_REQUIRE_REGULAR_VALIDATION_SELECTOR = 0xc17f8ccc;
// function EIP1271_MAGIC_VALUE()
bytes4 constant internal EIP_1271_MAGIC_VALUE_SELECTOR = 0xdd885e2d;
// function EIP712_EXCHANGE_DOMAIN_HASH()
bytes4 constant internal EIP_712_EXCHANGE_DOMAIN_HASH_SELECTOR = 0xc26cfecd;
// function EIP712_EXCHANGE_DOMAIN_NAME()
bytes4 constant internal EIP_712_EXCHANGE_DOMAIN_NAME_SELECTOR = 0x63c4e8cc;
// function EIP712_EXCHANGE_DOMAIN_VERSION()
bytes4 constant internal EIP_712_EXCHANGE_DOMAIN_VERSION_SELECTOR = 0x0f01323b;
// function EIP712_ORDER_SCHEMA_HASH()
bytes4 constant internal EIP_712_ORDER_SCHEMA_HASH_SELECTOR = 0xe4588b64;
// function EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH()
bytes4 constant internal EIP_712_ZEROEX_TRANSACTION_SCHEMA_HASH_SELECTOR = 0xc148c58a;
// function executeTransaction((uint256,uint256,address,bytes),bytes)
bytes4 constant internal EXECUTE_TRANSACTION_SELECTOR = 0xcba0648a;
// function filled(bytes32)
bytes4 constant internal FILLED_SELECTOR = 0x288cdc91;
// function fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),uint256,bytes)
bytes4 constant internal FILL_ORDER_SELECTOR = 0x9b44d556;
// function fillOrderNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),uint256,bytes)
bytes4 constant internal FILL_ORDER_NO_THROW_SELECTOR = 0x01da61ae;
// function fillOrKillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),uint256,bytes)
bytes4 constant internal FILL_OR_KILL_ORDER_SELECTOR = 0xe14b58c4;
// function getAssetProxy(bytes4)
bytes4 constant internal GET_ASSET_PROXY_SELECTOR = 0x60704108;
// function getOrderHash((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes))
bytes4 constant internal GET_ORDER_HASH_SELECTOR = 0xad3449bd;
// function getOrderInfo((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes))
bytes4 constant internal GET_ORDER_INFO_SELECTOR = 0x9d3fa4b9;
// function getOrdersInfo((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[])
bytes4 constant internal GET_ORDERS_INFO_SELECTOR = 0x9dfac06d;
// function getTransactionHash((uint256,uint256,address,bytes))
bytes4 constant internal GET_TRANSACTION_HASH_SELECTOR = 0xe0456690;
// function isValidHashSignature(bytes32,address,bytes)
bytes4 constant internal IS_VALID_HASH_SIGNATURE_SELECTOR = 0x8171c407;
// function isValidOrderSignature((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),address,bytes)
bytes4 constant internal IS_VALID_ORDER_SIGNATURE_SELECTOR = 0xf813e384;
// function isValidTransactionSignature((uint256,uint256,address,bytes),address,bytes)
bytes4 constant internal IS_VALID_TRANSACTION_SIGNATURE_SELECTOR = 0xfaa8b882;
// function marketBuyOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],uint256,bytes[])
bytes4 constant internal MARKET_BUY_ORDERS_SELECTOR = 0xdb702a9c;
// function marketSellOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],uint256,bytes[])
bytes4 constant internal MARKET_SELL_ORDERS_SELECTOR = 0x52b3ca9e;
// function 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)
bytes4 constant internal MATCH_ORDERS_SELECTOR = 0x88ec79fb;
// function 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)
bytes4 constant internal MATCH_ORDERS_WITH_MAXIMAL_FILL_SELECTOR = 0xb718e292;
// function orderEpoch(address,address)
bytes4 constant internal ORDER_EPOCH_SELECTOR = 0xd9bfa73e;
// function owner()
bytes4 constant internal OWNER_SELECTOR = 0x8da5cb5b;
// function preSign(bytes32)
bytes4 constant internal PRE_SIGN_SELECTOR = 0x46c02d7a;
// function preSigned(bytes32,address)
bytes4 constant internal PRE_SIGNED_SELECTOR = 0x82c174d0;
// function registerAssetProxy(address)
bytes4 constant internal REGISTER_ASSET_PROXY_SELECTOR = 0xc585bb93;
// function setSignatureValidatorApproval(address,bool)
bytes4 constant internal SET_SIGNATURE_VALIDATOR_APPROVAL_SELECTOR = 0x77fcce68;
// function simulateDispatchTransferFromCalls(bytes[],address[],address[],uint256[])
bytes4 constant internal SIMULATE_DISPATCH_TRANSFER_FROM_CALLS_SELECTOR = 0xb04fbddd;
// function transactionsExecuted(bytes32)
bytes4 constant internal TRANSACTIONS_EXECUTED_SELECTOR = 0x0228e168;
// function transferOwnership(address)
bytes4 constant internal TRANSFER_OWNERSHIP_SELECTOR = 0xf2fde38b;
// function VERSION()
bytes4 constant internal VERSION_SELECTOR = 0xffa1ad74;
// event AssetProxyRegistered(bytes4,address)
bytes32 constant internal EVENT_ASSET_PROXY_REGISTERED_SELECTOR = 0xd2c6b762299c609bdb96520b58a49bfb80186934d4f71a86a367571a15c03194;
// event Cancel(address,address,address,bytes32,bytes,bytes)
bytes32 constant internal EVENT_CANCEL_SELECTOR = 0xdc47b3613d9fe400085f6dbdc99453462279057e6207385042827ed6b1a62cf7;
// event CancelUpTo(address,address,uint256)
bytes32 constant internal EVENT_CANCEL_UP_TO_SELECTOR = 0x82af639571738f4ebd4268fb0363d8957ebe1bbb9e78dba5ebd69eed39b154f0;
// event Fill(address,address,bytes,bytes,bytes,bytes,uint256,uint256,uint256,uint256,address,address,bytes32)
bytes32 constant internal EVENT_FILL_SELECTOR = 0xa5a8f3e79ee70e3be6330220296f9075863b936f4098d942ab107367d193a197;
// event SignatureValidatorApproval(address,address,bool)
bytes32 constant internal EVENT_SIGNATURE_VALIDATOR_APPROVAL_SELECTOR = 0xa8656e308026eeabce8f0bc18048433252318ab80ac79da0b3d3d8697dfba891;
// event TransactionExecution(bytes32)
bytes32 constant internal EVENT_TRANSACTION_EXECUTION_SELECTOR = 0xa4a7329f1dd821363067e07d359e347b4af9b1efe4b6cccf13240228af3c800d;
}

View File

@ -18,12 +18,15 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "./LibMath.sol";
import "./LibOrder.sol";
contract LibFillResults is
SafeMath
{
library LibFillResults {
using LibSafeMath for uint256;
struct BatchMatchedFillResults {
FillResults[] left; // Fill results for left orders
FillResults[] right; // Fill results for right orders
@ -45,17 +48,358 @@ contract LibFillResults is
uint256 profitInRightMakerAsset; // Profit taken from the right maker
}
/// @dev Adds properties of both FillResults instances.
/// Modifies the first FillResults instance specified.
/// @param totalFillResults Fill results instance that will be added onto.
/// @param singleFillResults Fill results instance that will be added to totalFillResults.
function _addFillResults(FillResults memory totalFillResults, FillResults memory singleFillResults)
/// @dev Calculates amounts filled and fees paid by maker and taker.
/// @param order to be filled.
/// @param takerAssetFilledAmount Amount of takerAsset that will be filled.
/// @return fillResults Amounts filled and fees paid by maker and taker.
function calculateFillResults(
LibOrder.Order memory order,
uint256 takerAssetFilledAmount
)
internal
pure
returns (FillResults memory fillResults)
{
totalFillResults.makerAssetFilledAmount = _safeAdd(totalFillResults.makerAssetFilledAmount, singleFillResults.makerAssetFilledAmount);
totalFillResults.takerAssetFilledAmount = _safeAdd(totalFillResults.takerAssetFilledAmount, singleFillResults.takerAssetFilledAmount);
totalFillResults.makerFeePaid = _safeAdd(totalFillResults.makerFeePaid, singleFillResults.makerFeePaid);
totalFillResults.takerFeePaid = _safeAdd(totalFillResults.takerFeePaid, singleFillResults.takerFeePaid);
// Compute proportional transfer amounts
fillResults.takerAssetFilledAmount = takerAssetFilledAmount;
fillResults.makerAssetFilledAmount = LibMath.safeGetPartialAmountFloor(
takerAssetFilledAmount,
order.takerAssetAmount,
order.makerAssetAmount
);
fillResults.makerFeePaid = LibMath.safeGetPartialAmountFloor(
fillResults.makerAssetFilledAmount,
order.makerAssetAmount,
order.makerFee
);
fillResults.takerFeePaid = LibMath.safeGetPartialAmountFloor(
takerAssetFilledAmount,
order.takerAssetAmount,
order.takerFee
);
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 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,
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
);
// 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);
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;
}
}

View File

@ -18,21 +18,22 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "./LibMathRichErrors.sol";
contract LibMath is
SafeMath
{
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(
function safeGetPartialAmountFloor(
uint256 numerator,
uint256 denominator,
uint256 target
@ -41,22 +42,19 @@ contract LibMath is
pure
returns (uint256 partialAmount)
{
if (_isRoundingErrorFloor(
if (isRoundingErrorFloor(
numerator,
denominator,
target
)) {
LibRichErrors._rrevert(LibMathRichErrors.RoundingError(
LibRichErrors.rrevert(LibMathRichErrors.RoundingError(
numerator,
denominator,
target
));
}
partialAmount = _safeDiv(
_safeMul(numerator, target),
denominator
);
partialAmount = numerator.safeMul(target).safeDiv(denominator);
return partialAmount;
}
@ -66,7 +64,7 @@ contract LibMath is
/// @param denominator Denominator.
/// @param target Value to calculate partial of.
/// @return Partial value of target rounded up.
function _safeGetPartialAmountCeil(
function safeGetPartialAmountCeil(
uint256 numerator,
uint256 denominator,
uint256 target
@ -75,12 +73,12 @@ contract LibMath is
pure
returns (uint256 partialAmount)
{
if (_isRoundingErrorCeil(
if (isRoundingErrorCeil(
numerator,
denominator,
target
)) {
LibRichErrors._rrevert(LibMathRichErrors.RoundingError(
LibRichErrors.rrevert(LibMathRichErrors.RoundingError(
numerator,
denominator,
target
@ -90,13 +88,10 @@ contract LibMath is
// _safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
// ceil(a / b) = floor((a + b - 1) / b)
// To implement `ceil(a / b)` using _safeDiv.
partialAmount = _safeDiv(
_safeAdd(
_safeMul(numerator, target),
_safeSub(denominator, 1)
),
denominator
);
partialAmount = numerator.safeMul(target)
.safeAdd(denominator.safeSub(1))
.safeDiv(denominator);
return partialAmount;
}
@ -105,7 +100,7 @@ contract LibMath is
/// @param denominator Denominator.
/// @param target Value to calculate partial of.
/// @return Partial value of target rounded down.
function _getPartialAmountFloor(
function getPartialAmountFloor(
uint256 numerator,
uint256 denominator,
uint256 target
@ -114,10 +109,7 @@ contract LibMath is
pure
returns (uint256 partialAmount)
{
partialAmount = _safeDiv(
_safeMul(numerator, target),
denominator
);
partialAmount = numerator.safeMul(target).safeDiv(denominator);
return partialAmount;
}
@ -126,7 +118,7 @@ contract LibMath is
/// @param denominator Denominator.
/// @param target Value to calculate partial of.
/// @return Partial value of target rounded up.
function _getPartialAmountCeil(
function getPartialAmountCeil(
uint256 numerator,
uint256 denominator,
uint256 target
@ -138,13 +130,10 @@ contract LibMath is
// _safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
// ceil(a / b) = floor((a + b - 1) / b)
// To implement `ceil(a / b)` using _safeDiv.
partialAmount = _safeDiv(
_safeAdd(
_safeMul(numerator, target),
_safeSub(denominator, 1)
),
denominator
);
partialAmount = numerator.safeMul(target)
.safeAdd(denominator.safeSub(1))
.safeDiv(denominator);
return partialAmount;
}
@ -153,7 +142,7 @@ contract LibMath is
/// @param denominator Denominator.
/// @param target Value to multiply with numerator/denominator.
/// @return Rounding error is present.
function _isRoundingErrorFloor(
function isRoundingErrorFloor(
uint256 numerator,
uint256 denominator,
uint256 target
@ -163,7 +152,7 @@ contract LibMath is
returns (bool isError)
{
if (denominator == 0) {
LibRichErrors._rrevert(LibMathRichErrors.DivisionByZeroError());
LibRichErrors.rrevert(LibMathRichErrors.DivisionByZeroError());
}
// The absolute rounding error is the difference between the rounded
@ -197,7 +186,7 @@ contract LibMath is
numerator,
denominator
);
isError = _safeMul(1000, remainder) >= _safeMul(numerator, target);
isError = remainder.safeMul(1000) >= numerator.safeMul(target);
return isError;
}
@ -206,7 +195,7 @@ contract LibMath is
/// @param denominator Denominator.
/// @param target Value to multiply with numerator/denominator.
/// @return Rounding error is present.
function _isRoundingErrorCeil(
function isRoundingErrorCeil(
uint256 numerator,
uint256 denominator,
uint256 target
@ -216,7 +205,7 @@ contract LibMath is
returns (bool isError)
{
if (denominator == 0) {
LibRichErrors._rrevert(LibMathRichErrors.DivisionByZeroError());
LibRichErrors.rrevert(LibMathRichErrors.DivisionByZeroError());
}
// See the comments in `isRoundingError`.
@ -232,8 +221,8 @@ contract LibMath is
numerator,
denominator
);
remainder = _safeSub(denominator, remainder) % denominator;
isError = _safeMul(1000, remainder) >= _safeMul(numerator, target);
remainder = denominator.safeSub(remainder) % denominator;
isError = remainder.safeMul(1000) >= numerator.safeMul(target);
return isError;
}
}

View File

@ -17,14 +17,14 @@
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "./LibEIP712ExchangeDomain.sol";
import "@0x/contracts-utils/contracts/src/LibEIP712.sol";
contract LibOrder is
LibEIP712ExchangeDomain
{
library LibOrder {
using LibOrder for Order;
// Hash for the EIP712 Order Schema:
// keccak256(abi.encodePacked(
// "Order(",
@ -44,11 +44,11 @@ contract LibOrder is
// "bytes takerFeeAssetData",
// ")"
// ))
bytes32 constant public EIP712_ORDER_SCHEMA_HASH =
bytes32 constant internal _EIP712_ORDER_SCHEMA_HASH =
0xf80322eb8376aafb64eadf8f0d7623f22130fd9491a221e902b713cb984a7534;
// A valid order remains fillable until it is expired, fully filled, or cancelled.
// An order's state is unaffected by external factors, like account balances.
// 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
@ -67,44 +67,47 @@ contract LibOrder is
address senderAddress; // Address that is allowed to call Exchange contract methods that affect this order. If set to 0, any address is allowed to call these methods.
uint256 makerAssetAmount; // Amount of makerAsset being offered by maker. Must be greater than 0.
uint256 takerAssetAmount; // Amount of takerAsset being bid on by maker. Must be greater than 0.
uint256 makerFee; // Amount of ZRX paid to feeRecipient by maker when order is filled. If set to 0, no transfer of ZRX from maker to feeRecipient will be attempted.
uint256 takerFee; // Amount of ZRX paid to feeRecipient by taker when order is filled. If set to 0, no transfer of ZRX from taker to feeRecipient will be attempted.
uint256 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 makerAsset fees. 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 takerAsset fees. 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
struct OrderInfo {
uint8 orderStatus; // Status that describes order's validity and fillability.
bytes32 orderHash; // EIP712 hash of the order (see LibOrder.getOrderHash).
bytes32 orderHash; // EIP712 typed data hash of the order (see LibOrder.getTypedDataHash).
uint256 orderTakerAssetFilledAmount; // Amount of order that has already been filled.
}
/// @dev Calculates Keccak-256 hash of the order.
/// @dev Calculates the EIP712 typed data hash of an order with a given domain separator.
/// @param order The order structure.
/// @return Keccak-256 EIP712 hash of the order.
function getOrderHash(Order memory order)
public
view
/// @return EIP712 typed data hash of the order.
function getTypedDataHash(Order memory order, bytes32 eip712ExchangeDomainHash)
internal
pure
returns (bytes32 orderHash)
{
orderHash = _hashEIP712ExchangeMessage(_hashOrder(order));
orderHash = LibEIP712.hashEIP712Message(
eip712ExchangeDomainHash,
order.getStructHash()
);
return orderHash;
}
/// @dev Calculates EIP712 hash of the order.
/// @dev Calculates EIP712 hash of the order struct.
/// @param order The order structure.
/// @return EIP712 hash of the order.
function _hashOrder(Order memory order)
/// @return EIP712 hash of the order struct.
function getStructHash(Order memory order)
internal
pure
returns (bytes32 result)
{
bytes32 schemaHash = EIP712_ORDER_SCHEMA_HASH;
bytes32 schemaHash = _EIP712_ORDER_SCHEMA_HASH;
bytes memory makerAssetData = order.makerAssetData;
bytes memory takerAssetData = order.takerAssetData;
bytes memory makerFeeAssetData = order.makerFeeAssetData;
@ -113,10 +116,10 @@ contract LibOrder is
// Assembly for more efficiently computing:
// keccak256(abi.encodePacked(
// EIP712_ORDER_SCHEMA_HASH,
// bytes32(order.makerAddress),
// bytes32(order.takerAddress),
// bytes32(order.feeRecipientAddress),
// bytes32(order.senderAddress),
// uint256(order.makerAddress),
// uint256(order.takerAddress),
// uint256(order.feeRecipientAddress),
// uint256(order.senderAddress),
// order.makerAssetAmount,
// order.takerAssetAmount,
// order.makerFee,

View File

@ -19,12 +19,13 @@
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "./LibEIP712ExchangeDomain.sol";
import "@0x/contracts-utils/contracts/src/LibEIP712.sol";
contract LibZeroExTransaction is
LibEIP712ExchangeDomain
{
library LibZeroExTransaction {
using LibZeroExTransaction for ZeroExTransaction;
// Hash for the EIP712 0x transaction schema
// keccak256(abi.encodePacked(
// "ZeroExTransaction(",
@ -34,7 +35,7 @@ contract LibZeroExTransaction is
// "bytes data",
// ")"
// ));
bytes32 constant public EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = 0x6b4c70d217b44d0ff0d3bf7aeb18eb8604c5cd06f615a4b497aeefa4f01d2775;
bytes32 constant internal _EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = 0x6b4c70d217b44d0ff0d3bf7aeb18eb8604c5cd06f615a4b497aeefa4f01d2775;
struct ZeroExTransaction {
uint256 salt; // Arbitrary number to ensure uniqueness of transaction hash.
@ -43,28 +44,31 @@ contract LibZeroExTransaction is
bytes data; // AbiV2 encoded calldata.
}
/// @dev Calculates the EIP712 hash of a 0x transaction using the domain separator of the Exchange contract.
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @return EIP712 hash of the transaction with the domain separator of this contract.
function getTransactionHash(ZeroExTransaction memory transaction)
public
view
/// @dev Calculates the EIP712 typed data hash of a transaction with a given domain separator.
/// @param transaction 0x transaction structure.
/// @return EIP712 typed data hash of the transaction.
function getTypedDataHash(ZeroExTransaction memory transaction, bytes32 eip712ExchangeDomainHash)
internal
pure
returns (bytes32 transactionHash)
{
// Hash the transaction with the domain separator of the Exchange contract.
transactionHash = _hashEIP712ExchangeMessage(_hashZeroExTransaction(transaction));
transactionHash = LibEIP712.hashEIP712Message(
eip712ExchangeDomainHash,
transaction.getStructHash()
);
return transactionHash;
}
/// @dev Calculates EIP712 hash of the 0x transaction with no domain separator.
/// @param transaction 0x transaction containing salt, signerAddress, and data.
/// @return EIP712 hash of the transaction with no domain separator.
function _hashZeroExTransaction(ZeroExTransaction memory transaction)
/// @dev Calculates EIP712 hash of the 0x transaction struct.
/// @param transaction 0x transaction structure.
/// @return EIP712 hash of the transaction struct.
function getStructHash(ZeroExTransaction memory transaction)
internal
pure
returns (bytes32 result)
{
bytes32 schemaHash = EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH;
bytes32 schemaHash = _EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH;
bytes memory data = transaction.data;
uint256 salt = transaction.salt;
uint256 expirationTimeSeconds = transaction.expirationTimeSeconds;

View File

@ -0,0 +1,36 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "../src/LibEIP712ExchangeDomain.sol";
contract TestLibEIP712ExchangeDomain is
LibEIP712ExchangeDomain
{
constructor(
uint256 chainId,
address verifyingContractAddressIfExists
)
public
LibEIP712ExchangeDomain(chainId, verifyingContractAddressIfExists)
{}
}

View File

@ -0,0 +1,74 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "../src/LibOrder.sol";
import "../src/LibFillResults.sol";
contract TestLibFillResults {
using LibFillResults for *;
function calculateFillResults(
LibOrder.Order memory order,
uint256 takerAssetFilledAmount
)
public
pure
returns (LibFillResults.FillResults memory fillResults)
{
fillResults = LibFillResults.calculateFillResults(order, takerAssetFilledAmount);
return fillResults;
}
function calculateMatchedFillResults(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,
uint256 leftOrderTakerAssetFilledAmount,
uint256 rightOrderTakerAssetFilledAmount,
bool shouldMaximallyFillOrders
)
public
pure
returns (LibFillResults.MatchedFillResults memory matchedFillResults)
{
matchedFillResults = LibFillResults.calculateMatchedFillResults(
leftOrder,
rightOrder,
leftOrderTakerAssetFilledAmount,
rightOrderTakerAssetFilledAmount,
shouldMaximallyFillOrders
);
return matchedFillResults;
}
function addFillResults(
LibFillResults.FillResults memory fillResults1,
LibFillResults.FillResults memory fillResults2
)
public
pure
returns (LibFillResults.FillResults memory totalFillResults)
{
totalFillResults = LibFillResults.addFillResults(fillResults1, fillResults2);
return totalFillResults;
}
}

View File

@ -0,0 +1,130 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "../src/LibMath.sol";
contract TestLibMath {
/// @dev Calculates partial value given a numerator and denominator.
/// Reverts if rounding error is >= 0.1%
/// @param numerator Numerator.
/// @param denominator Denominator.
/// @param target Value to calculate partial of.
/// @return Partial value of target.
function safeGetPartialAmountFloor(
uint256 numerator,
uint256 denominator,
uint256 target
)
public
pure
returns (uint256 partialAmount)
{
return LibMath.safeGetPartialAmountFloor(numerator, denominator, target);
}
/// @dev Calculates partial value given a numerator and denominator.
/// Reverts if rounding error is >= 0.1%
/// @param numerator Numerator.
/// @param denominator Denominator.
/// @param target Value to calculate partial of.
/// @return Partial value of target.
function safeGetPartialAmountCeil(
uint256 numerator,
uint256 denominator,
uint256 target
)
public
pure
returns (uint256 partialAmount)
{
return LibMath.safeGetPartialAmountCeil(numerator, denominator, target);
}
/// @dev Calculates partial value given a numerator and denominator.
/// @param numerator Numerator.
/// @param denominator Denominator.
/// @param target Value to calculate partial of.
/// @return Partial value of target.
function getPartialAmountFloor(
uint256 numerator,
uint256 denominator,
uint256 target
)
public
pure
returns (uint256 partialAmount)
{
return LibMath.getPartialAmountFloor(numerator, denominator, target);
}
/// @dev Calculates partial value given a numerator and denominator.
/// @param numerator Numerator.
/// @param denominator Denominator.
/// @param target Value to calculate partial of.
/// @return Partial value of target.
function getPartialAmountCeil(
uint256 numerator,
uint256 denominator,
uint256 target
)
public
pure
returns (uint256 partialAmount)
{
return LibMath.getPartialAmountCeil(numerator, denominator, target);
}
/// @dev Checks if rounding error >= 0.1%.
/// @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
)
public
pure
returns (bool isError)
{
return LibMath.isRoundingErrorFloor(numerator, denominator, target);
}
/// @dev Checks if rounding error >= 0.1%.
/// @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
)
public
pure
returns (bool isError)
{
return LibMath.isRoundingErrorCeil(numerator, denominator, target);
}
}

View File

@ -0,0 +1,44 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "../src/LibOrder.sol";
contract TestLibOrder {
function getTypedDataHash(LibOrder.Order memory order, bytes32 eip712ExchangeDomainHash)
public
pure
returns (bytes32 orderHash)
{
orderHash = LibOrder.getTypedDataHash(order, eip712ExchangeDomainHash);
return orderHash;
}
function getStructHash(LibOrder.Order memory order)
public
pure
returns (bytes32 result)
{
result = LibOrder.getStructHash(order);
return result;
}
}

View File

@ -0,0 +1,44 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "../src/LibZeroExTransaction.sol";
contract TestLibZeroExTransaction {
function getTypedDataHash(LibZeroExTransaction.ZeroExTransaction memory transaction, bytes32 eip712ExchangeDomainHash)
public
pure
returns (bytes32 transactionHash)
{
transactionHash = LibZeroExTransaction.getTypedDataHash(transaction, eip712ExchangeDomainHash);
return transactionHash;
}
function getStructHash(LibZeroExTransaction.ZeroExTransaction memory transaction)
public
pure
returns (bytes32 result)
{
result = LibZeroExTransaction.getStructHash(transaction);
return result;
}
}

View File

@ -1,200 +0,0 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "../src/LibEIP712ExchangeDomain.sol";
import "../src/LibMath.sol";
import "../src/LibOrder.sol";
import "../src/LibZeroExTransaction.sol";
import "../src/LibFillResults.sol";
// solhint-disable no-empty-blocks
contract TestLibs is
LibEIP712ExchangeDomain,
LibMath,
LibOrder,
LibZeroExTransaction,
LibFillResults
{
constructor (uint256 chainId)
public
LibEIP712ExchangeDomain(chainId, address(0))
{}
function getPartialAmountFloor(
uint256 numerator,
uint256 denominator,
uint256 target
)
public
pure
returns (uint256 partialAmount)
{
partialAmount = _getPartialAmountFloor(
numerator,
denominator,
target
);
return partialAmount;
}
function getPartialAmountCeil(
uint256 numerator,
uint256 denominator,
uint256 target
)
public
pure
returns (uint256 partialAmount)
{
partialAmount = _getPartialAmountCeil(
numerator,
denominator,
target
);
return partialAmount;
}
function safeGetPartialAmountFloor(
uint256 numerator,
uint256 denominator,
uint256 target
)
public
pure
returns (uint256 partialAmount)
{
partialAmount = _safeGetPartialAmountFloor(
numerator,
denominator,
target
);
return partialAmount;
}
function safeGetPartialAmountCeil(
uint256 numerator,
uint256 denominator,
uint256 target
)
public
pure
returns (uint256 partialAmount)
{
partialAmount = _safeGetPartialAmountCeil(
numerator,
denominator,
target
);
return partialAmount;
}
function isRoundingErrorFloor(
uint256 numerator,
uint256 denominator,
uint256 target
)
public
pure
returns (bool isError)
{
isError = _isRoundingErrorFloor(
numerator,
denominator,
target
);
return isError;
}
function isRoundingErrorCeil(
uint256 numerator,
uint256 denominator,
uint256 target
)
public
pure
returns (bool isError)
{
isError = _isRoundingErrorCeil(
numerator,
denominator,
target
);
return isError;
}
function getOrderSchemaHash()
public
pure
returns (bytes32)
{
return EIP712_ORDER_SCHEMA_HASH;
}
function getDomainSeparatorSchemaHash()
public
pure
returns (bytes32)
{
return EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH;
}
function getDomainSeparator()
public
view
returns (bytes32)
{
return EIP712_EXCHANGE_DOMAIN_HASH;
}
function addFillResults(FillResults memory totalFillResults, FillResults memory singleFillResults)
public
pure
returns (FillResults memory)
{
_addFillResults(totalFillResults, singleFillResults);
return totalFillResults;
}
function hashOrder(Order memory order)
public
pure
returns (bytes32)
{
return _hashOrder(order);
}
function hashZeroExTransaction(ZeroExTransaction memory transaction)
public
pure
returns (bytes32)
{
return _hashZeroExTransaction(transaction);
}
function hashEIP712ExchangeMessage(bytes32 hashStruct)
public
view
returns (bytes32)
{
return _hashEIP712ExchangeMessage(hashStruct);
}
}

View File

@ -12,7 +12,7 @@
"scripts": {
"build": "yarn pre_build && tsc -b",
"build:ci": "yarn build",
"pre_build": "run-s compile generate_contract_wrappers",
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
@ -31,11 +31,10 @@
"coverage:report:lcov": "istanbul report lcov",
"test:circleci": "yarn test",
"contracts:gen": "contracts-gen",
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
"generate-exchange-selectors": "node lib/scripts/generate-exchange-selectors.js ../../../exchange/generated-artifacts/Exchange.json ./contracts/src/LibExchangeSelectors.sol"
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
},
"config": {
"abis": "./generated-artifacts/@(LibEIP712ExchangeDomain|LibFillResults|LibMath|LibOrder|LibZeroExTransaction|TestLibs).json",
"abis": "./generated-artifacts/@(LibEIP712ExchangeDomain|LibExchangeRichErrors|LibFillResults|LibMath|LibMathRichErrors|LibOrder|LibZeroExTransaction|TestLibEIP712ExchangeDomain|TestLibFillResults|TestLibMath|TestLibOrder|TestLibZeroExTransaction).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {

View File

@ -1,162 +0,0 @@
import { AbiDefinition, ContractAbi, DataItem, EventAbi, MethodAbi } from 'ethereum-types';
import * as ethUtil from 'ethereumjs-util';
import * as fs from 'fs';
import * as _ from 'lodash';
import * as path from 'path';
import * as process from 'process';
const keccak256 = ethUtil.sha3;
const ARGS = process.argv.slice(2);
const INDENT = ' ';
const LINEBREAK = '\n';
const VISIBILITY = 'internal';
interface ParsedContract {
methods: {
[functionName: string]: Array<{
selector: string;
signature: string;
}>;
};
events: {
[eventName: string]: {
selector: string;
signature: string;
};
};
}
// tslint:disable: no-console
(() => {
const [exchangeArtifactsFile, outputFile] = ARGS;
const exchangeArtifacts = require(exchangeArtifactsFile);
const contractName = path.basename(outputFile, '.sol');
const parsedContract = parseContract(exchangeArtifacts.compilerOutput.abi);
const contractDefinition = defineContract(contractName, parsedContract);
const preamble = extractOutputFilePreamble(outputFile);
const outputFileContents = `${preamble}${contractDefinition}${LINEBREAK}`;
fs.writeFileSync(outputFile, outputFileContents);
console.log(`Wrote exchange selectors to "${path.resolve(outputFile)}."`);
})();
function parseContract(abi: ContractAbi): ParsedContract {
const parsedContract: ParsedContract = {
methods: {},
events: {},
};
for (const abiItem of abi) {
if (isMethodAbi(abiItem)) {
const name = abiItem.name;
const signature = `${name}(${encodeMethodInputs(abiItem.inputs)})`;
const selector = `0x${keccak256(signature)
.slice(0, 4)
.toString('hex')}`;
if (parsedContract.methods[name] === undefined) {
parsedContract.methods[name] = [];
}
parsedContract.methods[name].push({
selector,
signature,
});
} else if (isEventAbi(abiItem)) {
const name = abiItem.name;
const signature = `${name}(${encodeMethodInputs(abiItem.inputs)})`;
const selector = `0x${keccak256(signature).toString('hex')}`;
parsedContract.events[name] = {
selector,
signature,
};
}
}
return parsedContract;
}
function isMethodAbi(abiItem: AbiDefinition): abiItem is MethodAbi {
return abiItem.type === 'function';
}
function isEventAbi(abiItem: AbiDefinition): abiItem is EventAbi {
return abiItem.type === 'event';
}
function defineContract(contractName: string, parsedContract: ParsedContract): string {
const constantDefinitions = [];
// Define function selectors.
const sortedMethodNames = _.sortBy(_.keys(parsedContract.methods), name => name.toLowerCase());
for (const name of sortedMethodNames) {
const methods = parsedContract.methods[name];
for (let idx = 0; idx < methods.length; idx++) {
const constantLines = generateFunctionSelectorConstantDefinition(
name,
methods[idx].signature,
methods[idx].selector,
idx,
methods.length,
);
constantDefinitions.push(constantLines);
}
}
// Define event selectors.
const sortedEventNames = _.sortBy(_.keys(parsedContract.events), name => name.toLowerCase());
for (const name of sortedEventNames) {
const event = parsedContract.events[name];
const constantLines = generateEventSelectorConstantDefinition(name, event.signature, event.selector);
constantDefinitions.push(constantLines);
}
return [
`contract ${contractName} {`,
`${INDENT}// solhint-disable max-line-length`,
'',
constantDefinitions
.map(lines => lines.map(line => `${INDENT}${line}`))
.map(lines => lines.join(LINEBREAK))
.join(`${LINEBREAK}${LINEBREAK}`),
`}`,
].join(LINEBREAK);
}
function extractOutputFilePreamble(outputFile: string): string {
const preambleLines = [];
const outputFileLines = fs.readFileSync(outputFile, 'utf-8').split(/\r?\n/);
for (const line of outputFileLines) {
if (/^\s*contract\s+[a-zA-Z][a-zA-Z0-9_]+/.test(line)) {
preambleLines.push('');
break;
}
preambleLines.push(line);
}
return preambleLines.join(LINEBREAK);
}
function generateFunctionSelectorConstantDefinition(
name: string,
signature: string,
selector: string,
idx: number,
total: number,
): string[] {
const varName = _.snakeCase(total === 1 ? name : `${name}_${idx + 1}`).toUpperCase();
return [`// function ${signature}`, `bytes4 constant ${VISIBILITY} ${varName}_SELECTOR = ${selector};`];
}
function generateEventSelectorConstantDefinition(name: string, signature: string, selector: string): string[] {
const varName = _.snakeCase(name).toUpperCase();
return [`// event ${signature}`, `bytes32 constant ${VISIBILITY} EVENT_${varName}_SELECTOR = ${selector};`];
}
function encodeMethodInputs(inputs?: DataItem[]): string {
if (inputs === undefined) {
throw new Error('encodeMethodInputs: inputs are undefined');
}
const types = [];
for (const input of inputs) {
if (input.type === 'tuple') {
types.push(`(${encodeMethodInputs(input.components)})`);
} else if (input.type === 'tuple[]') {
types.push(`(${encodeMethodInputs(input.components)})[]`);
} else {
types.push(input.type);
}
}
return types.join(',');
}

View File

@ -6,16 +6,28 @@
import { ContractArtifact } from 'ethereum-types';
import * as LibEIP712ExchangeDomain from '../generated-artifacts/LibEIP712ExchangeDomain.json';
import * as LibExchangeRichErrors from '../generated-artifacts/LibExchangeRichErrors.json';
import * as LibFillResults from '../generated-artifacts/LibFillResults.json';
import * as LibMath from '../generated-artifacts/LibMath.json';
import * as LibMathRichErrors from '../generated-artifacts/LibMathRichErrors.json';
import * as LibOrder from '../generated-artifacts/LibOrder.json';
import * as LibZeroExTransaction from '../generated-artifacts/LibZeroExTransaction.json';
import * as TestLibs from '../generated-artifacts/TestLibs.json';
import * as TestLibEIP712ExchangeDomain from '../generated-artifacts/TestLibEIP712ExchangeDomain.json';
import * as TestLibFillResults from '../generated-artifacts/TestLibFillResults.json';
import * as TestLibMath from '../generated-artifacts/TestLibMath.json';
import * as TestLibOrder from '../generated-artifacts/TestLibOrder.json';
import * as TestLibZeroExTransaction from '../generated-artifacts/TestLibZeroExTransaction.json';
export const artifacts = {
LibEIP712ExchangeDomain: LibEIP712ExchangeDomain as ContractArtifact,
LibExchangeRichErrors: LibExchangeRichErrors as ContractArtifact,
LibFillResults: LibFillResults as ContractArtifact,
LibMath: LibMath as ContractArtifact,
LibMathRichErrors: LibMathRichErrors as ContractArtifact,
LibOrder: LibOrder as ContractArtifact,
LibZeroExTransaction: LibZeroExTransaction as ContractArtifact,
TestLibs: TestLibs as ContractArtifact,
TestLibEIP712ExchangeDomain: TestLibEIP712ExchangeDomain as ContractArtifact,
TestLibFillResults: TestLibFillResults as ContractArtifact,
TestLibMath: TestLibMath as ContractArtifact,
TestLibOrder: TestLibOrder as ContractArtifact,
TestLibZeroExTransaction: TestLibZeroExTransaction as ContractArtifact,
};

View File

@ -1,6 +1,6 @@
import { ReferenceFunctions } from '@0x/contracts-utils';
import { LibMathRevertErrors } from '@0x/order-utils';
import { FillResults } from '@0x/types';
import { FillResults, OrderWithoutDomain } from '@0x/types';
import { BigNumber } from '@0x/utils';
const { safeAdd, safeSub, safeMul, safeDiv } = ReferenceFunctions;
@ -87,3 +87,22 @@ export function addFillResults(a: FillResults, b: FillResults): FillResults {
takerFeePaid: safeAdd(a.takerFeePaid, b.takerFeePaid),
};
}
/**
* Calculates amounts filled and fees paid by maker and taker.
*/
export function calculateFillResults(order: OrderWithoutDomain, takerAssetFilledAmount: BigNumber): FillResults {
const makerAssetFilledAmount = safeGetPartialAmountFloor(
takerAssetFilledAmount,
order.takerAssetAmount,
order.makerAssetAmount,
);
const makerFeePaid = safeGetPartialAmountFloor(makerAssetFilledAmount, order.makerAssetAmount, order.makerFee);
const takerFeePaid = safeGetPartialAmountFloor(takerAssetFilledAmount, order.takerAssetAmount, order.takerFee);
return {
makerAssetFilledAmount,
takerAssetFilledAmount,
makerFeePaid,
takerFeePaid,
};
}

View File

@ -4,8 +4,14 @@
* -----------------------------------------------------------------------------
*/
export * from '../generated-wrappers/lib_e_i_p712_exchange_domain';
export * from '../generated-wrappers/lib_exchange_rich_errors';
export * from '../generated-wrappers/lib_fill_results';
export * from '../generated-wrappers/lib_math';
export * from '../generated-wrappers/lib_math_rich_errors';
export * from '../generated-wrappers/lib_order';
export * from '../generated-wrappers/lib_zero_ex_transaction';
export * from '../generated-wrappers/test_libs';
export * from '../generated-wrappers/test_lib_e_i_p712_exchange_domain';
export * from '../generated-wrappers/test_lib_fill_results';
export * from '../generated-wrappers/test_lib_math';
export * from '../generated-wrappers/test_lib_order';
export * from '../generated-wrappers/test_lib_zero_ex_transaction';

View File

@ -1,212 +0,0 @@
import { addressUtils, chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils, orderHashUtils, transactionHashUtils } from '@0x/order-utils';
import { constants as orderConstants } from '@0x/order-utils/lib/src/constants';
import { Order, ZeroExTransaction } from '@0x/types';
import { BigNumber, providerUtils } from '@0x/utils';
import * as chai from 'chai';
import * as ethUtil from 'ethereumjs-util';
import { TestLibsContract } from '../src';
import { artifacts } from '../src/artifacts';
import { stringifySchema } from './utils';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('Exchange libs', () => {
let chainId: number;
let order: Order;
let transaction: ZeroExTransaction;
let libs: TestLibsContract;
let libsAlternateChain: TestLibsContract;
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
const [makerAddress, takerAddress, senderAddress, feeRecipientAddress, signerAddress] = accounts.slice(0, 5);
chainId = await providerUtils.getChainIdAsync(provider);
libs = await TestLibsContract.deployFrom0xArtifactAsync(
artifacts.TestLibs,
provider,
txDefaults,
new BigNumber(chainId),
);
// Deploy a version with a different chain ID.
const alternateChainId = chainId + 1;
libsAlternateChain = await TestLibsContract.deployFrom0xArtifactAsync(
artifacts.TestLibs,
provider,
txDefaults,
new BigNumber(alternateChainId),
);
const domain = {
verifyingContractAddress: libs.address,
chainId,
};
order = {
...constants.STATIC_ORDER_PARAMS,
makerAddress,
takerAddress,
senderAddress,
feeRecipientAddress,
makerAssetData: assetDataUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()),
takerAssetData: assetDataUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()),
makerFeeAssetData: assetDataUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()),
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()),
salt: new BigNumber(0),
expirationTimeSeconds: new BigNumber(0),
domain,
};
transaction = {
signerAddress,
salt: new BigNumber(0),
expirationTimeSeconds: new BigNumber(0),
data: constants.NULL_BYTES,
domain,
};
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
// Note(albrow): These tests are designed to be supplemental to the
// combinatorial tests in test/exchange/internal. They test specific edge
// cases that are not covered by the combinatorial tests.
describe('LibMath', () => {
describe('isRoundingError', () => {
it('should return true if there is a rounding error of 0.1%', async () => {
const numerator = new BigNumber(20);
const denominator = new BigNumber(999);
const target = new BigNumber(50);
// rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1%
const isRoundingError = await libs.isRoundingErrorFloor.callAsync(numerator, denominator, target);
expect(isRoundingError).to.be.true();
});
it('should return false if there is a rounding of 0.09%', async () => {
const numerator = new BigNumber(20);
const denominator = new BigNumber(9991);
const target = new BigNumber(500);
// rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09%
const isRoundingError = await libs.isRoundingErrorFloor.callAsync(numerator, denominator, target);
expect(isRoundingError).to.be.false();
});
it('should return true if there is a rounding error of 0.11%', async () => {
const numerator = new BigNumber(20);
const denominator = new BigNumber(9989);
const target = new BigNumber(500);
// rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011%
const isRoundingError = await libs.isRoundingErrorFloor.callAsync(numerator, denominator, target);
expect(isRoundingError).to.be.true();
});
});
describe('isRoundingErrorCeil', () => {
it('should return true if there is a rounding error of 0.1%', async () => {
const numerator = new BigNumber(20);
const denominator = new BigNumber(1001);
const target = new BigNumber(50);
// rounding error = (ceil(20*50/1001) - (20*50/1001)) / (20*50/1001) = 0.1%
const isRoundingError = await libs.isRoundingErrorCeil.callAsync(numerator, denominator, target);
expect(isRoundingError).to.be.true();
});
it('should return false if there is a rounding of 0.09%', async () => {
const numerator = new BigNumber(20);
const denominator = new BigNumber(10009);
const target = new BigNumber(500);
// rounding error = (ceil(20*500/10009) - (20*500/10009)) / (20*500/10009) = 0.09%
const isRoundingError = await libs.isRoundingErrorCeil.callAsync(numerator, denominator, target);
expect(isRoundingError).to.be.false();
});
it('should return true if there is a rounding error of 0.11%', async () => {
const numerator = new BigNumber(20);
const denominator = new BigNumber(10011);
const target = new BigNumber(500);
// rounding error = (ceil(20*500/10011) - (20*500/10011)) / (20*500/10011) = 0.11%
const isRoundingError = await libs.isRoundingErrorCeil.callAsync(numerator, denominator, target);
expect(isRoundingError).to.be.true();
});
});
});
describe('LibOrder', () => {
describe('getOrderHash', () => {
it('should return the correct orderHash', async () => {
const orderHashHex = await libs.getOrderHash.callAsync(order);
expect(orderHashUtils.getOrderHashHex(order)).to.be.equal(orderHashHex);
});
it('orderHash should differ if chainId is different', async () => {
const orderHashHex1 = await libsAlternateChain.getOrderHash.callAsync(order);
const orderHashHex2 = await libs.getOrderHash.callAsync(order);
expect(orderHashHex1).to.be.not.equal(orderHashHex2);
});
});
});
describe('LibZeroExTransaction', () => {
describe('EIP712ZeroExTransactionSchemaHash', () => {
it('should return the correct schema hash', async () => {
const schemaHash = await libs.EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH.callAsync();
const schemaString =
'ZeroExTransaction(uint256 salt,uint256 expirationTimeSeconds,address signerAddress,bytes data)';
const expectedSchemaHash = ethUtil.addHexPrefix(ethUtil.bufferToHex(ethUtil.sha3(schemaString)));
expect(schemaHash).to.equal(expectedSchemaHash);
});
});
describe('getTransactionHash', () => {
it('should return the correct transactionHash', async () => {
const transactionHash = await libs.getTransactionHash.callAsync(transaction);
const expectedTransactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(transactionHash).to.equal(expectedTransactionHash);
});
it('transactionHash should differ if chainId is different', async () => {
const transactionHash1 = await libsAlternateChain.getTransactionHash.callAsync(transaction);
const transactionHash2 = await libs.getTransactionHash.callAsync(transaction);
expect(transactionHash1).to.not.equal(transactionHash2);
});
});
});
describe('LibEIP712', () => {
it('should return the correct domain separator schema hash', async () => {
const schema = stringifySchema(orderConstants.DEFAULT_DOMAIN_SCHEMA);
const expectedSchemaHash = ethUtil.bufferToHex(ethUtil.sha3(Buffer.from(schema)));
const actualSchemaHash = await libs.getDomainSeparatorSchemaHash.callAsync();
expect(actualSchemaHash).to.be.equal(expectedSchemaHash);
});
it('should return the correct order schema hash', async () => {
const schema = stringifySchema(orderConstants.EXCHANGE_ORDER_SCHEMA);
const expectedSchemaHash = ethUtil.bufferToHex(ethUtil.sha3(Buffer.from(schema)));
const actualSchemaHash = await libs.getOrderSchemaHash.callAsync();
expect(actualSchemaHash).to.be.equal(expectedSchemaHash);
});
it('should return the correct domain separator', async () => {
const schema = stringifySchema(orderConstants.DEFAULT_DOMAIN_SCHEMA);
const schemaHash = ethUtil.sha3(Buffer.from(schema));
const payload = Buffer.concat([
schemaHash,
ethUtil.sha3(Buffer.from(orderConstants.EXCHANGE_DOMAIN_NAME)),
ethUtil.sha3(Buffer.from(orderConstants.EXCHANGE_DOMAIN_VERSION)),
ethUtil.setLengthLeft(ethUtil.toBuffer(chainId), 32),
ethUtil.setLengthLeft(ethUtil.toBuffer(libs.address), 32),
]);
const expectedDomain = ethUtil.bufferToHex(ethUtil.sha3(payload));
const actualDomain = await libs.getDomainSeparator.callAsync();
expect(actualDomain).to.be.equal(expectedDomain);
});
it('should return a different domain separator if chainId is different', async () => {
const domain1 = await libsAlternateChain.getDomainSeparator.callAsync();
const domain2 = await libs.getDomainSeparator.callAsync();
expect(domain1).to.be.not.equal(domain2);
});
});
});

View File

@ -1,65 +1,49 @@
import { blockchainTests, constants, describe, expect, hexRandom } from '@0x/contracts-test-utils';
import { addressUtils, blockchainTests, constants, expect } from '@0x/contracts-test-utils';
import { BigNumber, signTypedDataUtils } from '@0x/utils';
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
import { artifacts, TestLibsContract } from '../src';
import { artifacts, TestLibEIP712ExchangeDomainContract } from '../src';
blockchainTests('LibEIP712ExchangeDomain', env => {
let libsContract: TestLibsContract;
let exchangeDomainHash: string;
const CHAIN_ID = 1337;
// Random generator functions
const randomHash = () => hexRandom(constants.WORD_LENGTH);
/**
* Tests a specific instance of EIP712 message hashing.
* @param lib The LibEIP712 contract to call.
* @param domainHash The hash of the EIP712 domain of this instance.
* @param hashStruct The hash of the struct of this instance.
*/
async function testHashEIP712MessageAsync(hashStruct: string): Promise<void> {
// Remove the hex-prefix from the exchangeDomainHash and the hashStruct
const unprefixedHashStruct = hashStruct.slice(2, hashStruct.length);
// Hash the provided input to get the expected hash
const input = '0x1901'.concat(exchangeDomainHash, unprefixedHashStruct);
const expectedHash = '0x'.concat(ethUtil.sha3(input).toString('hex'));
// Get the actual hash by calling the smart contract
const actualHash = await libsContract.hashEIP712ExchangeMessage.callAsync(hashStruct);
// Verify that the actual hash matches the expected hash
expect(actualHash).to.be.eq(expectedHash);
}
before(async () => {
libsContract = await TestLibsContract.deployFrom0xArtifactAsync(
artifacts.TestLibs,
describe('constructor', () => {
it('should calculate the correct domain hash when verifyingContractAddressIfExists is set to null', async () => {
const chainId = 1;
const libEIP712ExchangeDomainContract = await TestLibEIP712ExchangeDomainContract.deployFrom0xArtifactAsync(
artifacts.TestLibEIP712ExchangeDomain,
env.provider,
env.txDefaults,
new BigNumber(CHAIN_ID),
new BigNumber(chainId),
constants.NULL_ADDRESS,
);
// Generate the domain hash of 0x Exchange V3
exchangeDomainHash = signTypedDataUtils
.generateDomainHash({
name: '0x Protocol',
version: '3.0.0',
chainId: CHAIN_ID,
verifyingContractAddress: libsContract.address,
})
.toString('hex');
const domain = {
verifyingContractAddress: libEIP712ExchangeDomainContract.address,
chainId,
name: constants.EIP712_DOMAIN_NAME,
version: constants.EIP712_DOMAIN_VERSION,
};
const expectedDomainHash = ethUtil.bufferToHex(signTypedDataUtils.generateDomainHash(domain));
const actualDomainHash = await libEIP712ExchangeDomainContract.EIP712_EXCHANGE_DOMAIN_HASH.callAsync();
expect(actualDomainHash).to.be.equal(expectedDomainHash);
});
describe('hashEIP712ExchangeMessage', () => {
it('should correctly match an empty hash', async () => {
await testHashEIP712MessageAsync(constants.NULL_BYTES32);
});
it('should correctly match a non-empty hash', async () => {
await testHashEIP712MessageAsync(randomHash());
it('should calculate the correct domain hash when verifyingContractAddressIfExists is set to a non-null address', async () => {
const chainId = 1;
const verifyingContractAddress = addressUtils.generatePseudoRandomAddress();
const libEIP712ExchangeDomainContract = await TestLibEIP712ExchangeDomainContract.deployFrom0xArtifactAsync(
artifacts.TestLibEIP712ExchangeDomain,
env.provider,
env.txDefaults,
new BigNumber(chainId),
verifyingContractAddress,
);
const domain = {
verifyingContractAddress,
chainId,
name: constants.EIP712_DOMAIN_NAME,
version: constants.EIP712_DOMAIN_VERSION,
};
const expectedDomainHash = ethUtil.bufferToHex(signTypedDataUtils.generateDomainHash(domain));
const actualDomainHash = await libEIP712ExchangeDomainContract.EIP712_EXCHANGE_DOMAIN_HASH.callAsync();
expect(actualDomainHash).to.be.equal(expectedDomainHash);
});
});
});

File diff suppressed because it is too large Load Diff

View File

@ -9,19 +9,17 @@ import {
import { LibMathRevertErrors } from '@0x/order-utils';
import { BigNumber, SafeMathRevertErrors } from '@0x/utils';
import { artifacts, ReferenceFunctions, TestLibsContract } from '../src';
import { artifacts, ReferenceFunctions, TestLibMathContract } from '../src';
blockchainTests('LibMath', env => {
const CHAIN_ID = 1337;
const { ONE_ETHER, MAX_UINT256, MAX_UINT256_ROOT, ZERO_AMOUNT } = constants;
let libsContract: TestLibsContract;
let libsContract: TestLibMathContract;
before(async () => {
libsContract = await TestLibsContract.deployFrom0xArtifactAsync(
artifacts.TestLibs,
libsContract = await TestLibMathContract.deployFrom0xArtifactAsync(
artifacts.TestLibMath,
env.provider,
env.txDefaults,
new BigNumber(CHAIN_ID),
);
});

View File

@ -2,13 +2,13 @@ import { blockchainTests, constants, describe, expect, hexRandom } from '@0x/con
import { eip712Utils, orderHashUtils } from '@0x/order-utils';
import { Order } from '@0x/types';
import { BigNumber, signTypedDataUtils } from '@0x/utils';
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
import { artifacts, TestLibsContract } from '../src';
import { artifacts, TestLibOrderContract } from '../src';
blockchainTests('LibOrder', env => {
const CHAIN_ID = 1337;
let libsContract: TestLibsContract;
let libOrderContract: TestLibOrderContract;
const randomAddress = () => hexRandom(constants.ADDRESS_LENGTH);
const randomHash = () => hexRandom(constants.WORD_LENGTH);
@ -37,38 +37,44 @@ blockchainTests('LibOrder', env => {
};
before(async () => {
libsContract = await TestLibsContract.deployFrom0xArtifactAsync(
artifacts.TestLibs,
libOrderContract = await TestLibOrderContract.deployFrom0xArtifactAsync(
artifacts.TestLibOrder,
env.provider,
env.txDefaults,
new BigNumber(CHAIN_ID),
);
});
/**
* Tests the `getOrderHash()` function against a reference hash.
* Tests the `getTypedDataHash()` function against a reference hash.
*/
async function testGetOrderHashAsync(order: Order): Promise<void> {
async function testGetTypedDataHashAsync(order: Order): Promise<void> {
const expectedHash = orderHashUtils.getOrderHashHex(order);
const actualHash = await libsContract.getOrderHash.callAsync(order);
const domainHash = ethUtil.bufferToHex(
signTypedDataUtils.generateDomainHash({
...order.domain,
name: constants.EIP712_DOMAIN_NAME,
version: constants.EIP712_DOMAIN_VERSION,
}),
);
const actualHash = await libOrderContract.getTypedDataHash.callAsync(order, domainHash);
expect(actualHash).to.be.eq(expectedHash);
}
describe('getOrderHash', () => {
describe('getTypedDataHash', () => {
it('should correctly hash an empty order', async () => {
await testGetOrderHashAsync({
await testGetTypedDataHashAsync({
...EMPTY_ORDER,
domain: {
verifyingContractAddress: libsContract.address,
chainId: 1337,
...EMPTY_ORDER.domain,
verifyingContractAddress: libOrderContract.address,
},
});
});
it('should correctly hash a non-empty order', async () => {
await testGetOrderHashAsync({
await testGetTypedDataHashAsync({
domain: {
verifyingContractAddress: libsContract.address,
verifyingContractAddress: libOrderContract.address,
chainId: 1337,
},
senderAddress: randomAddress(),
@ -87,27 +93,46 @@ blockchainTests('LibOrder', env => {
expirationTimeSeconds: randomUint256(),
});
});
it('orderHash should differ if the domain hash is different', async () => {
const domainHash1 = ethUtil.bufferToHex(
signTypedDataUtils.generateDomainHash({
...EMPTY_ORDER.domain,
name: constants.EIP712_DOMAIN_NAME,
version: constants.EIP712_DOMAIN_VERSION,
}),
);
const domainHash2 = ethUtil.bufferToHex(
signTypedDataUtils.generateDomainHash({
...EMPTY_ORDER.domain,
name: constants.EIP712_DOMAIN_NAME,
version: constants.EIP712_DOMAIN_VERSION,
chainId: 1337,
}),
);
const orderHashHex1 = await libOrderContract.getTypedDataHash.callAsync(EMPTY_ORDER, domainHash1);
const orderHashHex2 = await libOrderContract.getTypedDataHash.callAsync(EMPTY_ORDER, domainHash2);
expect(orderHashHex1).to.be.not.equal(orderHashHex2);
});
});
/**
* Tests the `_hashOrder()` function against a reference hash.
* Tests the `getStructHash()` function against a reference hash.
*/
async function testHashOrderAsync(order: Order): Promise<void> {
async function testGetStructHashAsync(order: Order): Promise<void> {
const typedData = eip712Utils.createOrderTypedData(order);
const expectedHash = '0x'.concat(
signTypedDataUtils.generateTypedDataHashWithoutDomain(typedData).toString('hex'),
);
const actualHash = await libsContract.hashOrder.callAsync(order);
const expectedHash = ethUtil.bufferToHex(signTypedDataUtils.generateTypedDataHashWithoutDomain(typedData));
const actualHash = await libOrderContract.getStructHash.callAsync(order);
expect(actualHash).to.be.eq(expectedHash);
}
describe('hashOrder', () => {
describe('getStructHash', () => {
it('should correctly hash an empty order', async () => {
await testHashOrderAsync(EMPTY_ORDER);
await testGetStructHashAsync(EMPTY_ORDER);
});
it('should correctly hash a non-empty order', async () => {
await testHashOrderAsync({
await testGetStructHashAsync({
// The domain is not used in this test, so it's okay if it is left empty.
domain: {
verifyingContractAddress: constants.NULL_ADDRESS,

View File

@ -1,14 +1,14 @@
import { blockchainTests, constants, describe, expect, hexRandom } from '@0x/contracts-test-utils';
import { eip712Utils } from '@0x/order-utils';
import { eip712Utils, transactionHashUtils } from '@0x/order-utils';
import { ZeroExTransaction } from '@0x/types';
import { BigNumber, signTypedDataUtils } from '@0x/utils';
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
import { artifacts, TestLibsContract } from '../src';
import { artifacts, TestLibZeroExTransactionContract } from '../src';
blockchainTests('LibZeroExTransaction', env => {
const CHAIN_ID = 1337;
let libsContract: TestLibsContract;
let libZeroExTransactionContract: TestLibZeroExTransactionContract;
const randomAddress = () => hexRandom(constants.ADDRESS_LENGTH);
const randomHash = () => hexRandom(constants.WORD_LENGTH);
@ -27,68 +27,97 @@ blockchainTests('LibZeroExTransaction', env => {
};
before(async () => {
libsContract = await TestLibsContract.deployFrom0xArtifactAsync(
artifacts.TestLibs,
libZeroExTransactionContract = await TestLibZeroExTransactionContract.deployFrom0xArtifactAsync(
artifacts.TestLibZeroExTransaction,
env.provider,
env.txDefaults,
new BigNumber(CHAIN_ID),
);
});
/**
* Tests the `getTransactionHash()` function against a reference hash.
* Tests the `getTypedDataHash()` function against a reference hash.
*/
async function testGetTransactionHashAsync(transaction: ZeroExTransaction): Promise<void> {
const typedData = eip712Utils.createZeroExTransactionTypedData(transaction);
const expectedHash = '0x'.concat(signTypedDataUtils.generateTypedDataHash(typedData).toString('hex'));
const actualHash = await libsContract.getTransactionHash.callAsync(transaction);
async function testGetTypedDataHashAsync(transaction: ZeroExTransaction): Promise<void> {
const expectedHash = transactionHashUtils.getTransactionHashHex(transaction);
const domainHash = ethUtil.bufferToHex(
signTypedDataUtils.generateDomainHash({
...transaction.domain,
name: constants.EIP712_DOMAIN_NAME,
version: constants.EIP712_DOMAIN_VERSION,
}),
);
const actualHash = await libZeroExTransactionContract.getTypedDataHash.callAsync(transaction, domainHash);
expect(actualHash).to.be.eq(expectedHash);
}
describe('getTransactionHash', () => {
describe('getTypedDataHash', () => {
it('should correctly hash an empty transaction', async () => {
await testGetTransactionHashAsync({
await testGetTypedDataHashAsync({
...EMPTY_TRANSACTION,
domain: {
verifyingContractAddress: libsContract.address,
chainId: 1337,
...EMPTY_TRANSACTION.domain,
verifyingContractAddress: libZeroExTransactionContract.address,
},
});
});
it('should correctly hash a non-empty transaction', async () => {
await testGetTransactionHashAsync({
await testGetTypedDataHashAsync({
salt: randomUint256(),
expirationTimeSeconds: randomUint256(),
signerAddress: randomAddress(),
data: randomAssetData(),
domain: {
verifyingContractAddress: libsContract.address,
chainId: 1337,
...EMPTY_TRANSACTION.domain,
verifyingContractAddress: libZeroExTransactionContract.address,
},
});
});
it('transactionHash should differ if the domain hash is different', async () => {
const domainHash1 = ethUtil.bufferToHex(
signTypedDataUtils.generateDomainHash({
...EMPTY_TRANSACTION.domain,
name: constants.EIP712_DOMAIN_NAME,
version: constants.EIP712_DOMAIN_VERSION,
}),
);
const domainHash2 = ethUtil.bufferToHex(
signTypedDataUtils.generateDomainHash({
...EMPTY_TRANSACTION.domain,
name: constants.EIP712_DOMAIN_NAME,
version: constants.EIP712_DOMAIN_VERSION,
chainId: 1337,
}),
);
const transactionHashHex1 = await libZeroExTransactionContract.getTypedDataHash.callAsync(
EMPTY_TRANSACTION,
domainHash1,
);
const transactionHashHex2 = await libZeroExTransactionContract.getTypedDataHash.callAsync(
EMPTY_TRANSACTION,
domainHash2,
);
expect(transactionHashHex1).to.be.not.equal(transactionHashHex2);
});
});
/**
* Tests the `_hashZeroExTransaction()` function against a reference hash.
* Tests the `getStructHash()` function against a reference hash.
*/
async function testHashZeroExTransactionAsync(transaction: ZeroExTransaction): Promise<void> {
async function testGetStructHashAsync(transaction: ZeroExTransaction): Promise<void> {
const typedData = eip712Utils.createZeroExTransactionTypedData(transaction);
const expectedHash = '0x'.concat(
signTypedDataUtils.generateTypedDataHashWithoutDomain(typedData).toString('hex'),
);
const actualHash = await libsContract.hashZeroExTransaction.callAsync(transaction);
const expectedHash = ethUtil.bufferToHex(signTypedDataUtils.generateTypedDataHashWithoutDomain(typedData));
const actualHash = await libZeroExTransactionContract.getStructHash.callAsync(transaction);
expect(actualHash).to.be.eq(expectedHash);
}
describe('hashOrder', () => {
describe('getStructHash', () => {
it('should correctly hash an empty transaction', async () => {
await testHashZeroExTransactionAsync(EMPTY_TRANSACTION);
await testGetStructHashAsync(EMPTY_TRANSACTION);
});
it('should correctly hash a non-empty transaction', async () => {
await testHashZeroExTransactionAsync({
await testGetStructHashAsync({
salt: randomUint256(),
expirationTimeSeconds: randomUint256(),
signerAddress: randomAddress(),

View File

@ -4,11 +4,17 @@
"include": ["./src/**/*", "./test/**/*", "./scripts/**/*", "./generated-wrappers/**/*"],
"files": [
"generated-artifacts/LibEIP712ExchangeDomain.json",
"generated-artifacts/LibExchangeRichErrors.json",
"generated-artifacts/LibFillResults.json",
"generated-artifacts/LibMath.json",
"generated-artifacts/LibMathRichErrors.json",
"generated-artifacts/LibOrder.json",
"generated-artifacts/LibZeroExTransaction.json",
"generated-artifacts/TestLibs.json"
"generated-artifacts/TestLibEIP712ExchangeDomain.json",
"generated-artifacts/TestLibFillResults.json",
"generated-artifacts/TestLibMath.json",
"generated-artifacts/TestLibOrder.json",
"generated-artifacts/TestLibZeroExTransaction.json"
],
"exclude": ["./deploy/solc/solc_bin"]
}

View File

@ -153,6 +153,26 @@
{
"note": "Add (semi) automated reentrancy tests and remove manual ones",
"pr": 2042
},
{
"note": "Refactor to use new `LibFillResults`, `LibOrder`, `LibZeroExTransaction`, and `LibMath` to libraries",
"pr": 2055
},
{
"note": "Remove `LibExchangeRichErrors` and `IExchangeRichErrors`",
"pr": 2055
},
{
"note": "Use built in selectors instead of `LibExchangeSelectors` constants",
"pr": 2055
},
{
"note": "Move `calculateFillResults` and `calculateMatchedFillResults` to `LibFillResults` in `exchange-libs` package",
"pr": 2055
},
{
"note": "Compile and export all contracts, artifacts, and wrappers by default",
"pr": 2055
}
]
},

View File

@ -22,27 +22,5 @@
]
}
}
},
"contracts": [
"examples/ExchangeWrapper.sol",
"examples/Whitelist.sol",
"src/Exchange.sol",
"src/interfaces/IAssetProxyDispatcher.sol",
"src/interfaces/IEIP1271Wallet.sol",
"src/interfaces/IExchange.sol",
"src/interfaces/IExchangeCore.sol",
"src/interfaces/IMatchOrders.sol",
"src/interfaces/ISignatureValidator.sol",
"src/interfaces/ITransactions.sol",
"src/interfaces/IWallet.sol",
"src/interfaces/IWrapperFunctions.sol",
"test/IsolatedExchange.sol",
"test/ReentrancyTester.sol",
"test/TestAssetProxyDispatcher.sol",
"test/TestExchangeInternals.sol",
"test/TestLibExchangeRichErrorDecoder.sol",
"test/TestSignatureValidator.sol",
"test/TestValidatorWallet.sol",
"test/TestWrapperFunctions.sol"
]
}
}

View File

@ -19,18 +19,18 @@
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
import "./MixinMatchOrders.sol";
import "./MixinSignatureValidator.sol";
import "./MixinWrapperFunctions.sol";
import "./MixinTransferSimulator.sol";
// solhint-disable no-empty-blocks
// MixinAssetProxyDispatcher, MixinExchangeCore, MixinExchangeRichErrors,
// MixinAssetProxyDispatcher, MixinExchangeCore, MixinSignatureValidator,
// and MixinTransactions are all inherited via the other Mixins that are
// used.
contract Exchange is
MixinSignatureValidator,
LibEIP712ExchangeDomain,
MixinMatchOrders,
MixinWrapperFunctions,
MixinTransferSimulator
@ -42,12 +42,5 @@ contract Exchange is
constructor (uint256 chainId)
public
LibEIP712ExchangeDomain(chainId, address(0))
MixinExchangeCore()
MixinMatchOrders()
MixinSignatureValidator()
MixinTransactions()
MixinAssetProxyDispatcher()
MixinTransferSimulator()
MixinWrapperFunctions()
{}
}

View File

@ -21,10 +21,9 @@ 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";
import "./interfaces/IExchangeRichErrors.sol";
import "./LibExchangeRichErrors.sol";
contract MixinAssetProxyDispatcher is
@ -47,7 +46,7 @@ contract MixinAssetProxyDispatcher is
bytes4 assetProxyId = IAssetProxy(assetProxy).getProxyId();
address currentAssetProxy = assetProxies[assetProxyId];
if (currentAssetProxy != address(0)) {
LibRichErrors._rrevert(LibExchangeRichErrors.AssetProxyExistsError(currentAssetProxy));
LibRichErrors.rrevert(LibExchangeRichErrors.AssetProxyExistsError(currentAssetProxy));
}
// Add asset proxy and log registration.
@ -88,8 +87,8 @@ contract MixinAssetProxyDispatcher is
if (amount > 0 && from != to) {
// Ensure assetData length is valid
if (assetData.length <= 3) {
LibRichErrors._rrevert(LibExchangeRichErrors.AssetProxyDispatchError(
IExchangeRichErrors.AssetProxyDispatchErrorCodes.INVALID_ASSET_DATA_LENGTH,
LibRichErrors.rrevert(LibExchangeRichErrors.AssetProxyDispatchError(
LibExchangeRichErrors.AssetProxyDispatchErrorCodes.INVALID_ASSET_DATA_LENGTH,
orderHash,
assetData
));
@ -101,8 +100,8 @@ contract MixinAssetProxyDispatcher is
// Ensure that assetProxy exists
if (assetProxy == address(0)) {
LibRichErrors._rrevert(LibExchangeRichErrors.AssetProxyDispatchError(
IExchangeRichErrors.AssetProxyDispatchErrorCodes.UNKNOWN_ASSET_PROXY,
LibRichErrors.rrevert(LibExchangeRichErrors.AssetProxyDispatchError(
LibExchangeRichErrors.AssetProxyDispatchErrorCodes.UNKNOWN_ASSET_PROXY,
orderHash,
assetData
));
@ -122,7 +121,7 @@ contract MixinAssetProxyDispatcher is
// If the transaction did not succeed, revert with the returned data.
if (!didSucceed) {
LibRichErrors._rrevert(LibExchangeRichErrors.AssetProxyTransferError(
LibRichErrors.rrevert(LibExchangeRichErrors.AssetProxyTransferError(
orderHash,
assetData,
revertData

View File

@ -18,27 +18,26 @@
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-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 "./interfaces/IExchangeRichErrors.sol";
import "./LibExchangeRichErrors.sol";
import "./MixinAssetProxyDispatcher.sol";
import "./MixinSignatureValidator.sol";
contract MixinExchangeCore is
LibEIP712ExchangeDomain,
IExchangeCore,
IExchangeRichErrors,
LibMath,
LibFillResults,
MixinAssetProxyDispatcher,
MixinSignatureValidator
{
using LibBytes for bytes;
using LibOrder for LibOrder.Order;
using LibSafeMath for uint256;
// Mapping of orderHash => amount of takerAsset already bought by maker
mapping (bytes32 => uint256) public filled;
@ -68,7 +67,7 @@ contract MixinExchangeCore is
// Ensure orderEpoch is monotonically increasing
if (newOrderEpoch <= oldOrderEpoch) {
LibRichErrors._rrevert(LibExchangeRichErrors.OrderEpochError(
LibRichErrors.rrevert(LibExchangeRichErrors.OrderEpochError(
makerAddress,
orderSenderAddress,
oldOrderEpoch
@ -90,13 +89,13 @@ contract MixinExchangeCore is
/// @param signature Proof that order has been created by maker.
/// @return Amounts filled and fees paid by maker and taker.
function fillOrder(
Order memory order,
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
public
nonReentrant
returns (FillResults memory fillResults)
returns (LibFillResults.FillResults memory fillResults)
{
fillResults = _fillOrder(
order,
@ -109,7 +108,7 @@ contract MixinExchangeCore is
/// @dev After calling, the order can not be filled anymore.
/// Throws if order is invalid or sender does not have permission to cancel.
/// @param order Order to cancel. Order must be OrderStatus.FILLABLE.
function cancelOrder(Order memory order)
function cancelOrder(LibOrder.Order memory order)
public
nonReentrant
{
@ -120,13 +119,13 @@ contract MixinExchangeCore is
/// @param order Order to gather information on.
/// @return OrderInfo Information about the order and its state.
/// See LibOrder.OrderInfo for a complete description.
function getOrderInfo(Order memory order)
function getOrderInfo(LibOrder.Order memory order)
public
view
returns (OrderInfo memory orderInfo)
returns (LibOrder.OrderInfo memory orderInfo)
{
// Compute the order hash
orderInfo.orderHash = getOrderHash(order);
orderInfo.orderHash = order.getTypedDataHash(EIP712_EXCHANGE_DOMAIN_HASH);
// Fetch filled amount
orderInfo.orderTakerAssetFilledAmount = filled[orderInfo.orderHash];
@ -136,7 +135,7 @@ contract MixinExchangeCore is
// edge cases in the supporting infrastructure because they have
// an 'infinite' price when computed by a simple division.
if (order.makerAssetAmount == 0) {
orderInfo.orderStatus = uint8(OrderStatus.INVALID_MAKER_ASSET_AMOUNT);
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.INVALID_MAKER_ASSET_AMOUNT);
return orderInfo;
}
@ -145,35 +144,35 @@ contract MixinExchangeCore is
// Instead of distinguishing between unfilled and filled zero taker
// amount orders, we choose not to support them.
if (order.takerAssetAmount == 0) {
orderInfo.orderStatus = uint8(OrderStatus.INVALID_TAKER_ASSET_AMOUNT);
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.INVALID_TAKER_ASSET_AMOUNT);
return orderInfo;
}
// Validate order availability
if (orderInfo.orderTakerAssetFilledAmount >= order.takerAssetAmount) {
orderInfo.orderStatus = uint8(OrderStatus.FULLY_FILLED);
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.FULLY_FILLED);
return orderInfo;
}
// Validate order expiration
// solhint-disable-next-line not-rely-on-time
if (block.timestamp >= order.expirationTimeSeconds) {
orderInfo.orderStatus = uint8(OrderStatus.EXPIRED);
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.EXPIRED);
return orderInfo;
}
// Check if order has been cancelled
if (cancelled[orderInfo.orderHash]) {
orderInfo.orderStatus = uint8(OrderStatus.CANCELLED);
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.CANCELLED);
return orderInfo;
}
if (orderEpoch[order.makerAddress][order.senderAddress] > order.salt) {
orderInfo.orderStatus = uint8(OrderStatus.CANCELLED);
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.CANCELLED);
return orderInfo;
}
// All other statuses are ruled out: order is Fillable
orderInfo.orderStatus = uint8(OrderStatus.FILLABLE);
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.FILLABLE);
return orderInfo;
}
@ -183,15 +182,15 @@ contract MixinExchangeCore is
/// @param signature Proof that order has been created by maker.
/// @return Amounts filled and fees paid by maker and taker.
function _fillOrder(
Order memory order,
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
internal
returns (FillResults memory fillResults)
returns (LibFillResults.FillResults memory fillResults)
{
// Fetch order info
OrderInfo memory orderInfo = getOrderInfo(order);
LibOrder.OrderInfo memory orderInfo = getOrderInfo(order);
// Fetch taker address
address takerAddress = _getCurrentContextAddress();
@ -205,11 +204,11 @@ contract MixinExchangeCore is
);
// Get amount of takerAsset to fill
uint256 remainingTakerAssetAmount = _safeSub(order.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount);
uint256 takerAssetFilledAmount = _min256(takerAssetFillAmount, remainingTakerAssetAmount);
uint256 remainingTakerAssetAmount = order.takerAssetAmount.safeSub(orderInfo.orderTakerAssetFilledAmount);
uint256 takerAssetFilledAmount = LibSafeMath.min256(takerAssetFillAmount, remainingTakerAssetAmount);
// Compute proportional fill amounts
fillResults = _calculateFillResults(order, takerAssetFilledAmount);
fillResults = LibFillResults.calculateFillResults(order, takerAssetFilledAmount);
bytes32 orderHash = orderInfo.orderHash;
@ -236,17 +235,17 @@ contract MixinExchangeCore is
/// @dev After calling, the order can not be filled anymore.
/// Throws if order is invalid or sender does not have permission to cancel.
/// @param order Order to cancel. Order must be OrderStatus.FILLABLE.
function _cancelOrder(Order memory order)
function _cancelOrder(LibOrder.Order memory order)
internal
{
// Fetch current order status
OrderInfo memory orderInfo = getOrderInfo(order);
LibOrder.OrderInfo memory orderInfo = getOrderInfo(order);
// Validate context
_assertValidCancel(order, orderInfo);
// Noop if order is already unfillable
if (orderInfo.orderStatus != uint8(OrderStatus.FILLABLE)) {
if (orderInfo.orderStatus != uint8(LibOrder.OrderStatus.FILLABLE)) {
return;
}
@ -259,16 +258,16 @@ contract MixinExchangeCore is
/// @param takerAddress Address of taker who filled the order.
/// @param orderTakerAssetFilledAmount Amount of order already filled.
function _updateFilledState(
Order memory order,
LibOrder.Order memory order,
address takerAddress,
bytes32 orderHash,
uint256 orderTakerAssetFilledAmount,
FillResults memory fillResults
LibFillResults.FillResults memory fillResults
)
internal
{
// Update state
filled[orderHash] = _safeAdd(orderTakerAssetFilledAmount, fillResults.takerAssetFilledAmount);
filled[orderHash] = orderTakerAssetFilledAmount.safeAdd(fillResults.takerAssetFilledAmount);
// Emit a Fill() event THE HARD WAY to avoid a stack overflow.
// All this logic is equivalent to:
@ -295,7 +294,7 @@ contract MixinExchangeCore is
/// @param order that was cancelled.
/// @param orderHash Hash of order that was cancelled.
function _updateCancelledState(
Order memory order,
LibOrder.Order memory order,
bytes32 orderHash
)
internal
@ -320,8 +319,8 @@ contract MixinExchangeCore is
/// @param takerAddress Address of order taker.
/// @param signature Proof that the orders was created by its maker.
function _assertFillableOrder(
Order memory order,
OrderInfo memory orderInfo,
LibOrder.Order memory order,
LibOrder.OrderInfo memory orderInfo,
address takerAddress,
bytes memory signature
)
@ -329,17 +328,17 @@ contract MixinExchangeCore is
view
{
// An order can only be filled if its status is FILLABLE.
if (orderInfo.orderStatus != uint8(OrderStatus.FILLABLE)) {
LibRichErrors._rrevert(LibExchangeRichErrors.OrderStatusError(
if (orderInfo.orderStatus != uint8(LibOrder.OrderStatus.FILLABLE)) {
LibRichErrors.rrevert(LibExchangeRichErrors.OrderStatusError(
orderInfo.orderHash,
OrderStatus(orderInfo.orderStatus)
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.InvalidSenderError(
LibRichErrors.rrevert(LibExchangeRichErrors.InvalidSenderError(
orderInfo.orderHash, msg.sender
));
}
@ -348,7 +347,7 @@ contract MixinExchangeCore is
// Validate taker is allowed to fill this order
if (order.takerAddress != address(0)) {
if (order.takerAddress != takerAddress) {
LibRichErrors._rrevert(LibExchangeRichErrors.InvalidTakerError(
LibRichErrors.rrevert(LibExchangeRichErrors.InvalidTakerError(
orderInfo.orderHash, takerAddress
));
}
@ -367,8 +366,8 @@ contract MixinExchangeCore is
order,
orderInfo.orderHash,
signature)) {
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureError(
SignatureErrorCodes.BAD_SIGNATURE,
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
LibExchangeRichErrors.SignatureErrorCodes.BAD_SIGNATURE,
orderInfo.orderHash,
makerAddress,
signature
@ -381,8 +380,8 @@ contract MixinExchangeCore is
/// @param order to be cancelled.
/// @param orderInfo OrderStatus, orderHash, and amount already filled of order.
function _assertValidCancel(
Order memory order,
OrderInfo memory orderInfo
LibOrder.Order memory order,
LibOrder.OrderInfo memory orderInfo
)
internal
view
@ -390,50 +389,17 @@ contract MixinExchangeCore is
// Validate sender is allowed to cancel this order
if (order.senderAddress != address(0)) {
if (order.senderAddress != msg.sender) {
LibRichErrors._rrevert(LibExchangeRichErrors.InvalidSenderError(orderInfo.orderHash, msg.sender));
LibRichErrors.rrevert(LibExchangeRichErrors.InvalidSenderError(orderInfo.orderHash, msg.sender));
}
}
// Validate transaction signed by maker
address makerAddress = _getCurrentContextAddress();
if (order.makerAddress != makerAddress) {
LibRichErrors._rrevert(LibExchangeRichErrors.InvalidMakerError(orderInfo.orderHash, makerAddress));
LibRichErrors.rrevert(LibExchangeRichErrors.InvalidMakerError(orderInfo.orderHash, makerAddress));
}
}
/// @dev Calculates amounts filled and fees paid by maker and taker.
/// @param order to be filled.
/// @param takerAssetFilledAmount Amount of takerAsset that will be filled.
/// @return fillResults Amounts filled and fees paid by maker and taker.
function _calculateFillResults(
Order memory order,
uint256 takerAssetFilledAmount
)
internal
pure
returns (FillResults memory fillResults)
{
// Compute proportional transfer amounts
fillResults.takerAssetFilledAmount = takerAssetFilledAmount;
fillResults.makerAssetFilledAmount = _safeGetPartialAmountFloor(
takerAssetFilledAmount,
order.takerAssetAmount,
order.makerAssetAmount
);
fillResults.makerFeePaid = _safeGetPartialAmountFloor(
fillResults.makerAssetFilledAmount,
order.makerAssetAmount,
order.makerFee
);
fillResults.takerFeePaid = _safeGetPartialAmountFloor(
takerAssetFilledAmount,
order.takerAssetAmount,
order.takerFee
);
return fillResults;
}
/// @dev Settles an order by transferring assets between counterparties.
/// @param orderHash The order hash.
/// @param order Order struct containing order specifications.

View File

@ -16,14 +16,10 @@ 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/ReentrancyGuard.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
import "./interfaces/IAssetProxyDispatcher.sol";
import "./interfaces/IExchangeRichErrors.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeRichErrors.sol";
import "./interfaces/IMatchOrders.sol";
import "./interfaces/ITransactions.sol";
import "./LibExchangeRichErrors.sol";
import "./MixinExchangeCore.sol";
@ -32,6 +28,7 @@ contract MixinMatchOrders is
IMatchOrders
{
using LibBytes for bytes;
using LibSafeMath for uint256;
/// @dev Match complementary orders that have a profitable spread.
/// Each order is filled at their respective price point, and
@ -88,93 +85,6 @@ contract MixinMatchOrders is
);
}
/// @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 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,
bool shouldMaximallyFillOrders
)
public
pure
returns (LibFillResults.MatchedFillResults memory matchedFillResults)
{
// Derive maker asset amounts for left & right orders, given store taker assert amounts
uint256 leftTakerAssetAmountRemaining = _safeSub(leftOrder.takerAssetAmount, leftOrderTakerAssetFilledAmount);
uint256 leftMakerAssetAmountRemaining = _safeGetPartialAmountFloor(
leftOrder.makerAssetAmount,
leftOrder.takerAssetAmount,
leftTakerAssetAmountRemaining
);
uint256 rightTakerAssetAmountRemaining = _safeSub(rightOrder.takerAssetAmount, rightOrderTakerAssetFilledAmount);
uint256 rightMakerAssetAmountRemaining = _safeGetPartialAmountFloor(
rightOrder.makerAssetAmount,
rightOrder.takerAssetAmount,
rightTakerAssetAmountRemaining
);
// Maximally fill the orders and pay out profits to the matcher in one or both of the maker assets.
if (shouldMaximallyFillOrders) {
_calculateMatchedFillResultsWithMaximalFill(
matchedFillResults,
leftOrder,
rightOrder,
leftMakerAssetAmountRemaining,
leftTakerAssetAmountRemaining,
rightMakerAssetAmountRemaining,
rightTakerAssetAmountRemaining
);
} else {
_calculateMatchedFillResults(
matchedFillResults,
leftOrder,
rightOrder,
leftMakerAssetAmountRemaining,
leftTakerAssetAmountRemaining,
rightMakerAssetAmountRemaining,
rightTakerAssetAmountRemaining
);
}
// Compute fees for left order
matchedFillResults.left.makerFeePaid = _safeGetPartialAmountFloor(
matchedFillResults.left.makerAssetFilledAmount,
leftOrder.makerAssetAmount,
leftOrder.makerFee
);
matchedFillResults.left.takerFeePaid = _safeGetPartialAmountFloor(
matchedFillResults.left.takerAssetFilledAmount,
leftOrder.takerAssetAmount,
leftOrder.takerFee
);
// Compute fees for right order
matchedFillResults.right.makerFeePaid = _safeGetPartialAmountFloor(
matchedFillResults.right.makerAssetFilledAmount,
rightOrder.makerAssetAmount,
rightOrder.makerFee
);
matchedFillResults.right.takerFeePaid = _safeGetPartialAmountFloor(
matchedFillResults.right.takerAssetFilledAmount,
rightOrder.takerAssetAmount,
rightOrder.takerFee
);
// Return fill results
return matchedFillResults;
}
/// @dev 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.
@ -234,9 +144,13 @@ contract MixinMatchOrders is
/// @dev Validates context for matchOrders. Succeeds or throws.
/// @param leftOrder First order to match.
/// @param rightOrder Second order to match.
/// @param leftOrderInfo OrderStatus, orderHash, and amount already filled of leftOrder.
/// @param rightOrderInfo OrderStatus, orderHash, and amount already filled of rightOrder.
function _assertValidMatch(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder
LibOrder.Order memory rightOrder,
LibOrder.OrderInfo memory leftOrderInfo,
LibOrder.OrderInfo memory rightOrderInfo
)
internal
view
@ -249,340 +163,15 @@ contract MixinMatchOrders is
// AND
// <rightOrder.makerAssetAmount> / <rightOrder.takerAssetAmount> >= <leftOrder.takerAssetAmount> / <leftOrder.makerAssetAmount>
// These equations can be combined to get the following:
if (_safeMul(leftOrder.makerAssetAmount, rightOrder.makerAssetAmount) <
_safeMul(leftOrder.takerAssetAmount, rightOrder.takerAssetAmount)) {
LibRichErrors._rrevert(LibExchangeRichErrors.NegativeSpreadError(
getOrderHash(leftOrder),
getOrderHash(rightOrder)
if (leftOrder.makerAssetAmount.safeMul(rightOrder.makerAssetAmount) <
leftOrder.takerAssetAmount.safeMul(rightOrder.takerAssetAmount)) {
LibRichErrors.rrevert(LibExchangeRichErrors.NegativeSpreadError(
leftOrderInfo.orderHash,
rightOrderInfo.orderHash
));
}
}
/// @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 matchedFillResults The MatchedFillResults struct to update with fill result calculations.
/// @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.
function _calculateMatchedFillResults(
MatchedFillResults memory matchedFillResults,
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,
uint256 leftMakerAssetAmountRemaining,
uint256 leftTakerAssetAmountRemaining,
uint256 rightMakerAssetAmountRemaining,
uint256 rightTakerAssetAmountRemaining
)
internal
pure
{
// 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
_calculateCompleteRightFill(
matchedFillResults,
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 = _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.
_calculateCompleteFillBoth(
matchedFillResults,
leftMakerAssetAmountRemaining,
leftTakerAssetAmountRemaining,
rightMakerAssetAmountRemaining,
rightTakerAssetAmountRemaining
);
}
// Calculate amount given to taker
matchedFillResults.profitInLeftMakerAsset = _safeSub(
matchedFillResults.left.makerAssetFilledAmount,
matchedFillResults.right.takerAssetFilledAmount
);
}
/// @dev Calculates part of the matched fill results for a given situation using the maximal fill order matching
/// strategy.
/// @param matchedFillResults The MatchedFillResults struct to update with fill result calculations.
/// @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.
function _calculateMatchedFillResultsWithMaximalFill(
MatchedFillResults memory matchedFillResults,
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,
uint256 leftMakerAssetAmountRemaining,
uint256 leftTakerAssetAmountRemaining,
uint256 rightMakerAssetAmountRemaining,
uint256 rightTakerAssetAmountRemaining
)
internal
pure
{
// 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
_calculateCompleteRightFill(
matchedFillResults,
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 = _safeGetPartialAmountFloor(
rightOrder.makerAssetAmount,
rightOrder.takerAssetAmount,
leftMakerAssetAmountRemaining
);
matchedFillResults.right.takerAssetFilledAmount = leftMakerAssetAmountRemaining;
} else {
// Case 3: The right and left orders are fully filled
_calculateCompleteFillBoth(
matchedFillResults,
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 = _safeSub(
matchedFillResults.left.makerAssetFilledAmount,
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 = _safeSub(
matchedFillResults.right.makerAssetFilledAmount,
matchedFillResults.left.takerAssetFilledAmount
);
}
}
/// @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 matchedFillResults The fill results object to populate with calculations.
/// @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.
function _calculateCompleteFillBoth(
MatchedFillResults memory matchedFillResults,
uint256 leftMakerAssetAmountRemaining,
uint256 leftTakerAssetAmountRemaining,
uint256 rightMakerAssetAmountRemaining,
uint256 rightTakerAssetAmountRemaining
)
internal
pure
{
// Calculate the fully filled results for both orders.
matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining;
matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining;
matchedFillResults.right.makerAssetFilledAmount = rightMakerAssetAmountRemaining;
matchedFillResults.right.takerAssetFilledAmount = rightTakerAssetAmountRemaining;
}
/// @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 matchedFillResults The fill results object to populate with calculations.
/// @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.
function _calculateCompleteRightFill(
MatchedFillResults memory matchedFillResults,
LibOrder.Order memory leftOrder,
uint256 rightMakerAssetAmountRemaining,
uint256 rightTakerAssetAmountRemaining
)
internal
pure
{
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 = _safeGetPartialAmountFloor(
leftOrder.makerAssetAmount,
leftOrder.takerAssetAmount,
rightMakerAssetAmountRemaining
);
}
/// @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 leftFeeRecipientAddress = leftOrder.feeRecipientAddress;
address rightFeeRecipientAddress = rightOrder.feeRecipientAddress;
// Right maker asset -> left maker
_dispatchTransferFrom(
rightOrderHash,
rightOrder.makerAssetData,
rightOrder.makerAddress,
leftOrder.makerAddress,
matchedFillResults.left.takerAssetFilledAmount
);
// Left maker asset -> right maker
_dispatchTransferFrom(
leftOrderHash,
leftOrder.makerAssetData,
leftOrder.makerAddress,
rightOrder.makerAddress,
matchedFillResults.right.takerAssetFilledAmount
);
// Right maker fee -> right fee recipient
_dispatchTransferFrom(
rightOrderHash,
rightOrder.makerFeeAssetData,
rightOrder.makerAddress,
rightFeeRecipientAddress,
matchedFillResults.right.makerFeePaid
);
// Left maker fee -> left fee recipient
_dispatchTransferFrom(
leftOrderHash,
leftOrder.makerFeeAssetData,
leftOrder.makerAddress,
leftFeeRecipientAddress,
matchedFillResults.left.makerFeePaid
);
// Settle taker profits.
_dispatchTransferFrom(
leftOrderHash,
leftOrder.makerAssetData,
leftOrder.makerAddress,
takerAddress,
matchedFillResults.profitInLeftMakerAsset
);
_dispatchTransferFrom(
rightOrderHash,
rightOrder.makerAssetData,
rightOrder.makerAddress,
takerAddress,
matchedFillResults.profitInRightMakerAsset
);
// 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,
_safeAdd(
matchedFillResults.left.takerFeePaid,
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
);
}
}
/// @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.
@ -606,25 +195,25 @@ contract MixinMatchOrders is
{
// Ensure that the left and right orders have nonzero lengths.
if (leftOrders.length == 0) {
LibRichErrors._rrevert(LibExchangeRichErrors.BatchMatchOrdersError(
BatchMatchOrdersErrorCodes.ZERO_LEFT_ORDERS
LibRichErrors.rrevert(LibExchangeRichErrors.BatchMatchOrdersError(
LibExchangeRichErrors.BatchMatchOrdersErrorCodes.ZERO_LEFT_ORDERS
));
}
if (rightOrders.length == 0) {
LibRichErrors._rrevert(LibExchangeRichErrors.BatchMatchOrdersError(
BatchMatchOrdersErrorCodes.ZERO_RIGHT_ORDERS
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(
BatchMatchOrdersErrorCodes.INVALID_LENGTH_LEFT_SIGNATURES
LibRichErrors.rrevert(LibExchangeRichErrors.BatchMatchOrdersError(
LibExchangeRichErrors.BatchMatchOrdersErrorCodes.INVALID_LENGTH_LEFT_SIGNATURES
));
}
if (rightOrders.length != rightSignatures.length) {
LibRichErrors._rrevert(LibExchangeRichErrors.BatchMatchOrdersError(
BatchMatchOrdersErrorCodes.INVALID_LENGTH_RIGHT_SIGNATURES
LibRichErrors.rrevert(LibExchangeRichErrors.BatchMatchOrdersError(
LibExchangeRichErrors.BatchMatchOrdersErrorCodes.INVALID_LENGTH_RIGHT_SIGNATURES
));
}
@ -656,33 +245,29 @@ contract MixinMatchOrders is
);
// Update the orderInfo structs with the updated takerAssetFilledAmount
leftOrderInfo.orderTakerAssetFilledAmount = _safeAdd(
leftOrderInfo.orderTakerAssetFilledAmount,
leftOrderInfo.orderTakerAssetFilledAmount = leftOrderInfo.orderTakerAssetFilledAmount.safeAdd(
matchResults.left.takerAssetFilledAmount
);
rightOrderInfo.orderTakerAssetFilledAmount = _safeAdd(
rightOrderInfo.orderTakerAssetFilledAmount,
rightOrderInfo.orderTakerAssetFilledAmount = rightOrderInfo.orderTakerAssetFilledAmount.safeAdd(
matchResults.right.takerAssetFilledAmount
);
// Aggregate the new fill results with the previous fill results for the current orders.
_addFillResults(
leftFillResults = LibFillResults.addFillResults(
leftFillResults,
matchResults.left
);
_addFillResults(
rightFillResults = LibFillResults.addFillResults(
rightFillResults,
matchResults.right
);
// Update the profit in the left and right maker assets using the profits from
// the match.
batchMatchedFillResults.profitInLeftMakerAsset = _safeAdd(
batchMatchedFillResults.profitInLeftMakerAsset,
batchMatchedFillResults.profitInLeftMakerAsset = batchMatchedFillResults.profitInLeftMakerAsset.safeAdd(
matchResults.profitInLeftMakerAsset
);
batchMatchedFillResults.profitInRightMakerAsset = _safeAdd(
batchMatchedFillResults.profitInRightMakerAsset,
batchMatchedFillResults.profitInRightMakerAsset = batchMatchedFillResults.profitInRightMakerAsset.safeAdd(
matchResults.profitInRightMakerAsset
);
@ -779,10 +364,15 @@ contract MixinMatchOrders is
takerAddress,
rightSignature
);
_assertValidMatch(leftOrder, rightOrder);
_assertValidMatch(
leftOrder,
rightOrder,
leftOrderInfo,
rightOrderInfo
);
// Compute proportional fill amounts
matchedFillResults = calculateMatchedFillResults(
matchedFillResults = LibFillResults.calculateMatchedFillResults(
leftOrder,
rightOrder,
leftOrderInfo.orderTakerAssetFilledAmount,
@ -818,4 +408,112 @@ contract MixinMatchOrders is
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 leftFeeRecipientAddress = leftOrder.feeRecipientAddress;
address rightFeeRecipientAddress = rightOrder.feeRecipientAddress;
// Right maker asset -> left maker
_dispatchTransferFrom(
rightOrderHash,
rightOrder.makerAssetData,
rightOrder.makerAddress,
leftOrder.makerAddress,
matchedFillResults.left.takerAssetFilledAmount
);
// Left maker asset -> right maker
_dispatchTransferFrom(
leftOrderHash,
leftOrder.makerAssetData,
leftOrder.makerAddress,
rightOrder.makerAddress,
matchedFillResults.right.takerAssetFilledAmount
);
// Right maker fee -> right fee recipient
_dispatchTransferFrom(
rightOrderHash,
rightOrder.makerFeeAssetData,
rightOrder.makerAddress,
rightFeeRecipientAddress,
matchedFillResults.right.makerFeePaid
);
// Left maker fee -> left fee recipient
_dispatchTransferFrom(
leftOrderHash,
leftOrder.makerFeeAssetData,
leftOrder.makerAddress,
leftFeeRecipientAddress,
matchedFillResults.left.makerFeePaid
);
// Settle taker profits.
_dispatchTransferFrom(
leftOrderHash,
leftOrder.makerAssetData,
leftOrder.makerAddress,
takerAddress,
matchedFillResults.profitInLeftMakerAsset
);
_dispatchTransferFrom(
rightOrderHash,
rightOrder.makerAssetData,
rightOrder.makerAddress,
takerAddress,
matchedFillResults.profitInRightMakerAsset
);
// 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
);
}
}
}

View File

@ -25,23 +25,24 @@ import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-utils/contracts/src/ReentrancyGuard.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/IExchangeRichErrors.sol";
import "./interfaces/ISignatureValidator.sol";
import "./LibExchangeRichErrors.sol";
import "./MixinTransactions.sol";
contract MixinSignatureValidator is
ReentrancyGuard,
LibEIP712ExchangeDomain,
LibEIP1271,
LibOrder,
LibZeroExTransaction,
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)"))
@ -109,8 +110,8 @@ contract MixinSignatureValidator is
signatureType == SignatureType.Validator ||
signatureType == SignatureType.EIP1271Wallet
) {
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureError(
IExchangeRichErrors.SignatureErrorCodes.INAPPROPRIATE_SIGNATURE_TYPE,
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
LibExchangeRichErrors.SignatureErrorCodes.INAPPROPRIATE_SIGNATURE_TYPE,
hash,
signerAddress,
signature
@ -129,14 +130,14 @@ contract MixinSignatureValidator is
/// @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(
Order memory order,
LibOrder.Order memory order,
bytes memory signature
)
public
view
returns (bool isValid)
{
bytes32 orderHash = getOrderHash(order);
bytes32 orderHash = order.getTypedDataHash(EIP712_EXCHANGE_DOMAIN_HASH);
return _isValidOrderWithHashSignature(
order,
orderHash,
@ -149,14 +150,14 @@ contract MixinSignatureValidator is
/// @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(
ZeroExTransaction memory transaction,
LibZeroExTransaction.ZeroExTransaction memory transaction,
bytes memory signature
)
public
view
returns (bool isValid)
{
bytes32 transactionHash = getTransactionHash(transaction);
bytes32 transactionHash = transaction.getTypedDataHash(EIP712_EXCHANGE_DOMAIN_HASH);
isValid = _isValidTransactionWithHashSignature(
transaction,
transactionHash,
@ -198,7 +199,7 @@ contract MixinSignatureValidator is
/// @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(
Order memory order,
LibOrder.Order memory order,
bytes32 orderHash,
bytes memory signature
)
@ -246,7 +247,7 @@ contract MixinSignatureValidator is
/// @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(
ZeroExTransaction memory transaction,
LibZeroExTransaction.ZeroExTransaction memory transaction,
bytes32 transactionHash,
bytes memory signature
)
@ -305,8 +306,8 @@ contract MixinSignatureValidator is
// a correctly formatted but incorrect signature.
if (signatureType == SignatureType.Invalid) {
if (signature.length != 1) {
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureError(
IExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
LibExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
hash,
signerAddress,
signature
@ -317,8 +318,8 @@ contract MixinSignatureValidator is
// Signature using EIP712
} else if (signatureType == SignatureType.EIP712) {
if (signature.length != 66) {
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureError(
IExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
LibExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
hash,
signerAddress,
signature
@ -338,8 +339,8 @@ contract MixinSignatureValidator is
// Signed using web3.eth_sign
} else if (signatureType == SignatureType.EthSign) {
if (signature.length != 66) {
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureError(
IExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
LibExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
hash,
signerAddress,
signature
@ -388,8 +389,8 @@ contract MixinSignatureValidator is
// Disallow address zero because it is ecrecover() returns zero on
// failure.
if (signerAddress == address(0)) {
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureError(
IExchangeRichErrors.SignatureErrorCodes.INVALID_SIGNER,
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
LibExchangeRichErrors.SignatureErrorCodes.INVALID_SIGNER,
hash,
signerAddress,
signature
@ -397,8 +398,8 @@ contract MixinSignatureValidator is
}
if (signature.length == 0) {
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureError(
IExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
LibExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
hash,
signerAddress,
signature
@ -410,8 +411,8 @@ contract MixinSignatureValidator is
// Ensure signature is supported
if (signatureTypeRaw >= uint8(SignatureType.NSignatureTypes)) {
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureError(
IExchangeRichErrors.SignatureErrorCodes.UNSUPPORTED,
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
LibExchangeRichErrors.SignatureErrorCodes.UNSUPPORTED,
hash,
signerAddress,
signature
@ -424,8 +425,8 @@ contract MixinSignatureValidator is
// it an explicit option. This aids testing and analysis. It is
// also the initialization value for the enum type.
if (SignatureType(signatureTypeRaw) == SignatureType.Illegal) {
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureError(
IExchangeRichErrors.SignatureErrorCodes.ILLEGAL,
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
LibExchangeRichErrors.SignatureErrorCodes.ILLEGAL,
hash,
signerAddress,
signature
@ -479,7 +480,7 @@ contract MixinSignatureValidator is
return returnData.readBytes4(0) == LEGACY_WALLET_MAGIC_VALUE;
}
// Static call to verifier failed.
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureWalletError(
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureWalletError(
hash,
walletAddress,
signature,
@ -533,7 +534,7 @@ contract MixinSignatureValidator is
return returnData.readBytes4(0) == EIP1271_MAGIC_VALUE;
}
// Static call to verifier failed.
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureWalletError(
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureWalletError(
hash,
walletAddress,
signature,
@ -569,7 +570,7 @@ contract MixinSignatureValidator is
address validatorAddress = signature.readAddress(signatureLength - 21);
// Ensure signer has approved validator.
if (!allowedValidators[signerAddress][validatorAddress]) {
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureValidatorNotApprovedError(
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureValidatorNotApprovedError(
signerAddress,
validatorAddress
));
@ -597,7 +598,7 @@ contract MixinSignatureValidator is
return returnData.readBytes4(0) == EIP1271_MAGIC_VALUE;
}
// Static call to verifier failed.
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureValidatorError(
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureValidatorError(
hash,
signerAddress,
validatorAddress,

View File

@ -20,18 +20,20 @@ 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 "./interfaces/IExchangeRichErrors.sol";
import "./interfaces/ITransactions.sol";
import "./interfaces/ISignatureValidator.sol";
import "./LibExchangeRichErrors.sol";
contract MixinTransactions is
LibZeroExTransaction,
LibEIP712ExchangeDomain,
ISignatureValidator,
ITransactions
{
using LibZeroExTransaction for LibZeroExTransaction.ZeroExTransaction;
// Mapping of transaction hash => executed
// This prevents transactions from being executed more than once.
mapping (bytes32 => bool) public transactionsExecuted;
@ -44,7 +46,7 @@ contract MixinTransactions is
/// @param signature Proof that transaction has been signed by signer.
/// @return ABI encoded return data of the underlying Exchange function call.
function executeTransaction(
ZeroExTransaction memory transaction,
LibZeroExTransaction.ZeroExTransaction memory transaction,
bytes memory signature
)
public
@ -58,7 +60,7 @@ contract MixinTransactions is
/// @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(
ZeroExTransaction[] memory transactions,
LibZeroExTransaction.ZeroExTransaction[] memory transactions,
bytes[] memory signatures
)
public
@ -77,35 +79,35 @@ contract MixinTransactions is
/// @param signature Proof that transaction has been signed by signer.
/// @return ABI encoded return data of the underlying Exchange function call.
function _executeTransaction(
ZeroExTransaction memory transaction,
LibZeroExTransaction.ZeroExTransaction memory transaction,
bytes memory signature
)
internal
returns (bytes memory)
{
bytes32 transactionHash = getTransactionHash(transaction);
bytes32 transactionHash = transaction.getTypedDataHash(EIP712_EXCHANGE_DOMAIN_HASH);
// Check transaction is not expired
// solhint-disable-next-line not-rely-on-time
if (block.timestamp >= transaction.expirationTimeSeconds) {
LibRichErrors._rrevert(LibExchangeRichErrors.TransactionError(
IExchangeRichErrors.TransactionErrorCodes.EXPIRED,
LibRichErrors.rrevert(LibExchangeRichErrors.TransactionError(
LibExchangeRichErrors.TransactionErrorCodes.EXPIRED,
transactionHash
));
}
// Prevent reentrancy
if (currentContextAddress != address(0)) {
LibRichErrors._rrevert(LibExchangeRichErrors.TransactionError(
IExchangeRichErrors.TransactionErrorCodes.NO_REENTRANCY,
LibRichErrors.rrevert(LibExchangeRichErrors.TransactionError(
LibExchangeRichErrors.TransactionErrorCodes.NO_REENTRANCY,
transactionHash
));
}
// Validate transaction has not been executed
if (transactionsExecuted[transactionHash]) {
LibRichErrors._rrevert(LibExchangeRichErrors.TransactionError(
IExchangeRichErrors.TransactionErrorCodes.ALREADY_EXECUTED,
LibRichErrors.rrevert(LibExchangeRichErrors.TransactionError(
LibExchangeRichErrors.TransactionErrorCodes.ALREADY_EXECUTED,
transactionHash
));
}
@ -118,7 +120,7 @@ contract MixinTransactions is
transaction,
transactionHash,
signature)) {
LibRichErrors._rrevert(LibExchangeRichErrors.TransactionSignatureError(
LibRichErrors.rrevert(LibExchangeRichErrors.TransactionSignatureError(
transactionHash,
signerAddress,
signature
@ -133,7 +135,7 @@ contract MixinTransactions is
transactionsExecuted[transactionHash] = true;
(bool didSucceed, bytes memory returnData) = address(this).delegatecall(transaction.data);
if (!didSucceed) {
LibRichErrors._rrevert(LibExchangeRichErrors.TransactionExecutionError(
LibRichErrors.rrevert(LibExchangeRichErrors.TransactionExecutionError(
transactionHash,
returnData
));

View File

@ -19,15 +19,14 @@
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/ReentrancyGuard.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.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/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/IExchangeRichErrors.sol";
import "./interfaces/IWrapperFunctions.sol";
import "./LibExchangeRichErrors.sol";
import "./MixinExchangeCore.sol";
@ -35,6 +34,8 @@ 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.
@ -46,7 +47,7 @@ contract MixinWrapperFunctions is
)
public
nonReentrant
returns (FillResults memory fillResults)
returns (LibFillResults.FillResults memory fillResults)
{
fillResults = _fillOrKillOrder(
order,
@ -68,11 +69,11 @@ contract MixinWrapperFunctions is
bytes memory signature
)
public
returns (FillResults memory fillResults)
returns (LibFillResults.FillResults memory fillResults)
{
// ABI encode calldata for `fillOrder`
bytes memory fillOrderCalldata = abi.encodeWithSelector(
IExchangeCore(0).fillOrder.selector,
IExchangeCore(address(0)).fillOrder.selector,
order,
takerAssetFillAmount,
signature
@ -81,7 +82,7 @@ contract MixinWrapperFunctions is
(bool didSucceed, bytes memory returnData) = address(this).delegatecall(fillOrderCalldata);
if (didSucceed) {
assert(returnData.length == 128);
fillResults = abi.decode(returnData, (FillResults));
fillResults = abi.decode(returnData, (LibFillResults.FillResults));
}
// fillResults values will be 0 by default if call was unsuccessful
return fillResults;
@ -99,10 +100,10 @@ contract MixinWrapperFunctions is
)
public
nonReentrant
returns (FillResults[] memory fillResults)
returns (LibFillResults.FillResults[] memory fillResults)
{
uint256 ordersLength = orders.length;
fillResults = new FillResults[](ordersLength);
fillResults = new LibFillResults.FillResults[](ordersLength);
for (uint256 i = 0; i != ordersLength; i++) {
fillResults[i] = _fillOrder(
orders[i],
@ -125,10 +126,10 @@ contract MixinWrapperFunctions is
)
public
nonReentrant
returns (FillResults[] memory fillResults)
returns (LibFillResults.FillResults[] memory fillResults)
{
uint256 ordersLength = orders.length;
fillResults = new FillResults[](ordersLength);
fillResults = new LibFillResults.FillResults[](ordersLength);
for (uint256 i = 0; i != ordersLength; i++) {
fillResults[i] = _fillOrKillOrder(
orders[i],
@ -150,10 +151,10 @@ contract MixinWrapperFunctions is
bytes[] memory signatures
)
public
returns (FillResults[] memory fillResults)
returns (LibFillResults.FillResults[] memory fillResults)
{
uint256 ordersLength = orders.length;
fillResults = new FillResults[](ordersLength);
fillResults = new LibFillResults.FillResults[](ordersLength);
for (uint256 i = 0; i != ordersLength; i++) {
fillResults[i] = fillOrderNoThrow(
orders[i],
@ -175,7 +176,7 @@ contract MixinWrapperFunctions is
bytes[] memory signatures
)
public
returns (FillResults memory fillResults)
returns (LibFillResults.FillResults memory fillResults)
{
bytes memory takerAssetData = orders[0].takerAssetData;
@ -188,17 +189,17 @@ contract MixinWrapperFunctions is
orders[i].takerAssetData = takerAssetData;
// Calculate the remaining amount of takerAsset to sell
uint256 remainingTakerAssetFillAmount = _safeSub(takerAssetFillAmount, fillResults.takerAssetFilledAmount);
uint256 remainingTakerAssetFillAmount = takerAssetFillAmount.safeSub(fillResults.takerAssetFilledAmount);
// Attempt to sell the remaining amount of takerAsset
FillResults memory singleFillResults = fillOrderNoThrow(
LibFillResults.FillResults memory singleFillResults = fillOrderNoThrow(
orders[i],
remainingTakerAssetFillAmount,
signatures[i]
);
// Update amounts filled and fees paid by maker and taker
_addFillResults(fillResults, singleFillResults);
fillResults = LibFillResults.addFillResults(fillResults, singleFillResults);
// Stop execution if the entire amount of takerAsset has been sold
if (fillResults.takerAssetFilledAmount >= takerAssetFillAmount) {
@ -219,7 +220,7 @@ contract MixinWrapperFunctions is
bytes[] memory signatures
)
public
returns (FillResults memory fillResults)
returns (LibFillResults.FillResults memory fillResults)
{
bytes memory makerAssetData = orders[0].makerAssetData;
@ -232,25 +233,25 @@ contract MixinWrapperFunctions is
orders[i].makerAssetData = makerAssetData;
// Calculate the remaining amount of makerAsset to buy
uint256 remainingMakerAssetFillAmount = _safeSub(makerAssetFillAmount, fillResults.makerAssetFilledAmount);
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 = _getPartialAmountFloor(
uint256 remainingTakerAssetFillAmount = LibMath.getPartialAmountFloor(
orders[i].takerAssetAmount,
orders[i].makerAssetAmount,
remainingMakerAssetFillAmount
);
// Attempt to sell the remaining amount of takerAsset
FillResults memory singleFillResults = fillOrderNoThrow(
LibFillResults.FillResults memory singleFillResults = fillOrderNoThrow(
orders[i],
remainingTakerAssetFillAmount,
signatures[i]
);
// Update amounts filled and fees paid by maker and taker
_addFillResults(fillResults, singleFillResults);
fillResults = LibFillResults.addFillResults(fillResults, singleFillResults);
// Stop execution if the entire amount of makerAsset has been bought
if (fillResults.makerAssetFilledAmount >= makerAssetFillAmount) {
@ -298,7 +299,7 @@ contract MixinWrapperFunctions is
bytes memory signature
)
internal
returns (FillResults memory fillResults)
returns (LibFillResults.FillResults memory fillResults)
{
fillResults = _fillOrder(
order,
@ -306,7 +307,7 @@ contract MixinWrapperFunctions is
signature
);
if (fillResults.takerAssetFilledAmount != takerAssetFillAmount) {
LibRichErrors._rrevert(LibExchangeRichErrors.IncompleteFillError(
LibRichErrors.rrevert(LibExchangeRichErrors.IncompleteFillError(
getOrderInfo(order).orderHash
));
}

View File

@ -39,7 +39,7 @@ contract IExchangeCore {
uint256 takerFeePaid, // Amount of takerFeeAssetData paid to feeRecipient by taker.
address takerAddress, // Address that filled the order.
address senderAddress, // Address that called the Exchange contract (msg.sender).
bytes32 indexed orderHash // EIP712 hash of order (see LibOrder.getOrderHash).
bytes32 indexed orderHash // EIP712 hash of order (see LibOrder.getTypedDataHash).
);
// Cancel event is emitted whenever an individual order is cancelled.
@ -47,7 +47,7 @@ contract IExchangeCore {
address indexed makerAddress, // Address that created the order.
address indexed feeRecipientAddress, // Address that would have recieved fees if order was filled.
address senderAddress, // Address that called the Exchange contract (msg.sender).
bytes32 indexed orderHash, // EIP712 hash of order (see LibOrder.getOrderHash).
bytes32 indexed orderHash, // EIP712 hash of order (see LibOrder.getTypedDataHash).
bytes makerAssetData, // Encoded data specific to makerAsset.
bytes takerAssetData // Encoded data specific to takerAsset.
);

View File

@ -60,26 +60,6 @@ contract IMatchOrders {
public
returns (LibFillResults.BatchMatchedFillResults memory batchMatchedFillResults);
/// @dev Calculates fill amounts for the matched orders.
/// Each order is filled at their respective price point. However, the calculations are
/// carried out as though the orders are both being filled at the right order's price point.
/// The profit made by the leftOrder order goes to the taker (who matched the two orders).
/// @param leftOrder First order to match.
/// @param rightOrder Second order to match.
/// @param leftOrderTakerAssetFilledAmount Amount of left order already filled.
/// @param rightOrderTakerAssetFilledAmount Amount of right order already filled.
/// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.
function calculateMatchedFillResults(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,
uint256 leftOrderTakerAssetFilledAmount,
uint256 rightOrderTakerAssetFilledAmount,
bool shouldMaximallyFillOrders
)
public
pure
returns (LibFillResults.MatchedFillResults memory matchedFillResults);
/// @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.

View File

@ -19,14 +19,12 @@
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";
import "../interfaces/IExchangeRichErrors.sol";
import "../LibExchangeRichErrors.sol";
contract LibExchangeRichErrorDecoder is
IExchangeRichErrors
{
contract LibExchangeRichErrorDecoder {
/// @dev Decompose an ABI-encoded SignatureError.
/// @param encoded ABI-encoded revert error.
/// @return errorCode The error code.
@ -36,14 +34,14 @@ contract LibExchangeRichErrorDecoder is
public
pure
returns (
SignatureErrorCodes errorCode,
LibExchangeRichErrors.SignatureErrorCodes errorCode,
bytes32 hash,
address signerAddress,
bytes memory signature
)
{
_assertSelectorBytes(encoded, LibExchangeRichErrors.SignatureErrorSelector());
errorCode = SignatureErrorCodes(_readErrorParameterAsUint256(encoded, 0));
errorCode = LibExchangeRichErrors.SignatureErrorCodes(_readErrorParameterAsUint256(encoded, 0));
hash = _readErrorParameterAsBytes32(encoded, 1);
signerAddress = _readErrorParameterAsAddress(encoded, 2);
signature = _readErrorParameterAsBytes(encoded, 3);
@ -189,12 +187,12 @@ contract LibExchangeRichErrorDecoder is
public
pure
returns (
FillErrorCodes errorCode,
LibExchangeRichErrors.FillErrorCodes errorCode,
bytes32 orderHash
)
{
_assertSelectorBytes(encoded, LibExchangeRichErrors.FillErrorSelector());
errorCode = FillErrorCodes(_readErrorParameterAsUint256(encoded, 0));
errorCode = LibExchangeRichErrors.FillErrorCodes(_readErrorParameterAsUint256(encoded, 0));
orderHash = _readErrorParameterAsBytes32(encoded, 1);
}
@ -239,13 +237,13 @@ contract LibExchangeRichErrorDecoder is
public
pure
returns (
AssetProxyDispatchErrorCodes errorCode,
LibExchangeRichErrors.AssetProxyDispatchErrorCodes errorCode,
bytes32 orderHash,
bytes memory assetData
)
{
_assertSelectorBytes(encoded, LibExchangeRichErrors.AssetProxyDispatchErrorSelector());
errorCode = AssetProxyDispatchErrorCodes(_readErrorParameterAsUint256(encoded, 0));
errorCode = LibExchangeRichErrors.AssetProxyDispatchErrorCodes(_readErrorParameterAsUint256(encoded, 0));
orderHash = _readErrorParameterAsBytes32(encoded, 1);
assetData = _readErrorParameterAsBytes(encoded, 2);
}
@ -295,12 +293,12 @@ contract LibExchangeRichErrorDecoder is
public
pure
returns (
TransactionErrorCodes errorCode,
LibExchangeRichErrors.TransactionErrorCodes errorCode,
bytes32 transactionHash
)
{
_assertSelectorBytes(encoded, LibExchangeRichErrors.TransactionErrorSelector());
errorCode = TransactionErrorCodes(_readErrorParameterAsUint256(encoded, 0));
errorCode = LibExchangeRichErrors.TransactionErrorCodes(_readErrorParameterAsUint256(encoded, 0));
transactionHash = _readErrorParameterAsBytes32(encoded, 1);
}

View File

@ -19,6 +19,7 @@
pragma solidity ^0.5.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "../src/Exchange.sol";
@ -71,7 +72,7 @@ contract IsolatedExchange is
/// @dev Overriden to simplify signature validation.
/// Unfortunately, this is `view`, so it can't log arguments.
function _isValidOrderWithHashSignature(
Order memory order,
LibOrder.Order memory order,
bytes32 orderHash,
bytes memory signature
)

View File

@ -20,6 +20,9 @@ pragma solidity ^0.5.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/LibReentrancyGuardRichErrors.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/LibFillResults.sol";
import "../src/Exchange.sol";
@ -71,27 +74,27 @@ contract ReentrancyTester is
/// @dev Overriden to do nothing.
function _fillOrder(
Order memory order,
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
internal
returns (FillResults memory fillResults)
returns (LibFillResults.FillResults memory fillResults)
{}
/// @dev Overriden to do nothing.
function _fillOrKillOrder(
Order memory order,
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
internal
returns (FillResults memory fillResults)
returns (LibFillResults.FillResults memory fillResults)
{}
/// @dev Overridden to do nothing.
function _executeTransaction(
ZeroExTransaction memory transaction,
LibZeroExTransaction.ZeroExTransaction memory transaction,
bytes memory signature
)
internal
@ -126,7 +129,7 @@ contract ReentrancyTester is
{}
/// @dev Overriden to do nothing.
function _cancelOrder(Order memory order)
function _cancelOrder(LibOrder.Order memory order)
internal
{}
}

View File

@ -19,6 +19,8 @@
pragma solidity ^0.5.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
import "../src/Exchange.sol";
@ -46,69 +48,26 @@ contract TestExchangeInternals is
public
view
{
_assertValidMatch(leftOrder, rightOrder);
}
function calculateFillResults(
Order memory order,
uint256 takerAssetFilledAmount
)
public
pure
returns (FillResults memory fillResults)
{
return _calculateFillResults(order, takerAssetFilledAmount);
}
function calculateCompleteFillBoth(
uint256 leftMakerAssetAmountRemaining,
uint256 leftTakerAssetAmountRemaining,
uint256 rightMakerAssetAmountRemaining,
uint256 rightTakerAssetAmountRemaining
)
public
pure
returns (MatchedFillResults memory fillResults)
{
_calculateCompleteFillBoth(
fillResults,
leftMakerAssetAmountRemaining,
leftTakerAssetAmountRemaining,
rightMakerAssetAmountRemaining,
rightTakerAssetAmountRemaining
);
return fillResults;
}
function calculateCompleteRightFill(
LibOrder.Order memory leftOrder,
uint256 rightMakerAssetAmountRemaining,
uint256 rightTakerAssetAmountRemaining
)
public
pure
returns (MatchedFillResults memory fillResults)
{
_calculateCompleteRightFill(
fillResults,
_assertValidMatch(
leftOrder,
rightMakerAssetAmountRemaining,
rightTakerAssetAmountRemaining
rightOrder,
getOrderInfo(leftOrder),
getOrderInfo(rightOrder)
);
}
/// @dev Call `_updateFilledState()` but first set `filled[order]` to
/// `orderTakerAssetFilledAmount`.
function testUpdateFilledState(
Order memory order,
LibOrder.Order memory order,
address takerAddress,
bytes32 orderHash,
uint256 orderTakerAssetFilledAmount,
FillResults memory fillResults
LibFillResults.FillResults memory fillResults
)
public
{
filled[getOrderHash(order)] = orderTakerAssetFilledAmount;
filled[LibOrder.getTypedDataHash(order, EIP712_EXCHANGE_DOMAIN_HASH)] = orderTakerAssetFilledAmount;
_updateFilledState(
order,
takerAddress,

View File

@ -20,14 +20,12 @@ pragma solidity ^0.5.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "../src/MixinSignatureValidator.sol";
import "../src/MixinTransactions.sol";
contract TestSignatureValidator is
LibEIP712ExchangeDomain,
LibOrder,
MixinSignatureValidator
{

View File

@ -21,24 +21,12 @@ pragma experimental ABIEncoderV2;
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-erc20/contracts/src/interfaces/IERC20Token.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibEIP1271.sol";
interface ISimplifiedExchange {
function getOrderHash(LibOrder.Order calldata order)
external
view
returns (bytes32 orderHash);
function getTransactionHash(LibZeroExTransaction.ZeroExTransaction calldata transaction)
external
view
returns (bytes32 transactionHash);
}
// solhint-disable no-unused-vars
contract TestValidatorWallet is
LibEIP1271
@ -80,8 +68,8 @@ contract TestValidatorWallet is
NTypes
}
/// @dev The Exchange contract.
ISimplifiedExchange internal _exchange;
/// @dev The Exchange domain hash..
LibEIP712ExchangeDomain internal _exchange;
/// @dev Internal state to modify.
uint256 internal _state = 1;
/// @dev What action to execute when a hash is validated .
@ -92,7 +80,7 @@ contract TestValidatorWallet is
mapping (bytes32 => bytes32) internal _hashSignatureHashes;
constructor(address exchange) public {
_exchange = ISimplifiedExchange(exchange);
_exchange = LibEIP712ExchangeDomain(exchange);
}
/// @dev Approves an ERC20 token to spend tokens from this address.
@ -239,7 +227,7 @@ contract TestValidatorWallet is
// Use the Exchange to calculate the hash of the order and assert
// that it matches the one we extracted previously.
require(
_exchange.getOrderHash(order) == hash,
LibOrder.getTypedDataHash(order, _exchange.EIP712_EXCHANGE_DOMAIN_HASH()) == hash,
"UNEXPECTED_ORDER_HASH"
);
} else {
@ -250,7 +238,7 @@ contract TestValidatorWallet is
// Use the Exchange to calculate the hash of the transaction and assert
// that it matches the one we extracted previously.
require(
_exchange.getTransactionHash(transaction) == hash,
LibZeroExTransaction.getTypedDataHash(transaction, _exchange.EIP712_EXCHANGE_DOMAIN_HASH()) == hash,
"UNEXPECTED_TRANSACTION_HASH"
);
}

View File

@ -19,6 +19,8 @@
pragma solidity ^0.5.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
import "../src/Exchange.sol";
@ -28,19 +30,19 @@ import "../src/Exchange.sol";
contract TestWrapperFunctions is
Exchange
{
uint8 internal constant MAX_ORDER_STATUS = uint8(OrderStatus.CANCELLED);
uint8 internal constant MAX_ORDER_STATUS = uint8(LibOrder.OrderStatus.CANCELLED);
uint256 internal constant ALWAYS_FAILING_SALT = uint256(-1);
string internal constant ALWAYS_FAILING_SALT_REVERT_REASON = "ALWAYS_FAILING_SALT";
// solhint-disable no-unused-vars
event FillOrderCalled(
Order order,
LibOrder.Order order,
uint256 takerAssetFillAmount,
bytes signature
);
event CancelOrderCalled(
Order order
LibOrder.Order order
);
// solhint-disable no-empty-blocks
@ -51,26 +53,26 @@ contract TestWrapperFunctions is
{}
/// @dev Overridden to be deterministic and simplified.
function getOrderInfo(Order memory order)
function getOrderInfo(LibOrder.Order memory order)
public
view
returns (OrderInfo memory orderInfo)
returns (LibOrder.OrderInfo memory orderInfo)
{
// Lower uint128 of `order.salt` is the `orderTakerAssetFilledAmount`.
orderInfo.orderTakerAssetFilledAmount = uint128(order.salt);
// High byte of `order.salt` is the `orderStatus`.
orderInfo.orderStatus = uint8(order.salt >> 248) % (MAX_ORDER_STATUS + 1);
orderInfo.orderHash = _getOrderHash(order);
orderInfo.orderHash = _getTypedDataHash(order);
}
/// @dev Overridden to log arguments, be deterministic, and revert with certain inputs.
function _fillOrder(
Order memory order,
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
internal
returns (FillResults memory fillResults)
returns (LibFillResults.FillResults memory fillResults)
{
emit FillOrderCalled(
order,
@ -94,7 +96,7 @@ contract TestWrapperFunctions is
/// @dev Overridden to only log arguments and revert with certain inputs.
function _cancelOrder(
Order memory order
LibOrder.Order memory order
)
internal
{
@ -109,7 +111,7 @@ contract TestWrapperFunctions is
}
/// @dev Simplified order hashing.
function _getOrderHash(Order memory order)
function _getTypedDataHash(LibOrder.Order memory order)
internal
pure
returns (bytes32 hash)

View File

@ -12,7 +12,7 @@
"scripts": {
"build": "yarn pre_build && tsc -b",
"build:ci": "yarn build",
"pre_build": "run-s compile generate_contract_wrappers",
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
@ -34,8 +34,8 @@
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
},
"config": {
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./generated-artifacts/@(Exchange|ExchangeWrapper|IAssetProxyDispatcher|IEIP1271Wallet|IExchange|IExchangeCore|IMatchOrders|ISignatureValidator|ITransactions|IWallet|IWrapperFunctions|IsolatedExchange|ReentrancyTester|TestAssetProxyDispatcher|TestExchangeInternals|TestLibExchangeRichErrorDecoder|TestSignatureValidator|TestValidatorWallet|TestWrapperFunctions|Whitelist).json"
"abis": "./generated-artifacts/@(Exchange|ExchangeWrapper|IAssetProxy|IAssetProxyDispatcher|IEIP1271Wallet|IExchange|IExchangeCore|IExchangeRichErrors|IMatchOrders|ISignatureValidator|ITransactions|ITransferSimulator|IWallet|IWrapperFunctions|IsolatedExchange|LibExchangeRichErrorDecoder|MixinAssetProxyDispatcher|MixinExchangeCore|MixinMatchOrders|MixinSignatureValidator|MixinTransactions|MixinTransferSimulator|MixinWrapperFunctions|ReentrancyTester|TestAssetProxyDispatcher|TestExchangeInternals|TestLibExchangeRichErrorDecoder|TestSignatureValidator|TestValidatorWallet|TestWrapperFunctions|Whitelist).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {
"type": "git",

View File

@ -7,16 +7,27 @@ import { ContractArtifact } from 'ethereum-types';
import * as Exchange from '../generated-artifacts/Exchange.json';
import * as ExchangeWrapper from '../generated-artifacts/ExchangeWrapper.json';
import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
import * as IAssetProxyDispatcher from '../generated-artifacts/IAssetProxyDispatcher.json';
import * as IEIP1271Wallet from '../generated-artifacts/IEIP1271Wallet.json';
import * as IExchange from '../generated-artifacts/IExchange.json';
import * as IExchangeCore from '../generated-artifacts/IExchangeCore.json';
import * as IExchangeRichErrors from '../generated-artifacts/IExchangeRichErrors.json';
import * as IMatchOrders from '../generated-artifacts/IMatchOrders.json';
import * as ISignatureValidator from '../generated-artifacts/ISignatureValidator.json';
import * as ITransactions from '../generated-artifacts/ITransactions.json';
import * as ITransferSimulator from '../generated-artifacts/ITransferSimulator.json';
import * as IWallet from '../generated-artifacts/IWallet.json';
import * as IWrapperFunctions from '../generated-artifacts/IWrapperFunctions.json';
import * as IsolatedExchange from '../generated-artifacts/IsolatedExchange.json';
import * as LibExchangeRichErrorDecoder from '../generated-artifacts/LibExchangeRichErrorDecoder.json';
import * as MixinAssetProxyDispatcher from '../generated-artifacts/MixinAssetProxyDispatcher.json';
import * as MixinExchangeCore from '../generated-artifacts/MixinExchangeCore.json';
import * as MixinMatchOrders from '../generated-artifacts/MixinMatchOrders.json';
import * as MixinSignatureValidator from '../generated-artifacts/MixinSignatureValidator.json';
import * as MixinTransactions from '../generated-artifacts/MixinTransactions.json';
import * as MixinTransferSimulator from '../generated-artifacts/MixinTransferSimulator.json';
import * as MixinWrapperFunctions from '../generated-artifacts/MixinWrapperFunctions.json';
import * as ReentrancyTester from '../generated-artifacts/ReentrancyTester.json';
import * as TestAssetProxyDispatcher from '../generated-artifacts/TestAssetProxyDispatcher.json';
import * as TestExchangeInternals from '../generated-artifacts/TestExchangeInternals.json';
@ -29,15 +40,26 @@ export const artifacts = {
ExchangeWrapper: ExchangeWrapper as ContractArtifact,
Whitelist: Whitelist as ContractArtifact,
Exchange: Exchange as ContractArtifact,
MixinAssetProxyDispatcher: MixinAssetProxyDispatcher as ContractArtifact,
MixinExchangeCore: MixinExchangeCore as ContractArtifact,
MixinMatchOrders: MixinMatchOrders as ContractArtifact,
MixinSignatureValidator: MixinSignatureValidator as ContractArtifact,
MixinTransactions: MixinTransactions as ContractArtifact,
MixinTransferSimulator: MixinTransferSimulator as ContractArtifact,
MixinWrapperFunctions: MixinWrapperFunctions as ContractArtifact,
IAssetProxy: IAssetProxy as ContractArtifact,
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
IEIP1271Wallet: IEIP1271Wallet as ContractArtifact,
IExchange: IExchange as ContractArtifact,
IExchangeCore: IExchangeCore as ContractArtifact,
IExchangeRichErrors: IExchangeRichErrors as ContractArtifact,
IMatchOrders: IMatchOrders as ContractArtifact,
ISignatureValidator: ISignatureValidator as ContractArtifact,
ITransactions: ITransactions as ContractArtifact,
ITransferSimulator: ITransferSimulator as ContractArtifact,
IWallet: IWallet as ContractArtifact,
IWrapperFunctions: IWrapperFunctions as ContractArtifact,
LibExchangeRichErrorDecoder: LibExchangeRichErrorDecoder as ContractArtifact,
IsolatedExchange: IsolatedExchange as ContractArtifact,
ReentrancyTester: ReentrancyTester as ContractArtifact,
TestAssetProxyDispatcher: TestAssetProxyDispatcher as ContractArtifact,

View File

@ -5,16 +5,27 @@
*/
export * from '../generated-wrappers/exchange';
export * from '../generated-wrappers/exchange_wrapper';
export * from '../generated-wrappers/i_asset_proxy';
export * from '../generated-wrappers/i_asset_proxy_dispatcher';
export * from '../generated-wrappers/i_e_i_p1271_wallet';
export * from '../generated-wrappers/i_exchange';
export * from '../generated-wrappers/i_exchange_core';
export * from '../generated-wrappers/i_exchange_rich_errors';
export * from '../generated-wrappers/i_match_orders';
export * from '../generated-wrappers/i_signature_validator';
export * from '../generated-wrappers/i_transactions';
export * from '../generated-wrappers/i_transfer_simulator';
export * from '../generated-wrappers/i_wallet';
export * from '../generated-wrappers/i_wrapper_functions';
export * from '../generated-wrappers/isolated_exchange';
export * from '../generated-wrappers/lib_exchange_rich_error_decoder';
export * from '../generated-wrappers/mixin_asset_proxy_dispatcher';
export * from '../generated-wrappers/mixin_exchange_core';
export * from '../generated-wrappers/mixin_match_orders';
export * from '../generated-wrappers/mixin_signature_validator';
export * from '../generated-wrappers/mixin_transactions';
export * from '../generated-wrappers/mixin_transfer_simulator';
export * from '../generated-wrappers/mixin_wrapper_functions';
export * from '../generated-wrappers/reentrancy_tester';
export * from '../generated-wrappers/test_asset_proxy_dispatcher';
export * from '../generated-wrappers/test_exchange_internals';

File diff suppressed because it is too large Load Diff

View File

@ -5,16 +5,27 @@
"files": [
"generated-artifacts/Exchange.json",
"generated-artifacts/ExchangeWrapper.json",
"generated-artifacts/IAssetProxy.json",
"generated-artifacts/IAssetProxyDispatcher.json",
"generated-artifacts/IEIP1271Wallet.json",
"generated-artifacts/IExchange.json",
"generated-artifacts/IExchangeCore.json",
"generated-artifacts/IExchangeRichErrors.json",
"generated-artifacts/IMatchOrders.json",
"generated-artifacts/ISignatureValidator.json",
"generated-artifacts/ITransactions.json",
"generated-artifacts/ITransferSimulator.json",
"generated-artifacts/IWallet.json",
"generated-artifacts/IWrapperFunctions.json",
"generated-artifacts/IsolatedExchange.json",
"generated-artifacts/LibExchangeRichErrorDecoder.json",
"generated-artifacts/MixinAssetProxyDispatcher.json",
"generated-artifacts/MixinExchangeCore.json",
"generated-artifacts/MixinMatchOrders.json",
"generated-artifacts/MixinSignatureValidator.json",
"generated-artifacts/MixinTransactions.json",
"generated-artifacts/MixinTransferSimulator.json",
"generated-artifacts/MixinWrapperFunctions.json",
"generated-artifacts/ReentrancyTester.json",
"generated-artifacts/TestAssetProxyDispatcher.json",
"generated-artifacts/TestExchangeInternals.json",

View File

@ -22,13 +22,5 @@
]
}
}
},
"contracts": [
"@0x/contracts-erc20/contracts/src/WETH9.sol",
"@0x/contracts-exchange/contracts/examples/ExchangeWrapper.sol",
"@0x/contracts-exchange/contracts/src/Exchange.sol",
"src/BalanceThresholdFilter/BalanceThresholdFilter.sol",
"src/DutchAuction/DutchAuction.sol",
"src/OrderMatcher/OrderMatcher.sol"
]
}
}

View File

@ -19,16 +19,15 @@
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeSelectors.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "./MixinExchangeCalldata.sol";
import "./interfaces/IBalanceThresholdFilterCore.sol";
contract MixinBalanceThresholdFilterCore is
IBalanceThresholdFilterCore,
MixinExchangeCalldata,
LibExchangeSelectors
MixinExchangeCalldata
{
/// @dev Executes an Exchange transaction iff the maker and taker meet
@ -100,34 +99,34 @@ contract MixinBalanceThresholdFilterCore is
bytes4 exchangeFunctionSelector = bytes4(_exchangeCalldataload(0));
// solhint-disable expression-indent
if (
exchangeFunctionSelector == BATCH_FILL_ORDERS_SELECTOR ||
exchangeFunctionSelector == BATCH_FILL_ORDERS_NO_THROW_SELECTOR ||
exchangeFunctionSelector == BATCH_FILL_OR_KILL_ORDERS_SELECTOR ||
exchangeFunctionSelector == MARKET_BUY_ORDERS_SELECTOR ||
exchangeFunctionSelector == MARKET_BUY_ORDERS_NO_THROW_SELECTOR ||
exchangeFunctionSelector == MARKET_SELL_ORDERS_SELECTOR ||
exchangeFunctionSelector == MARKET_SELL_ORDERS_NO_THROW_SELECTOR
exchangeFunctionSelector == IExchange(address(0)).batchFillOrders.selector ||
exchangeFunctionSelector == IExchange(address(0)).batchFillOrdersNoThrow.selector ||
exchangeFunctionSelector == IExchange(address(0)).batchFillOrKillOrders.selector ||
exchangeFunctionSelector == IExchange(address(0)).marketBuyOrders.selector ||
exchangeFunctionSelector == IExchange(address(0)).marketBuyOrdersNoThrow.selector ||
exchangeFunctionSelector == IExchange(address(0)).marketSellOrders.selector ||
exchangeFunctionSelector == IExchange(address(0)).marketSellOrdersNoThrow.selector
) {
addressesToValidate = _loadMakerAddressesFromOrderArray(0);
addressesToValidate = addressesToValidate.append(signerAddress);
} else if (
exchangeFunctionSelector == FILL_ORDER_SELECTOR ||
exchangeFunctionSelector == FILL_ORDER_NO_THROW_SELECTOR ||
exchangeFunctionSelector == FILL_OR_KILL_ORDER_SELECTOR
exchangeFunctionSelector == IExchange(address(0)).fillOrder.selector ||
exchangeFunctionSelector == IExchange(address(0)).fillOrderNoThrow.selector ||
exchangeFunctionSelector == IExchange(address(0)).fillOrKillOrder.selector
) {
address makerAddress = _loadMakerAddressFromOrder(0);
addressesToValidate = addressesToValidate.append(makerAddress);
addressesToValidate = addressesToValidate.append(signerAddress);
} else if (exchangeFunctionSelector == MATCH_ORDERS_SELECTOR) {
} else if (exchangeFunctionSelector == IExchange(address(0)).matchOrders.selector) {
address leftMakerAddress = _loadMakerAddressFromOrder(0);
addressesToValidate = addressesToValidate.append(leftMakerAddress);
address rightMakerAddress = _loadMakerAddressFromOrder(1);
addressesToValidate = addressesToValidate.append(rightMakerAddress);
addressesToValidate = addressesToValidate.append(signerAddress);
} else if (
exchangeFunctionSelector != CANCEL_ORDER_SELECTOR &&
exchangeFunctionSelector != BATCH_CANCEL_ORDERS_SELECTOR &&
exchangeFunctionSelector != CANCEL_ORDERS_UP_TO_SELECTOR
exchangeFunctionSelector != IExchange(address(0)).cancelOrder.selector &&
exchangeFunctionSelector != IExchange(address(0)).batchCancelOrders.selector &&
exchangeFunctionSelector != IExchange(address(0)).cancelOrdersUpTo.selector
) {
revert("INVALID_OR_BLOCKED_EXCHANGE_SELECTOR");
}

View File

@ -12,7 +12,7 @@
"scripts": {
"build": "yarn pre_build && tsc -b",
"build:ci": "yarn build",
"pre_build": "run-s compile generate_contract_wrappers",
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",

View File

@ -22,12 +22,5 @@
]
}
}
},
"contracts": [
"src/AssetProxyOwner.sol",
"src/MultiSigWallet.sol",
"src/MultiSigWalletWithTimeLock.sol",
"test/TestAssetProxyOwner.sol",
"test/TestRejectEther.sol"
]
}
}

View File

@ -12,7 +12,7 @@
"scripts": {
"build": "yarn pre_build && tsc -b",
"build:ci": "yarn build",
"pre_build": "run-s compile generate_contract_wrappers",
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",

View File

@ -21,6 +21,5 @@
]
}
}
},
"contracts": ["src/IStaking.sol", "src/Staking.sol"]
}
}

View File

@ -12,7 +12,7 @@
"scripts": {
"build": "yarn pre_build && tsc -b",
"build:ci": "yarn build",
"pre_build": "run-s compile generate_contract_wrappers",
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",

View File

@ -66,4 +66,6 @@ export const constants = {
KECCAK256_NULL: ethUtil.addHexPrefix(ethUtil.bufferToHex(ethUtil.SHA3_NULL)),
MAX_UINT256_ROOT: new BigNumber('340282366920938463463374607431768211456'),
ONE_ETHER: new BigNumber(1e18),
EIP712_DOMAIN_NAME: '0x Protocol',
EIP712_DOMAIN_VERSION: '3.0.0',
};

View File

@ -41,6 +41,18 @@
{
"note": "Throw a `SafeMathError` in `SafeMath._safeDiv()` when denominator is zero.",
"pr": 2031
},
{
"note": "Create `LibSafeMath`",
"pr": 2055
},
{
"note": "Rename `_rrevert` to `rrevert` in `LibRichErrors` contract",
"pr": 2055
},
{
"note": "Compile and export all contracts, artifacts, and wrappers by default",
"pr": 2055
}
]
},

View File

@ -22,26 +22,5 @@
]
}
}
},
"contracts": [
"src/Authorizable.sol",
"src/LibAddress.sol",
"src/LibBytes.sol",
"src/LibEIP1271.sol",
"src/LibEIP712.sol",
"src/Ownable.sol",
"src/ReentrancyGuard.sol",
"src/SafeMath.sol",
"src/interfaces/IAuthorizable.sol",
"src/interfaces/IOwnable.sol",
"test/TestConstants.sol",
"test/TestLibAddress.sol",
"test/TestLibAddressArray.sol",
"test/TestLibBytes.sol",
"test/TestLibEIP712.sol",
"test/TestLibRichErrors.sol",
"test/TestOwnable.sol",
"test/TestReentrancyGuard.sol",
"test/TestSafeMath.sol"
]
}
}

View File

@ -31,7 +31,7 @@ contract Authorizable is
/// @dev Only authorized addresses can invoke functions with this modifier.
modifier onlyAuthorized {
if (!authorized[msg.sender]) {
LibRichErrors._rrevert(LibAuthorizableRichErrors.SenderNotAuthorizedError(msg.sender));
LibRichErrors.rrevert(LibAuthorizableRichErrors.SenderNotAuthorizedError(msg.sender));
}
_;
}
@ -47,12 +47,12 @@ contract Authorizable is
{
// Ensure that the target is not the zero address.
if (target == address(0)) {
LibRichErrors._rrevert(LibAuthorizableRichErrors.ZeroCantBeAuthorizedError());
LibRichErrors.rrevert(LibAuthorizableRichErrors.ZeroCantBeAuthorizedError());
}
// Ensure that the target is not already authorized.
if (authorized[target]) {
LibRichErrors._rrevert(LibAuthorizableRichErrors.TargetAlreadyAuthorizedError(target));
LibRichErrors.rrevert(LibAuthorizableRichErrors.TargetAlreadyAuthorizedError(target));
}
authorized[target] = true;
@ -67,7 +67,7 @@ contract Authorizable is
onlyOwner
{
if (!authorized[target]) {
LibRichErrors._rrevert(LibAuthorizableRichErrors.TargetNotAuthorizedError(target));
LibRichErrors.rrevert(LibAuthorizableRichErrors.TargetNotAuthorizedError(target));
}
delete authorized[target];
@ -92,16 +92,16 @@ contract Authorizable is
onlyOwner
{
if (!authorized[target]) {
LibRichErrors._rrevert(LibAuthorizableRichErrors.TargetNotAuthorizedError(target));
LibRichErrors.rrevert(LibAuthorizableRichErrors.TargetNotAuthorizedError(target));
}
if (index >= authorities.length) {
LibRichErrors._rrevert(LibAuthorizableRichErrors.IndexOutOfBoundsError(
LibRichErrors.rrevert(LibAuthorizableRichErrors.IndexOutOfBoundsError(
index,
authorities.length
));
}
if (authorities[index] != target) {
LibRichErrors._rrevert(LibAuthorizableRichErrors.AuthorizedAddressMismatchError(
LibRichErrors.rrevert(LibAuthorizableRichErrors.AuthorizedAddressMismatchError(
authorities[index],
target
));

View File

@ -54,7 +54,7 @@ library LibAddressArray {
// `freeMemPtr` > `addressArrayEndPtr`: Some value occupies memory after `addressArray`
// `freeMemPtr` < `addressArrayEndPtr`: Memory has not been managed properly.
if (freeMemPtr < addressArrayEndPtr) {
LibRichErrors._rrevert(LibAddressArrayRichErrors.MismanagedMemoryError(
LibRichErrors.rrevert(LibAddressArrayRichErrors.MismanagedMemoryError(
freeMemPtr,
addressArrayEndPtr
));

View File

@ -180,14 +180,14 @@ library LibBytes {
// Ensure that the from and to positions are valid positions for a slice within
// the byte array that is being used.
if (from > to) {
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
from,
to
));
}
if (to > b.length) {
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
to,
b.length
@ -222,14 +222,14 @@ library LibBytes {
// Ensure that the from and to positions are valid positions for a slice within
// the byte array that is being used.
if (from > to) {
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
from,
to
));
}
if (to > b.length) {
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
to,
b.length
@ -253,7 +253,7 @@ library LibBytes {
returns (bytes1 result)
{
if (b.length == 0) {
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanZeroRequired,
b.length,
0
@ -280,7 +280,7 @@ library LibBytes {
returns (address result)
{
if (b.length < 20) {
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
b.length,
20 // 20 is length of address
@ -329,7 +329,7 @@ library LibBytes {
returns (address result)
{
if (b.length < index + 20) {
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
b.length,
index + 20 // 20 is length of address
@ -364,7 +364,7 @@ library LibBytes {
pure
{
if (b.length < index + 20) {
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
b.length,
index + 20 // 20 is length of address
@ -413,7 +413,7 @@ library LibBytes {
returns (bytes32 result)
{
if (b.length < index + 32) {
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
b.length,
index + 32
@ -443,7 +443,7 @@ library LibBytes {
pure
{
if (b.length < index + 32) {
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
b.length,
index + 32
@ -503,7 +503,7 @@ library LibBytes {
returns (bytes4 result)
{
if (b.length < index + 4) {
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsFourRequired,
b.length,
index + 4
@ -544,7 +544,7 @@ library LibBytes {
// Assert length of <b> is valid, given
// length of nested bytes
if (b.length < index + nestedBytesLength) {
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors
.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsNestedBytesLengthRequired,
b.length,
@ -574,7 +574,7 @@ library LibBytes {
// Assert length of <b> is valid, given
// length of input
if (b.length < index + 32 + input.length) {
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors
.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsNestedBytesLengthRequired,
b.length,
@ -603,7 +603,7 @@ library LibBytes {
uint256 sourceLen = source.length;
// Dest length must be >= source length, or some bytes would not be copied.
if (dest.length < sourceLen) {
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors
.InvalidByteOperationErrorCodes.DestinationLengthGreaterThanOrEqualSourceLengthRequired,
dest.length,

View File

@ -19,24 +19,25 @@
pragma solidity ^0.5.9;
contract LibEIP712 {
library LibEIP712 {
// Hash of the EIP712 Domain Separator Schema
bytes32 constant internal EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked(
"EIP712Domain(",
"string name,",
"string version,",
"uint256 chainId,",
"address verifyingContractAddress",
")"
));
// keccak256(abi.encodePacked(
// "EIP712Domain(",
// "string name,",
// "string version,",
// "uint256 chainId,",
// "address verifyingContractAddress",
// ")"
// ))
bytes32 constant internal _EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = 0xb1b295f2c1ed6b459ddeb95701466e4e0b385527a6cfa3873ae72a63c08466b6;
/// @dev Calculates a EIP712 domain separator.
/// @param name The EIP712 domain name.
/// @param version The EIP712 domain version.
/// @param verifyingContractAddress The EIP712 verifying contract.
/// @return EIP712 domain separator.
function _hashEIP712Domain(
function hashEIP712Domain(
string memory name,
string memory version,
uint256 chainId,
@ -46,13 +47,36 @@ contract LibEIP712 {
pure
returns (bytes32 result)
{
return keccak256(abi.encodePacked(
EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
keccak256(bytes(name)),
keccak256(bytes(version)),
chainId,
uint256(verifyingContractAddress)
));
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(verifyingContractAddress)
// ))
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), verifyingContractAddress)
// Compute hash
result := keccak256(memPtr, 160)
}
return result;
}
/// @dev Calculates EIP712 encoding for a hash struct with a given domain hash.
@ -60,7 +84,7 @@ contract LibEIP712 {
/// with getDomainHash().
/// @param hashStruct The EIP712 hash struct.
/// @return EIP712 hash applied to the given EIP712 Domain.
function _hashEIP712Message(bytes32 eip712DomainHash, bytes32 hashStruct)
function hashEIP712Message(bytes32 eip712DomainHash, bytes32 hashStruct)
internal
pure
returns (bytes32 result)

View File

@ -47,7 +47,7 @@ library LibRichErrors {
/// @dev Reverts an encoded rich revert reason `errorData`.
/// @param errorData ABI encoded error data.
function _rrevert(bytes memory errorData)
function rrevert(bytes memory errorData)
internal
pure
{

View File

@ -0,0 +1,90 @@
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.SafeMathError(
LibSafeMathRichErrors.SafeMathErrorCodes.UINT256_MULTIPLICATION_OVERFLOW,
a,
b
));
}
return c;
}
function safeDiv(uint256 a, uint256 b)
internal
pure
returns (uint256)
{
if (b == 0) {
LibRichErrors.rrevert(LibSafeMathRichErrors.SafeMathError(
LibSafeMathRichErrors.SafeMathErrorCodes.UINT256_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.SafeMathError(
LibSafeMathRichErrors.SafeMathErrorCodes.UINT256_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.SafeMathError(
LibSafeMathRichErrors.SafeMathErrorCodes.UINT256_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;
}
}

View File

@ -18,7 +18,7 @@ contract Ownable is
modifier onlyOwner() {
if (msg.sender != owner) {
LibRichErrors._rrevert(LibOwnableRichErrors.OnlyOwnerError(
LibRichErrors.rrevert(LibOwnableRichErrors.OnlyOwnerError(
msg.sender,
owner
));
@ -31,7 +31,7 @@ contract Ownable is
onlyOwner
{
if (newOwner == address(0)) {
LibRichErrors._rrevert(LibOwnableRichErrors.TransferOwnerToZeroError());
LibRichErrors.rrevert(LibOwnableRichErrors.TransferOwnerToZeroError());
} else {
owner = newOwner;
}

Some files were not shown because too many files have changed in this diff Show More