207 lines
7.8 KiB
Solidity
207 lines
7.8 KiB
Solidity
/*
|
|
|
|
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;
|
|
|
|
|
|
/// @dev THIS IS A SELF-CONTAINED CONVENIENCE CONTRACT FOR TESTING THE DYDX BRIDGE ON MAINNET.
|
|
/// Currently deployed at 0xeb58c2caa96f39626dcceb74fdbb7a9a8b54ec18.
|
|
/// Steps:
|
|
/// 1. Deploy to mainnet (can copy-paste this into Remix and deploy).
|
|
/// 2. Send some DAI to the contract.
|
|
/// 3. Call `init()` to configure.
|
|
interface IERC20Token {
|
|
|
|
/// @dev `msg.sender` approves `_spender` to spend `_value` tokens
|
|
/// @param spender The address of the account able to transfer the tokens
|
|
/// @param value The amount of wei to be approved for transfer
|
|
/// @return Always true if the call has enough gas to complete execution
|
|
function approve(address spender, uint256 value)
|
|
external
|
|
returns (bool);
|
|
|
|
/// @dev Query the balance of owner
|
|
/// @param owner The address from which the balance will be retrieved
|
|
/// @return Balance of owner
|
|
function balanceOf(address owner)
|
|
external
|
|
view
|
|
returns (uint256);
|
|
}
|
|
|
|
|
|
/// @dev TestDydxUser uses this interface to interact with dydx.
|
|
interface IDydx {
|
|
|
|
/// @dev Respresents an operator's privileges.
|
|
struct OperatorArg {
|
|
address operator;
|
|
bool trusted;
|
|
}
|
|
|
|
/// @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 Sets operators of dydx account.
|
|
/// @param operators to give/remove access.
|
|
function setOperators(OperatorArg[] calldata operators)
|
|
external;
|
|
}
|
|
|
|
|
|
/// @dev Deploy this contract and call `init` to run the mainnet DydxBridge integration tests.
|
|
contract TestDydxUser {
|
|
|
|
address public constant DYDX_BRIDGE_ADDRESS = 0x55dC8f21D20D4c6ED3C82916A438A413ca68e335;
|
|
address public constant DYDX_ADDRESS = 0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e;
|
|
address public constant DAI_ADDRESS = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
|
|
uint256 public constant DYDX_DAI_MARKET_ID = 3;
|
|
bytes4 public constant MAGIC_BYTES = bytes4(keccak256("init()"));
|
|
|
|
function init()
|
|
public
|
|
returns (bytes4)
|
|
{
|
|
// 1. Assert that this account has DAI.
|
|
uint256 daiBalance = IERC20Token(DAI_ADDRESS).balanceOf(address(this));
|
|
require(
|
|
daiBalance > 0,
|
|
"TestDydxUser/DAI_BALANCE_MUST_BE_NONZERO"
|
|
);
|
|
|
|
// 2. Set allowance for Dydx to transfer DAI.
|
|
require(
|
|
IERC20Token(DAI_ADDRESS).approve(
|
|
DYDX_ADDRESS,
|
|
daiBalance
|
|
),
|
|
"TestDydxUser/FAILED_TO_SET_DAI_ALLOWANCE"
|
|
);
|
|
|
|
// 3. Add DydxBridge as operator on dydx.
|
|
// This will revert on failure.
|
|
IDydx.OperatorArg[] memory operatorArgs = new IDydx.OperatorArg[](1);
|
|
operatorArgs[0] = IDydx.OperatorArg({
|
|
operator: DYDX_BRIDGE_ADDRESS,
|
|
trusted: true
|
|
});
|
|
IDydx(DYDX_ADDRESS).setOperators(operatorArgs);
|
|
|
|
// 4. Deposit 1/2 DAI balance into dydx. This allows us to test withdrawals.
|
|
// 4.i Create dydx account struct.
|
|
IDydx.AccountInfo[] memory accounts = new IDydx.AccountInfo[](1);
|
|
accounts[0] = IDydx.AccountInfo({
|
|
owner: address(this),
|
|
number: 0
|
|
});
|
|
|
|
// 4.ii Create dydx amount.
|
|
IDydx.AssetAmount memory dydxAmount = IDydx.AssetAmount({
|
|
sign: true, // true if positive.
|
|
denomination: IDydx.AssetDenomination.Wei, // Wei => actual token amount held in account.
|
|
ref: IDydx.AssetReference.Delta, // Delta => a relative amount.
|
|
value: daiBalance / 2 // amount to deposit.
|
|
});
|
|
|
|
// 4.iii Create dydx deposit action.
|
|
IDydx.ActionArgs[] memory actions = new IDydx.ActionArgs[](1);
|
|
actions[0] = IDydx.ActionArgs({
|
|
actionType: IDydx.ActionType.Deposit, // deposit tokens.
|
|
amount: dydxAmount, // amount to deposit.
|
|
accountId: 0, // index in the `accounts` when calling `operate`.
|
|
primaryMarketId: DYDX_DAI_MARKET_ID, // indicates which token to deposit.
|
|
otherAddress: address(this), // deposit from the account owner.
|
|
// unused parameters
|
|
secondaryMarketId: 0,
|
|
otherAccountId: 0,
|
|
data: hex''
|
|
});
|
|
|
|
// 4.iv Deposit DAI into dydx. This will revert on failure.
|
|
IDydx(DYDX_ADDRESS).operate(
|
|
accounts,
|
|
actions
|
|
);
|
|
|
|
// Return magic bytes on success.
|
|
return MAGIC_BYTES;
|
|
}
|
|
} |