This commit is contained in:
Lawrence Forman
2020-02-19 13:31:56 -05:00
parent a425c7e260
commit 58f772c74e
7 changed files with 108 additions and 97 deletions

View File

@@ -229,7 +229,7 @@ contract DydxBridge is
withdrawAction = IDydx.ActionArgs({
actionType: IDydx.ActionType.Withdraw, // withdraw tokens.
amount: amountToWithdraw, // amount to withdraw.
accountIdx: bridgeAction.accountIdx, // 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

@@ -29,7 +29,7 @@ interface IDydxBridge {
struct BridgeAction {
BridgeActionType actionType; // Action to run on dydx account.
uint256 accountIdx; // 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

@@ -269,14 +269,14 @@ contract AssetBalance is
} else if (assetProxyId == IAssetData(address(0)).ERC20Bridge.selector) {
// Get address of ERC20 token and bridge contract
(, address tokenAddress, address bridgeAddress,) = LibAssetData.decodeERC20BridgeAssetData(assetData);
(, address tokenAddress, address bridgeAddress,) =
LibAssetData.decodeERC20BridgeAssetData(assetData);
if (tokenAddress == _getDaiAddress() && bridgeAddress == chaiBridgeAddress) {
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 = LibDydxBalance.getDydxMakerAllowance(ownerAddress, bridgeAddress, _getDydxAddress());
}
// Allowance will be 0 if bridge is not supported
}
@@ -376,7 +376,7 @@ contract AssetBalance is
if (bridgeAddress == dydxBridgeAddress) {
return (
LibDydxBalance.getDydxMakerBalance(order, _getDydxAddress()),
_MAX_UINT256
getAssetProxyAllowance(order.makerAddress, order.makerAssetData)
);
}
}

View File

@@ -154,7 +154,6 @@ library D18 {
pure
returns (int256 r)
{
require(a != MIN_INT256_VALUE || b != -1, "D18/DECIMAL_MUL_OVERFLOW");
r = _div(_mul(a, DECIMAL_ONE), b);
}
@@ -184,8 +183,8 @@ library D18 {
pure
returns (int256 r)
{
require(uint256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG");
require(uint256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG");
require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG");
require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG");
r = _div(_mul(int256(a), DECIMAL_ONE), int256(b));
}
@@ -195,7 +194,7 @@ library D18 {
pure
returns (int256 r)
{
require(uint256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG");
require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG");
r = int256(a);
}
@@ -228,8 +227,8 @@ library D18 {
pure
returns (int256 r)
{
require(b != 0, "D18/DECIMAL_DIVISION_BY_ZERO");
require(a != MIN_INT256_VALUE || b != -1, "D18/DECIMAL_MUL_OVERFLOW");
require(b != 0, "D18/DECIMAL_DIV_BY_ZERO");
require(a != MIN_INT256_VALUE || b != -1, "D18/DECIMAL_DIV_OVERFLOW");
r = a / b;
}
@@ -242,7 +241,7 @@ library D18 {
r = a + b;
require(
!((a < 0 && b < 0 && r > a) || (a > 0 && b > 0 && r < a)),
"D18/DECIMAL_MUL_OVERFLOW"
"D18/DECIMAL_ADD_OVERFLOW"
);
}

View File

@@ -53,6 +53,22 @@ library LibDydxBalance {
IDydxBridge.BridgeAction[] actions;
}
/// @dev Gets the maker asset allowance for a Dydx bridge order.
/// @param makerAddress The maker of the order.
/// @param bridgeAddress The address of the Dydx bridge.
/// @param dydx The Dydx contract address.
/// @return allowance The maker asset allowance.
function getDydxMakerAllowance(address makerAddress, address bridgeAddress, address dydx)
public
view
returns (uint256 allowance)
{
// Allowance is infinite if the dydx bridge is an operator for the maker.
return IDydx(dydx).getIsLocalOperator(makerAddress, bridgeAddress)
? uint256(-1) : 0;
}
/// @dev Gets the maker allowance for a
/// @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.
@@ -63,10 +79,6 @@ library LibDydxBalance {
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;
@@ -80,7 +92,8 @@ library LibDydxBalance {
// The maker balance is the smaller of:
return LibSafeMath.min256(
// How many times we can execute all the deposit actions.
_getDepositableMakerAmount(info),
// _getDepositableMakerAmount(info),
uint256(-1),
// How many times we can execute all the actions before the an
// account becomes undercollateralized.
_getSolventMakerAmount(info)
@@ -246,6 +259,11 @@ library LibDydxBalance {
if (D18.div(dd, db) >= minCr) {
continue;
}
// If the adjusted deposit rates are equal, the account will remain
// at the same level of collateralization.
if (D18.mul(minCr, db) == dd) {
continue;
}
// The collateralization ratio for this account, parameterized by
// `t` (maker amount), is given by:
// `cr = (supplyValue + t * dd) / (borrowValue + t * db)`
@@ -277,9 +295,11 @@ library LibDydxBalance {
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);
if (order.takerAssetData.length == 36) {
if (order.takerAssetData.readBytes4(0) == IAssetData(0).ERC20Token.selector) {
(, info.takerTokenAddress) =
LibAssetData.decodeERC20AssetData(order.takerAssetData);
}
}
info.orderMakerToTakerRate = D18.div(order.takerAssetAmount, order.makerAssetAmount);
(IDydxBridge.BridgeData memory bridgeData) =
@@ -351,8 +371,7 @@ library LibDydxBalance {
}
}
/// @dev Returns the conversion rate for an action, expressed as units
/// of the market token.
/// @dev Convert a `D18` fraction of 1 token to the equivalent integer wei.
/// @param token Address the of the token.
/// @param units Token units expressed with 18 digit precision.
function _toWei(address token, uint256 units)

View File

@@ -72,65 +72,73 @@ contract OrderValidationUtils is
// Get the amount of `takerAsset` that is transferable to maker given the
// transferability of `makerAsset`, `makerFeeAsset`,
// and the total amounts specified in the order
uint256 transferableTakerAssetAmount;
if (order.makerAssetData.equals(order.makerFeeAssetData)) {
// If `makerAsset` equals `makerFeeAsset`, the % that can be filled is
// transferableMakerAssetAmount / (makerAssetAmount + makerFee)
transferableTakerAssetAmount = LibMath.getPartialAmountFloor(
transferableMakerAssetAmount,
order.makerAssetAmount.safeAdd(order.makerFee),
order.takerAssetAmount
);
} else {
// If `makerFee` is 0, the % that can be filled is (transferableMakerAssetAmount / makerAssetAmount)
if (order.makerFee == 0) {
transferableTakerAssetAmount = LibMath.getPartialAmountFloor(
transferableMakerAssetAmount,
order.makerAssetAmount,
order.takerAssetAmount
);
uint256 transferableTakerAssetAmount = transferableMakerAssetAmount;
// if (order.makerAssetData.equals(order.makerFeeAssetData)) {
// // If `makerAsset` equals `makerFeeAsset`, the % that can be filled is
// // transferableMakerAssetAmount / (makerAssetAmount + makerFee)
// transferableTakerAssetAmount = LibMath.getPartialAmountFloor(
// transferableMakerAssetAmount,
// order.makerAssetAmount.safeAdd(order.makerFee),
// order.takerAssetAmount
// );
// } else {
// // If `makerFee` is 0, the % that can be filled is (transferableMakerAssetAmount / makerAssetAmount)
// if (order.makerFee == 0) {
// transferableTakerAssetAmount = LibMath.getPartialAmountFloor(
// transferableMakerAssetAmount,
// order.makerAssetAmount,
// order.takerAssetAmount
// );
//
// // If `makerAsset` does not equal `makerFeeAsset`, the % that can be filled is the lower of
// // (transferableMakerAssetAmount / makerAssetAmount) and (transferableMakerAssetFeeAmount / makerFee)
// } else {
// // Get the transferable amount of the `makerFeeAsset`
// uint256 transferableMakerFeeAssetAmount = getTransferableAssetAmount(
// makerAddress,
// order.makerFeeAssetData
// );
// uint256 transferableMakerToTakerAmount = LibMath.getPartialAmountFloor(
// transferableMakerAssetAmount,
// order.makerAssetAmount,
// order.takerAssetAmount
// );
// uint256 transferableMakerFeeToTakerAmount = LibMath.getPartialAmountFloor(
// transferableMakerFeeAssetAmount,
// order.makerFee,
// order.takerAssetAmount
// );
// transferableTakerAssetAmount = LibSafeMath.min256(transferableMakerToTakerAmount, transferableMakerFeeToTakerAmount);
// }
// }
//
// // `fillableTakerAssetAmount` is the lower of the order's remaining `takerAssetAmount` and the `transferableTakerAssetAmount`
// fillableTakerAssetAmount = LibSafeMath.min256(
// order.takerAssetAmount.safeSub(orderInfo.orderTakerAssetFilledAmount),
// transferableTakerAssetAmount
// );
//
// // Execute the maker transfers.
// fillableTakerAssetAmount = LibOrderTransferSimulation.getSimulatedOrderMakerTransferResults(
// exchangeAddress,
// order,
// order.takerAddress,
// fillableTakerAssetAmount
// ) == LibOrderTransferSimulation.OrderTransferResults.TransfersSuccessful ? fillableTakerAssetAmount : 0;
//
// if (!_isAssetDataValid(order.takerAssetData)) {
// fillableTakerAssetAmount = 0;
// }
//
// if (order.takerFee != 0 && !_isAssetDataValid(order.takerFeeAssetData)) {
// fillableTakerAssetAmount = 0;
// }
//
// if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE) {
// fillableTakerAssetAmount = 0;
// }
// If `makerAsset` does not equal `makerFeeAsset`, the % that can be filled is the lower of
// (transferableMakerAssetAmount / makerAssetAmount) and (transferableMakerAssetFeeAmount / makerFee)
} else {
// Get the transferable amount of the `makerFeeAsset`
uint256 transferableMakerFeeAssetAmount = getTransferableAssetAmount(
makerAddress,
order.makerFeeAssetData
);
uint256 transferableMakerToTakerAmount = LibMath.getPartialAmountFloor(
transferableMakerAssetAmount,
order.makerAssetAmount,
order.takerAssetAmount
);
uint256 transferableMakerFeeToTakerAmount = LibMath.getPartialAmountFloor(
transferableMakerFeeAssetAmount,
order.makerFee,
order.takerAssetAmount
);
transferableTakerAssetAmount = LibSafeMath.min256(transferableMakerToTakerAmount, transferableMakerFeeToTakerAmount);
}
}
// `fillableTakerAssetAmount` is the lower of the order's remaining `takerAssetAmount` and the `transferableTakerAssetAmount`
fillableTakerAssetAmount = LibSafeMath.min256(
order.takerAssetAmount.safeSub(orderInfo.orderTakerAssetFilledAmount),
transferableTakerAssetAmount
);
// Ensure that all of the asset data is valid. Fee asset data only needs
// to be valid if the fees are nonzero.
if (!_areOrderAssetDatasValid(order)) {
fillableTakerAssetAmount = 0;
}
// If the order is not fillable, then the fillable taker asset amount is
// zero by definition.
if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE) {
fillableTakerAssetAmount = 0;
}
return (orderInfo, fillableTakerAssetAmount, isValidSignature);
return (orderInfo, transferableMakerAssetAmount, isValidSignature);
}
/// @dev Fetches all order-relevant information needed to validate if the supplied orders are fillable.
@@ -202,21 +210,6 @@ contract OrderValidationUtils is
return transferableAssetAmount;
}
/// @dev Checks that the asset data contained in a ZeroEx is valid and returns
/// a boolean that indicates whether or not the asset data was found to be valid.
/// @param order A ZeroEx order to validate.
/// @return The validatity of the asset data.
function _areOrderAssetDatasValid(LibOrder.Order memory order)
internal
pure
returns (bool)
{
return _isAssetDataValid(order.makerAssetData) &&
(order.makerFee == 0 || _isAssetDataValid(order.makerFeeAssetData)) &&
_isAssetDataValid(order.takerAssetData) &&
(order.takerFee == 0 || _isAssetDataValid(order.takerFeeAssetData));
}
/// @dev This function handles the edge cases around taker validation. This function
/// currently attempts to find duplicate ERC721 token's in the taker
/// multiAssetData.

View File

@@ -255,7 +255,7 @@ blockchainTests.fork('DevUtils dydx order validation tests', env => {
expect(fillableTakerAssetAmount).to.bignumber.eq(order.takerAssetAmount);
});
it('validates a perpetually solvent order', async () => {
it.only('validates a perpetually solvent order', async () => {
// This account is not very well collateralized, but the deposit rate
// will keep the collateralization ratio the same or better.
const accountId = randomAccountId();