Merge pull request #2055 from 0xProject/feat/3.0/optimizeConstants
Refactor library usage
This commit is contained in:
commit
4c78b7d4bb
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
@ -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": {
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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';
|
||||
|
@ -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"
|
||||
],
|
||||
|
@ -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
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -21,6 +21,5 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"contracts": ["src/Coordinator.sol", "src/registry/CoordinatorRegistry.sol"]
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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.
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -22,12 +22,5 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"contracts": [
|
||||
"src/DevUtils.sol",
|
||||
"src/EthBalanceChecker.sol",
|
||||
"src/LibAssetData.sol",
|
||||
"src/LibTransactionDecoder.sol",
|
||||
"src/OrderTransferSimulationUtils.sol"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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": {
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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';
|
||||
|
@ -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);
|
||||
|
@ -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"]
|
||||
}
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -22,6 +22,5 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"contracts": ["src/Forwarder.sol"]
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -22,13 +22,5 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"contracts": [
|
||||
"src/LibEIP712ExchangeDomain.sol",
|
||||
"src/LibFillResults.sol",
|
||||
"src/LibMath.sol",
|
||||
"src/LibOrder.sol",
|
||||
"src/LibZeroExTransaction.sol",
|
||||
"test/TestLibs.sol"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
130
contracts/exchange-libs/contracts/test/TestLibMath.sol
Normal file
130
contracts/exchange-libs/contracts/test/TestLibMath.sol
Normal 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);
|
||||
}
|
||||
}
|
44
contracts/exchange-libs/contracts/test/TestLibOrder.sol
Normal file
44
contracts/exchange-libs/contracts/test/TestLibOrder.sol
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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": {
|
||||
|
@ -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(',');
|
||||
}
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
@ -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,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
new BigNumber(CHAIN_ID),
|
||||
);
|
||||
|
||||
// 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');
|
||||
});
|
||||
|
||||
describe('hashEIP712ExchangeMessage', () => {
|
||||
it('should correctly match an empty hash', async () => {
|
||||
await testHashEIP712MessageAsync(constants.NULL_BYTES32);
|
||||
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(chainId),
|
||||
constants.NULL_ADDRESS,
|
||||
);
|
||||
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);
|
||||
});
|
||||
|
||||
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
@ -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),
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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(),
|
||||
|
@ -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"]
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
{}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
));
|
||||
|
@ -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
|
||||
));
|
||||
}
|
||||
|
@ -32,14 +32,14 @@ contract IExchangeCore {
|
||||
bytes makerAssetData, // Encoded data specific to makerAsset.
|
||||
bytes takerAssetData, // Encoded data specific to takerAsset.
|
||||
bytes makerFeeAssetData, // Encoded data specific to makerFeeAsset.
|
||||
bytes takerFeeAssetData, // Encoded data specific to takerFeeAsset.
|
||||
bytes takerFeeAssetData, // Encoded data specific to takerFeeAsset.
|
||||
uint256 makerAssetFilledAmount, // Amount of makerAsset sold by maker and bought by taker.
|
||||
uint256 takerAssetFilledAmount, // Amount of takerAsset sold by taker and bought by maker.
|
||||
uint256 makerFeePaid, // Amount of makerFeeAssetData paid to feeRecipient by maker.
|
||||
uint256 takerFeePaid, // Amount of takerFeeAssetData paid to feeRecipient by taker.
|
||||
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.
|
||||
);
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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
|
||||
{}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
{
|
||||
|
||||
|
@ -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"
|
||||
);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -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
@ -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",
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -22,12 +22,5 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"contracts": [
|
||||
"src/AssetProxyOwner.sol",
|
||||
"src/MultiSigWallet.sol",
|
||||
"src/MultiSigWalletWithTimeLock.sol",
|
||||
"test/TestAssetProxyOwner.sol",
|
||||
"test/TestRejectEther.sol"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -21,6 +21,5 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"contracts": ["src/IStaking.sol", "src/Staking.sol"]
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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',
|
||||
};
|
||||
|
@ -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
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
));
|
||||
|
@ -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
|
||||
));
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
{
|
||||
|
90
contracts/utils/contracts/src/LibSafeMath.sol
Normal file
90
contracts/utils/contracts/src/LibSafeMath.sol
Normal 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;
|
||||
}
|
||||
}
|
@ -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
Loading…
x
Reference in New Issue
Block a user