From 732202fe8e4f3eb1847515a0c3f8f2d068d35baf Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Tue, 24 Apr 2018 11:24:28 -0700 Subject: Implement sender abstraction --- .../current/protocol/Exchange/Exchange.sol | 5 +- .../current/protocol/Exchange/LibOrder.sol | 3 + .../protocol/Exchange/MixinExchangeCore.sol | 61 ++++++++++++---- .../protocol/Exchange/MixinTransactions.sol | 85 ++++++++++++++++++++++ .../protocol/Exchange/MixinWrapperFunctions.sol | 62 ++++++++-------- .../protocol/Exchange/mixins/MTransactions.sol | 41 +++++++++++ 6 files changed, 210 insertions(+), 47 deletions(-) create mode 100644 packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol create mode 100644 packages/contracts/src/contracts/current/protocol/Exchange/mixins/MTransactions.sol diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol b/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol index 5ccb06b88..caf48bfca 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol @@ -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() {} } diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol b/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol index 61a4a382d..a98d7cbeb 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol @@ -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, diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol index 710315515..97c1f1541 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol @@ -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 + ); + } } diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol new file mode 100644 index 000000000..e4fc4767b --- /dev/null +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol @@ -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; + } +} diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol index 2c0fd0751..4d6ba17dd 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol @@ -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 - mstore(add(dataAreaStart, mul(9, 0x20)), sub(dataAreaEnd, dataAreaStart)) + mstore(add(dataAreaStart, mul(10, 0x20)), sub(dataAreaEnd, dataAreaStart)) // Calculate length of arrayLenBytes := mload(sourceOffset) @@ -181,7 +183,7 @@ contract MixinWrapperFunctions is } // Write offset to - mstore(add(dataAreaStart, mul(10, 0x20)), sub(dataAreaEnd, dataAreaStart)) + mstore(add(dataAreaStart, mul(11, 0x20)), sub(dataAreaEnd, dataAreaStart)) // Calculate length of arrayLenBytes := mload(sourceOffset) diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MTransactions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MTransactions.sol new file mode 100644 index 000000000..31209de02 --- /dev/null +++ b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MTransactions.sol @@ -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); +} -- cgit v1.2.3