DyDx bridge implementation using contract as maker with signature validation.

This commit is contained in:
Greg Hysen
2019-11-20 16:42:13 -08:00
parent 70870ffcd2
commit 56cbb69401
9 changed files with 605 additions and 1 deletions

View 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;
}
}

View 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);
}

View 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
{}
}

View File

@@ -38,8 +38,13 @@
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES" "docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
}, },
"config": { "config": {
<<<<<<< HEAD
"publicInterfaceContracts": "ERC1155Proxy,ERC20Proxy,ERC721Proxy,MultiAssetProxy,StaticCallProxy,ERC20BridgeProxy,Eth2DaiBridge,IAssetData,IAssetProxy,UniswapBridge,KyberBridge,ChaiBridge,TestStaticCallTarget", "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." "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
}, },
"repository": { "repository": {

View File

@@ -5,7 +5,12 @@
*/ */
import { ContractArtifact } from 'ethereum-types'; import { ContractArtifact } from 'ethereum-types';
<<<<<<< HEAD
import * as ChaiBridge from '../test/generated-artifacts/ChaiBridge.json'; 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 ERC1155Proxy from '../test/generated-artifacts/ERC1155Proxy.json';
import * as ERC20BridgeProxy from '../test/generated-artifacts/ERC20BridgeProxy.json'; import * as ERC20BridgeProxy from '../test/generated-artifacts/ERC20BridgeProxy.json';
import * as ERC20Proxy from '../test/generated-artifacts/ERC20Proxy.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 IAssetProxy from '../test/generated-artifacts/IAssetProxy.json';
import * as IAssetProxyDispatcher from '../test/generated-artifacts/IAssetProxyDispatcher.json'; import * as IAssetProxyDispatcher from '../test/generated-artifacts/IAssetProxyDispatcher.json';
import * as IAuthorizable from '../test/generated-artifacts/IAuthorizable.json'; import * as IAuthorizable from '../test/generated-artifacts/IAuthorizable.json';
<<<<<<< HEAD
import * as IChai from '../test/generated-artifacts/IChai.json'; 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 IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json';
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json'; import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
import * as IKyberNetworkProxy from '../test/generated-artifacts/IKyberNetworkProxy.json'; import * as IKyberNetworkProxy from '../test/generated-artifacts/IKyberNetworkProxy.json';
@@ -44,7 +54,12 @@ export const artifacts = {
ERC721Proxy: ERC721Proxy as ContractArtifact, ERC721Proxy: ERC721Proxy as ContractArtifact,
MultiAssetProxy: MultiAssetProxy as ContractArtifact, MultiAssetProxy: MultiAssetProxy as ContractArtifact,
StaticCallProxy: StaticCallProxy as ContractArtifact, StaticCallProxy: StaticCallProxy as ContractArtifact,
<<<<<<< HEAD
ChaiBridge: ChaiBridge as ContractArtifact, ChaiBridge: ChaiBridge as ContractArtifact,
DyDxBridge: DyDxBridge as ContractArtifact,
=======
DydxBridge: DydxBridge as ContractArtifact,
>>>>>>> ee82a0f67... Consistent capitalization for "dydx"`
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact, Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
KyberBridge: KyberBridge as ContractArtifact, KyberBridge: KyberBridge as ContractArtifact,
UniswapBridge: UniswapBridge as ContractArtifact, UniswapBridge: UniswapBridge as ContractArtifact,
@@ -52,7 +67,12 @@ export const artifacts = {
IAssetProxy: IAssetProxy as ContractArtifact, IAssetProxy: IAssetProxy as ContractArtifact,
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact, IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
IAuthorizable: IAuthorizable as ContractArtifact, IAuthorizable: IAuthorizable as ContractArtifact,
<<<<<<< HEAD
IChai: IChai as ContractArtifact, IChai: IChai as ContractArtifact,
IDyDx: IDyDx as ContractArtifact,
=======
IDydx: IDydx as ContractArtifact,
>>>>>>> ee82a0f67... Consistent capitalization for "dydx"`
IERC20Bridge: IERC20Bridge as ContractArtifact, IERC20Bridge: IERC20Bridge as ContractArtifact,
IEth2Dai: IEth2Dai as ContractArtifact, IEth2Dai: IEth2Dai as ContractArtifact,
IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact, IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact,

View 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);
});
});
});

View File

@@ -3,7 +3,12 @@
* Warning: This file is auto-generated by contracts-gen. Don't edit manually. * 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/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/erc1155_proxy';
export * from '../test/generated-wrappers/erc20_bridge_proxy'; export * from '../test/generated-wrappers/erc20_bridge_proxy';
export * from '../test/generated-wrappers/erc20_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';
export * from '../test/generated-wrappers/i_asset_proxy_dispatcher'; export * from '../test/generated-wrappers/i_asset_proxy_dispatcher';
export * from '../test/generated-wrappers/i_authorizable'; export * from '../test/generated-wrappers/i_authorizable';
<<<<<<< HEAD
export * from '../test/generated-wrappers/i_chai'; 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_erc20_bridge';
export * from '../test/generated-wrappers/i_eth2_dai'; export * from '../test/generated-wrappers/i_eth2_dai';
export * from '../test/generated-wrappers/i_kyber_network_proxy'; export * from '../test/generated-wrappers/i_kyber_network_proxy';

View File

@@ -16,7 +16,12 @@
"generated-artifacts/StaticCallProxy.json", "generated-artifacts/StaticCallProxy.json",
"generated-artifacts/TestStaticCallTarget.json", "generated-artifacts/TestStaticCallTarget.json",
"generated-artifacts/UniswapBridge.json", "generated-artifacts/UniswapBridge.json",
<<<<<<< HEAD
"test/generated-artifacts/ChaiBridge.json", "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/ERC1155Proxy.json",
"test/generated-artifacts/ERC20BridgeProxy.json", "test/generated-artifacts/ERC20BridgeProxy.json",
"test/generated-artifacts/ERC20Proxy.json", "test/generated-artifacts/ERC20Proxy.json",
@@ -26,7 +31,12 @@
"test/generated-artifacts/IAssetProxy.json", "test/generated-artifacts/IAssetProxy.json",
"test/generated-artifacts/IAssetProxyDispatcher.json", "test/generated-artifacts/IAssetProxyDispatcher.json",
"test/generated-artifacts/IAuthorizable.json", "test/generated-artifacts/IAuthorizable.json",
<<<<<<< HEAD
"test/generated-artifacts/IChai.json", "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/IERC20Bridge.json",
"test/generated-artifacts/IEth2Dai.json", "test/generated-artifacts/IEth2Dai.json",
"test/generated-artifacts/IKyberNetworkProxy.json", "test/generated-artifacts/IKyberNetworkProxy.json",

View File

@@ -42,6 +42,8 @@ contract DeploymentConstants {
address constant private DEV_UTILS_ADDRESS = 0xcCc2431a7335F21d9268bA62F0B32B0f2EFC463f; address constant private DEV_UTILS_ADDRESS = 0xcCc2431a7335F21d9268bA62F0B32B0f2EFC463f;
/// @dev Kyber ETH pseudo-address. /// @dev Kyber ETH pseudo-address.
address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; 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. /// @dev Overridable way to get the `KyberNetworkProxy` address.
/// @return kyberAddress The `IKyberNetworkProxy` address. /// @return kyberAddress The `IKyberNetworkProxy` address.
@@ -122,4 +124,14 @@ contract DeploymentConstants {
{ {
return DEV_UTILS_ADDRESS; 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;
}
} }