文件 1 的 1:Forwarder.sol
pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
contract SafeMath {
function safeMul(uint256 a, uint256 b)
internal
pure
returns (uint256)
{
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(
c / a == b,
"UINT256_OVERFLOW"
);
return c;
}
function safeDiv(uint256 a, uint256 b)
internal
pure
returns (uint256)
{
uint256 c = a / b;
return c;
}
function safeSub(uint256 a, uint256 b)
internal
pure
returns (uint256)
{
require(
b <= a,
"UINT256_UNDERFLOW"
);
return a - b;
}
function safeAdd(uint256 a, uint256 b)
internal
pure
returns (uint256)
{
uint256 c = a + b;
require(
c >= a,
"UINT256_OVERFLOW"
);
return c;
}
function max64(uint64 a, uint64 b)
internal
pure
returns (uint256)
{
return a >= b ? a : b;
}
function min64(uint64 a, uint64 b)
internal
pure
returns (uint256)
{
return a < b ? a : b;
}
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;
}
}
contract LibFillResults is
SafeMath
{
struct FillResults {
uint256 makerAssetFilledAmount;
uint256 takerAssetFilledAmount;
uint256 makerFeePaid;
uint256 takerFeePaid;
}
struct MatchedFillResults {
FillResults left;
FillResults right;
uint256 leftMakerAssetSpreadAmount;
}
function addFillResults(FillResults memory totalFillResults, FillResults memory singleFillResults)
internal
pure
{
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);
}
}
contract LibEIP712 {
string constant internal EIP191_HEADER = "\x19\x01";
string constant internal EIP712_DOMAIN_NAME = "0x Protocol";
string constant internal EIP712_DOMAIN_VERSION = "2";
bytes32 constant internal EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked(
"EIP712Domain(",
"string name,",
"string version,",
"address verifyingContract",
")"
));
bytes32 public EIP712_DOMAIN_HASH;
constructor ()
public
{
EIP712_DOMAIN_HASH = keccak256(abi.encodePacked(
EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
keccak256(bytes(EIP712_DOMAIN_NAME)),
keccak256(bytes(EIP712_DOMAIN_VERSION)),
uint256(address(this))
));
}
function hashEIP712Message(bytes32 hashStruct)
internal
view
returns (bytes32 result)
{
bytes32 eip712DomainHash = EIP712_DOMAIN_HASH;
assembly {
let memPtr := mload(64)
mstore(memPtr, 0x1901000000000000000000000000000000000000000000000000000000000000)
mstore(add(memPtr, 2), eip712DomainHash)
mstore(add(memPtr, 34), hashStruct)
result := keccak256(memPtr, 66)
}
return result;
}
}
contract LibOrder is
LibEIP712
{
bytes32 constant internal EIP712_ORDER_SCHEMA_HASH = keccak256(abi.encodePacked(
"Order(",
"address makerAddress,",
"address takerAddress,",
"address feeRecipientAddress,",
"address senderAddress,",
"uint256 makerAssetAmount,",
"uint256 takerAssetAmount,",
"uint256 makerFee,",
"uint256 takerFee,",
"uint256 expirationTimeSeconds,",
"uint256 salt,",
"bytes makerAssetData,",
"bytes takerAssetData",
")"
));
enum OrderStatus {
INVALID,
INVALID_MAKER_ASSET_AMOUNT,
INVALID_TAKER_ASSET_AMOUNT,
FILLABLE,
EXPIRED,
FULLY_FILLED,
CANCELLED
}
struct Order {
address makerAddress;
address takerAddress;
address feeRecipientAddress;
address senderAddress;
uint256 makerAssetAmount;
uint256 takerAssetAmount;
uint256 makerFee;
uint256 takerFee;
uint256 expirationTimeSeconds;
uint256 salt;
bytes makerAssetData;
bytes takerAssetData;
}
struct OrderInfo {
uint8 orderStatus;
bytes32 orderHash;
uint256 orderTakerAssetFilledAmount;
}
function getOrderHash(Order memory order)
internal
view
returns (bytes32 orderHash)
{
orderHash = hashEIP712Message(hashOrder(order));
return orderHash;
}
function hashOrder(Order memory order)
internal
pure
returns (bytes32 result)
{
bytes32 schemaHash = EIP712_ORDER_SCHEMA_HASH;
bytes32 makerAssetDataHash = keccak256(order.makerAssetData);
bytes32 takerAssetDataHash = keccak256(order.takerAssetData);
assembly {
let pos1 := sub(order, 32)
let pos2 := add(order, 320)
let pos3 := add(order, 352)
let temp1 := mload(pos1)
let temp2 := mload(pos2)
let temp3 := mload(pos3)
mstore(pos1, schemaHash)
mstore(pos2, makerAssetDataHash)
mstore(pos3, takerAssetDataHash)
result := keccak256(pos1, 416)
mstore(pos1, temp1)
mstore(pos2, temp2)
mstore(pos3, temp3)
}
return result;
}
}
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
contract IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) public view returns (uint256 balance);
function ownerOf(uint256 tokenId) public view returns (address owner);
function safeTransferFrom(address from, address to, uint256 tokenId) public;
function transferFrom(address from, address to, uint256 tokenId) public;
function approve(address to, uint256 tokenId) public;
function getApproved(uint256 tokenId) public view returns (address operator);
function setApprovalForAll(address operator, bool _approved) public;
function isApprovedForAll(address owner, address operator) public view returns (bool);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
}
contract IExchange {
function fillOrKillOrder(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
public
returns (LibFillResults.FillResults memory fillResults);
function fillOrderNoThrow(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
public
returns (LibFillResults.FillResults memory fillResults);
function batchFillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
public
returns (LibFillResults.FillResults memory totalFillResults);
function batchFillOrKillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
public
returns (LibFillResults.FillResults memory totalFillResults);
function batchFillOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
public
returns (LibFillResults.FillResults memory totalFillResults);
function marketSellOrders(
LibOrder.Order[] memory orders,
uint256 takerAssetFillAmount,
bytes[] memory signatures
)
public
returns (LibFillResults.FillResults memory totalFillResults);
function marketSellOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256 takerAssetFillAmount,
bytes[] memory signatures
)
public
returns (LibFillResults.FillResults memory totalFillResults);
function marketBuyOrders(
LibOrder.Order[] memory orders,
uint256 makerAssetFillAmount,
bytes[] memory signatures
)
public
returns (LibFillResults.FillResults memory totalFillResults);
function marketBuyOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256 makerAssetFillAmount,
bytes[] memory signatures
)
public
returns (LibFillResults.FillResults memory totalFillResults);
function batchCancelOrders(LibOrder.Order[] memory orders)
public;
function getOrdersInfo(LibOrder.Order[] memory orders)
public
view
returns (LibOrder.OrderInfo[] memory);
}
contract IEtherToken is IERC20 {
function deposit() public payable;
function withdraw(uint256 _amount) public;
function withdrawTo(address _to, uint256 _amount) public;
}
library LibBytesRichErrors {
enum InvalidByteOperationErrorCodes {
FromLessThanOrEqualsToRequired,
ToLessThanOrEqualsLengthRequired,
LengthGreaterThanZeroRequired,
LengthGreaterThanOrEqualsFourRequired,
LengthGreaterThanOrEqualsTwentyRequired,
LengthGreaterThanOrEqualsThirtyTwoRequired,
LengthGreaterThanOrEqualsNestedBytesLengthRequired,
DestinationLengthGreaterThanOrEqualSourceLengthRequired
}
bytes4 internal constant INVALID_BYTE_OPERATION_ERROR_SELECTOR =
0x28006595;
function InvalidByteOperationError(
InvalidByteOperationErrorCodes errorCode,
uint256 offset,
uint256 required
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
INVALID_BYTE_OPERATION_ERROR_SELECTOR,
errorCode,
offset,
required
);
}
}
library LibRichErrors {
bytes4 internal constant STANDARD_ERROR_SELECTOR =
0x08c379a0;
function StandardError(
string memory message
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
STANDARD_ERROR_SELECTOR,
bytes(message)
);
}
function rrevert(bytes memory errorData)
internal
pure
{
assembly {
revert(add(errorData, 0x20), mload(errorData))
}
}
}
library LibBytes {
using LibBytes for bytes;
function rawAddress(bytes memory input)
internal
pure
returns (uint256 memoryAddress)
{
assembly {
memoryAddress := input
}
return memoryAddress;
}
function contentAddress(bytes memory input)
internal
pure
returns (uint256 memoryAddress)
{
assembly {
memoryAddress := add(input, 32)
}
return memoryAddress;
}
function memCopy(
uint256 dest,
uint256 source,
uint256 length
)
internal
pure
{
if (length < 32) {
assembly {
let mask := sub(exp(256, sub(32, length)), 1)
let s := and(mload(source), not(mask))
let d := and(mload(dest), mask)
mstore(dest, or(s, d))
}
} else {
if (source == dest) {
return;
}
if (source > dest) {
assembly {
length := sub(length, 32)
let sEnd := add(source, length)
let dEnd := add(dest, length)
let last := mload(sEnd)
for {} lt(source, sEnd) {} {
mstore(dest, mload(source))
source := add(source, 32)
dest := add(dest, 32)
}
mstore(dEnd, last)
}
} else {
assembly {
length := sub(length, 32)
let sEnd := add(source, length)
let dEnd := add(dest, length)
let first := mload(source)
for {} slt(dest, dEnd) {} {
mstore(dEnd, mload(sEnd))
sEnd := sub(sEnd, 32)
dEnd := sub(dEnd, 32)
}
mstore(dest, first)
}
}
}
}
function slice(
bytes memory b,
uint256 from,
uint256 to
)
internal
pure
returns (bytes memory result)
{
if (from > to) {
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
from,
to
));
}
if (to > b.length) {
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
to,
b.length
));
}
result = new bytes(to - from);
memCopy(
result.contentAddress(),
b.contentAddress() + from,
result.length
);
return result;
}
function sliceDestructive(
bytes memory b,
uint256 from,
uint256 to
)
internal
pure
returns (bytes memory result)
{
if (from > to) {
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
from,
to
));
}
if (to > b.length) {
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
to,
b.length
));
}
assembly {
result := add(b, from)
mstore(result, sub(to, from))
}
return result;
}
function popLastByte(bytes memory b)
internal
pure
returns (bytes1 result)
{
if (b.length == 0) {
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanZeroRequired,
b.length,
0
));
}
result = b[b.length - 1];
assembly {
let newLen := sub(mload(b), 1)
mstore(b, newLen)
}
return result;
}
function equals(
bytes memory lhs,
bytes memory rhs
)
internal
pure
returns (bool equal)
{
return lhs.length == rhs.length && keccak256(lhs) == keccak256(rhs);
}
function readAddress(
bytes memory b,
uint256 index
)
internal
pure
returns (address result)
{
if (b.length < index + 20) {
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
b.length,
index + 20
));
}
index += 20;
assembly {
result := and(mload(add(b, index)), 0xffffffffffffffffffffffffffffffffffffffff)
}
return result;
}
function writeAddress(
bytes memory b,
uint256 index,
address input
)
internal
pure
{
if (b.length < index + 20) {
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
b.length,
index + 20
));
}
index += 20;
assembly {
let neighbors := and(
mload(add(b, index)),
0xffffffffffffffffffffffff0000000000000000000000000000000000000000
)
input := and(input, 0xffffffffffffffffffffffffffffffffffffffff)
mstore(add(b, index), xor(input, neighbors))
}
}
function readBytes32(
bytes memory b,
uint256 index
)
internal
pure
returns (bytes32 result)
{
if (b.length < index + 32) {
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
b.length,
index + 32
));
}
index += 32;
assembly {
result := mload(add(b, index))
}
return result;
}
function writeBytes32(
bytes memory b,
uint256 index,
bytes32 input
)
internal
pure
{
if (b.length < index + 32) {
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
b.length,
index + 32
));
}
index += 32;
assembly {
mstore(add(b, index), input)
}
}
function readUint256(
bytes memory b,
uint256 index
)
internal
pure
returns (uint256 result)
{
result = uint256(readBytes32(b, index));
return result;
}
function writeUint256(
bytes memory b,
uint256 index,
uint256 input
)
internal
pure
{
writeBytes32(b, index, bytes32(input));
}
function readBytes4(
bytes memory b,
uint256 index
)
internal
pure
returns (bytes4 result)
{
if (b.length < index + 4) {
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsFourRequired,
b.length,
index + 4
));
}
index += 32;
assembly {
result := mload(add(b, index))
result := and(result, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)
}
return result;
}
function writeLength(bytes memory b, uint256 length)
internal
pure
{
assembly {
mstore(b, length)
}
}
}
contract Forwarder is LibOrder {
using LibBytes for bytes;
address public ZERO_EX_EXCHANGE;
address public ZERO_EX_TOKEN_PROXY;
address payable public ETHER_TOKEN;
constructor(
address zeroExExchange,
address zeroExProxy,
address payable etherToken
)
public
{
ZERO_EX_EXCHANGE = zeroExExchange;
ZERO_EX_TOKEN_PROXY = zeroExProxy;
ETHER_TOKEN = etherToken;
IEtherToken(ETHER_TOKEN).approve(ZERO_EX_TOKEN_PROXY, (2**256)-1);
}
function fillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
public
payable
{
IEtherToken token = IEtherToken(ETHER_TOKEN);
IExchange v2Exchange = IExchange(ZERO_EX_EXCHANGE);
token.deposit.value(msg.value)();
for (uint i = 0; i < orders.length; i++) {
LibFillResults.FillResults memory result = v2Exchange.fillOrderNoThrow(
orders[i],
takerAssetFillAmounts[i],
signatures[i]
);
(address cardToken, uint256 tokenId) = abi.decode(
orders[i].makerAssetData.sliceDestructive(
4,
orders[i].makerAssetData.length
),
(address, uint256)
);
if (result.takerAssetFilledAmount == takerAssetFillAmounts[i]) {
IERC721(cardToken).transferFrom(address(this), msg.sender, tokenId);
}
}
uint remainingBalance = token.balanceOf(address(this));
token.withdraw(remainingBalance);
address(msg.sender).transfer(remainingBalance);
require(
address(this).balance == 0,
"Forwarder: must have zero ETH at the end"
);
}
function ()
external
payable
{
require(
msg.sender == address(ETHER_TOKEN),
"Forwarder: will not accept ETH from only ether token address"
);
}
}