Skip to content
This repository has been archived by the owner on Jul 9, 2021. It is now read-only.

Commit

Permalink
Merge branch 'development' into feature/python/generate-exchange-wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
feuGeneA committed Jul 18, 2019
2 parents 6833c91 + 1aeea39 commit 33af363
Show file tree
Hide file tree
Showing 54 changed files with 3,443 additions and 1,483 deletions.
2 changes: 1 addition & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
# Automatically collapse generated files in GitHub.
*.svg linguist-generated=true
packages/contract-artifacts/artifacts/*json linguist-generated=true
packages/abi-gen-wrappers/src/generated-wrappers/*.ts liguist-generated=true
packages/abi-gen-wrappers/src/generated-wrappers/*.ts linguist-generated=true

384 changes: 53 additions & 331 deletions contracts/asset-proxy/contracts/src/ERC1155Proxy.sol

Large diffs are not rendered by default.

61 changes: 45 additions & 16 deletions contracts/asset-proxy/contracts/src/MultiAssetProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,10 @@ contract MultiAssetProxy is
// offset to assetData.

// Load offset to `assetData`
let assetDataOffset := calldataload(4)
let assetDataOffset := add(calldataload(4), 4)

// Load length in bytes of `assetData`
let assetDataLength := calldataload(assetDataOffset)

// Asset data itself is encoded as follows:
//
Expand All @@ -108,41 +111,62 @@ contract MultiAssetProxy is
// | | 132 + a | b | nestedAssetData Contents (offsets) |
// | | 132 + a + b | | nestedAssetData[0, ..., len] |

// Assert that the length of asset data:
// 1. Must be at least 68 bytes (see table above)
// 2. Must be a multiple of 32 (excluding the 4-byte selector)
if or(lt(assetDataLength, 68), mod(sub(assetDataLength, 4), 32)) {
// Revert with `Error("INVALID_ASSET_DATA_LENGTH")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x00000019494e56414c49445f41535345545f444154415f4c454e475448000000)
mstore(96, 0)
revert(0, 100)
}

// End of asset data in calldata
// assetDataOffset
// + 32 (assetData len)
let assetDataEnd := add(assetDataOffset, add(assetDataLength, 32))
if gt(assetDataEnd, calldatasize()) {
// Revert with `Error("INVALID_ASSET_DATA_END")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x00000016494e56414c49445f41535345545f444154415f454e44000000000000)
mstore(96, 0)
revert(0, 100)
}

// In order to find the offset to `amounts`, we must add:
// 4 (function selector)
// + assetDataOffset
// assetDataOffset
// + 32 (assetData len)
// + 4 (assetProxyId)
let amountsOffset := calldataload(add(assetDataOffset, 40))
let amountsOffset := calldataload(add(assetDataOffset, 36))

// In order to find the offset to `nestedAssetData`, we must add:
// 4 (function selector)
// + assetDataOffset
// assetDataOffset
// + 32 (assetData len)
// + 4 (assetProxyId)
// + 32 (amounts offset)
let nestedAssetDataOffset := calldataload(add(assetDataOffset, 72))
let nestedAssetDataOffset := calldataload(add(assetDataOffset, 68))

// In order to find the start of the `amounts` contents, we must add:
// 4 (function selector)
// + assetDataOffset
// assetDataOffset
// + 32 (assetData len)
// + 4 (assetProxyId)
// + amountsOffset
// + 32 (amounts len)
let amountsContentsStart := add(assetDataOffset, add(amountsOffset, 72))
let amountsContentsStart := add(assetDataOffset, add(amountsOffset, 68))

// Load number of elements in `amounts`
let amountsLen := calldataload(sub(amountsContentsStart, 32))

// In order to find the start of the `nestedAssetData` contents, we must add:
// 4 (function selector)
// + assetDataOffset
// assetDataOffset
// + 32 (assetData len)
// + 4 (assetProxyId)
// + nestedAssetDataOffset
// + 32 (nestedAssetData len)
let nestedAssetDataContentsStart := add(assetDataOffset, add(nestedAssetDataOffset, 72))
let nestedAssetDataContentsStart := add(assetDataOffset, add(nestedAssetDataOffset, 68))

// Load number of elements in `nestedAssetData`
let nestedAssetDataLen := calldataload(sub(nestedAssetDataContentsStart, 32))
Expand Down Expand Up @@ -204,15 +228,20 @@ contract MultiAssetProxy is
let nestedAssetDataElementOffset := calldataload(add(nestedAssetDataContentsStart, i))

// In order to find the start of the `nestedAssetData[i]` contents, we must add:
// 4 (function selector)
// + assetDataOffset
// assetDataOffset
// + 32 (assetData len)
// + 4 (assetProxyId)
// + nestedAssetDataOffset
// + 32 (nestedAssetData len)
// + nestedAssetDataElementOffset
// + 32 (nestedAssetDataElement len)
let nestedAssetDataElementContentsStart := add(assetDataOffset, add(nestedAssetDataOffset, add(nestedAssetDataElementOffset, 104)))
let nestedAssetDataElementContentsStart := add(
assetDataOffset,
add(
nestedAssetDataOffset,
add(nestedAssetDataElementOffset, 100)
)
)

// Load length of `nestedAssetData[i]`
let nestedAssetDataElementLenStart := sub(nestedAssetDataElementContentsStart, 32)
Expand Down
208 changes: 42 additions & 166 deletions contracts/asset-proxy/contracts/src/StaticCallProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,181 +18,57 @@

pragma solidity ^0.5.9;

import "@0x/contracts-utils/contracts/src/LibBytes.sol";


// solhint-disable no-unused-vars
contract StaticCallProxy {

using LibBytes for bytes;

// Id of this proxy.
bytes4 constant internal PROXY_ID = bytes4(keccak256("StaticCall(address,bytes,bytes32)"));

// solhint-disable-next-line payable-fallback
function ()
/// @dev Makes a staticcall to a target address and verifies that the data returned matches the expected return data.
/// @param assetData Byte array encoded with staticCallTarget, staticCallData, and expectedCallResultHash
/// @param from This value is ignored.
/// @param to This value is ignored.
/// @param amount This value is ignored.
function transferFrom(
bytes calldata assetData,
address from,
address to,
uint256 amount
)
external
view
{
assembly {
// The first 4 bytes of calldata holds the function selector
let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)

// `transferFrom` will be called with the following parameters:
// assetData Encoded byte array.
// from Address to transfer asset from.
// to Address to transfer asset to.
// amount Amount of asset to transfer.
// bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {

// `transferFrom`.
// The function is marked `external`, so no abi decoding is done for
// us. Instead, we expect the `calldata` memory to contain the
// following:
//
// | Area | Offset | Length | Contents |
// |----------|--------|---------|-------------------------------------|
// | Header | 0 | 4 | function selector |
// | Params | | 4 * 32 | function parameters: |
// | | 4 | | 1. offset to assetData (*) |
// | | 36 | | 2. from |
// | | 68 | | 3. to |
// | | 100 | | 4. amount |
// | Data | | | assetData: |
// | | 132 | 32 | assetData Length |
// | | 164 | ** | assetData Contents |
//
// (*): offset is computed from start of function parameters, so offset
// by an additional 4 bytes in the calldata.
//
// (**): see table below to compute length of assetData Contents
// (***): Note that the `from`, `to`, and `amount` params in calldata are ignored in this function.
//
// WARNING: The ABIv2 specification allows additional padding between
// the Params and Data section. This will result in a larger
// offset to assetData.

// Load offset to `assetData`
let assetDataOffset := add(calldataload(4), 4)

// Validate length of `assetData`
let assetDataLen := calldataload(assetDataOffset)
if or(lt(assetDataLen, 100), mod(sub(assetDataLen, 4), 32)) {
// Revert with `Error("INVALID_ASSET_DATA_LENGTH")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x00000019494e56414c49445f41535345545f444154415f4c454e475448000000)
mstore(96, 0)
revert(0, 100)
}

// Ensure that `assetData` ends inside of calldata
let assetDataEnd := add(assetDataOffset, add(assetDataLen, 32))
if gt(assetDataEnd, calldatasize()) {
// Revert with `Error("INVALID_ASSET_DATA_END")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x00000016494e56414c49445f41535345545f444154415f454e44000000000000)
mstore(96, 0)
revert(0, 100)
}

// Asset data is encoded as follows:
// | Area | Offset | Length | Contents |
// |----------|-------------|---------|--------------------------------------|
// | Header | 0 | 4 | assetProxyId |
// | Params | | 4 * 32 | function parameters: |
// | | 4 | | 1. address of callTarget |
// | | 36 | | 2. offset to staticCallData (*) |
// | | 68 | | 3. expected 32 byte hash of output |
// | Data | | | staticCallData: |
// | | 100 | 32 | 1. staticCallData Length |
// | | 132 | a | 2. staticCallData Contents |

// In order to find the offset to `staticCallData`, we must add:
// assetDataOffset
// + 32 (assetData len)
// + 4 (proxyId)
// + 32 (callTarget)
let paramsInAssetDataOffset := add(assetDataOffset, 36)
let staticCallDataOffset := add(paramsInAssetDataOffset, calldataload(add(assetDataOffset, 68)))

// Load length of `staticCallData`
let staticCallDataLen := calldataload(staticCallDataOffset)

// Ensure `staticCallData` does not begin to outside of `assetData`
let staticCallDataBegin := add(staticCallDataOffset, 32)
let staticCallDataEnd := add(staticCallDataBegin, staticCallDataLen)
if gt(staticCallDataEnd, assetDataEnd) {
// Revert with `Error("INVALID_STATIC_CALL_DATA_OFFSET")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x0000001f494e56414c49445f5354415449435f43414c4c5f444154415f4f4646)
mstore(96, 0x5345540000000000000000000000000000000000000000000000000000000000)
revert(0, 100)
}

// Copy `staticCallData` into memory
calldatacopy(
0, // memory can be safely overwritten from beginning
staticCallDataBegin, // start of `staticCallData`
staticCallDataLen // copy the entire `staticCallData`
)

// In order to find the offset to `callTarget`, we must add:
// assetDataOffset
// + 32 (assetData len)
// + 4 (proxyId)
let callTarget := and(
calldataload(add(assetDataOffset, 36)),
0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff
)

// Perform `callTarget.staticcall(staticCallData)`
let success := staticcall(
gas, // forward all gas
callTarget, // call address `callTarget`
0, // pointer to start of input
staticCallDataLen, // length of input
0, // start of memory can be safely overwritten
0 // don't copy output to memory
)

// Copy entire output to start of memory
let outputLen := returndatasize()
returndatacopy(
0, // copy to memory at 0
0, // copy from return data at 0
outputLen // copy all return data
)

// Revert with reason given by `callTarget` if staticcall is unsuccessful
if iszero(success) {
revert(0, outputLen)
}

// Calculate hash of output
let callResultHash := keccak256(0, outputLen)

// In order to find the offset to `expectedCallResultHash`, we must add:
// assetDataOffset
// + 32 (assetData len)
// + 4 (proxyId)
// + 32 (callTarget)
// + 32 (staticCallDataOffset)
let expectedResultHash := calldataload(add(assetDataOffset, 100))

if sub(callResultHash, expectedResultHash) {
// Revert with `Error("UNEXPECTED_STATIC_CALL_RESULT")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x0000001d554e45585045435445445f5354415449435f43414c4c5f524553554c)
mstore(96, 0x5400000000000000000000000000000000000000000000000000000000000000)
revert(0, 100)
}

// Return if output matched expected output
return(0, 0)
// Decode params from `assetData`
(
address staticCallTarget,
bytes memory staticCallData,
bytes32 expectedReturnDataHash
) = abi.decode(
assetData.sliceDestructive(4, assetData.length),
(address, bytes, bytes32)
);

// Execute staticcall
(bool success, bytes memory returnData) = staticCallTarget.staticcall(staticCallData);

// Revert with returned data if staticcall is unsuccessful
if (!success) {
assembly {
revert(add(returnData, 32), mload(returnData))
}

// Revert if undefined function is called
revert(0, 0)
}

// Revert if hash of return data is not as expected
bytes32 returnDataHash = keccak256(returnData);
require(
expectedReturnDataHash == returnDataHash,
"UNEXPECTED_STATIC_CALL_RESULT"
);
}

/// @dev Gets the proxy id associated with the proxy address.
Expand All @@ -204,4 +80,4 @@ contract StaticCallProxy {
{
return PROXY_ID;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ pragma solidity ^0.5.5;
import "./IAuthorizable.sol";


contract IAssetProxy is
IAuthorizable
{
contract IAssetProxy {

/// @dev Transfers assets. Either succeeds or throws.
/// @param assetData Byte array encoded for the respective asset proxy.
/// @param from Address to transfer asset from.
Expand Down
Loading

0 comments on commit 33af363

Please sign in to comment.