Cherry pick dydx validation from #2456

This commit is contained in:
Lawrence Forman 2020-01-31 21:02:58 -05:00
parent d89243a0d3
commit 9b3781abf1
38 changed files with 2252 additions and 36 deletions

View File

@ -11,6 +11,14 @@
{
"version": "3.2.0",
"changes": [
{
"note": "Add more types and functions to `IDydx`",
"pr": 2466
},
{
"note": "Rename `DydxBrigeAction.accountId` to `DydxBridgeAction.accountIdx`",
"pr": 2466
},
{
"note": "Fix broken tests.",
"pr": 2462

View File

@ -191,7 +191,7 @@ contract DydxBridge is
depositAction = IDydx.ActionArgs({
actionType: IDydx.ActionType.Deposit, // deposit tokens.
amount: dydxAmount, // amount to deposit.
accountId: bridgeAction.accountId, // index in the `accounts` when calling `operate`.
accountIdx: bridgeAction.accountIdx, // index in the `accounts` when calling `operate`.
primaryMarketId: bridgeAction.marketId, // indicates which token to deposit.
otherAddress: depositFrom, // deposit from the account owner.
// unused parameters
@ -229,7 +229,7 @@ contract DydxBridge is
withdrawAction = IDydx.ActionArgs({
actionType: IDydx.ActionType.Withdraw, // withdraw tokens.
amount: amountToWithdraw, // amount to withdraw.
accountId: bridgeAction.accountId, // index in the `accounts` when calling `operate`.
accountIdx: bridgeAction.accountIdx, // index in the `accounts` when calling `operate`.
primaryMarketId: bridgeAction.marketId, // indicates which token to withdraw.
otherAddress: withdrawTo, // withdraw tokens to this address.
// unused parameters

View File

@ -45,7 +45,7 @@ interface IDydx {
/// parsed into before being processed.
struct ActionArgs {
ActionType actionType;
uint256 accountId;
uint256 accountIdx;
AssetAmount amount;
uint256 primaryMarketId;
uint256 secondaryMarketId;
@ -71,6 +71,31 @@ interface IDydx {
uint256 value;
}
struct D256 {
uint256 value;
}
struct Value {
uint256 value;
}
struct Price {
uint256 value;
}
/// @dev The global risk parameters that govern the health and security of the system
struct RiskParams {
// Required ratio of over-collateralization
D256 marginRatio;
// Percentage penalty incurred by liquidated accounts
D256 liquidationSpread;
// Percentage of the borrower's interest fee that gets passed to the suppliers
D256 earningsRate;
// The minimum absolute borrow value of an account
// There must be sufficient incentivize to liquidate undercollateralized accounts
Value minBorrowedValue;
}
/// @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.
@ -86,4 +111,59 @@ interface IDydx {
ActionArgs[] calldata actions
)
external;
/// @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 isLocalOperator True if operator is approved for owner's accounts
function getIsLocalOperator(
address owner,
address operator
)
external
view
returns (bool isLocalOperator);
/// @dev Get the ERC20 token address for a market.
/// @param marketId The market to query
/// @return tokenAddress The token address
function getMarketTokenAddress(
uint256 marketId
)
external
view
returns (address tokenAddress);
/// @dev Get all risk parameters in a single struct.
/// @return riskParams All global risk parameters
function getRiskParams()
external
view
returns (RiskParams memory riskParams);
/// @dev Get the price of the token for a market.
/// @param marketId The market to query
/// @return price The price of each atomic unit of the token
function getMarketPrice(
uint256 marketId
)
external
view
returns (Price memory price);
/// @dev Get the total supplied and total borrowed values of an account adjusted by the marginPremium
/// of each market. Supplied values are divided by (1 + marginPremium) for each market and
/// borrowed values are multiplied by (1 + marginPremium) for each market. Comparing these
/// adjusted values gives the margin-ratio of the account which will be compared to the global
/// margin-ratio when determining if the account can be liquidated.
/// @param account The account to query
/// @return supplyValue The supplied value of the account (adjusted for marginPremium)
/// @return borrowValue The borrowed value of the account (adjusted for marginPremium)
function getAdjustedAccountValues(
AccountInfo calldata account
)
external
view
returns (Value memory supplyValue, Value memory borrowValue);
}

View File

@ -29,7 +29,7 @@ interface IDydxBridge {
struct BridgeAction {
BridgeActionType actionType; // Action to run on dydx account.
uint256 accountId; // Index in `BridgeData.accountNumbers` for this action.
uint256 accountIdx; // Index in `BridgeData.accountNumbers` for this action.
uint256 marketId; // Market to operate on.
uint256 conversionRateNumerator; // Optional. If set, transfer amount is scaled by (conversionRateNumerator/conversionRateDenominator).
uint256 conversionRateDenominator; // Optional. If set, transfer amount is scaled by (conversionRateNumerator/conversionRateDenominator).

View File

@ -23,6 +23,7 @@ import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "../src/bridges/DydxBridge.sol";
// solhint-disable no-empty-blocks
contract TestDydxBridgeToken {
uint256 private constant INIT_HOLDER_BALANCE = 10 * 10**18; // 10 tokens
@ -79,7 +80,7 @@ contract TestDydxBridge is
event OperateAction(
ActionType actionType,
uint256 accountId,
uint256 accountIdx,
bool amountSign,
AssetDenomination amountDenomination,
AssetReference amountRef,
@ -120,7 +121,7 @@ contract TestDydxBridge is
for (uint i = 0; i < actions.length; ++i) {
emit OperateAction(
actions[i].actionType,
actions[i].accountId,
actions[i].accountIdx,
actions[i].amount.sign,
actions[i].amount.denomination,
actions[i].amount.ref,
@ -171,6 +172,50 @@ contract TestDydxBridge is
return _testTokenAddress;
}
/// @dev Unused.
function getIsLocalOperator(
address owner,
address operator
)
external
view
returns (bool isLocalOperator)
{}
/// @dev Unused.
function getMarketTokenAddress(
uint256 marketId
)
external
view
returns (address tokenAddress)
{}
/// @dev Unused.
function getRiskParams()
external
view
returns (RiskParams memory riskParams)
{}
/// @dev Unsused.
function getMarketPrice(
uint256 marketId
)
external
view
returns (Price memory price)
{}
/// @dev Unused.
function getAdjustedAccountValues(
AccountInfo calldata account
)
external
view
returns (Value memory supplyValue, Value memory borrowValue)
{}
/// @dev overrides `_getDydxAddress()` from `DeploymentConstants` to return this address.
function _getDydxAddress()
internal

View File

@ -7,7 +7,7 @@ export enum DydxBridgeActionType {
export interface DydxBridgeAction {
actionType: DydxBridgeActionType;
accountId: BigNumber;
accountIdx: BigNumber;
marketId: BigNumber;
conversionRateNumerator: BigNumber;
conversionRateDenominator: BigNumber;
@ -29,7 +29,7 @@ export const dydxBridgeDataEncoder = AbiEncoder.create([
type: 'tuple[]',
components: [
{ name: 'actionType', type: 'uint8' },
{ name: 'accountId', type: 'uint256' },
{ name: 'accountIdx', type: 'uint256' },
{ name: 'marketId', type: 'uint256' },
{ name: 'conversionRateNumerator', type: 'uint256' },
{ name: 'conversionRateDenominator', type: 'uint256' },

View File

@ -17,14 +17,14 @@ blockchainTests.resets('DydxBridge unit tests', env => {
const notAuthorized = '0x0000000000000000000000000000000000000001';
const defaultDepositAction = {
actionType: DydxBridgeActionType.Deposit,
accountId: constants.ZERO_AMOUNT,
accountIdx: constants.ZERO_AMOUNT,
marketId,
conversionRateNumerator: constants.ZERO_AMOUNT,
conversionRateDenominator: constants.ZERO_AMOUNT,
};
const defaultWithdrawAction = {
actionType: DydxBridgeActionType.Withdraw,
accountId: constants.ZERO_AMOUNT,
accountIdx: constants.ZERO_AMOUNT,
marketId,
conversionRateNumerator: constants.ZERO_AMOUNT,
conversionRateDenominator: constants.ZERO_AMOUNT,
@ -118,7 +118,7 @@ blockchainTests.resets('DydxBridge unit tests', env => {
for (const action of bridgeData.actions) {
expectedOperateActionEvents.push({
actionType: action.actionType as number,
accountId: action.accountId,
accountIdx: action.accountIdx,
amountSign: action.actionType === DydxBridgeActionType.Deposit ? true : false,
amountDenomination: weiDenomination,
amountRef: deltaAmountRef,

View File

@ -18,6 +18,10 @@
{
"note": "Remove `LibTransactionDecoder` export",
"pr": 2464
},
{
"note": "Add `DydxBridge` order validation",
"pr": 2466
}
],
"timestamp": 1581204851

View File

@ -34,15 +34,18 @@ contract Addresses is
address public erc1155ProxyAddress;
address public staticCallProxyAddress;
address public chaiBridgeAddress;
address public dydxBridgeAddress;
constructor (
address exchange_,
address chaiBridge_
address chaiBridge_,
address dydxBridge_
)
public
{
exchangeAddress = exchange_;
chaiBridgeAddress = chaiBridge_;
dydxBridgeAddress = dydxBridge_;
erc20ProxyAddress = IExchange(exchange_).getAssetProxy(IAssetData(address(0)).ERC20Token.selector);
erc721ProxyAddress = IExchange(exchange_).getAssetProxy(IAssetData(address(0)).ERC721Token.selector);
erc1155ProxyAddress = IExchange(exchange_).getAssetProxy(IAssetData(address(0)).ERC1155Assets.selector);

View File

@ -28,7 +28,7 @@ import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IChai.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "./Addresses.sol";
import "./LibAssetData.sol";
import "./LibDydxBalance.sol";
contract AssetBalance is
@ -274,6 +274,9 @@ contract AssetBalance is
uint256 chaiAllowance = LibERC20Token.allowance(_getChaiAddress(), ownerAddress, chaiBridgeAddress);
// Dai allowance is unlimited if Chai allowance is unlimited
allowance = chaiAllowance == _MAX_UINT256 ? _MAX_UINT256 : _convertChaiToDaiAmount(chaiAllowance);
} else if (bridgeAddress == dydxBridgeAddress) {
// Dydx bridges always have infinite allowance.
allowance = _MAX_UINT256;
}
// Allowance will be 0 if bridge is not supported
}
@ -366,6 +369,17 @@ contract AssetBalance is
if (order.makerAssetData.length < 4) {
return (0, 0);
}
bytes4 assetProxyId = order.makerAssetData.readBytes4(0);
// Handle dydx bridge assets.
if (assetProxyId == IAssetData(address(0)).ERC20Bridge.selector) {
(, , address bridgeAddress, ) = LibAssetData.decodeERC20BridgeAssetData(order.makerAssetData);
if (bridgeAddress == dydxBridgeAddress) {
return (
LibDydxBalance.getDydxMakerBalance(order, dydxBridgeAddress),
_MAX_UINT256
);
}
}
return (
getBalance(order.makerAddress, order.makerAssetData),
getAssetProxyAllowance(order.makerAddress, order.makerAssetData)

View File

@ -40,12 +40,14 @@ contract DevUtils is
{
constructor (
address exchange_,
address chaiBridge_
address chaiBridge_,
address dydxBridge_
)
public
Addresses(
exchange_,
chaiBridge_
chaiBridge_,
dydxBridge_
)
LibEIP712ExchangeDomain(uint256(0), address(0)) // null args because because we only use constants
{}

View File

@ -0,0 +1,483 @@
/*
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.16;
pragma experimental ABIEncoderV2;
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IDydxBridge.sol";
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IDydx.sol";
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibFractions.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "./LibAssetData.sol";
// solhint-disable separate-by-one-line-in-contract
library LibDydxBalance {
using LibBytes for bytes;
using LibSafeMath for uint256;
/// @dev Decimal places for dydx value quantities.
uint256 private constant DYDX_UNITS_DECIMALS = 18;
/// @dev Base units for dydx value quantities.
uint256 private constant DYDX_UNITS_BASE = 10 ** DYDX_UNITS_DECIMALS;
/// @dev A fraction/rate.
struct Fraction {
uint256 n;
uint256 d;
}
/// @dev Structure that holds all pertinent info needed to perform a balance
/// check.
struct BalanceCheckInfo {
IDydx dydx;
address bridgeAddress;
address makerAddress;
address makerTokenAddress;
address takerTokenAddress;
uint256 makerAssetAmount;
uint256 takerAssetAmount;
uint256[] accounts;
IDydxBridge.BridgeAction[] actions;
}
/// @dev Get the maker asset balance of an order with a `DydxBridge` maker asset.
/// @param order An order with a dydx maker asset.
/// @param dydx The address of the dydx contract.
/// @return balance The maker asset balance.
function getDydxMakerBalance(LibOrder.Order memory order, address dydx)
public
view
returns (uint256 balance)
{
BalanceCheckInfo memory info = _getBalanceCheckInfo(order, dydx);
// The Dydx bridge must be an operator for the maker.
if (!info.dydx.getIsLocalOperator(info.makerAddress, info.bridgeAddress)) {
return 0;
}
// Actions must be well-formed.
if (!_areActionsWellFormed(info)) {
return 0;
}
// If the rate we withdraw maker tokens is < 1, the asset proxy will
// throw because we will always transfer less maker tokens than asked.
if (_ltf(_getMakerTokenWithdrawRate(info), Fraction(1, 1))) {
return 0;
}
// The maker balance is the smaller of:
return LibSafeMath.min256(
// How many times we can execute all the deposit actions.
_getDepositableMakerAmount(info),
// How many times we can execute all the actions before the an
// account becomes undercollateralized.
_getSolventMakerAmount(info)
);
}
/// @dev Checks that:
/// 1. Actions are arranged as [...deposits, withdraw].
/// 2. There is only one deposit for each market ID.
/// 3. Every action has a valid account index.
/// 4. There is exactly one withdraw at the end and it is for the
/// maker token.
/// @param info State from `_getBalanceCheckInfo()`.
/// @return areWellFormed Whether the actions are well-formed.
function _areActionsWellFormed(BalanceCheckInfo memory info)
internal
view
returns (bool areWellFormed)
{
if (info.actions.length == 0) {
return false;
}
uint256 depositCount = 0;
// Count the number of deposits.
for (; depositCount < info.actions.length; ++depositCount) {
IDydxBridge.BridgeAction memory action = info.actions[depositCount];
if (action.actionType != IDydxBridge.BridgeActionType.Deposit) {
break;
}
// Search all prior actions for the same market ID.
uint256 marketId = action.marketId;
for (uint256 j = 0; j < depositCount; ++j) {
if (info.actions[j].marketId == marketId) {
// Market ID is not unique.
return false;
}
}
// Check that the account index is within the valid range.
if (action.accountIdx >= info.accounts.length) {
return false;
}
}
// There must be exactly one withdraw action at the end.
if (depositCount + 1 != info.actions.length) {
return false;
}
IDydxBridge.BridgeAction memory withdraw = info.actions[depositCount];
if (withdraw.actionType != IDydxBridge.BridgeActionType.Withdraw) {
return false;
}
// And it must be for the maker token.
if (info.dydx.getMarketTokenAddress(withdraw.marketId) != info.makerTokenAddress) {
return false;
}
// Check the account index.
return withdraw.accountIdx < info.accounts.length;
}
/// @dev Returns the rate at which we withdraw maker tokens.
/// @param info State from `_getBalanceCheckInfo()`.
/// @return makerTokenWithdrawRate Maker token withdraw rate.
function _getMakerTokenWithdrawRate(BalanceCheckInfo memory info)
internal
pure
returns (Fraction memory makerTokenWithdrawRate)
{
// The last action is always a withdraw for the maker token.
IDydxBridge.BridgeAction memory withdraw = info.actions[info.actions.length - 1];
return _getActionRate(withdraw);
}
/// @dev Get how much maker asset we can transfer before a deposit fails.
/// @param info State from `_getBalanceCheckInfo()`.
function _getDepositableMakerAmount(BalanceCheckInfo memory info)
internal
view
returns (uint256 depositableMakerAmount)
{
depositableMakerAmount = uint256(-1);
// The conversion rate from maker -> taker.
Fraction memory makerToTakerRate = Fraction(
info.takerAssetAmount,
info.makerAssetAmount
);
// Take the minimum maker amount from all deposits.
for (uint256 i = 0; i < info.actions.length; ++i) {
IDydxBridge.BridgeAction memory action = info.actions[i];
// Only looking at deposit actions.
if (action.actionType != IDydxBridge.BridgeActionType.Deposit) {
continue;
}
Fraction memory depositRate = _getActionRate(action);
// Taker tokens will be transferred to the maker for every fill, so
// we reduce the effective deposit rate if we're depositing the taker
// token.
address depositToken = info.dydx.getMarketTokenAddress(action.marketId);
if (info.takerTokenAddress != address(0) && depositToken == info.takerTokenAddress) {
// `depositRate = max(0, depositRate - makerToTakerRate)`
if (_ltf(makerToTakerRate, depositRate)) {
depositRate = _subf(depositRate, makerToTakerRate);
} else {
depositRate = Fraction(0, 1);
}
}
// If the deposit rate is > 0, we are limited by the transferrable
// token balance of the maker.
if (_gtf(depositRate, Fraction(0, 1))) {
uint256 supply = _getTransferabeTokenAmount(
depositToken,
info.makerAddress,
address(info.dydx)
);
depositableMakerAmount = LibSafeMath.min256(
depositableMakerAmount,
LibMath.getPartialAmountFloor(
depositRate.d,
depositRate.n,
supply
)
);
}
}
}
/// @dev Get how much maker asset we can transfer before an account
/// becomes insolvent.
/// @param info State from `_getBalanceCheckInfo()`.
function _getSolventMakerAmount(BalanceCheckInfo memory info)
internal
view
returns (uint256 solventMakerAmount)
{
solventMakerAmount = uint256(-1);
assert(info.actions.length >= 1);
IDydxBridge.BridgeAction memory withdraw = info.actions[info.actions.length - 1];
assert(withdraw.actionType == IDydxBridge.BridgeActionType.Withdraw);
Fraction memory minCr = _getMinimumCollateralizationRatio(info.dydx);
// CR < 1 will cause math underflows.
require(_gtef(minCr, Fraction(1, 1)), "DevUtils/MIN_CR_MUST_BE_GTE_ONE");
// Loop through the accounts.
for (uint256 accountIdx = 0; accountIdx < info.accounts.length; ++accountIdx) {
(uint256 supplyValue, uint256 borrowValue) =
_getAccountValues(info, info.accounts[accountIdx]);
// All accounts must currently be solvent.
if (borrowValue != 0 && _ltf(Fraction(supplyValue, borrowValue), minCr)) {
return 0;
}
// If this is the same account used to in the withdraw/borrow action,
// compute the maker amount at which it will become insolvent.
if (accountIdx != withdraw.accountIdx) {
continue;
}
// Compute the deposit/collateralization rate, which is the rate at
// which (USD) value is added to the account across all markets.
Fraction memory dd = Fraction(0, 1);
for (uint256 i = 0; i < info.actions.length - 1; ++i) {
IDydxBridge.BridgeAction memory deposit = info.actions[i];
assert(deposit.actionType == IDydxBridge.BridgeActionType.Deposit);
if (deposit.accountIdx == accountIdx) {
dd = _addf(
dd,
_toQuoteValue(
info.dydx,
deposit.marketId,
_getActionRate(deposit)
)
);
}
}
// Compute the borrow/withdraw rate, which is the rate at which
// (USD) value is deducted from the account.
Fraction memory db = _toQuoteValue(
info.dydx,
withdraw.marketId,
_getActionRate(withdraw)
);
// If the deposit to withdraw ratio is >= the minimum collateralization
// rate, then we will never become insolvent at these prices.
if (_gtef(_divf(dd, db), minCr)) {
continue;
}
// The collateralization ratio for this account, parameterized by
// `t` (maker amount), is given by:
// `cr = (supplyValue + t * dd) / (borrowValue + t * db)`
// Solving for `t` gives us:
// `t = (supplyValue - cr * borrowValue) / (cr * db - dd)`
// TODO(dorothy-zbornak): It'll also revert when getting extremely
// close to the minimum collateralization ratio.
Fraction memory t = _divf(
_subf(
Fraction(supplyValue, DYDX_UNITS_BASE),
_mulf(minCr, Fraction(borrowValue, DYDX_UNITS_BASE))
),
_subf(_mulf(minCr, db), dd)
);
solventMakerAmount = LibSafeMath.min256(
solventMakerAmount,
t.n.safeDiv(t.d)
);
}
}
/// @dev Create a `BalanceCheckInfo` struct.
/// @param order An order with a `DydxBridge` maker asset.
/// @param dydx The address of the Dydx contract.
/// @return info The `BalanceCheckInfo` struct.
function _getBalanceCheckInfo(LibOrder.Order memory order, address dydx)
private
pure
returns (BalanceCheckInfo memory info)
{
bytes memory rawBridgeData;
(, info.makerTokenAddress, info.bridgeAddress, rawBridgeData) =
LibAssetData.decodeERC20BridgeAssetData(order.makerAssetData);
info.dydx = IDydx(dydx);
info.makerAddress = order.makerAddress;
if (order.takerAssetData.readBytes4(0) == IAssetData(0).ERC20Token.selector) {
(, info.takerTokenAddress) =
LibAssetData.decodeERC20AssetData(order.takerAssetData);
}
info.makerAssetAmount = order.makerAssetAmount;
info.takerAssetAmount = order.takerAssetAmount;
(IDydxBridge.BridgeData memory bridgeData) =
abi.decode(rawBridgeData, (IDydxBridge.BridgeData));
info.accounts = bridgeData.accountNumbers;
info.actions = bridgeData.actions;
}
/// @dev Returns the conversion rate for an action, treating infinites as 1.
/// @param action A `BridgeAction`.
function _getActionRate(IDydxBridge.BridgeAction memory action)
private
pure
returns (Fraction memory rate)
{
rate = action.conversionRateDenominator == 0
? Fraction(1, 1)
: _normalizef(
Fraction(
action.conversionRateNumerator,
action.conversionRateDenominator
)
);
}
/// @dev Get the global minimum collateralization ratio required for
/// an account to be considered solvent.
/// @param dydx The Dydx interface.
function _getMinimumCollateralizationRatio(IDydx dydx)
private
view
returns (Fraction memory ratio)
{
IDydx.RiskParams memory riskParams = dydx.getRiskParams();
return _normalizef(
Fraction(
riskParams.marginRatio.value,
DYDX_UNITS_BASE
)
);
}
/// @dev Get the quote (USD) value of a rate within a market.
/// @param dydx The Dydx interface.
/// @param marketId Dydx market ID.
/// @param rate Rate to scale by price.
function _toQuoteValue(IDydx dydx, uint256 marketId, Fraction memory rate)
private
view
returns (Fraction memory quotedRate)
{
IDydx.Price memory price = dydx.getMarketPrice(marketId);
uint8 tokenDecimals = LibERC20Token.decimals(dydx.getMarketTokenAddress(marketId));
return _mulf(
Fraction(
price.value,
10 ** uint256(DYDX_UNITS_DECIMALS + tokenDecimals)
),
rate
);
}
/// @dev Get the total supply and borrow values for an account across all markets.
/// @param info State from `_getBalanceCheckInfo()`.
/// @param account The Dydx account identifier.
function _getAccountValues(BalanceCheckInfo memory info, uint256 account)
private
view
returns (uint256 supplyValue, uint256 borrowValue)
{
(IDydx.Value memory supplyValue_, IDydx.Value memory borrowValue_) =
info.dydx.getAdjustedAccountValues(IDydx.AccountInfo(
info.makerAddress,
account
));
return (supplyValue_.value, borrowValue_.value);
}
/// @dev Get the amount of an ERC20 token held by `owner` that can be transferred
/// by `spender`.
/// @param tokenAddress The address of the ERC20 token.
/// @param owner The address of the token holder.
/// @param spender The address of the token spender.
function _getTransferabeTokenAmount(
address tokenAddress,
address owner,
address spender
)
private
view
returns (uint256 transferableAmount)
{
return LibSafeMath.min256(
LibERC20Token.allowance(tokenAddress, owner, spender),
LibERC20Token.balanceOf(tokenAddress, owner)
);
}
/*** Fraction helpers ***/
/// @dev Check if `a < b`.
function _ltf(Fraction memory a, Fraction memory b)
private
pure
returns (bool isLessThan)
{
return LibFractions.cmp(a.n, a.d, b.n, b.d) == -1;
}
/// @dev Check if `a > b`.
function _gtf(Fraction memory a, Fraction memory b)
private
pure
returns (bool isGreaterThan)
{
return LibFractions.cmp(a.n, a.d, b.n, b.d) == 1;
}
/// @dev Check if `a >= b`.
function _gtef(Fraction memory a, Fraction memory b)
private
pure
returns (bool isGreaterThanOrEqual)
{
return !_ltf(a, b);
}
/// @dev Compute `a + b`.
function _addf(Fraction memory a, Fraction memory b)
private
pure
returns (Fraction memory r)
{
(r.n, r.d) = LibFractions.add(a.n, a.d, b.n, b.d);
}
/// @dev Compute `a - b`.
function _subf(Fraction memory a, Fraction memory b)
private
pure
returns (Fraction memory r)
{
(r.n, r.d) = LibFractions.sub(a.n, a.d, b.n, b.d);
}
/// @dev Compute `a * b`.
function _mulf(Fraction memory a, Fraction memory b)
private
pure
returns (Fraction memory r)
{
(r.n, r.d) = LibFractions.mul(a.n, a.d, b.n, b.d);
}
/// @dev Compute `a / b`.
function _divf(Fraction memory a, Fraction memory b)
private
pure
returns (Fraction memory r)
{
(r.n, r.d) = LibFractions.mul(a.n, a.d, b.d, b.n);
}
/// @dev Normalize a fraction to prevent arithmetic overflows.
function _normalizef(Fraction memory f)
private
pure
returns (Fraction memory r)
{
(r.n, r.d) = LibFractions.normalize(f.n, f.d);
}
}

View File

@ -0,0 +1,162 @@
/*
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.16;
pragma experimental ABIEncoderV2;
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IDydx.sol";
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
// solhint-disable separate-by-one-line-in-contract
contract TestDydx {
struct OperatorConfig {
address owner;
address operator;
}
struct AccountConfig {
address owner;
uint256 accountId;
int256[] balances;
}
struct MarketInfo {
address token;
uint256 price;
}
struct TestConfig {
uint256 marginRatio;
OperatorConfig[] operators;
AccountConfig[] accounts;
MarketInfo[] markets;
}
mapping (bytes32 => bool) private _operators;
mapping (bytes32 => int256) private _balance;
MarketInfo[] private _markets;
uint256 private _marginRatio;
constructor(TestConfig memory config) public {
_marginRatio = config.marginRatio;
for (uint256 marketId = 0; marketId < config.markets.length; ++marketId) {
_markets.push(config.markets[marketId]);
}
for (uint256 i = 0; i < config.operators.length; ++i) {
OperatorConfig memory op = config.operators[i];
_operators[_getOperatorHash(op.owner, op.operator)] = true;
}
for (uint256 i = 0; i < config.accounts.length; ++i) {
AccountConfig memory acct = config.accounts[i];
for (uint256 marketId = 0; marketId < acct.balances.length; ++marketId) {
_balance[_getBalanceHash(acct.owner, acct.accountId, marketId)] =
acct.balances[marketId];
}
}
}
function getIsLocalOperator(
address owner,
address operator
)
external
view
returns (bool isLocalOperator)
{
return _operators[_getOperatorHash(owner, operator)];
}
function getMarketTokenAddress(
uint256 marketId
)
external
view
returns (address tokenAddress)
{
return _markets[marketId].token;
}
function getRiskParams()
external
view
returns (IDydx.RiskParams memory riskParams)
{
return IDydx.RiskParams({
marginRatio: IDydx.D256(_marginRatio),
liquidationSpread: IDydx.D256(0),
earningsRate: IDydx.D256(0),
minBorrowedValue: IDydx.Value(0)
});
}
function getMarketPrice(
uint256 marketId
)
external
view
returns (IDydx.Price memory price)
{
return IDydx.Price(_markets[marketId].price);
}
function getAdjustedAccountValues(
IDydx.AccountInfo calldata account
)
external
view
returns (IDydx.Value memory supplyValue, IDydx.Value memory borrowValue)
{
for (uint256 marketId = 0; marketId < _markets.length; ++marketId) {
MarketInfo memory market = _markets[marketId];
int256 balance =
_balance[_getBalanceHash(account.owner, account.number, marketId)];
uint256 decimals = LibERC20Token.decimals(market.token);
balance = balance * int256(market.price) / int256(10 ** decimals);
if (balance >= 0) {
supplyValue.value += uint256(balance);
} else {
borrowValue.value += uint256(-balance);
}
}
}
function _getOperatorHash(address owner, address operator)
private
pure
returns (bytes32 operatorHash)
{
return keccak256(abi.encode(
owner,
operator
));
}
function _getBalanceHash(address owner, uint256 accountId, uint256 marketId)
private
pure
returns (bytes32 balanceHash)
{
return keccak256(abi.encode(
owner,
accountId,
marketId
));
}
}

View File

@ -0,0 +1,116 @@
/*
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.16;
pragma experimental ABIEncoderV2;
import "../src/LibDydxBalance.sol";
contract TestLibDydxBalanceToken {
uint8 public decimals;
mapping (address => uint256) public balanceOf;
mapping (address => mapping (address => uint256)) public allowance;
constructor(uint8 decimals_) public {
decimals = decimals_;
}
function setBalance(address owner, uint256 balance) external {
balanceOf[owner] = balance;
}
function setApproval(
address owner,
address spender,
uint256 allowance_
)
external
{
allowance[owner][spender] = allowance_;
}
}
contract TestLibDydxBalance {
mapping (address => TestLibDydxBalanceToken) private tokens;
function createToken(uint8 decimals) external returns (address) {
TestLibDydxBalanceToken token = new TestLibDydxBalanceToken(decimals);
return address(tokens[address(token)] = token);
}
function setTokenBalance(
address tokenAddress,
address owner,
uint256 balance
)
external
{
tokens[tokenAddress].setBalance(owner, balance);
}
function setTokenApproval(
address tokenAddress,
address owner,
address spender,
uint256 allowance
)
external
{
tokens[tokenAddress].setApproval(owner, spender, allowance);
}
function getDydxMakerBalance(LibOrder.Order memory order, address dydx)
public
view
returns (uint256 balance)
{
return LibDydxBalance.getDydxMakerBalance(order, dydx);
}
function getSolventMakerAmount(
LibDydxBalance.BalanceCheckInfo memory info
)
public
view
returns (uint256 solventMakerAmount)
{
return LibDydxBalance._getSolventMakerAmount(info);
}
function getDepositableMakerAmount(
LibDydxBalance.BalanceCheckInfo memory info
)
public
view
returns (uint256 depositableMakerAmount)
{
return LibDydxBalance._getDepositableMakerAmount(info);
}
function areActionsWellFormed(LibDydxBalance.BalanceCheckInfo memory info)
public
view
returns (bool areWellFormed)
{
return LibDydxBalance._areActionsWellFormed(info);
}
}

View File

@ -8,7 +8,7 @@
"main": "lib/src/index.js",
"scripts": {
"build": "yarn pre_build && tsc -b",
"test": "yarn assert_deployable",
"test": "yarn assert_deployable && yarn mocha -t 10000 -b ./lib/test/**_test.js",
"assert_deployable": "node -e \"const bytecodeLen = (require('./generated-artifacts/DevUtils.json').compilerOutput.evm.bytecode.object.length-2)/2; assert(bytecodeLen<=0x6000,'DevUtils contract is too big to deploy, per EIP-170. '+bytecodeLen+'>'+0x6000)\"",
"build:ci": "yarn build",
"pre_build": "run-s compile quantify_bytecode contracts:gen generate_contract_wrappers contracts:copy",
@ -27,8 +27,8 @@
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
},
"config": {
"publicInterfaceContracts": "DevUtils,LibAssetData,LibOrderTransferSimulation,LibTransactionDecoder",
"abis": "./test/generated-artifacts/@(Addresses|AssetBalance|DevUtils|EthBalanceChecker|ExternalFunctions|LibAssetData|LibOrderTransferSimulation|LibTransactionDecoder|OrderTransferSimulationUtils|OrderValidationUtils).json",
"publicInterfaceContracts": "DevUtils,LibAssetData,LibDydxBalance,LibOrderTransferSimulation,LibTransactionDecoder",
"abis": "./test/generated-artifacts/@(Addresses|AssetBalance|DevUtils|EthBalanceChecker|ExternalFunctions|LibAssetData|LibDydxBalance|LibOrderTransferSimulation|LibTransactionDecoder|OrderTransferSimulationUtils|OrderValidationUtils|TestDydx|TestLibDydxBalance).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {

View File

@ -7,11 +7,13 @@ import { ContractArtifact } from 'ethereum-types';
import * as DevUtils from '../generated-artifacts/DevUtils.json';
import * as LibAssetData from '../generated-artifacts/LibAssetData.json';
import * as LibDydxBalance from '../generated-artifacts/LibDydxBalance.json';
import * as LibOrderTransferSimulation from '../generated-artifacts/LibOrderTransferSimulation.json';
import * as LibTransactionDecoder from '../generated-artifacts/LibTransactionDecoder.json';
export const artifacts = {
DevUtils: DevUtils as ContractArtifact,
LibAssetData: LibAssetData as ContractArtifact,
LibDydxBalance: LibDydxBalance as ContractArtifact,
LibOrderTransferSimulation: LibOrderTransferSimulation as ContractArtifact,
LibTransactionDecoder: LibTransactionDecoder as ContractArtifact,
};

View File

@ -5,5 +5,6 @@
*/
export * from '../generated-wrappers/dev_utils';
export * from '../generated-wrappers/lib_asset_data';
export * from '../generated-wrappers/lib_dydx_balance';
export * from '../generated-wrappers/lib_order_transfer_simulation';
export * from '../generated-wrappers/lib_transaction_decoder';

View File

@ -11,10 +11,13 @@ import * as DevUtils from '../test/generated-artifacts/DevUtils.json';
import * as EthBalanceChecker from '../test/generated-artifacts/EthBalanceChecker.json';
import * as ExternalFunctions from '../test/generated-artifacts/ExternalFunctions.json';
import * as LibAssetData from '../test/generated-artifacts/LibAssetData.json';
import * as LibDydxBalance from '../test/generated-artifacts/LibDydxBalance.json';
import * as LibOrderTransferSimulation from '../test/generated-artifacts/LibOrderTransferSimulation.json';
import * as LibTransactionDecoder from '../test/generated-artifacts/LibTransactionDecoder.json';
import * as OrderTransferSimulationUtils from '../test/generated-artifacts/OrderTransferSimulationUtils.json';
import * as OrderValidationUtils from '../test/generated-artifacts/OrderValidationUtils.json';
import * as TestDydx from '../test/generated-artifacts/TestDydx.json';
import * as TestLibDydxBalance from '../test/generated-artifacts/TestLibDydxBalance.json';
export const artifacts = {
Addresses: Addresses as ContractArtifact,
AssetBalance: AssetBalance as ContractArtifact,
@ -22,8 +25,11 @@ export const artifacts = {
EthBalanceChecker: EthBalanceChecker as ContractArtifact,
ExternalFunctions: ExternalFunctions as ContractArtifact,
LibAssetData: LibAssetData as ContractArtifact,
LibDydxBalance: LibDydxBalance as ContractArtifact,
LibOrderTransferSimulation: LibOrderTransferSimulation as ContractArtifact,
LibTransactionDecoder: LibTransactionDecoder as ContractArtifact,
OrderTransferSimulationUtils: OrderTransferSimulationUtils as ContractArtifact,
OrderValidationUtils: OrderValidationUtils as ContractArtifact,
TestDydx: TestDydx as ContractArtifact,
TestLibDydxBalance: TestLibDydxBalance as ContractArtifact,
};

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,10 @@ export * from '../test/generated-wrappers/dev_utils';
export * from '../test/generated-wrappers/eth_balance_checker';
export * from '../test/generated-wrappers/external_functions';
export * from '../test/generated-wrappers/lib_asset_data';
export * from '../test/generated-wrappers/lib_dydx_balance';
export * from '../test/generated-wrappers/lib_order_transfer_simulation';
export * from '../test/generated-wrappers/lib_transaction_decoder';
export * from '../test/generated-wrappers/order_transfer_simulation_utils';
export * from '../test/generated-wrappers/order_validation_utils';
export * from '../test/generated-wrappers/test_dydx';
export * from '../test/generated-wrappers/test_lib_dydx_balance';

View File

@ -5,6 +5,7 @@
"files": [
"generated-artifacts/DevUtils.json",
"generated-artifacts/LibAssetData.json",
"generated-artifacts/LibDydxBalance.json",
"generated-artifacts/LibOrderTransferSimulation.json",
"generated-artifacts/LibTransactionDecoder.json",
"test/generated-artifacts/Addresses.json",
@ -13,10 +14,13 @@
"test/generated-artifacts/EthBalanceChecker.json",
"test/generated-artifacts/ExternalFunctions.json",
"test/generated-artifacts/LibAssetData.json",
"test/generated-artifacts/LibDydxBalance.json",
"test/generated-artifacts/LibOrderTransferSimulation.json",
"test/generated-artifacts/LibTransactionDecoder.json",
"test/generated-artifacts/OrderTransferSimulationUtils.json",
"test/generated-artifacts/OrderValidationUtils.json"
"test/generated-artifacts/OrderValidationUtils.json",
"test/generated-artifacts/TestDydx.json",
"test/generated-artifacts/TestLibDydxBalance.json"
],
"exclude": ["./deploy/solc/solc_bin"]
}

View File

@ -31,6 +31,10 @@
{
"note": "Update tests for refactored `DevUtils`",
"pr": 2464
},
{
"note": "Add DydxBridge validation",
"pr": 2466
}
],
"timestamp": 1581204851

View File

@ -23,14 +23,14 @@ blockchainTests.fork.resets('Mainnet dydx bridge tests', env => {
const defaultAmount = toBaseUnitAmount(0.01);
const defaultDepositAction = {
actionType: DydxBridgeActionType.Deposit as number,
accountId: constants.ZERO_AMOUNT,
accountIdx: constants.ZERO_AMOUNT,
marketId: daiMarketId,
conversionRateNumerator: constants.ZERO_AMOUNT,
conversionRateDenominator: constants.ZERO_AMOUNT,
};
const defaultWithdrawAction = {
actionType: DydxBridgeActionType.Withdraw as number,
accountId: constants.ZERO_AMOUNT,
accountIdx: constants.ZERO_AMOUNT,
marketId: daiMarketId,
// This ratio must be less than the `1` to account
// for interest in dydx balances because the test
@ -71,7 +71,7 @@ blockchainTests.fork.resets('Mainnet dydx bridge tests', env => {
case DydxBridgeActionType.Deposit:
expectedDepositEvents.push({
accountOwner: dydxAccountOwner,
accountNumber: bridgeData.accountNumbers[action.accountId.toNumber()],
accountNumber: bridgeData.accountNumbers[action.accountIdx.toNumber()],
market: action.marketId,
update: { deltaWei: { sign: true, value: scaledAmount } },
from: dydxAccountOwner,
@ -81,7 +81,7 @@ blockchainTests.fork.resets('Mainnet dydx bridge tests', env => {
case DydxBridgeActionType.Withdraw:
expectedWithdrawEvents.push({
accountOwner: dydxAccountOwner,
accountNumber: bridgeData.accountNumbers[action.accountId.toNumber()],
accountNumber: bridgeData.accountNumbers[action.accountIdx.toNumber()],
market: action.marketId,
update: { deltaWei: { sign: false, value: scaledAmount } },
to: receiver,

View File

@ -34,6 +34,7 @@ blockchainTests.fork.resets('DevUtils mainnet tests', env => {
devUtilsArtifacts,
contractAddresses.exchange,
contractAddresses.chaiBridge,
contractAddresses.dydxBridge,
);
await dai.approve(chai.address, constants.MAX_UINT256).awaitTransactionSuccessAsync({ from: daiHolder });
await chai.join(daiHolder, daiDepositAmount).awaitTransactionSuccessAsync({ from: daiHolder });

View File

@ -27,6 +27,7 @@ blockchainTests('DevUtils.getOrderHash', env => {
artifacts,
exchange.address,
constants.NULL_ADDRESS,
constants.NULL_ADDRESS,
);
});

View File

@ -81,6 +81,7 @@ blockchainTests.resets('LibAssetData', env => {
artifacts,
deployment.exchange.address,
constants.NULL_ADDRESS,
constants.NULL_ADDRESS,
);
staticCallTarget = await TestStaticCallTargetContract.deployFrom0xArtifactAsync(

View File

@ -43,6 +43,7 @@ blockchainTests('LibTransactionDecoder', env => {
artifacts,
exchange.address,
constants.NULL_ADDRESS,
constants.NULL_ADDRESS,
);
});

View File

@ -34,14 +34,14 @@ blockchainTests.resets('Exchange fills dydx orders', env => {
let testTokenAddress: string;
const defaultDepositAction = {
actionType: DydxBridgeActionType.Deposit as number,
accountId: constants.ZERO_AMOUNT,
accountIdx: constants.ZERO_AMOUNT,
marketId,
conversionRateNumerator: dydxConversionRateNumerator,
conversionRateDenominator: dydxConversionRateDenominator,
};
const defaultWithdrawAction = {
actionType: DydxBridgeActionType.Withdraw as number,
accountId: constants.ZERO_AMOUNT,
accountIdx: constants.ZERO_AMOUNT,
marketId,
conversionRateNumerator: constants.ZERO_AMOUNT,
conversionRateDenominator: constants.ZERO_AMOUNT,

View File

@ -203,6 +203,7 @@ export class DeploymentManager {
devUtilsArtifacts,
exchange.address,
constants.NULL_ADDRESS,
constants.NULL_ADDRESS,
);
// Construct the new instance and return it.

View File

@ -37,6 +37,10 @@
{
"note": "Export `EvmBytecodeOutputLinkReferences` type.",
"pr": 2462
},
{
"note": "Add more functions to `LibFractions`.",
"pr": 2466
}
],
"timestamp": 1580811564

View File

@ -37,7 +37,101 @@ library LibFractions {
.safeMul(d2)
.safeAdd(n2.safeMul(d1));
denominator = d1.safeMul(d2);
return (numerator, denominator);
return normalize(numerator, denominator);
}
/// @dev Safely subracts two fractions `n1/d1 - n2/d2`
/// @param n1 numerator of `1`
/// @param d1 denominator of `1`
/// @param n2 numerator of `2`
/// @param d2 denominator of `2`
/// @return numerator Numerator of sum
/// @return denominator Denominator of sum
function sub(
uint256 n1,
uint256 d1,
uint256 n2,
uint256 d2
)
internal
pure
returns (
uint256 numerator,
uint256 denominator
)
{
if (n2 == 0) {
return (numerator = n1, denominator = d1);
}
numerator = n1
.safeMul(d2)
.safeSub(n2.safeMul(d1));
denominator = d1.safeMul(d2);
return normalize(numerator, denominator);
}
/// @dev Multiply two fractions.
/// @param n1 numerator of `1`
/// @param d1 denominator of `1`
/// @param n2 numerator of `2`
/// @param d2 denominator of `2`
/// @return numerator numerator of product.
/// @return denominator numerator of product.
function mul(
uint256 n1,
uint256 d1,
uint256 n2,
uint256 d2
)
internal
pure
returns (
uint256 numerator,
uint256 denominator
)
{
return normalize(n1.safeMul(n2), d1.safeMul(d2));
}
/// @dev Compares two fractions.
/// @param n1 numerator of `1`
/// @param d1 denominator of `1`
/// @param n2 numerator of `2`
/// @param d2 denominator of `2`
/// @return compareResult
/// `-1` if `n1/d1 < n2/d2`.
/// `0` if `n1/d1 == n2/d2`.
/// `1` if `n1/d1 > n2/d2`.
function cmp(
uint256 n1,
uint256 d1,
uint256 n2,
uint256 d2
)
internal
pure
returns (int8 compareResult)
{
// Handle infinities.
if (d1 == 0) {
if (d2 == 0) {
return 0;
}
return 1;
} else {
if (d2 == 0) {
return -1;
}
}
uint256 nd1 = n1.safeMul(d2);
uint256 nd2 = n2.safeMul(d1);
if (nd1 > nd2) {
return 1;
}
if (nd1 < nd2) {
return -1;
}
return 0;
}
/// @dev Rescales a fraction to prevent overflows during addition if either

View File

@ -5,6 +5,10 @@
{
"note": "Support deploying contracts with unliked libraries through `deployWithLibrariesFrom0xArtifactAsync()`",
"pr": 2463
},
{
"note": "Update reference outputs",
"pr": 2466
}
],
"timestamp": 1581204851

View File

@ -60,7 +60,7 @@
},
{
"note": "Update snapshot addresses",
"pr": 2464
"pr": 2466
}
],
"timestamp": 1580811564

View File

@ -131,18 +131,18 @@
"etherToken": "0x0b1ba0af832d7c05fd64161e0db78e85978e8082",
"exchange": "0x48bacb9266a570d521063ef5dd96e61686dbe788",
"assetProxyOwner": "0x0000000000000000000000000000000000000000",
"erc20BridgeProxy": "0x038f9b392fb9a9676dbaddf78ea5fdbf6c7d9710",
"erc20BridgeProxy": "0x371b13d97f4bf77d724e78c16b7dc74099f40e84",
"zeroExGovernor": "0x0000000000000000000000000000000000000000",
"forwarder": "0xe704967449b57b2382b7fa482718748c13c63190",
"coordinatorRegistry": "0xaa86dda78e9434aca114b6676fc742a18d15a1cc",
"coordinator": "0x4d3d5c850dd5bd9d6f4adda3dd039a3c8054ca29",
"multiAssetProxy": "0xcfc18cec799fbd1793b5c43e773c98d4d61cc2db",
"staticCallProxy": "0x6dfff22588be9b3ef8cf0ad6dc9b84796f9fb45f",
"devUtils": "0x74341e87b1c4db7d5ed95f92b37509f2525a7a90",
"devUtils": "0xb23672f74749bf7916ba6827c64111a4d6de7f11",
"exchangeV2": "0x48bacb9266a570d521063ef5dd96e61686dbe788",
"zrxVault": "0xc4df27466183c0fe2a5924d6ea56e334deff146a",
"staking": "0xf23276778860e420acfc18ebeebf7e829b06965c",
"stakingProxy": "0x8a063452f7df2614db1bca3a85ef35da40cf0835",
"zrxVault": "0xf23276778860e420acfc18ebeebf7e829b06965c",
"staking": "0x8a063452f7df2614db1bca3a85ef35da40cf0835",
"stakingProxy": "0x59adefa01843c627ba5d6aa350292b4b7ccae67a",
"uniswapBridge": "0x0000000000000000000000000000000000000000",
"eth2DaiBridge": "0x0000000000000000000000000000000000000000",
"erc20BridgeSampler": "0x0000000000000000000000000000000000000000",

View File

@ -18,7 +18,7 @@
"changes": [
{
"note": "Update `DevUtils` artifact",
"pr": 2464
"pr": 2466
},
{
"note": "Remove `LibTransactionDecoder` artifact",

View File

@ -23,6 +23,10 @@
{
"note": "Use contract package artifacts in ganache migrations",
"pr": 2456
},
{
"note": "Update deployment for new DevUtils",
"pr": 2466
}
],
"timestamp": 1581204851

View File

@ -198,6 +198,7 @@ export async function runMigrationsAsync(
allArtifacts,
exchange.address,
constants.NULL_ADDRESS,
constants.NULL_ADDRESS,
);
// tslint:disable-next-line:no-unused-variable

View File

@ -120,7 +120,7 @@ export async function runMigrationsAsync(supportedProvider: SupportedProvider, t
assetProxyArtifacts,
);
await DydxBridgeContract.deployFrom0xArtifactAsync(
const dydxBridge = await DydxBridgeContract.deployFrom0xArtifactAsync(
assetProxyArtifacts.DydxBridge,
provider,
txDefaults,
@ -253,6 +253,7 @@ export async function runMigrationsAsync(supportedProvider: SupportedProvider, t
devUtilsArtifacts,
exchange.address,
chaiBridge.address,
dydxBridge.address,
);
await CoordinatorContract.deployFrom0xArtifactAsync(