Implement sender abstraction

This commit is contained in:
Amir Bandeali
2018-04-24 11:24:28 -07:00
parent e1cab78c09
commit 732202fe8e
6 changed files with 210 additions and 47 deletions

View File

@@ -24,13 +24,15 @@ import "./MixinSignatureValidator.sol";
import "./MixinSettlement.sol";
import "./MixinWrapperFunctions.sol";
import "./MixinAssetProxyDispatcher.sol";
import "./MixinTransactions.sol";
contract Exchange is
MixinExchangeCore,
MixinSignatureValidator,
MixinSettlement,
MixinWrapperFunctions,
MixinAssetProxyDispatcher
MixinAssetProxyDispatcher,
MixinTransactions
{
string constant public VERSION = "2.0.1-alpha";
@@ -42,5 +44,6 @@ contract Exchange is
MixinSettlement(_zrxProxyData)
MixinWrapperFunctions()
MixinAssetProxyDispatcher()
MixinTransactions()
{}
}

View File

@@ -26,6 +26,7 @@ contract LibOrder {
"address makerAddress",
"address takerAddress",
"address feeRecipientAddress",
"address senderAddress",
"uint256 makerAssetAmount",
"uint256 takerAssetAmount",
"uint256 makerFee",
@@ -40,6 +41,7 @@ contract LibOrder {
address makerAddress;
address takerAddress;
address feeRecipientAddress;
address senderAddress;
uint256 makerAssetAmount;
uint256 takerAssetAmount;
uint256 makerFee;
@@ -66,6 +68,7 @@ contract LibOrder {
order.makerAddress,
order.takerAddress,
order.feeRecipientAddress,
order.senderAddress,
order.makerAssetAmount,
order.takerAssetAmount,
order.makerFee,

View File

@@ -18,10 +18,12 @@
pragma solidity ^0.4.21;
pragma experimental ABIEncoderV2;
pragma experimental "v0.5.0";
import "./mixins/MExchangeCore.sol";
import "./mixins/MSettlement.sol";
import "./mixins/MSignatureValidator.sol";
import "./mixins/MTransactions.sol";
import "./LibOrder.sol";
import "./LibErrors.sol";
import "./LibPartialAmount.sol";
@@ -35,6 +37,7 @@ contract MixinExchangeCore is
MExchangeCore,
MSettlement,
MSignatureValidator,
MTransactions,
SafeMath,
LibErrors,
LibPartialAmount
@@ -113,10 +116,16 @@ contract MixinExchangeCore is
require(order.takerAssetAmount > 0);
require(isValidSignature(orderHash, order.makerAddress, signature));
}
// Validate sender
if (order.senderAddress != address(0)) {
require(order.senderAddress == msg.sender);
}
// Validate taker
// Validate transaction signed by taker
address takerAddress = getSignerAddress();
if (order.takerAddress != address(0)) {
require(order.takerAddress == msg.sender);
require(order.takerAddress == takerAddress);
}
require(takerAssetFillAmount > 0);
@@ -146,21 +155,10 @@ contract MixinExchangeCore is
// Settle order
(fillResults.makerAssetFilledAmount, fillResults.makerFeePaid, fillResults.takerFeePaid) =
settleOrder(order, msg.sender, fillResults.takerAssetFilledAmount);
settleOrder(order, takerAddress, fillResults.takerAssetFilledAmount);
// Log order
emit Fill(
order.makerAddress,
msg.sender,
order.feeRecipientAddress,
fillResults.makerAssetFilledAmount,
fillResults.takerAssetFilledAmount,
fillResults.makerFeePaid,
fillResults.takerFeePaid,
orderHash,
order.makerAssetData,
order.takerAssetData
);
emitFillEvent(order, takerAddress, orderHash, fillResults);
return fillResults;
}
@@ -178,8 +176,16 @@ contract MixinExchangeCore is
// Validate the order
require(order.makerAssetAmount > 0);
require(order.takerAssetAmount > 0);
require(order.makerAddress == msg.sender);
// Validate sender
if (order.senderAddress != address(0)) {
require(order.senderAddress == msg.sender);
}
// Validate transaction signed by maker
address makerAddress = getSignerAddress();
require(order.makerAddress == makerAddress);
if (block.timestamp >= order.expirationTimeSeconds) {
emit ExchangeError(uint8(Errors.ORDER_EXPIRED), orderHash);
return false;
@@ -233,4 +239,27 @@ contract MixinExchangeCore is
isError = errPercentageTimes1000000 > 1000;
return isError;
}
/// @dev Logs a Fill event with the given arguments.
/// The sole purpose of this function is to get around the stack variable limit.
function emitFillEvent(
Order memory order,
address takerAddress,
bytes32 orderHash,
FillResults memory fillResults)
internal
{
emit Fill(
order.makerAddress,
takerAddress,
order.feeRecipientAddress,
fillResults.makerAssetFilledAmount,
fillResults.takerAssetFilledAmount,
fillResults.makerFeePaid,
fillResults.takerFeePaid,
orderHash,
order.makerAssetData,
order.takerAssetData
);
}
}

