aboutsummaryrefslogtreecommitdiffstats
path: root/contracts/exchange-libs/contracts/libs/LibAbiEncoder.sol
diff options
context:
space:
mode:
Diffstat (limited to 'contracts/exchange-libs/contracts/libs/LibAbiEncoder.sol')
-rw-r--r--contracts/exchange-libs/contracts/libs/LibAbiEncoder.sol215
1 files changed, 215 insertions, 0 deletions
diff --git a/contracts/exchange-libs/contracts/libs/LibAbiEncoder.sol b/contracts/exchange-libs/contracts/libs/LibAbiEncoder.sol
new file mode 100644
index 000000000..5422bfeec
--- /dev/null
+++ b/contracts/exchange-libs/contracts/libs/LibAbiEncoder.sol
@@ -0,0 +1,215 @@
+/*
+
+ 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.24;
+pragma experimental ABIEncoderV2;
+
+import "./LibOrder.sol";
+
+
+contract LibAbiEncoder {
+
+ /// @dev ABI encodes calldata for `fillOrder`.
+ /// @param order Order struct containing order specifications.
+ /// @param takerAssetFillAmount Desired amount of takerAsset to sell.
+ /// @param signature Proof that order has been created by maker.
+ /// @return ABI encoded calldata for `fillOrder`.
+ function abiEncodeFillOrder(
+ LibOrder.Order memory order,
+ uint256 takerAssetFillAmount,
+ bytes memory signature
+ )
+ internal
+ pure
+ returns (bytes memory fillOrderCalldata)
+ {
+ // We need to call MExchangeCore.fillOrder using a delegatecall in
+ // assembly so that we can intercept a call that throws. For this, we
+ // need the input encoded in memory in the Ethereum ABIv2 format [1].
+
+ // | Area | Offset | Length | Contents |
+ // | -------- |--------|---------|-------------------------------------------- |
+ // | Header | 0x00 | 4 | function selector |
+ // | Params | | 3 * 32 | function parameters: |
+ // | | 0x00 | | 1. offset to order (*) |
+ // | | 0x20 | | 2. takerAssetFillAmount |
+ // | | 0x40 | | 3. offset to signature (*) |
+ // | 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 makerAssetData (*) |
+ // | | 0x160 | | 12. Offset to takerAssetData (*) |
+ // | | 0x180 | 32 | makerAssetData Length |
+ // | | 0x1A0 | ** | makerAssetData Contents |
+ // | | 0x1C0 | 32 | takerAssetData Length |
+ // | | 0x1E0 | ** | takerAssetData 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.
+ // An offset stored in the Data area is calculated from the beginning of the Data section.
+
+ // ** The length of dynamic array contents are stored in the field immediately preceeding the contents.
+
+ // [1]: https://solidity.readthedocs.io/en/develop/abi-spec.html
+
+ assembly {
+
+ // Areas below may use the following variables:
+ // 1. <area>Start -- Start of this area in memory
+ // 2. <area>End -- End of this area in memory. This value may
+ // be precomputed (before writing contents),
+ // or it may be computed as contents are written.
+ // 3. <area>Offset -- Current offset into area. If an area's End
+ // is precomputed, this variable tracks the
+ // offsets of contents as they are written.
+
+ /////// Setup Header Area ///////
+ // Load free memory pointer
+ fillOrderCalldata := mload(0x40)
+ // bytes4(keccak256("fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)"))
+ // = 0xb4be83d5
+ // Leave 0x20 bytes to store the length
+ mstore(add(fillOrderCalldata, 0x20), 0xb4be83d500000000000000000000000000000000000000000000000000000000)
+ let headerAreaEnd := add(fillOrderCalldata, 0x24)
+
+ /////// Setup Params Area ///////
+ // This area is preallocated and written to later.
+ // This is because we need to fill in offsets that have not yet been calculated.
+ let paramsAreaStart := headerAreaEnd
+ let paramsAreaEnd := add(paramsAreaStart, 0x60)
+ let paramsAreaOffset := paramsAreaStart
+
+ /////// Setup Data Area ///////
+ let dataAreaStart := paramsAreaEnd
+ let dataAreaEnd := dataAreaStart
+
+ // Offset from the source data we're reading from
+ let sourceOffset := order
+ // arrayLenBytes and arrayLenWords track the length of a dynamically-allocated bytes array.
+ let arrayLenBytes := 0
+ let arrayLenWords := 0
+
+ /////// Write order Struct ///////
+ // Write memory location of Order, relative to the start of the
+ // parameter list, then increment the paramsAreaOffset respectively.
+ mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart))
+ paramsAreaOffset := add(paramsAreaOffset, 0x20)
+
+ // Write values for each field in the order
+ // It would be nice to use a loop, but we save on gas by writing
+ // the stores sequentially.
+ 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))) // 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 makerAssetData
+ mstore(add(dataAreaEnd, 0x160), mload(add(sourceOffset, 0x160))) // Offset to takerAssetData
+ dataAreaEnd := add(dataAreaEnd, 0x180)
+ sourceOffset := add(sourceOffset, 0x180)
+
+ // Write offset to <order.makerAssetData>
+ mstore(add(dataAreaStart, mul(10, 0x20)), sub(dataAreaEnd, dataAreaStart))
+
+ // Calculate length of <order.makerAssetData>
+ sourceOffset := mload(add(order, 0x140)) // makerAssetData
+ arrayLenBytes := mload(sourceOffset)
+ sourceOffset := add(sourceOffset, 0x20)
+ arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)
+
+ // Write length of <order.makerAssetData>
+ mstore(dataAreaEnd, arrayLenBytes)
+ dataAreaEnd := add(dataAreaEnd, 0x20)
+
+ // Write contents of <order.makerAssetData>
+ for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
+ mstore(dataAreaEnd, mload(sourceOffset))
+ dataAreaEnd := add(dataAreaEnd, 0x20)
+ sourceOffset := add(sourceOffset, 0x20)
+ }
+
+ // Write offset to <order.takerAssetData>
+ mstore(add(dataAreaStart, mul(11, 0x20)), sub(dataAreaEnd, dataAreaStart))
+
+ // Calculate length of <order.takerAssetData>
+ sourceOffset := mload(add(order, 0x160)) // takerAssetData
+ arrayLenBytes := mload(sourceOffset)
+ sourceOffset := add(sourceOffset, 0x20)
+ arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)
+
+ // Write length of <order.takerAssetData>
+ mstore(dataAreaEnd, arrayLenBytes)
+ dataAreaEnd := add(dataAreaEnd, 0x20)
+
+ // Write contents of <order.takerAssetData>
+ for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
+ mstore(dataAreaEnd, mload(sourceOffset))
+ dataAreaEnd := add(dataAreaEnd, 0x20)
+ sourceOffset := add(sourceOffset, 0x20)
+ }
+
+ /////// Write takerAssetFillAmount ///////
+ mstore(paramsAreaOffset, takerAssetFillAmount)
+ paramsAreaOffset := add(paramsAreaOffset, 0x20)
+
+ /////// Write signature ///////
+ // Write offset to paramsArea
+ mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart))
+
+ // Calculate length of signature
+ sourceOffset := signature
+ arrayLenBytes := mload(sourceOffset)
+ sourceOffset := add(sourceOffset, 0x20)
+ arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)
+
+ // Write length of signature
+ mstore(dataAreaEnd, arrayLenBytes)
+ dataAreaEnd := add(dataAreaEnd, 0x20)
+
+ // Write contents of signature
+ for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
+ mstore(dataAreaEnd, mload(sourceOffset))
+ dataAreaEnd := add(dataAreaEnd, 0x20)
+ sourceOffset := add(sourceOffset, 0x20)
+ }
+
+ // Set length of calldata
+ mstore(fillOrderCalldata, sub(dataAreaEnd, add(fillOrderCalldata, 0x20)))
+
+ // Increment free memory pointer
+ mstore(0x40, dataAreaEnd)
+ }
+
+ return fillOrderCalldata;
+ }
+}