Simplified the dydx bridge implememtation that does not use the bridge as the maker.
This commit is contained in:
		@@ -64,6 +64,10 @@
 | 
			
		||||
            {
 | 
			
		||||
                "note": "Implement `KyberBridge`.",
 | 
			
		||||
                "pr": 2352
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "note": "Implement `DydxBridge`.",
 | 
			
		||||
                "pr": 2365
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "timestamp": 1575290197
 | 
			
		||||
 
 | 
			
		||||
@@ -1,293 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										179
									
								
								contracts/asset-proxy/contracts/src/bridges/DydxBridge.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								contracts/asset-proxy/contracts/src/bridges/DydxBridge.sol
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,179 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  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-utils/contracts/src/DeploymentConstants.sol";
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/Authorizable.sol";
 | 
			
		||||
import "../interfaces/IERC20Bridge.sol";
 | 
			
		||||
import "../interfaces/IDydxBridge.sol";
 | 
			
		||||
import "../interfaces/IDydx.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract DydxBridge is
 | 
			
		||||
    IERC20Bridge,
 | 
			
		||||
    IDydxBridge,
 | 
			
		||||
    DeploymentConstants,
 | 
			
		||||
    Authorizable
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /// @dev Callback for `IERC20Bridge`. Deposits or withdraws tokens from a dydx account.
 | 
			
		||||
    ///      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 signature validated by the 0x Exchange.
 | 
			
		||||
    ///         4. The `from` address must be the maker (and hence is the owner or operator of the dydx account).
 | 
			
		||||
    ///            This is asserted during execution of this function.
 | 
			
		||||
    /// @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());
 | 
			
		||||
 | 
			
		||||
        // Assert that `from` is the owner or an operator of the dydx account.
 | 
			
		||||
        require(
 | 
			
		||||
            from == bridgeData.accountOwner || dydx.getIsLocalOperator(bridgeData.accountOwner, from),
 | 
			
		||||
            "INVALID_DYDX_OWNER_OR_OPERATOR"
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Construct dydx account info.
 | 
			
		||||
        IDydx.AccountInfo[] memory accounts = new IDydx.AccountInfo[](1);
 | 
			
		||||
        accounts[0] = IDydx.AccountInfo({
 | 
			
		||||
            owner: bridgeData.accountOwner,
 | 
			
		||||
            number: bridgeData.accountNumber
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Create dydx action.
 | 
			
		||||
        IDydx.ActionArgs[] memory actions = new IDydx.ActionArgs[](1);
 | 
			
		||||
        if (bridgeData.action == BridgeAction.Deposit) {
 | 
			
		||||
            actions[0] = _createDepositAction(
 | 
			
		||||
                from,
 | 
			
		||||
                amount,
 | 
			
		||||
                bridgeData
 | 
			
		||||
            );
 | 
			
		||||
        } else if (bridgeData.action == BridgeAction.Withdraw) {
 | 
			
		||||
            actions[0] = _createWithdrawAction(
 | 
			
		||||
                to,
 | 
			
		||||
                amount,
 | 
			
		||||
                bridgeData
 | 
			
		||||
            );
 | 
			
		||||
        } else {
 | 
			
		||||
            // If all values in the `Action` enum are handled then this
 | 
			
		||||
            // revert is unreachable: Solidity will revert when casting
 | 
			
		||||
            // from `uint8` to `Action`.
 | 
			
		||||
            revert("UNRECOGNIZED_ACTION");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Run operation. This will revert on failure.
 | 
			
		||||
        dydx.operate(accounts, actions);
 | 
			
		||||
        return BRIDGE_SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Returns a dydx `DepositAction`.
 | 
			
		||||
    /// @param depositFrom Deposit tokens from this address.
 | 
			
		||||
    /// @param amount of tokens to deposit.
 | 
			
		||||
    /// @param bridgeData A `BridgeData` struct.
 | 
			
		||||
    function _createDepositAction(
 | 
			
		||||
        address depositFrom,
 | 
			
		||||
        uint256 amount,
 | 
			
		||||
        BridgeData memory bridgeData
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        pure
 | 
			
		||||
        returns (IDydx.ActionArgs memory)
 | 
			
		||||
    {
 | 
			
		||||
        // Create dydx amount.
 | 
			
		||||
        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.
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Create dydx deposit action.
 | 
			
		||||
        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`.
 | 
			
		||||
            primaryMarketId: bridgeData.marketId,           // indicates which token to deposit.
 | 
			
		||||
            otherAddress: depositFrom,                      // deposit from this address.
 | 
			
		||||
            // unused parameters
 | 
			
		||||
            secondaryMarketId: 0,
 | 
			
		||||
            otherAccountId: 0,
 | 
			
		||||
            data: hex''
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return depositAction;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Returns a dydx `WithdrawAction`.
 | 
			
		||||
    /// @param withdrawTo Withdraw tokens to this address.
 | 
			
		||||
    /// @param amount of tokens to withdraw.
 | 
			
		||||
    /// @param bridgeData A `BridgeData` struct.
 | 
			
		||||
    function _createWithdrawAction(
 | 
			
		||||
        address withdrawTo,
 | 
			
		||||
        uint256 amount,
 | 
			
		||||
        BridgeData memory bridgeData
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        pure
 | 
			
		||||
        returns (IDydx.ActionArgs memory)
 | 
			
		||||
    {
 | 
			
		||||
        // Create dydx amount.
 | 
			
		||||
        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.
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Create withdraw action.
 | 
			
		||||
        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`.
 | 
			
		||||
            primaryMarketId: bridgeData.marketId,           // indicates which token to withdraw.
 | 
			
		||||
            otherAddress: withdrawTo,                       // withdraw tokens to this address.
 | 
			
		||||
            // unused parameters
 | 
			
		||||
            secondaryMarketId: 0,
 | 
			
		||||
            otherAccountId: 0,
 | 
			
		||||
            data: hex''
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return withdrawAction;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -87,13 +87,17 @@ interface IDydx {
 | 
			
		||||
    )
 | 
			
		||||
        external;
 | 
			
		||||
 | 
			
		||||
    /// @dev Get the ERC20 token address for a market.
 | 
			
		||||
    /// @param  marketId  The market to query
 | 
			
		||||
    /// @return           The token address
 | 
			
		||||
    function getMarketTokenAddress(
 | 
			
		||||
        uint256 marketId
 | 
			
		||||
    ///
 | 
			
		||||
    /// @dev Return true if a particular address is approved as an operator for an owner's accounts.
 | 
			
		||||
    /// Approved operators can act on the accounts of the owner as if it were the operator's own.
 | 
			
		||||
    /// @param  owner     The owner of the accounts
 | 
			
		||||
    /// @param  operator  The possible operator
 | 
			
		||||
    /// @return           True if operator is approved for owner's accounts
 | 
			
		||||
    function getIsLocalOperator(
 | 
			
		||||
        address owner,
 | 
			
		||||
        address operator
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (address);
 | 
			
		||||
        returns (bool);
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
interface IDydxBridge {
 | 
			
		||||
 | 
			
		||||
    enum BridgeAction {
 | 
			
		||||
        Deposit,                    // Deposit tokens into dydx account.
 | 
			
		||||
        Withdraw                    // Withdraw tokens from dydx account.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct BridgeData {
 | 
			
		||||
        BridgeAction action;        // Action to run on dydx account.
 | 
			
		||||
        address accountOwner;       // The owner of the dydx account.
 | 
			
		||||
        uint256 accountNumber;      // Account number used to identify the owner's specific account.
 | 
			
		||||
        uint256 marketId;           // Market to operate on.
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -19,17 +19,93 @@
 | 
			
		||||
pragma solidity ^0.5.9;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
 | 
			
		||||
import "../src/bridges/DydxBridge.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract TestDydxBridge {
 | 
			
		||||
// solhint-disable space-after-comma
 | 
			
		||||
contract TestDydxBridge is
 | 
			
		||||
    IDydx,
 | 
			
		||||
    DydxBridge
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
     function OrderWithHash(
 | 
			
		||||
        LibOrder.Order calldata order,
 | 
			
		||||
        bytes32 orderHash
 | 
			
		||||
    address public accountOperator;
 | 
			
		||||
 | 
			
		||||
    constructor(address _accountOperator)
 | 
			
		||||
        public
 | 
			
		||||
        DydxBridge()
 | 
			
		||||
    {
 | 
			
		||||
        accountOperator = _accountOperator;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    event OperateAccount(
 | 
			
		||||
        address owner,
 | 
			
		||||
        uint256 number
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    event OperateAction(
 | 
			
		||||
        ActionType actionType,
 | 
			
		||||
        uint256 accountId,
 | 
			
		||||
        bool amountSign,
 | 
			
		||||
        AssetDenomination amountDenomination,
 | 
			
		||||
        AssetReference amountRef,
 | 
			
		||||
        uint256 amountValue,
 | 
			
		||||
        uint256 primaryMarketId,
 | 
			
		||||
        uint256 secondaryMarketId,
 | 
			
		||||
        address otherAddress,
 | 
			
		||||
        uint256 otherAccountId,
 | 
			
		||||
        bytes data
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    /// @dev Simulates `operate` in dydx contract.
 | 
			
		||||
    ///      Emits events so that arguments can be validated client-side.
 | 
			
		||||
    function operate(
 | 
			
		||||
        AccountInfo[] calldata accounts,
 | 
			
		||||
        ActionArgs[] calldata actions
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        pure
 | 
			
		||||
    {}
 | 
			
		||||
    {
 | 
			
		||||
        for (uint i = 0; i < accounts.length; ++i) {
 | 
			
		||||
            emit OperateAccount(
 | 
			
		||||
                accounts[i].owner,
 | 
			
		||||
                accounts[i].number
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (uint i = 0; i < actions.length; ++i) {
 | 
			
		||||
            emit OperateAction(
 | 
			
		||||
                actions[i].actionType,
 | 
			
		||||
                actions[i].accountId,
 | 
			
		||||
                actions[i].amount.sign,
 | 
			
		||||
                actions[i].amount.denomination,
 | 
			
		||||
                actions[i].amount.ref,
 | 
			
		||||
                actions[i].amount.value,
 | 
			
		||||
                actions[i].primaryMarketId,
 | 
			
		||||
                actions[i].secondaryMarketId,
 | 
			
		||||
                actions[i].otherAddress,
 | 
			
		||||
                actions[i].otherAccountId,
 | 
			
		||||
                actions[i].data
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Return true iff `operator` equals `accountOperator` in state.
 | 
			
		||||
    function getIsLocalOperator(
 | 
			
		||||
        address /* owner */,
 | 
			
		||||
        address operator
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (bool)
 | 
			
		||||
    {
 | 
			
		||||
        return operator == accountOperator;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev overrides `_getDydxAddress()` from `DeploymentConstants` to return this address.
 | 
			
		||||
    function _getDydxAddress()
 | 
			
		||||
        internal
 | 
			
		||||
        view
 | 
			
		||||
        returns (address)
 | 
			
		||||
    {
 | 
			
		||||
        return address(this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,120 +1,204 @@
 | 
			
		||||
import {
 | 
			
		||||
    blockchainTests,
 | 
			
		||||
    constants,
 | 
			
		||||
    expect,
 | 
			
		||||
    getRandomInteger,
 | 
			
		||||
    hexLeftPad,
 | 
			
		||||
    hexRandom,
 | 
			
		||||
    OrderFactory,
 | 
			
		||||
    orderHashUtils,
 | 
			
		||||
    randomAddress,
 | 
			
		||||
    verifyEventsFromLogs,
 | 
			
		||||
} from '@0x/contracts-test-utils';
 | 
			
		||||
import { blockchainTests, constants, expect, verifyEventsFromLogs } from '@0x/contracts-test-utils';
 | 
			
		||||
import { AuthorizableRevertErrors } from '@0x/contracts-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 { TestDydxBridgeContract, TestDydxBridgeEvents } from './wrappers';
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
blockchainTests.resets('DydxBridge unit tests', env => {
 | 
			
		||||
    const accountNumber = new BigNumber(1);
 | 
			
		||||
    const marketId = new BigNumber(2);
 | 
			
		||||
    let testContract: TestDydxBridgeContract;
 | 
			
		||||
    let owner: string;
 | 
			
		||||
    let dydxAccountOwner: string;
 | 
			
		||||
    let bridgeDataEncoder: AbiEncoder.DataType;
 | 
			
		||||
    let eip1271Encoder: TestDydxBridgeContract;
 | 
			
		||||
    let assetDataEncoder: IAssetDataContract;
 | 
			
		||||
    let orderFactory: OrderFactory;
 | 
			
		||||
    let authorized: string;
 | 
			
		||||
    let notAuthorized: string;
 | 
			
		||||
    let accountOwner: string;
 | 
			
		||||
    let accountOperator: string;
 | 
			
		||||
    let notAccountOwnerNorOperator: string;
 | 
			
		||||
    let receiver: string;
 | 
			
		||||
 | 
			
		||||
    before(async () => {
 | 
			
		||||
        // Get accounts
 | 
			
		||||
        const accounts = await env.web3Wrapper.getAvailableAddressesAsync();
 | 
			
		||||
        [
 | 
			
		||||
            owner,
 | 
			
		||||
            authorized,
 | 
			
		||||
            notAuthorized,
 | 
			
		||||
            accountOwner,
 | 
			
		||||
            accountOperator,
 | 
			
		||||
            notAccountOwnerNorOperator,
 | 
			
		||||
            receiver,
 | 
			
		||||
        ] = accounts;
 | 
			
		||||
 | 
			
		||||
        // Deploy dydx bridge
 | 
			
		||||
        testContract = await DydxBridgeContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            artifacts.DydxBridge,
 | 
			
		||||
        testContract = await TestDydxBridgeContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            artifacts.TestDydxBridge,
 | 
			
		||||
            env.provider,
 | 
			
		||||
            env.txDefaults,
 | 
			
		||||
            artifacts,
 | 
			
		||||
            accountOperator,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // 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);
 | 
			
		||||
        // Authorize `authorized` account on `testContract`.
 | 
			
		||||
        await testContract.addAuthorizedAddress(authorized).awaitTransactionSuccessAsync({ from: owner });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    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(
 | 
			
		||||
    describe('bridgeTransferFrom()', () => {
 | 
			
		||||
        interface BridgeData {
 | 
			
		||||
            action: number;
 | 
			
		||||
            accountOwner: string;
 | 
			
		||||
            accountNumber: BigNumber;
 | 
			
		||||
            marketId: BigNumber;
 | 
			
		||||
        }
 | 
			
		||||
        enum DydxBridgeActions {
 | 
			
		||||
            Deposit,
 | 
			
		||||
            Withdraw,
 | 
			
		||||
        }
 | 
			
		||||
        let defaultBridgeData: any;
 | 
			
		||||
        let bridgeDataEncoder: AbiEncoder.DataType;
 | 
			
		||||
        const callBridgeTransferFrom = async (
 | 
			
		||||
            from: string,
 | 
			
		||||
            bridgeData: BridgeData,
 | 
			
		||||
            sender: string,
 | 
			
		||||
        ): Promise<string> => {
 | 
			
		||||
            const returnValue = await testContract
 | 
			
		||||
                .bridgeTransferFrom(
 | 
			
		||||
                    constants.NULL_ADDRESS,
 | 
			
		||||
                    testContract.address,
 | 
			
		||||
                    encodedBridgeData
 | 
			
		||||
                    from,
 | 
			
		||||
                    receiver,
 | 
			
		||||
                    new BigNumber(1),
 | 
			
		||||
                    bridgeDataEncoder.encode(bridgeData),
 | 
			
		||||
                )
 | 
			
		||||
            .getABIEncodedTransactionData()
 | 
			
		||||
            const signedOrder = await orderFactory.newSignedOrderAsync({
 | 
			
		||||
                makerAssetData,
 | 
			
		||||
            });
 | 
			
		||||
            const signedOrderHash = orderHashUtils.getOrderHashHex(signedOrder);
 | 
			
		||||
                .callAsync({ from: sender });
 | 
			
		||||
            return returnValue;
 | 
			
		||||
        };
 | 
			
		||||
        const callBridgeTransferFromAndVerifyEvents = async (
 | 
			
		||||
            actionType: number,
 | 
			
		||||
            actionAddress: string,
 | 
			
		||||
            from: string,
 | 
			
		||||
            bridgeData: BridgeData,
 | 
			
		||||
            sender: string,
 | 
			
		||||
        ): Promise<void> => {
 | 
			
		||||
            // Execute transaction.
 | 
			
		||||
            const txReceipt = await testContract
 | 
			
		||||
                .bridgeTransferFrom(
 | 
			
		||||
                    constants.NULL_ADDRESS,
 | 
			
		||||
                    from,
 | 
			
		||||
                    receiver,
 | 
			
		||||
                    new BigNumber(1),
 | 
			
		||||
                    bridgeDataEncoder.encode(bridgeData),
 | 
			
		||||
                )
 | 
			
		||||
                .awaitTransactionSuccessAsync({ from: sender });
 | 
			
		||||
 | 
			
		||||
            // 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
 | 
			
		||||
            // Verify `OperateAccount` event.
 | 
			
		||||
            verifyEventsFromLogs(
 | 
			
		||||
                txReceipt.logs,
 | 
			
		||||
                [
 | 
			
		||||
                    {
 | 
			
		||||
                        owner: accountOwner,
 | 
			
		||||
                        number: accountNumber,
 | 
			
		||||
                    },
 | 
			
		||||
                ],
 | 
			
		||||
                TestDydxBridgeEvents.OperateAccount,
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            // Validate signature
 | 
			
		||||
            const result = await testContract.isValidSignature(eip1271Data, eip1271Signature).callAsync();
 | 
			
		||||
            expect(result).to.eq(SUCCESS_BYTES);
 | 
			
		||||
            // Verify `OperateAction` event.
 | 
			
		||||
            const accountId = new BigNumber(0);
 | 
			
		||||
            const positiveAmountSign = true;
 | 
			
		||||
            const weiDenomination = 0;
 | 
			
		||||
            const absoluteAmountRef = 1;
 | 
			
		||||
            verifyEventsFromLogs(
 | 
			
		||||
                txReceipt.logs,
 | 
			
		||||
                [
 | 
			
		||||
                    {
 | 
			
		||||
                        actionType,
 | 
			
		||||
                        accountId,
 | 
			
		||||
                        amountSign: positiveAmountSign,
 | 
			
		||||
                        amountDenomination: weiDenomination,
 | 
			
		||||
                        amountRef: absoluteAmountRef,
 | 
			
		||||
                        amountValue: new BigNumber(1),
 | 
			
		||||
                        primaryMarketId: marketId,
 | 
			
		||||
                        secondaryMarketId: constants.ZERO_AMOUNT,
 | 
			
		||||
                        otherAddress: actionAddress,
 | 
			
		||||
                        otherAccountId: constants.ZERO_AMOUNT,
 | 
			
		||||
                        data: '0x',
 | 
			
		||||
                    },
 | 
			
		||||
                ],
 | 
			
		||||
                TestDydxBridgeEvents.OperateAction,
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
        before(async () => {
 | 
			
		||||
            // Construct default bridge data
 | 
			
		||||
            defaultBridgeData = {
 | 
			
		||||
                action: DydxBridgeActions.Deposit as number,
 | 
			
		||||
                accountOwner,
 | 
			
		||||
                accountNumber,
 | 
			
		||||
                marketId,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            // Create encoder for bridge data
 | 
			
		||||
            bridgeDataEncoder = AbiEncoder.create([
 | 
			
		||||
                { name: 'action', type: 'uint8' },
 | 
			
		||||
                { name: 'accountOwner', type: 'address' },
 | 
			
		||||
                { name: 'accountNumber', type: 'uint256' },
 | 
			
		||||
                { name: 'marketId', type: 'uint256' },
 | 
			
		||||
            ]);
 | 
			
		||||
        });
 | 
			
		||||
        it('succeeds if `from` owns the dydx account', async () => {
 | 
			
		||||
            await callBridgeTransferFrom(accountOwner, defaultBridgeData, authorized);
 | 
			
		||||
        });
 | 
			
		||||
        it('succeeds if `from` operates the dydx account', async () => {
 | 
			
		||||
            await callBridgeTransferFrom(accountOperator, defaultBridgeData, authorized);
 | 
			
		||||
        });
 | 
			
		||||
        it('reverts if `from` is neither the owner nor the operator of the dydx account', async () => {
 | 
			
		||||
            const tx = callBridgeTransferFrom(notAccountOwnerNorOperator, defaultBridgeData, authorized);
 | 
			
		||||
            const expectedError = 'INVALID_DYDX_OWNER_OR_OPERATOR';
 | 
			
		||||
            return expect(tx).to.revertWith(expectedError);
 | 
			
		||||
        });
 | 
			
		||||
        it('succeeds when calling `operate` with the `deposit` action', async () => {
 | 
			
		||||
            const depositAction = 0;
 | 
			
		||||
            const depositFrom = accountOwner;
 | 
			
		||||
            const bridgeData = {
 | 
			
		||||
                ...defaultBridgeData,
 | 
			
		||||
                action: depositAction,
 | 
			
		||||
            };
 | 
			
		||||
            await callBridgeTransferFromAndVerifyEvents(
 | 
			
		||||
                depositAction,
 | 
			
		||||
                depositFrom,
 | 
			
		||||
                accountOwner,
 | 
			
		||||
                bridgeData,
 | 
			
		||||
                authorized,
 | 
			
		||||
            );
 | 
			
		||||
        });
 | 
			
		||||
        it('succeeds when calling `operate` with the `withdraw` action', async () => {
 | 
			
		||||
            const withdrawAction = 1;
 | 
			
		||||
            const withdrawTo = receiver;
 | 
			
		||||
            const bridgeData = {
 | 
			
		||||
                ...defaultBridgeData,
 | 
			
		||||
                action: withdrawAction,
 | 
			
		||||
            };
 | 
			
		||||
            await callBridgeTransferFromAndVerifyEvents(
 | 
			
		||||
                withdrawAction,
 | 
			
		||||
                withdrawTo,
 | 
			
		||||
                accountOwner,
 | 
			
		||||
                bridgeData,
 | 
			
		||||
                authorized,
 | 
			
		||||
            );
 | 
			
		||||
        });
 | 
			
		||||
        it('reverts if called by an unauthorized account', async () => {
 | 
			
		||||
            const callBridgeTransferFromPromise = callBridgeTransferFrom(
 | 
			
		||||
                accountOwner,
 | 
			
		||||
                defaultBridgeData,
 | 
			
		||||
                notAuthorized,
 | 
			
		||||
            );
 | 
			
		||||
            const expectedError = new AuthorizableRevertErrors.SenderNotAuthorizedError(notAuthorized);
 | 
			
		||||
            return expect(callBridgeTransferFromPromise).to.revertWith(expectedError);
 | 
			
		||||
        });
 | 
			
		||||
        it('should return magic bytes if call succeeds', async () => {
 | 
			
		||||
            const returnValue = await callBridgeTransferFrom(accountOwner, defaultBridgeData, authorized);
 | 
			
		||||
            expect(returnValue).to.equal(AssetProxyId.ERC20Bridge);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,10 @@
 | 
			
		||||
        "changes": [
 | 
			
		||||
            {
 | 
			
		||||
                "note": "Add `DEV_UTILS_ADDRESS` and `KYBER_ETH_ADDRESS` to `DeploymentConstants`.",
 | 
			
		||||
                "pr": 2395
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "note": "Add `DydxBridge` to `DeploymentConstants`.",
 | 
			
		||||
                "pr": 2365
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user