View File

@@ -0,0 +1,85 @@
/*
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.4.21;
pragma experimental ABIEncoderV2;
import "./mixins/MSignatureValidator.sol";
import "./mixins/MTransactions.sol";
contract MixinTransactions is
MSignatureValidator,
MTransactions
{
// Mapping of transaction hash => executed
mapping (bytes32 => bool) public transactions;
// Address of current transaction signer
address currentSigner;
/// @dev Executes an exchange method call in the context of signer.
/// @param salt Arbitrary number to ensure uniqueness of transaction hash.
/// @param signer Address of transaction signer.
/// @param data AbiV2 encoded calldata.
/// @param signature Proof of signer transaction by signer.
function executeTransaction(
uint256 salt,
address signer,
bytes data,
bytes signature)
external
{
// Prevent reentrancy
require(currentSigner == address(0));
// Calculate transaction hash
bytes32 transactionHash = keccak256(
address(this),
salt,
data
);
// Validate transaction has not been executed
require(!transactions[transactionHash]);
// TODO: is SignatureType.Caller necessary if we make this check?
if (signer != msg.sender) {
// Validate signature
require(isValidSignature(transactionHash, signer, signature));
// Set the current transaction signer
currentSigner = signer;
}
// Execute transaction
transactions[transactionHash] = true;
require(address(this).delegatecall(data));
// Reset current transaction signer
currentSigner = address(0);
}
function getSignerAddress()
internal
view
returns (address)
{
address signerAddress = currentSigner == address(0) ? msg.sender : currentSigner;
return signerAddress;
}
}

View File

@@ -75,24 +75,25 @@ contract MixinWrapperFunctions is
// | | 0x00 | | 1. offset to order (*) |
// | | 0x20 | | 2. takerAssetFillAmount |
// | | 0x40 | | 3. offset to signature (*) |
// | Data | | 11 * 32 | order: |
// | | 0x000 | | 1. makerAddress |
// | | 0x020 | | 2. takerAddress |
// | | 0x040 | | 3. feeRecipientAddress |
// | | 0x060 | | 4. makerAssetAmount |
// | | 0x080 | | 5. takerAssetAmount |
// | | 0x0A0 | | 6. makerFeeAmount |
// | | 0x0C0 | | 7. takerFeeAmount |
// | | 0x0E0 | | 8. expirationTimeSeconds |
// | | 0x100 | | 9. salt |
// | | 0x120 | | 10. Offset to makerAssetProxyMetadata (*) |
// | | 0x140 | | 11. Offset to takerAssetProxyMetadata (*) |
// | | 0x160 | 32 | makerAssetProxyMetadata Length |
// | | 0x180 | ** | makerAssetProxyMetadata Contents |
// | | 0x1A0 | 32 | takerAssetProxyMetadata Length |
// | | 0x1C0 | ** | takerAssetProxyMetadata Contents |
// | | 0x1E0 | 32 | signature Length |
// | | 0x200 | ** | signature Contents |
// | Data | | 12 * 32 | order: |
// | | 0x000 | | 1. senderAddress |
// | | 0x020 | | 2. makerAddress |
// | | 0x040 | | 3. takerAddress |
// | | 0x060 | | 4. feeRecipientAddress |
// | | 0x080 | | 5. makerAssetAmount |
// | | 0x0A0 | | 6. takerAssetAmount |
// | | 0x0C0 | | 7. makerFeeAmount |
// | | 0x0E0 | | 8. takerFeeAmount |
// | | 0x100 | | 9. expirationTimeSeconds |
// | | 0x120 | | 10. salt |
// | | 0x140 | | 11. Offset to makerAssetProxyMetadata (*) |
// | | 0x160 | | 12. Offset to takerAssetProxyMetadata (*) |
// | | 0x180 | 32 | makerAssetProxyMetadata Length |
// | | 0x1A0 | ** | makerAssetProxyMetadata Contents |
// | | 0x1C0 | 32 | takerAssetProxyMetadata Length |
// | | 0x1E0 | ** | takerAssetProxyMetadata Contents |
// | | 0x200 | 32 | signature Length |
// | | 0x220 | ** | signature Contents |
// * Offsets are calculated from the beginning of the current area: Header, Params, Data:
// An offset stored in the Params area is calculated from the beginning of the Params section.
@@ -150,19 +151,20 @@ contract MixinWrapperFunctions is
mstore(dataAreaEnd, mload(sourceOffset)) // makerAddress
mstore(add(dataAreaEnd, 0x20), mload(add(sourceOffset, 0x20))) // takerAddress
mstore(add(dataAreaEnd, 0x40), mload(add(sourceOffset, 0x40))) // feeRecipientAddress
mstore(add(dataAreaEnd, 0x60), mload(add(sourceOffset, 0x60))) // makerAssetAmount
mstore(add(dataAreaEnd, 0x80), mload(add(sourceOffset, 0x80))) // takerAssetAmount
mstore(add(dataAreaEnd, 0xA0), mload(add(sourceOffset, 0xA0))) // makerFeeAmount
mstore(add(dataAreaEnd, 0xC0), mload(add(sourceOffset, 0xC0))) // takerFeeAmount
mstore(add(dataAreaEnd, 0xE0), mload(add(sourceOffset, 0xE0))) // expirationTimeSeconds
mstore(add(dataAreaEnd, 0x100), mload(add(sourceOffset, 0x100))) // salt
mstore(add(dataAreaEnd, 0x120), mload(add(sourceOffset, 0x120))) // Offset to makerAssetProxyMetadata
mstore(add(dataAreaEnd, 0x140), mload(add(sourceOffset, 0x140))) // Offset to takerAssetProxyMetadata
dataAreaEnd := add(dataAreaEnd, 0x160)
sourceOffset := add(sourceOffset, 0x160)
mstore(add(dataAreaEnd, 0x60), mload(add(sourceOffset, 0x60))) // senderAddress
mstore(add(dataAreaEnd, 0x80), mload(add(sourceOffset, 0x80))) // makerAssetAmount
mstore(add(dataAreaEnd, 0xA0), mload(add(sourceOffset, 0xA0))) // takerAssetAmount
mstore(add(dataAreaEnd, 0xC0), mload(add(sourceOffset, 0xC0))) // makerFeeAmount
mstore(add(dataAreaEnd, 0xE0), mload(add(sourceOffset, 0xE0))) // takerFeeAmount
mstore(add(dataAreaEnd, 0x100), mload(add(sourceOffset, 0x100))) // expirationTimeSeconds
mstore(add(dataAreaEnd, 0x120), mload(add(sourceOffset, 0x120))) // salt
mstore(add(dataAreaEnd, 0x140), mload(add(sourceOffset, 0x140))) // Offset to makerAssetProxyMetadata
mstore(add(dataAreaEnd, 0x160), mload(add(sourceOffset, 0x160))) // Offset to takerAssetProxyMetadata
dataAreaEnd := add(dataAreaEnd, 0x180)
sourceOffset := add(sourceOffset, 0x180)
// Write offset to <order.makerAssetProxyMetadata>
mstore(add(dataAreaStart, mul(9, 0x20)), sub(dataAreaEnd, dataAreaStart))
mstore(add(dataAreaStart, mul(10, 0x20)), sub(dataAreaEnd, dataAreaStart))
// Calculate length of <order.makerAssetProxyMetadata>
arrayLenBytes := mload(sourceOffset)
@@ -181,7 +183,7 @@ contract MixinWrapperFunctions is
}
// Write offset to <order.takerAssetProxyMetadata>
mstore(add(dataAreaStart, mul(10, 0x20)), sub(dataAreaEnd, dataAreaStart))
mstore(add(dataAreaStart, mul(11, 0x20)), sub(dataAreaEnd, dataAreaStart))
// Calculate length of <order.takerAssetProxyMetadata>
arrayLenBytes := mload(sourceOffset)

View File

@@ -0,0 +1,41 @@
/*
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.4.21;
pragma experimental ABIEncoderV2;
import "./MSignatureValidator.sol";
contract MTransactions is MSignatureValidator {
/// @dev Executes an exchange method call in the context of signer.
/// @param salt Arbitrary number to ensure uniqueness of transaction hash.
/// @param signer Address of transaction signer.
/// @param data AbiV2 encoded calldata.
/// @param signature Proof of signer transaction by signer.
function executeTransaction(
uint256 salt,
address signer,
bytes data,
bytes signature)
external;
function getSignerAddress()
internal
view
returns (address);
}