* Cherry-pick updated `@0x/contracts-exchange-forwarder` from `3.0` branch. * Add PR numbers to changelogs
144 lines
4.5 KiB
Solidity
144 lines
4.5 KiB
Solidity
/*
|
|
|
|
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.5;
|
|
|
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
|
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
|
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
|
|
import "./libs/LibConstants.sol";
|
|
import "./mixins/MAssets.sol";
|
|
|
|
|
|
contract MixinAssets is
|
|
Ownable,
|
|
LibConstants,
|
|
MAssets
|
|
{
|
|
using LibBytes for bytes;
|
|
|
|
bytes4 constant internal ERC20_TRANSFER_SELECTOR = bytes4(keccak256("transfer(address,uint256)"));
|
|
|
|
/// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to
|
|
/// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be
|
|
/// used to withdraw assets that were accidentally sent to this contract.
|
|
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
/// @param amount Amount of ERC20 token to withdraw.
|
|
function withdrawAsset(
|
|
bytes calldata assetData,
|
|
uint256 amount
|
|
)
|
|
external
|
|
onlyOwner
|
|
{
|
|
transferAssetToSender(assetData, amount);
|
|
}
|
|
|
|
/// @dev Transfers given amount of asset to sender.
|
|
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
/// @param amount Amount of asset to transfer to sender.
|
|
function transferAssetToSender(
|
|
bytes memory assetData,
|
|
uint256 amount
|
|
)
|
|
internal
|
|
{
|
|
bytes4 proxyId = assetData.readBytes4(0);
|
|
|
|
if (proxyId == ERC20_DATA_ID) {
|
|
transferERC20Token(assetData, amount);
|
|
} else if (proxyId == ERC721_DATA_ID) {
|
|
transferERC721Token(assetData, amount);
|
|
} else {
|
|
revert("UNSUPPORTED_ASSET_PROXY");
|
|
}
|
|
}
|
|
|
|
/// @dev Decodes ERC20 assetData and transfers given amount to sender.
|
|
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
/// @param amount Amount of asset to transfer to sender.
|
|
function transferERC20Token(
|
|
bytes memory assetData,
|
|
uint256 amount
|
|
)
|
|
internal
|
|
{
|
|
address token = assetData.readAddress(16);
|
|
|
|
// Transfer tokens.
|
|
// We do a raw call so we can check the success separate
|
|
// from the return data.
|
|
(bool success,) = token.call(abi.encodeWithSelector(
|
|
ERC20_TRANSFER_SELECTOR,
|
|
msg.sender,
|
|
amount
|
|
));
|
|
require(
|
|
success,
|
|
"TRANSFER_FAILED"
|
|
);
|
|
|
|
// Check return data.
|
|
// If there is no return data, we assume the token incorrectly
|
|
// does not return a bool. In this case we expect it to revert
|
|
// on failure, which was handled above.
|
|
// If the token does return data, we require that it is a single
|
|
// value that evaluates to true.
|
|
assembly {
|
|
if returndatasize {
|
|
success := 0
|
|
if eq(returndatasize, 32) {
|
|
// First 64 bytes of memory are reserved scratch space
|
|
returndatacopy(0, 0, 32)
|
|
success := mload(0)
|
|
}
|
|
}
|
|
}
|
|
require(
|
|
success,
|
|
"TRANSFER_FAILED"
|
|
);
|
|
}
|
|
|
|
/// @dev Decodes ERC721 assetData and transfers given amount to sender.
|
|
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
/// @param amount Amount of asset to transfer to sender.
|
|
function transferERC721Token(
|
|
bytes memory assetData,
|
|
uint256 amount
|
|
)
|
|
internal
|
|
{
|
|
require(
|
|
amount == 1,
|
|
"INVALID_AMOUNT"
|
|
);
|
|
// Decode asset data.
|
|
address token = assetData.readAddress(16);
|
|
uint256 tokenId = assetData.readUint256(36);
|
|
|
|
// Perform transfer.
|
|
IERC721Token(token).transferFrom(
|
|
address(this),
|
|
msg.sender,
|
|
tokenId
|
|
);
|
|
}
|
|
}
|