DyDx bridge implementation using contract as maker with signature validation.
This commit is contained in:
		
							
								
								
									
										293
									
								
								contracts/asset-proxy/contracts/src/bridges/DyDxBridge.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								contracts/asset-proxy/contracts/src/bridges/DyDxBridge.sol
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,293 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2019 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.5.9;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
 | 
			
		||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
 | 
			
		||||
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
 | 
			
		||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/Authorizable.sol";
 | 
			
		||||
import "../interfaces/IERC20Bridge.sol";
 | 
			
		||||
import "../interfaces/IDydx.sol";
 | 
			
		||||
import "../interfaces/IAssetData.sol";
 | 
			
		||||
 | 
			
		||||
// solhint-disable space-after-comma
 | 
			
		||||
contract DydxBridge is
 | 
			
		||||
    IERC20Bridge,
 | 
			
		||||
    DeploymentConstants,
 | 
			
		||||
    Authorizable
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    using LibBytes for bytes;
 | 
			
		||||
 | 
			
		||||
    // Return values for `isValidSignature`
 | 
			
		||||
    //  bytes4(keccak256('isValidSignature(bytes,bytes)'))
 | 
			
		||||
    bytes4 constant VALID_SIGNATURE_RETURN_VALUE = bytes4(0x20c13b0b);
 | 
			
		||||
    bytes4 constant INVALID_SIGNATURE_RETURN_VALUE = bytes4(0);
 | 
			
		||||
 | 
			
		||||
    // OrderWithHash(LibOrder.Order order, bytes32 orderHash).selector
 | 
			
		||||
    // == bytes4(keccak256('OrderWithHash((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),bytes32)'))
 | 
			
		||||
    bytes4 constant EIP1271_ORDER_WITH_HASH_SELECTOR = bytes4(0x3efe50c8);
 | 
			
		||||
 | 
			
		||||
    struct BridgeData {
 | 
			
		||||
        // Fields used by dydx
 | 
			
		||||
        address dydxAccountOwner;           // The owner of the dydx account.
 | 
			
		||||
        uint256 dydxAccountNumber;          // Account number used to identify the owner's specific account.
 | 
			
		||||
        address dydxAccountOperator;        // Operator of dydx account who signed the order (if transferred by an operator).
 | 
			
		||||
        uint256 dydxFromMarketId;           // Market ID of `from` asset.
 | 
			
		||||
        uint256 dydxToMarketId;             // Market ID of `to` asset.
 | 
			
		||||
        // Fields used by bridge
 | 
			
		||||
        bool shouldDepositIntodydx;         // True iff contract balance should be deposited into dydx account.
 | 
			
		||||
        address fromTokenAddress;           // The token given to `from` or deposited into the dydx account.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Callback for `IERC20Bridge`.
 | 
			
		||||
    ///      Function Prerequisite:
 | 
			
		||||
    ///        1. Tokens are held in this contract that correspond to `dydxFromMarketId`, and
 | 
			
		||||
    ///        2. Tokens are held in a dydx account that correspond to `dydxToMarketId`
 | 
			
		||||
    ///
 | 
			
		||||
    ///      When called, two actions take place:
 | 
			
		||||
    ///        1. The total balance held by this contract is either (i) deposited into the dydx account OR (ii) transferred to `from`.
 | 
			
		||||
    ///           This is dictated by `BridgeData.shouldDepositIntoDydx`.
 | 
			
		||||
    ///        2. Some `amount` of tokens are withdrawn from the dydx account into the address `to`.
 | 
			
		||||
    ///
 | 
			
		||||
    ///      Notes:
 | 
			
		||||
    ///         1. This bridge must be set as an operator of the dydx account that is being operated on.
 | 
			
		||||
    ///         2. This function may only be called in the context of the 0x Exchange executing an order (ERC20Bridge is authorized).
 | 
			
		||||
    ///         3. The order must be signed by the owner or an operator of the dydx account. This is validated in `isValidSignature`.
 | 
			
		||||
    /// @param from The sender of the tokens.
 | 
			
		||||
    /// @param to The recipient of the tokens.
 | 
			
		||||
    /// @param amount Minimum amount of `toTokenAddress` tokens to buy.
 | 
			
		||||
    /// @param encodedBridgeData An abi-encoded `BridgeData` struct.
 | 
			
		||||
    /// @return success The magic bytes if successful.
 | 
			
		||||
    function bridgeTransferFrom(
 | 
			
		||||
        address,
 | 
			
		||||
        address from,
 | 
			
		||||
        address to,
 | 
			
		||||
        uint256 amount,
 | 
			
		||||
        bytes calldata encodedBridgeData
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        onlyAuthorized
 | 
			
		||||
        returns (bytes4 success)
 | 
			
		||||
    {
 | 
			
		||||
        // Decode bridge data.
 | 
			
		||||
        (BridgeData memory bridgeData) = abi.decode(encodedBridgeData, (BridgeData));
 | 
			
		||||
 | 
			
		||||
        // Cache dydx contract.
 | 
			
		||||
        IDydx dydx = IDydx(_getDydxAddress());
 | 
			
		||||
 | 
			
		||||
        // Cache the balance held by this contract.
 | 
			
		||||
        IERC20Token fromToken = IERC20Token(bridgeData.fromTokenAddress);
 | 
			
		||||
        uint256 fromTokenAmount = fromToken.balanceOf(address(this));
 | 
			
		||||
        uint256 toTokenAmount = amount;
 | 
			
		||||
 | 
			
		||||
        // Construct dydx account info.
 | 
			
		||||
        IDydx.AccountInfo[] memory accounts = new IDydx.AccountInfo[](1);
 | 
			
		||||
        accounts[0] = IDydx.AccountInfo({
 | 
			
		||||
            owner: bridgeData.dydxAccountOwner,
 | 
			
		||||
            number: bridgeData.dydxAccountNumber
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Construct arguments to `dydx.operate`.
 | 
			
		||||
        IDydx.ActionArgs[] memory actions;
 | 
			
		||||
        if (bridgeData.shouldDepositIntodydx) {
 | 
			
		||||
            // Generate deposit/withdraw actions
 | 
			
		||||
            actions = new IDydx.ActionArgs[](2);
 | 
			
		||||
            actions[0] = _createDepositAction(
 | 
			
		||||
                address(this),                  // deposit `fromToken` into dydx from this contract.
 | 
			
		||||
                fromTokenAmount,                // amount to deposit.
 | 
			
		||||
                bridgeData                      // bridge data.
 | 
			
		||||
            );
 | 
			
		||||
            actions[1] = _createWithdrawAction(
 | 
			
		||||
                to,                             // withdraw `toToken` from dydx to `to`.
 | 
			
		||||
                toTokenAmount,                  // amount to withdraw.
 | 
			
		||||
                bridgeData                      // bridge data.
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            // Allow dydx to deposit `fromToken` from this contract.
 | 
			
		||||
            LibERC20Token.approve(
 | 
			
		||||
                bridgeData.fromTokenAddress,
 | 
			
		||||
                address(dydx),
 | 
			
		||||
                uint256(-1)
 | 
			
		||||
            );
 | 
			
		||||
        } else {
 | 
			
		||||
            // Generate withdraw action
 | 
			
		||||
            actions = new IDydx.ActionArgs[](1);
 | 
			
		||||
            actions[0] = _createWithdrawAction(to, toTokenAmount, bridgeData);
 | 
			
		||||
 | 
			
		||||
            // Transfer `fromToken` to `from`
 | 
			
		||||
            require(
 | 
			
		||||
                fromToken.transfer(from, fromTokenAmount),
 | 
			
		||||
                "TRANSFER_OF_FROM_TOKEN_FAILED"
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Run operations. This will revert on failure.
 | 
			
		||||
        dydx.operate(accounts, actions);
 | 
			
		||||
        return BRIDGE_SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Returns a dydx `DepositAction`.
 | 
			
		||||
    function _createDepositAction(
 | 
			
		||||
        address depositFrom,
 | 
			
		||||
        uint256 amount,
 | 
			
		||||
        BridgeData memory bridgeData
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        pure
 | 
			
		||||
        returns (IDydx.ActionArgs memory)
 | 
			
		||||
    {
 | 
			
		||||
        // Construct action to deposit tokens held by this contract into dydx.
 | 
			
		||||
        IDydx.AssetAmount memory amountToDeposit = IDydx.AssetAmount({
 | 
			
		||||
            sign: true,                                 // true if positive.
 | 
			
		||||
            denomination: IDydx.AssetDenomination.Wei,  // Wei => actual token amount held in account.
 | 
			
		||||
            ref: IDydx.AssetReference.Target,           // Target => an absolute amount.
 | 
			
		||||
            value: amount                               // amount to deposit.
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        IDydx.ActionArgs memory depositAction = IDydx.ActionArgs({
 | 
			
		||||
            actionType: IDydx.ActionType.Deposit,           // deposit tokens.
 | 
			
		||||
            amount: amountToDeposit,                        // amount to deposit.
 | 
			
		||||
            accountId: 0,                                   // index in the `accounts` when calling `operate` below.
 | 
			
		||||
            primaryMarketId: bridgeData.dydxFromMarketId,   // indicates which token to deposit.
 | 
			
		||||
            otherAddress: depositFrom,                      // deposit tokens from `this` address.
 | 
			
		||||
            // unused parameters
 | 
			
		||||
            secondaryMarketId: 0,
 | 
			
		||||
            otherAccountId: 0,
 | 
			
		||||
            data: hex''
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return depositAction;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Returns a dydx `WithdrawAction`.
 | 
			
		||||
    function _createWithdrawAction(
 | 
			
		||||
        address withdrawTo,
 | 
			
		||||
        uint256 amount,
 | 
			
		||||
        BridgeData memory bridgeData
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        pure
 | 
			
		||||
        returns (IDydx.ActionArgs memory)
 | 
			
		||||
    {
 | 
			
		||||
        // Construct action to withdraw tokens from dydx into `to`.
 | 
			
		||||
        IDydx.AssetAmount memory amountToWithdraw = IDydx.AssetAmount({
 | 
			
		||||
            sign: true,                                 // true if positive.
 | 
			
		||||
            denomination: IDydx.AssetDenomination.Wei,  // Wei => actual token amount held in account.
 | 
			
		||||
            ref: IDydx.AssetReference.Target,           // Target => an absolute amount.
 | 
			
		||||
            value: amount                               // amount to withdraw.
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        IDydx.ActionArgs memory withdrawAction = IDydx.ActionArgs({
 | 
			
		||||
            actionType: IDydx.ActionType.Withdraw,          // withdraw tokens.
 | 
			
		||||
            amount: amountToWithdraw,                       // amount to withdraw.
 | 
			
		||||
            accountId: 0,                                   // index in the `accounts` when calling `operate` below.
 | 
			
		||||
            primaryMarketId: bridgeData.dydxToMarketId,     // indicates which token to withdraw.
 | 
			
		||||
            otherAddress: withdrawTo,                       // withdraw tokens to `to` address.
 | 
			
		||||
            // unused parameters
 | 
			
		||||
            secondaryMarketId: 0,
 | 
			
		||||
            otherAccountId: 0,
 | 
			
		||||
            data: hex''
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return withdrawAction;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Given a 0x order where the `makerAssetData` corresponds to a dydx transfer via this bridge,
 | 
			
		||||
    ///      `isValidSignature` verifies that the corresponding dydx account owner (or operator)
 | 
			
		||||
    ///      has authorized the trade by signing the input order.
 | 
			
		||||
    /// @param data Signed tuple (ZeroExOrder, hash(ZeroExOrder))
 | 
			
		||||
    /// @param signature Proof that `data` has been signed.
 | 
			
		||||
    /// @return bytes4(0x20c13b0b) if the signature check succeeds.
 | 
			
		||||
    function isValidSignature(
 | 
			
		||||
        bytes calldata data,
 | 
			
		||||
        bytes calldata signature
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (bytes4)
 | 
			
		||||
    {
 | 
			
		||||
        // Assert that `data` is an encoded `OrderWithHash`.
 | 
			
		||||
        require(
 | 
			
		||||
            data.readBytes4(0) == EIP1271_ORDER_WITH_HASH_SELECTOR,
 | 
			
		||||
            "INVALID_DATA_EXPECTED_EIP1271_ORDER_WITH_HASH"
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Assert that signature is correct length.
 | 
			
		||||
        require(
 | 
			
		||||
            signature.length == 65,
 | 
			
		||||
            "INVALID_SIGNATURE_LENGTH"
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Decode the order and hash, plus extract the dydxBridge asset data.
 | 
			
		||||
        (
 | 
			
		||||
            LibOrder.Order memory order,
 | 
			
		||||
            bytes32 orderHash
 | 
			
		||||
        ) = abi.decode(
 | 
			
		||||
                data.slice(4, data.length),
 | 
			
		||||
                (LibOrder.Order, bytes32)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Decode and validate the asset proxy id.
 | 
			
		||||
        require(
 | 
			
		||||
            order.makerAssetData.readBytes4(0) == IAssetData(address(0)).ERC20Bridge.selector,
 | 
			
		||||
            "MAKER_ASSET_DATA_NOT_ENCODED_FOR_ERC20_BRIDGE"
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Decode the ERC20 Bridge asset data.
 | 
			
		||||
        (
 | 
			
		||||
             /* address tokenAddress */,
 | 
			
		||||
            address bridgeAddress,
 | 
			
		||||
            bytes memory encodedBridgeData
 | 
			
		||||
        ) = abi.decode(
 | 
			
		||||
            order.makerAssetData.slice(4, order.makerAssetData.length),
 | 
			
		||||
            (address, address, bytes)
 | 
			
		||||
        );
 | 
			
		||||
        require(
 | 
			
		||||
            bridgeAddress == address(this),
 | 
			
		||||
            "INVALID_BRIDGE_ADDRESS"
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Decode and validate the `bridgeData` and extract the expected signer address.
 | 
			
		||||
        (BridgeData memory bridgeData) = abi.decode(encodedBridgeData, (BridgeData));
 | 
			
		||||
        address signerAddress = bridgeData.dydxAccountOperator != address(0)
 | 
			
		||||
            ? bridgeData.dydxAccountOperator
 | 
			
		||||
            : bridgeData.dydxAccountOwner;
 | 
			
		||||
 | 
			
		||||
        // Validate signature.
 | 
			
		||||
        address recovered = ecrecover(
 | 
			
		||||
            keccak256(abi.encodePacked(
 | 
			
		||||
                    "\x19Ethereum Signed Message:\n32",
 | 
			
		||||
                    orderHash
 | 
			
		||||
            )),
 | 
			
		||||
            uint8(signature[0]),        // v
 | 
			
		||||
            signature.readBytes32(1),   // r
 | 
			
		||||
            signature.readBytes32(33)   // s
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Return `VALID_SIGNATURE_RETURN_VALUE` iff signature is valid.
 | 
			
		||||
       return (signerAddress == recovered)
 | 
			
		||||
            ? VALID_SIGNATURE_RETURN_VALUE
 | 
			
		||||
            : INVALID_SIGNATURE_RETURN_VALUE;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										99
									
								
								contracts/asset-proxy/contracts/src/interfaces/IDyDx.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								contracts/asset-proxy/contracts/src/interfaces/IDyDx.sol
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,99 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2019 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.5.9;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
interface IDydx {
 | 
			
		||||
 | 
			
		||||
    /// @dev Represents the unique key that specifies an account
 | 
			
		||||
    struct AccountInfo {
 | 
			
		||||
        address owner;  // The address that owns the account
 | 
			
		||||
        uint256 number; // A nonce that allows a single address to control many accounts
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    enum ActionType {
 | 
			
		||||
        Deposit,   // supply tokens
 | 
			
		||||
        Withdraw,  // borrow tokens
 | 
			
		||||
        Transfer,  // transfer balance between accounts
 | 
			
		||||
        Buy,       // buy an amount of some token (externally)
 | 
			
		||||
        Sell,      // sell an amount of some token (externally)
 | 
			
		||||
        Trade,     // trade tokens against another account
 | 
			
		||||
        Liquidate, // liquidate an undercollateralized or expiring account
 | 
			
		||||
        Vaporize,  // use excess tokens to zero-out a completely negative account
 | 
			
		||||
        Call       // send arbitrary data to an address
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Arguments that are passed to Solo in an ordered list as part of a single operation.
 | 
			
		||||
    /// Each ActionArgs has an actionType which specifies which action struct that this data will be
 | 
			
		||||
    /// parsed into before being processed.
 | 
			
		||||
    struct ActionArgs {
 | 
			
		||||
        ActionType actionType;
 | 
			
		||||
        uint256 accountId;
 | 
			
		||||
        AssetAmount amount;
 | 
			
		||||
        uint256 primaryMarketId;
 | 
			
		||||
        uint256 secondaryMarketId;
 | 
			
		||||
        address otherAddress;
 | 
			
		||||
        uint256 otherAccountId;
 | 
			
		||||
        bytes data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    enum AssetDenomination {
 | 
			
		||||
        Wei, // the amount is denominated in wei
 | 
			
		||||
        Par  // the amount is denominated in par
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    enum AssetReference {
 | 
			
		||||
        Delta, // the amount is given as a delta from the current value
 | 
			
		||||
        Target // the amount is given as an exact number to end up at
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct AssetAmount {
 | 
			
		||||
        bool sign; // true if positive
 | 
			
		||||
        AssetDenomination denomination;
 | 
			
		||||
        AssetReference ref;
 | 
			
		||||
        uint256 value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev The main entry-point to Solo that allows users and contracts to manage accounts.
 | 
			
		||||
    ///      Take one or more actions on one or more accounts. The msg.sender must be the owner or
 | 
			
		||||
    ///      operator of all accounts except for those being liquidated, vaporized, or traded with.
 | 
			
		||||
    ///      One call to operate() is considered a singular "operation". Account collateralization is
 | 
			
		||||
    ///      ensured only after the completion of the entire operation.
 | 
			
		||||
    /// @param  accounts  A list of all accounts that will be used in this operation. Cannot contain
 | 
			
		||||
    ///                   duplicates. In each action, the relevant account will be referred-to by its
 | 
			
		||||
    ///                   index in the list.
 | 
			
		||||
    /// @param  actions   An ordered list of all actions that will be taken in this operation. The
 | 
			
		||||
    ///                   actions will be processed in order.
 | 
			
		||||
    function operate(
 | 
			
		||||
        AccountInfo[] calldata accounts,
 | 
			
		||||
        ActionArgs[] calldata actions
 | 
			
		||||
    )
 | 
			
		||||
        external;
 | 
			
		||||
 | 
			
		||||
    /// @dev Get the ERC20 token address for a market.
 | 
			
		||||
    /// @param  marketId  The market to query
 | 
			
		||||
    /// @return           The token address
 | 
			
		||||
    function getMarketTokenAddress(
 | 
			
		||||
        uint256 marketId
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (address);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								contracts/asset-proxy/contracts/test/TestDydxBridge.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								contracts/asset-proxy/contracts/test/TestDydxBridge.sol
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2019 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.5.9;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
 | 
			
		||||
import "../src/bridges/DydxBridge.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract TestDydxBridge {
 | 
			
		||||
 | 
			
		||||
     function OrderWithHash(
 | 
			
		||||
        LibOrder.Order calldata order,
 | 
			
		||||
        bytes32 orderHash
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        pure
 | 
			
		||||
    {}
 | 
			
		||||
}
 | 
			
		||||
@@ -38,8 +38,13 @@
 | 
			
		||||
        "docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
 | 
			
		||||
    },
 | 
			
		||||
    "config": {
 | 
			
		||||
<<<<<<< HEAD
 | 
			
		||||
        "publicInterfaceContracts": "ERC1155Proxy,ERC20Proxy,ERC721Proxy,MultiAssetProxy,StaticCallProxy,ERC20BridgeProxy,Eth2DaiBridge,IAssetData,IAssetProxy,UniswapBridge,KyberBridge,ChaiBridge,TestStaticCallTarget",
 | 
			
		||||
        "abis": "./test/generated-artifacts/@(ChaiBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IChai|IERC20Bridge|IEth2Dai|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestChaiBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
 | 
			
		||||
        "abis": "./test/generated-artifacts/@(ChaiBridge|DyDxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IChai|IDyDx|IERC20Bridge|IEth2Dai|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestChaiBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
 | 
			
		||||
=======
 | 
			
		||||
        "publicInterfaceContracts": "ERC1155Proxy,ERC20Proxy,ERC721Proxy,MultiAssetProxy,StaticCallProxy,ERC20BridgeProxy,Eth2DaiBridge,IAssetData,IAssetProxy,UniswapBridge,KyberBridge,TestStaticCallTarget",
 | 
			
		||||
        "abis": "./test/generated-artifacts/@(DydxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IDydx|IERC20Bridge|IEth2Dai|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
 | 
			
		||||
>>>>>>> ee82a0f67... Consistent capitalization for "dydx"`
 | 
			
		||||
        "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
 | 
			
		||||
    },
 | 
			
		||||
    "repository": {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,12 @@
 | 
			
		||||
 */
 | 
			
		||||
import { ContractArtifact } from 'ethereum-types';
 | 
			
		||||
 | 
			
		||||
<<<<<<< HEAD
 | 
			
		||||
import * as ChaiBridge from '../test/generated-artifacts/ChaiBridge.json';
 | 
			
		||||
import * as DyDxBridge from '../test/generated-artifacts/DyDxBridge.json';
 | 
			
		||||
=======
 | 
			
		||||
import * as DydxBridge from '../test/generated-artifacts/DydxBridge.json';
 | 
			
		||||
>>>>>>> ee82a0f67... Consistent capitalization for "dydx"`
 | 
			
		||||
import * as ERC1155Proxy from '../test/generated-artifacts/ERC1155Proxy.json';
 | 
			
		||||
import * as ERC20BridgeProxy from '../test/generated-artifacts/ERC20BridgeProxy.json';
 | 
			
		||||
import * as ERC20Proxy from '../test/generated-artifacts/ERC20Proxy.json';
 | 
			
		||||
@@ -15,7 +20,12 @@ import * as IAssetData from '../test/generated-artifacts/IAssetData.json';
 | 
			
		||||
import * as IAssetProxy from '../test/generated-artifacts/IAssetProxy.json';
 | 
			
		||||
import * as IAssetProxyDispatcher from '../test/generated-artifacts/IAssetProxyDispatcher.json';
 | 
			
		||||
import * as IAuthorizable from '../test/generated-artifacts/IAuthorizable.json';
 | 
			
		||||
<<<<<<< HEAD
 | 
			
		||||
import * as IChai from '../test/generated-artifacts/IChai.json';
 | 
			
		||||
import * as IDyDx from '../test/generated-artifacts/IDyDx.json';
 | 
			
		||||
=======
 | 
			
		||||
import * as IDydx from '../test/generated-artifacts/IDydx.json';
 | 
			
		||||
>>>>>>> ee82a0f67... Consistent capitalization for "dydx"`
 | 
			
		||||
import * as IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json';
 | 
			
		||||
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
 | 
			
		||||
import * as IKyberNetworkProxy from '../test/generated-artifacts/IKyberNetworkProxy.json';
 | 
			
		||||
@@ -44,7 +54,12 @@ export const artifacts = {
 | 
			
		||||
    ERC721Proxy: ERC721Proxy as ContractArtifact,
 | 
			
		||||
    MultiAssetProxy: MultiAssetProxy as ContractArtifact,
 | 
			
		||||
    StaticCallProxy: StaticCallProxy as ContractArtifact,
 | 
			
		||||
<<<<<<< HEAD
 | 
			
		||||
    ChaiBridge: ChaiBridge as ContractArtifact,
 | 
			
		||||
    DyDxBridge: DyDxBridge as ContractArtifact,
 | 
			
		||||
=======
 | 
			
		||||
    DydxBridge: DydxBridge as ContractArtifact,
 | 
			
		||||
>>>>>>> ee82a0f67... Consistent capitalization for "dydx"`
 | 
			
		||||
    Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
 | 
			
		||||
    KyberBridge: KyberBridge as ContractArtifact,
 | 
			
		||||
    UniswapBridge: UniswapBridge as ContractArtifact,
 | 
			
		||||
@@ -52,7 +67,12 @@ export const artifacts = {
 | 
			
		||||
    IAssetProxy: IAssetProxy as ContractArtifact,
 | 
			
		||||
    IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
 | 
			
		||||
    IAuthorizable: IAuthorizable as ContractArtifact,
 | 
			
		||||
<<<<<<< HEAD
 | 
			
		||||
    IChai: IChai as ContractArtifact,
 | 
			
		||||
    IDyDx: IDyDx as ContractArtifact,
 | 
			
		||||
=======
 | 
			
		||||
    IDydx: IDydx as ContractArtifact,
 | 
			
		||||
>>>>>>> ee82a0f67... Consistent capitalization for "dydx"`
 | 
			
		||||
    IERC20Bridge: IERC20Bridge as ContractArtifact,
 | 
			
		||||
    IEth2Dai: IEth2Dai as ContractArtifact,
 | 
			
		||||
    IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										120
									
								
								contracts/asset-proxy/test/dydx_bridge.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								contracts/asset-proxy/test/dydx_bridge.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,120 @@
 | 
			
		||||
import {
 | 
			
		||||
    blockchainTests,
 | 
			
		||||
    constants,
 | 
			
		||||
    expect,
 | 
			
		||||
    getRandomInteger,
 | 
			
		||||
    hexLeftPad,
 | 
			
		||||
    hexRandom,
 | 
			
		||||
    OrderFactory,
 | 
			
		||||
    orderHashUtils,
 | 
			
		||||
    randomAddress,
 | 
			
		||||
    verifyEventsFromLogs,
 | 
			
		||||
} from '@0x/contracts-test-utils';
 | 
			
		||||
import { AssetProxyId } from '@0x/types';
 | 
			
		||||
import { AbiEncoder, BigNumber } from '@0x/utils';
 | 
			
		||||
import { DecodedLogs } from 'ethereum-types';
 | 
			
		||||
import * as _ from 'lodash';
 | 
			
		||||
import * as ethUtil from 'ethereumjs-util';
 | 
			
		||||
 | 
			
		||||
import { artifacts } from './artifacts';
 | 
			
		||||
 | 
			
		||||
import { DydxBridgeContract, IAssetDataContract, TestDydxBridgeContract } from './wrappers';
 | 
			
		||||
 | 
			
		||||
blockchainTests.resets.only('Dydx unit tests', env => {
 | 
			
		||||
    const dydxAccountNumber = new BigNumber(1);
 | 
			
		||||
    const dydxFromMarketId = new BigNumber(2);
 | 
			
		||||
    const dydxToMarketId = new BigNumber(3);
 | 
			
		||||
    let testContract: DydxBridgeContract;
 | 
			
		||||
    let owner: string;
 | 
			
		||||
    let dydxAccountOwner: string;
 | 
			
		||||
    let bridgeDataEncoder: AbiEncoder.DataType;
 | 
			
		||||
    let eip1271Encoder: TestDydxBridgeContract;
 | 
			
		||||
    let assetDataEncoder: IAssetDataContract;
 | 
			
		||||
    let orderFactory: OrderFactory;
 | 
			
		||||
 | 
			
		||||
    before(async () => {
 | 
			
		||||
        // Deploy dydx bridge
 | 
			
		||||
        testContract = await DydxBridgeContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            artifacts.DydxBridge,
 | 
			
		||||
            env.provider,
 | 
			
		||||
            env.txDefaults,
 | 
			
		||||
            artifacts,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Get accounts
 | 
			
		||||
        const accounts = await env.web3Wrapper.getAvailableAddressesAsync();
 | 
			
		||||
        [owner, dydxAccountOwner] = accounts;
 | 
			
		||||
        const dydxAccountOwnerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(dydxAccountOwner)];
 | 
			
		||||
 | 
			
		||||
        // Create order factory for dydx bridge
 | 
			
		||||
        const chainId = await env.getChainIdAsync();
 | 
			
		||||
        const defaultOrderParams = {
 | 
			
		||||
            ...constants.STATIC_ORDER_PARAMS,
 | 
			
		||||
            makerAddress: testContract.address,
 | 
			
		||||
            feeRecipientAddress: randomAddress(),
 | 
			
		||||
            makerAssetData: constants.NULL_BYTES,
 | 
			
		||||
            takerAssetData: constants.NULL_BYTES,
 | 
			
		||||
            makerFeeAssetData: constants.NULL_BYTES,
 | 
			
		||||
            takerFeeAssetData: constants.NULL_BYTES,
 | 
			
		||||
            makerFee: constants.ZERO_AMOUNT,
 | 
			
		||||
            takerFee: constants.ZERO_AMOUNT,
 | 
			
		||||
            exchangeAddress: constants.NULL_ADDRESS,
 | 
			
		||||
            chainId,
 | 
			
		||||
        };
 | 
			
		||||
        orderFactory = new OrderFactory(dydxAccountOwnerPrivateKey, defaultOrderParams);
 | 
			
		||||
 | 
			
		||||
        // Create encoder for Bridge Data
 | 
			
		||||
        bridgeDataEncoder = AbiEncoder.create([
 | 
			
		||||
            {name: 'dydxAccountOwner', type: 'address'},
 | 
			
		||||
            {name: 'dydxAccountNumber', type: 'uint256'},
 | 
			
		||||
            {name: 'dydxAccountOperator', type: 'address'},
 | 
			
		||||
            {name: 'dydxFromMarketId', type: 'uint256'},
 | 
			
		||||
            {name: 'dydxToMarketId', type: 'uint256'},
 | 
			
		||||
            {name: 'shouldDepositIntoDydx', type: 'bool'},
 | 
			
		||||
            {name: 'fromTokenAddress', type: 'address'},
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        // Create encoders
 | 
			
		||||
        assetDataEncoder = new IAssetDataContract(constants.NULL_ADDRESS, env.provider);
 | 
			
		||||
        eip1271Encoder = new TestDydxBridgeContract(constants.NULL_ADDRESS, env.provider);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe('isValidSignature()', () => {
 | 
			
		||||
        const SUCCESS_BYTES = '0x20c13b0b';
 | 
			
		||||
 | 
			
		||||
        it('returns success bytes if signature is valid', async () => {
 | 
			
		||||
            // Construct valid bridge data for dydx account owner
 | 
			
		||||
            const bridgeData = {
 | 
			
		||||
                dydxAccountOwner,
 | 
			
		||||
                dydxAccountNumber,
 | 
			
		||||
                dydxAccountOperator: constants.NULL_ADDRESS,
 | 
			
		||||
                dydxFromMarketId,
 | 
			
		||||
                dydxToMarketId,
 | 
			
		||||
                shouldDepositIntoDydx: false,
 | 
			
		||||
                fromTokenAddress: constants.NULL_ADDRESS,
 | 
			
		||||
            };
 | 
			
		||||
            const encodedBridgeData = bridgeDataEncoder.encode(bridgeData);
 | 
			
		||||
 | 
			
		||||
            // Construct valid order from dydx account owner
 | 
			
		||||
            const makerAssetData = assetDataEncoder
 | 
			
		||||
                .ERC20Bridge(
 | 
			
		||||
                    constants.NULL_ADDRESS,
 | 
			
		||||
                    testContract.address,
 | 
			
		||||
                    encodedBridgeData
 | 
			
		||||
                )
 | 
			
		||||
            .getABIEncodedTransactionData()
 | 
			
		||||
            const signedOrder = await orderFactory.newSignedOrderAsync({
 | 
			
		||||
                makerAssetData,
 | 
			
		||||
            });
 | 
			
		||||
            const signedOrderHash = orderHashUtils.getOrderHashHex(signedOrder);
 | 
			
		||||
 | 
			
		||||
            // Encode `isValidSignature` parameters
 | 
			
		||||
            const eip1271Data = eip1271Encoder.OrderWithHash(signedOrder, signedOrderHash).getABIEncodedTransactionData();
 | 
			
		||||
            const eip1271Signature = ethUtil.bufferToHex(ethUtil.toBuffer(signedOrder.signature).slice(0, 65)); // pop signature type from end
 | 
			
		||||
 | 
			
		||||
            // Validate signature
 | 
			
		||||
            const result = await testContract.isValidSignature(eip1271Data, eip1271Signature).callAsync();
 | 
			
		||||
            expect(result).to.eq(SUCCESS_BYTES);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
@@ -3,7 +3,12 @@
 | 
			
		||||
 * Warning: This file is auto-generated by contracts-gen. Don't edit manually.
 | 
			
		||||
 * -----------------------------------------------------------------------------
 | 
			
		||||
 */
 | 
			
		||||
<<<<<<< HEAD
 | 
			
		||||
export * from '../test/generated-wrappers/chai_bridge';
 | 
			
		||||
export * from '../test/generated-wrappers/dy_dx_bridge';
 | 
			
		||||
=======
 | 
			
		||||
export * from '../test/generated-wrappers/dydx_bridge';
 | 
			
		||||
>>>>>>> ee82a0f67... Consistent capitalization for "dydx"`
 | 
			
		||||
export * from '../test/generated-wrappers/erc1155_proxy';
 | 
			
		||||
export * from '../test/generated-wrappers/erc20_bridge_proxy';
 | 
			
		||||
export * from '../test/generated-wrappers/erc20_proxy';
 | 
			
		||||
@@ -13,7 +18,12 @@ export * from '../test/generated-wrappers/i_asset_data';
 | 
			
		||||
export * from '../test/generated-wrappers/i_asset_proxy';
 | 
			
		||||
export * from '../test/generated-wrappers/i_asset_proxy_dispatcher';
 | 
			
		||||
export * from '../test/generated-wrappers/i_authorizable';
 | 
			
		||||
<<<<<<< HEAD
 | 
			
		||||
export * from '../test/generated-wrappers/i_chai';
 | 
			
		||||
export * from '../test/generated-wrappers/i_dy_dx';
 | 
			
		||||
=======
 | 
			
		||||
export * from '../test/generated-wrappers/i_dydx';
 | 
			
		||||
>>>>>>> ee82a0f67... Consistent capitalization for "dydx"`
 | 
			
		||||
export * from '../test/generated-wrappers/i_erc20_bridge';
 | 
			
		||||
export * from '../test/generated-wrappers/i_eth2_dai';
 | 
			
		||||
export * from '../test/generated-wrappers/i_kyber_network_proxy';
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,12 @@
 | 
			
		||||
        "generated-artifacts/StaticCallProxy.json",
 | 
			
		||||
        "generated-artifacts/TestStaticCallTarget.json",
 | 
			
		||||
        "generated-artifacts/UniswapBridge.json",
 | 
			
		||||
<<<<<<< HEAD
 | 
			
		||||
        "test/generated-artifacts/ChaiBridge.json",
 | 
			
		||||
        "test/generated-artifacts/DyDxBridge.json",
 | 
			
		||||
=======
 | 
			
		||||
        "test/generated-artifacts/DydxBridge.json",
 | 
			
		||||
>>>>>>> ee82a0f67... Consistent capitalization for "dydx"`
 | 
			
		||||
        "test/generated-artifacts/ERC1155Proxy.json",
 | 
			
		||||
        "test/generated-artifacts/ERC20BridgeProxy.json",
 | 
			
		||||
        "test/generated-artifacts/ERC20Proxy.json",
 | 
			
		||||
@@ -26,7 +31,12 @@
 | 
			
		||||
        "test/generated-artifacts/IAssetProxy.json",
 | 
			
		||||
        "test/generated-artifacts/IAssetProxyDispatcher.json",
 | 
			
		||||
        "test/generated-artifacts/IAuthorizable.json",
 | 
			
		||||
<<<<<<< HEAD
 | 
			
		||||
        "test/generated-artifacts/IChai.json",
 | 
			
		||||
        "test/generated-artifacts/IDyDx.json",
 | 
			
		||||
=======
 | 
			
		||||
        "test/generated-artifacts/IDydx.json",
 | 
			
		||||
>>>>>>> ee82a0f67... Consistent capitalization for "dydx"`
 | 
			
		||||
        "test/generated-artifacts/IERC20Bridge.json",
 | 
			
		||||
        "test/generated-artifacts/IEth2Dai.json",
 | 
			
		||||
        "test/generated-artifacts/IKyberNetworkProxy.json",
 | 
			
		||||
 
 | 
			
		||||
@@ -42,6 +42,8 @@ contract DeploymentConstants {
 | 
			
		||||
    address constant private DEV_UTILS_ADDRESS = 0xcCc2431a7335F21d9268bA62F0B32B0f2EFC463f;
 | 
			
		||||
    /// @dev Kyber ETH pseudo-address.
 | 
			
		||||
    address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
 | 
			
		||||
    /// @dev Mainnet address of the DyDx contract.
 | 
			
		||||
    address constant private DYDX_ADDRESS = 0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e;
 | 
			
		||||
 | 
			
		||||
    /// @dev Overridable way to get the `KyberNetworkProxy` address.
 | 
			
		||||
    /// @return kyberAddress The `IKyberNetworkProxy` address.
 | 
			
		||||
@@ -122,4 +124,14 @@ contract DeploymentConstants {
 | 
			
		||||
    {
 | 
			
		||||
        return DEV_UTILS_ADDRESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Overridable way to get the DyDx contract.
 | 
			
		||||
    /// @return exchange The DyDx exchange contract.
 | 
			
		||||
    function _getDyDxAddress()
 | 
			
		||||
        internal
 | 
			
		||||
        view
 | 
			
		||||
        returns (address dydxAddress)
 | 
			
		||||
    {
 | 
			
		||||
        return DYDX_ADDRESS;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user