Broker contracts
This commit is contained in:
@@ -1,5 +1,217 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||
import "@0x/contracts-exchange-forwarder/contracts/src/libs/LibAssetDataTransfer.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
import "@0x/contracts-utils/contracts/src/Refundable.sol";
|
||||
import "./interfaces/IBroker.sol";
|
||||
import "./interfaces/IPropertyValidator.sol";
|
||||
|
||||
|
||||
// TODO(mzhu25): Implement, you know, the whole thing
|
||||
contract Broker { } // solhint-disable-line no-empty-blocks
|
||||
// solhint-disable space-after-comma
|
||||
contract Broker is
|
||||
Refundable,
|
||||
IBroker
|
||||
{
|
||||
bytes[] internal _cachedAssetData;
|
||||
uint256 internal _cacheIndex;
|
||||
address internal _sender;
|
||||
address internal _EXCHANGE; // solhint-disable-line var-name-mixedcase
|
||||
|
||||
using LibSafeMath for uint256;
|
||||
using LibBytes for bytes;
|
||||
using LibAssetDataTransfer for bytes;
|
||||
|
||||
/// @param exchange Address of the 0x Exchange contract.
|
||||
constructor (address exchange)
|
||||
public
|
||||
{
|
||||
_EXCHANGE = exchange;
|
||||
}
|
||||
|
||||
/// @dev A payable fallback function that makes this contract "payable". This is necessary to allow
|
||||
/// this contract to gracefully handle refunds from the Exchange.
|
||||
function ()
|
||||
external
|
||||
payable
|
||||
{} // solhint-disable-line no-empty-blocks
|
||||
|
||||
function safeBatchTransferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256[] calldata /* ids */,
|
||||
uint256[] calldata amounts,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
{
|
||||
require(from == address(this), "INVALID_FROM_ADDRESS");
|
||||
require(amounts.length == 1, "MUST_PROVIDE_ONE_AMOUNT");
|
||||
uint256 remainingAmount = amounts[0];
|
||||
require(_cachedAssetData.length.safeSub(_cacheIndex) >= remainingAmount, "TOO_FEW_BROKERED_ASSETS_PROVIDED");
|
||||
|
||||
while (remainingAmount != 0) {
|
||||
bytes memory assetToTransfer = _cachedAssetData[_cacheIndex];
|
||||
_cacheIndex++;
|
||||
|
||||
// Decode validator and params from `data`
|
||||
(address validator, bytes memory propertyData) = abi.decode(
|
||||
data,
|
||||
(address, bytes)
|
||||
);
|
||||
|
||||
// Execute staticcall
|
||||
(bool success, bytes memory returnData) = validator.staticcall(abi.encodeWithSelector(
|
||||
IPropertyValidator(0).checkBrokerAsset.selector,
|
||||
assetToTransfer,
|
||||
propertyData
|
||||
));
|
||||
|
||||
// Revert with returned data if staticcall is unsuccessful
|
||||
if (!success) {
|
||||
assembly {
|
||||
revert(add(returnData, 32), mload(returnData))
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the transfer
|
||||
assetToTransfer.transferERC721Token(
|
||||
_sender,
|
||||
to,
|
||||
1
|
||||
);
|
||||
|
||||
remainingAmount--;
|
||||
}
|
||||
}
|
||||
|
||||
function brokerTrade(
|
||||
bytes[] memory brokeredAssets,
|
||||
LibOrder.Order memory order,
|
||||
uint256 takerAssetFillAmount,
|
||||
bytes memory signature,
|
||||
bytes4 fillFunctionSelector
|
||||
)
|
||||
public
|
||||
payable
|
||||
refundFinalBalance
|
||||
returns (LibFillResults.FillResults memory fillResults)
|
||||
{
|
||||
// Cache the taker-supplied asset data
|
||||
_cachedAssetData = brokeredAssets;
|
||||
// Cache the sender's address
|
||||
_sender = msg.sender;
|
||||
|
||||
// Sanity-check the provided function selector
|
||||
require(
|
||||
fillFunctionSelector == IExchange(address(0)).fillOrder.selector ||
|
||||
fillFunctionSelector == IExchange(address(0)).fillOrKillOrder.selector,
|
||||
"UNRECOGNIZED_FUNCTION_SELECTOR"
|
||||
);
|
||||
|
||||
// Perform the fill
|
||||
bytes memory fillCalldata = abi.encodeWithSelector(
|
||||
fillFunctionSelector,
|
||||
order,
|
||||
takerAssetFillAmount,
|
||||
signature
|
||||
);
|
||||
// solhint-disable-next-line avoid-call-value
|
||||
(bool didSucceed, bytes memory returnData) = _EXCHANGE.call.value(msg.value)(fillCalldata);
|
||||
if (didSucceed) {
|
||||
fillResults = abi.decode(returnData, (LibFillResults.FillResults));
|
||||
} else {
|
||||
assembly {
|
||||
revert(add(returnData, 32), mload(returnData))
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer maker asset to taker
|
||||
order.makerAssetData.transferOut(fillResults.makerAssetFilledAmount);
|
||||
|
||||
// Clear storage
|
||||
delete _cachedAssetData;
|
||||
_cacheIndex = 0;
|
||||
_sender = address(0);
|
||||
|
||||
return fillResults;
|
||||
}
|
||||
|
||||
function batchBrokerTrade(
|
||||
bytes[] memory brokeredAssets,
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256[] memory takerAssetFillAmounts,
|
||||
bytes[] memory signatures,
|
||||
bytes4 batchFillFunctionSelector
|
||||
)
|
||||
public
|
||||
payable
|
||||
refundFinalBalance
|
||||
returns (LibFillResults.FillResults[] memory fillResults)
|
||||
{
|
||||
// Cache the taker-supplied asset data
|
||||
_cachedAssetData = brokeredAssets;
|
||||
// Cache the sender's address
|
||||
_sender = msg.sender;
|
||||
|
||||
// Sanity-check the provided function selector
|
||||
require(
|
||||
batchFillFunctionSelector == IExchange(address(0)).batchFillOrders.selector ||
|
||||
batchFillFunctionSelector == IExchange(address(0)).batchFillOrKillOrders.selector ||
|
||||
batchFillFunctionSelector == IExchange(address(0)).batchFillOrdersNoThrow.selector,
|
||||
"UNRECOGNIZED_FUNCTION_SELECTOR"
|
||||
);
|
||||
|
||||
// Perform the batch fill
|
||||
bytes memory batchFillCalldata = abi.encodeWithSelector(
|
||||
batchFillFunctionSelector,
|
||||
orders,
|
||||
takerAssetFillAmounts,
|
||||
signatures
|
||||
);
|
||||
// solhint-disable-next-line avoid-call-value
|
||||
(bool didSucceed, bytes memory returnData) = _EXCHANGE.call.value(msg.value)(batchFillCalldata);
|
||||
if (didSucceed) {
|
||||
// solhint-disable-next-line indent
|
||||
fillResults = abi.decode(returnData, (LibFillResults.FillResults[]));
|
||||
} else {
|
||||
assembly {
|
||||
revert(add(returnData, 32), mload(returnData))
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer maker assets to taker
|
||||
for (uint256 i = 0; i < orders.length; i++) {
|
||||
orders[i].makerAssetData.transferOut(fillResults[i].makerAssetFilledAmount);
|
||||
}
|
||||
|
||||
// Clear storage
|
||||
delete _cachedAssetData;
|
||||
_cacheIndex = 0;
|
||||
_sender = address(0);
|
||||
|
||||
return fillResults;
|
||||
}
|
||||
}
|
||||
|
||||
59
contracts/broker/contracts/src/interfaces/IBroker.sol
Normal file
59
contracts/broker/contracts/src/interfaces/IBroker.sol
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
|
||||
|
||||
// solhint-disable space-after-comma
|
||||
interface IBroker {
|
||||
|
||||
function brokerTrade(
|
||||
bytes[] calldata brokeredAssets,
|
||||
LibOrder.Order calldata order,
|
||||
uint256 takerAssetFillAmount,
|
||||
bytes calldata signature,
|
||||
bytes4 fillFunctionSelector
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (LibFillResults.FillResults memory fillResults);
|
||||
|
||||
function batchBrokerTrade(
|
||||
bytes[] calldata brokeredAssets,
|
||||
LibOrder.Order[] calldata orders,
|
||||
uint256[] calldata takerAssetFillAmounts,
|
||||
bytes[] calldata signatures,
|
||||
bytes4 batchFillFunctionSelector
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (LibFillResults.FillResults[] memory fillResults);
|
||||
|
||||
function safeBatchTransferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256[] calldata /* ids */,
|
||||
uint256[] calldata amounts,
|
||||
bytes calldata data
|
||||
)
|
||||
external;
|
||||
}
|
||||
29
contracts/broker/contracts/src/interfaces/IGodsUnchained.sol
Normal file
29
contracts/broker/contracts/src/interfaces/IGodsUnchained.sol
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
interface IGodsUnchained {
|
||||
|
||||
function getDetails(uint256 tokenId)
|
||||
external
|
||||
view
|
||||
returns (uint16 proto, uint8 quality);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
interface IPropertyValidator {
|
||||
|
||||
function checkBrokerAsset(
|
||||
bytes calldata assetData,
|
||||
bytes calldata propertyData
|
||||
)
|
||||
external
|
||||
view;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "../interfaces/IGodsUnchained.sol";
|
||||
import "../interfaces/IPropertyValidator.sol";
|
||||
|
||||
|
||||
contract GodsUnchainedValidator is
|
||||
IPropertyValidator
|
||||
{
|
||||
IGodsUnchained internal GODS_UNCHAINED; // solhint-disable-line var-name-mixedcase
|
||||
|
||||
using LibBytes for bytes;
|
||||
|
||||
constructor(address _godsUnchained)
|
||||
public
|
||||
{
|
||||
GODS_UNCHAINED = IGodsUnchained(_godsUnchained);
|
||||
}
|
||||
|
||||
function checkBrokerAsset(
|
||||
bytes calldata assetData,
|
||||
bytes calldata propertyData
|
||||
)
|
||||
external
|
||||
view
|
||||
{
|
||||
(uint16 expectedProto, uint8 expectedQuality) = abi.decode(
|
||||
propertyData,
|
||||
(uint16, uint8)
|
||||
);
|
||||
|
||||
// Decode and validate asset data.
|
||||
address token = assetData.readAddress(16);
|
||||
require(token == address(GODS_UNCHAINED), "TOKEN_ADDRESS_MISMATCH");
|
||||
uint256 tokenId = assetData.readUint256(36);
|
||||
|
||||
(uint16 proto, uint8 quality) = GODS_UNCHAINED.getDetails(tokenId);
|
||||
require(proto == expectedProto, "PROTO_MISMATCH");
|
||||
require(quality == expectedQuality, "QUALITY_MISMATCH");
|
||||
}
|
||||
}
|
||||
55
contracts/broker/contracts/test/TestGodsUnchained.sol
Normal file
55
contracts/broker/contracts/test/TestGodsUnchained.sol
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc721/contracts/test/DummyERC721Token.sol";
|
||||
import "../src/interfaces/IGodsUnchained.sol";
|
||||
|
||||
|
||||
contract TestGodsUnchained is
|
||||
IGodsUnchained,
|
||||
DummyERC721Token
|
||||
{
|
||||
mapping (uint256 => uint16) internal _protoByTokenId;
|
||||
mapping (uint256 => uint8) internal _qualityByTokenId;
|
||||
|
||||
constructor (
|
||||
string memory _name,
|
||||
string memory _symbol
|
||||
)
|
||||
public
|
||||
DummyERC721Token(_name, _symbol)
|
||||
{} // solhint-disable-line no-empty-blocks
|
||||
|
||||
function setTokenProperties(uint256 tokenId, uint16 proto, uint8 quality)
|
||||
external
|
||||
{
|
||||
_protoByTokenId[tokenId] = proto;
|
||||
_qualityByTokenId[tokenId] = quality;
|
||||
}
|
||||
|
||||
function getDetails(uint256 tokenId)
|
||||
external
|
||||
view
|
||||
returns (uint16 proto, uint8 quality)
|
||||
{
|
||||
return (_protoByTokenId[tokenId], _qualityByTokenId[tokenId]);
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@
|
||||
},
|
||||
"config": {
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||
"abis": "./test/generated-artifacts/@(Broker).json"
|
||||
"abis": "./test/generated-artifacts/@(Broker|GodsUnchainedValidator|IBroker|IGodsUnchained|IPropertyValidator).json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -52,10 +52,9 @@
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.1.0",
|
||||
"@0x/contracts-asset-proxy": "^3.1.1",
|
||||
"@0x/contracts-erc1155": "^2.0.4",
|
||||
"@0x/contracts-erc721": "^3.0.4",
|
||||
"@0x/contracts-exchange": "^3.1.0",
|
||||
"@0x/contracts-exchange-forwarder": "^4.0.4",
|
||||
"@0x/contracts-exchange-libs": "^4.1.0",
|
||||
"@0x/contracts-gen": "^2.0.4",
|
||||
"@0x/contracts-test-utils": "^5.1.1",
|
||||
|
||||
@@ -6,4 +6,14 @@
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as Broker from '../generated-artifacts/Broker.json';
|
||||
export const artifacts = { Broker: Broker as ContractArtifact };
|
||||
import * as GodsUnchainedValidator from '../generated-artifacts/GodsUnchainedValidator.json';
|
||||
import * as IBroker from '../generated-artifacts/IBroker.json';
|
||||
import * as IGodsUnchained from '../generated-artifacts/IGodsUnchained.json';
|
||||
import * as IPropertyValidator from '../generated-artifacts/IPropertyValidator.json';
|
||||
export const artifacts = {
|
||||
Broker: Broker as ContractArtifact,
|
||||
IBroker: IBroker as ContractArtifact,
|
||||
IGodsUnchained: IGodsUnchained as ContractArtifact,
|
||||
IPropertyValidator: IPropertyValidator as ContractArtifact,
|
||||
GodsUnchainedValidator: GodsUnchainedValidator as ContractArtifact,
|
||||
};
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export { artifacts } from './artifacts';
|
||||
export { BrokerContract } from './wrappers';
|
||||
export { BrokerContract, GodsUnchainedValidatorContract } from './wrappers';
|
||||
|
||||
@@ -4,3 +4,7 @@
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../generated-wrappers/broker';
|
||||
export * from '../generated-wrappers/gods_unchained_validator';
|
||||
export * from '../generated-wrappers/i_broker';
|
||||
export * from '../generated-wrappers/i_gods_unchained';
|
||||
export * from '../generated-wrappers/i_property_validator';
|
||||
|
||||
@@ -6,4 +6,14 @@
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as Broker from '../test/generated-artifacts/Broker.json';
|
||||
export const artifacts = { Broker: Broker as ContractArtifact };
|
||||
import * as GodsUnchainedValidator from '../test/generated-artifacts/GodsUnchainedValidator.json';
|
||||
import * as IBroker from '../test/generated-artifacts/IBroker.json';
|
||||
import * as IGodsUnchained from '../test/generated-artifacts/IGodsUnchained.json';
|
||||
import * as IPropertyValidator from '../test/generated-artifacts/IPropertyValidator.json';
|
||||
export const artifacts = {
|
||||
Broker: Broker as ContractArtifact,
|
||||
IBroker: IBroker as ContractArtifact,
|
||||
IGodsUnchained: IGodsUnchained as ContractArtifact,
|
||||
IPropertyValidator: IPropertyValidator as ContractArtifact,
|
||||
GodsUnchainedValidator: GodsUnchainedValidator as ContractArtifact,
|
||||
};
|
||||
|
||||
@@ -4,3 +4,7 @@
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../test/generated-wrappers/broker';
|
||||
export * from '../test/generated-wrappers/gods_unchained_validator';
|
||||
export * from '../test/generated-wrappers/i_broker';
|
||||
export * from '../test/generated-wrappers/i_gods_unchained';
|
||||
export * from '../test/generated-wrappers/i_property_validator';
|
||||
|
||||
@@ -2,6 +2,17 @@
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": ["generated-artifacts/Broker.json", "test/generated-artifacts/Broker.json"],
|
||||
"files": [
|
||||
"generated-artifacts/Broker.json",
|
||||
"generated-artifacts/GodsUnchainedValidator.json",
|
||||
"generated-artifacts/IBroker.json",
|
||||
"generated-artifacts/IGodsUnchained.json",
|
||||
"generated-artifacts/IPropertyValidator.json",
|
||||
"test/generated-artifacts/Broker.json",
|
||||
"test/generated-artifacts/GodsUnchainedValidator.json",
|
||||
"test/generated-artifacts/IBroker.json",
|
||||
"test/generated-artifacts/IGodsUnchained.json",
|
||||
"test/generated-artifacts/IPropertyValidator.json"
|
||||
],
|
||||
"exclude": ["./deploy/solc/solc_bin"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user