Compare commits
58 Commits
@0x/contra
...
@0x/contra
Author | SHA1 | Date | |
---|---|---|---|
|
2113fb490d | ||
|
0afedbd252 | ||
|
3dec38450a | ||
|
d7a00b05e3 | ||
|
ff2cc8c887 | ||
|
0571a96cea | ||
|
b7b457b076 | ||
|
d2c12005b2 | ||
|
25f26d7e5f | ||
|
9d5724e1a0 | ||
|
784d23ec87 | ||
|
709689a7ee | ||
|
35d5d3d995 | ||
|
630a8d8a4e | ||
|
54eb1d9055 | ||
|
cb63caea61 | ||
|
8e046bb022 | ||
|
189b53b8c4 | ||
|
9b7277d464 | ||
|
551a65c069 | ||
|
4c21a697f4 | ||
|
a8506c07ae | ||
|
b0feb85b5c | ||
|
f371eba8ad | ||
|
265fa52ace | ||
|
c1f5322d38 | ||
|
4415e00b38 | ||
|
d4e46c5a9c | ||
|
bf5b9949fe | ||
|
3c11a2b1da | ||
|
1248868169 | ||
|
930b95a548 | ||
|
27c9f68c7c | ||
|
358d4d86a7 | ||
|
d55eea2239 | ||
|
4507954ea5 | ||
|
8e0a83f8d8 | ||
|
b6ec09e6cf | ||
|
ed4e90623d | ||
|
38cdb48748 | ||
|
71bfe9b745 | ||
|
9e7645a167 | ||
|
6dccc37143 | ||
|
3310310d8c | ||
|
abb499aad8 | ||
|
1afc09b08a | ||
|
0e86d72f05 | ||
|
c9857a2764 | ||
|
701ba3902c | ||
|
bb3ec970a9 | ||
|
1d023e6db5 | ||
|
1bd906ecb3 | ||
|
7cbffdb86b | ||
|
b979196ffd | ||
|
2949db5f49 | ||
|
47c3ed9705 | ||
|
e00f059a4a | ||
|
731a823cc2 |
@@ -23,7 +23,7 @@ jobs:
|
||||
# command: npm set prefix=/home/circleci/npm && echo 'export PATH=$HOME/circleci/npm/bin:$PATH' >> /home/circleci/.bashrc
|
||||
- run:
|
||||
name: install-yarn
|
||||
command: npm install --global yarn@1.17.0
|
||||
command: npm install --force --global yarn@1.17.0
|
||||
- run:
|
||||
name: yarn
|
||||
command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange-forwarder @0x/contracts-tests @0x/contracts-staking @0x/contracts-coordinator @0x/contracts-erc20-bridge-sampler
|
||||
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange-forwarder @0x/contracts-tests @0x/contracts-staking @0x/contracts-coordinator @0x/contracts-erc20-bridge-sampler
|
||||
# TODO(dorothy-zbornak): Re-enable after updating this package for
|
||||
# 3.0. At that time, also remove exclusion from monorepo
|
||||
# package.json's test script.
|
||||
@@ -197,13 +197,10 @@ jobs:
|
||||
- image: 0xorg/mesh:0xV3
|
||||
environment:
|
||||
ETHEREUM_RPC_URL: 'http://localhost:8545'
|
||||
ETHEREUM_NETWORK_ID: '50'
|
||||
ETHEREUM_CHAIN_ID: '1337'
|
||||
USE_BOOTSTRAP_LIST: 'true'
|
||||
VERBOSITY: 3
|
||||
PRIVATE_KEY_PATH: ''
|
||||
BLOCK_POLLING_INTERVAL: '5s'
|
||||
P2P_LISTEN_PORT: '60557'
|
||||
VERBOSITY: 5
|
||||
BLOCK_POLLING_INTERVAL: '50ms'
|
||||
ETHEREUM_RPC_MAX_REQUESTS_PER_24_HR_UTC: '1778000'
|
||||
command: |
|
||||
sh -c "waitForGanache () { until printf 'POST /\r\nContent-Length: 26\r\n\r\n{\"method\":\"net_listening\"}' | nc localhost 8545 | grep true; do continue; done }; waitForGanache && ./mesh"
|
||||
- image: 0xorg/launch-kit-backend:v3
|
||||
|
28
.gitignore
vendored
28
.gitignore
vendored
@@ -160,33 +160,7 @@ contracts/exchange-forwarder/generated-wrappers/
|
||||
contracts/exchange-forwarder/test/generated-wrappers/
|
||||
contracts/dev-utils/generated-wrappers/
|
||||
contracts/dev-utils/test/generated-wrappers/
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dev_utils/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_token/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/asset_proxy_owner/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/coordinator/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/coordinator_registry/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dummy_erc20_token/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dummy_erc721_token/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dutch_auction/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_mintable/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_bridge_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_token/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/forwarder/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_asset_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_validator/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_wallet/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/multi_asset_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_validator/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/staking/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/staking_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/static_call_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/weth9/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_token/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_vault/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/*/__init__.py
|
||||
|
||||
# solc-bin in sol-compiler
|
||||
packages/sol-compiler/solc_bin/
|
||||
|
@@ -14,8 +14,8 @@ packages/abi-gen/ @feuGeneA
|
||||
packages/base-contract/ @xianny
|
||||
packages/connect/ @fragosti
|
||||
packages/abi-gen-templates/ @feuGeneA @xianny
|
||||
packages/contract-addresses/ @albrow
|
||||
packages/contract-artifacts/ @albrow
|
||||
packages/contract-addresses/ @abandeali1
|
||||
packages/contract-artifacts/ @abandeali1
|
||||
packages/dev-utils/ @LogvinovLeon @fabioberger
|
||||
packages/devnet/ @albrow
|
||||
packages/ethereum-types/ @LogvinovLeon
|
||||
|
@@ -1,4 +1,22 @@
|
||||
[
|
||||
{
|
||||
"version": "3.1.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Integration tests for DydxBridge with ERC20BridgeProxy.",
|
||||
"pr": 2401
|
||||
},
|
||||
{
|
||||
"note": "Fix `UniswapBridge` token -> token transfer call.",
|
||||
"pr": 2412
|
||||
},
|
||||
{
|
||||
"note": "Fix `KyberBridge` incorrect `minConversionRate` calculation.",
|
||||
"pr": 2412
|
||||
}
|
||||
],
|
||||
"timestamp": 1578272714
|
||||
},
|
||||
{
|
||||
"timestamp": 1576540892,
|
||||
"version": "3.0.2",
|
||||
|
@@ -5,6 +5,12 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.1.0 - _January 6, 2020_
|
||||
|
||||
* Integration tests for DydxBridge with ERC20BridgeProxy. (#2401)
|
||||
* Fix `UniswapBridge` token -> token transfer call. (#2412)
|
||||
* Fix `KyberBridge` incorrect `minConversionRate` calculation. (#2412)
|
||||
|
||||
## v3.0.2 - _December 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -24,6 +24,7 @@ import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
import "../interfaces/IERC20Bridge.sol";
|
||||
import "../interfaces/IKyberNetworkProxy.sol";
|
||||
|
||||
@@ -34,6 +35,8 @@ contract KyberBridge is
|
||||
IWallet,
|
||||
DeploymentConstants
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
// @dev Structure used internally to get around stack limits.
|
||||
struct TradeState {
|
||||
IKyberNetworkProxy kyber;
|
||||
@@ -41,6 +44,7 @@ contract KyberBridge is
|
||||
address fromTokenAddress;
|
||||
uint256 fromTokenBalance;
|
||||
uint256 payableAmount;
|
||||
uint256 conversionRate;
|
||||
}
|
||||
|
||||
/// @dev Kyber ETH pseudo-address.
|
||||
@@ -81,11 +85,23 @@ contract KyberBridge is
|
||||
state.weth = IEtherToken(_getWethAddress());
|
||||
// Decode the bridge data to get the `fromTokenAddress`.
|
||||
(state.fromTokenAddress) = abi.decode(bridgeData, (address));
|
||||
// Query the balance of "from" tokens.
|
||||
state.fromTokenBalance = IERC20Token(state.fromTokenAddress).balanceOf(address(this));
|
||||
if (state.fromTokenBalance == 0) {
|
||||
// Return failure if no input tokens.
|
||||
return BRIDGE_FAILED;
|
||||
}
|
||||
// Compute the conversion rate, expressed in 18 decimals.
|
||||
// The sequential notation is to get around stack limits.
|
||||
state.conversionRate = KYBER_RATE_BASE;
|
||||
state.conversionRate = state.conversionRate.safeMul(amount);
|
||||
state.conversionRate = state.conversionRate.safeMul(
|
||||
10 ** uint256(LibERC20Token.decimals(state.fromTokenAddress))
|
||||
);
|
||||
state.conversionRate = state.conversionRate.safeDiv(state.fromTokenBalance);
|
||||
state.conversionRate = state.conversionRate.safeDiv(
|
||||
10 ** uint256(LibERC20Token.decimals(toTokenAddress))
|
||||
);
|
||||
if (state.fromTokenAddress == toTokenAddress) {
|
||||
// Just transfer the tokens if they're the same.
|
||||
LibERC20Token.transfer(state.fromTokenAddress, to, state.fromTokenBalance);
|
||||
@@ -118,7 +134,7 @@ contract KyberBridge is
|
||||
uint256(-1),
|
||||
// Compute the minimum conversion rate, which is expressed in units with
|
||||
// 18 decimal places.
|
||||
(KYBER_RATE_BASE * amount) / state.fromTokenBalance,
|
||||
state.conversionRate,
|
||||
// No affiliate address.
|
||||
address(0)
|
||||
);
|
||||
|
@@ -134,8 +134,8 @@ contract UniswapBridge is
|
||||
state.fromTokenBalance,
|
||||
// Minimum buy amount.
|
||||
amount,
|
||||
// No minimum intermediate ETH buy amount.
|
||||
0,
|
||||
// Must buy at least 1 intermediate ETH.
|
||||
1,
|
||||
// Expires after this block.
|
||||
block.timestamp,
|
||||
// Recipient is `to`.
|
||||
|
@@ -19,9 +19,49 @@
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
import "../src/bridges/DydxBridge.sol";
|
||||
|
||||
|
||||
contract TestDydxBridgeToken {
|
||||
|
||||
uint256 private constant INIT_HOLDER_BALANCE = 10 * 10**18; // 10 tokens
|
||||
mapping (address => uint256) private _balances;
|
||||
|
||||
/// @dev Sets initial balance of token holders.
|
||||
constructor(address[] memory holders)
|
||||
public
|
||||
{
|
||||
for (uint256 i = 0; i != holders.length; ++i) {
|
||||
_balances[holders[i]] = INIT_HOLDER_BALANCE;
|
||||
}
|
||||
_balances[msg.sender] = INIT_HOLDER_BALANCE;
|
||||
}
|
||||
|
||||
/// @dev Basic transferFrom implementation.
|
||||
function transferFrom(address from, address to, uint256 amount)
|
||||
external
|
||||
returns (bool)
|
||||
{
|
||||
if (_balances[from] < amount || _balances[to] + amount < _balances[to]) {
|
||||
return false;
|
||||
}
|
||||
_balances[from] -= amount;
|
||||
_balances[to] += amount;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @dev Returns balance of `holder`.
|
||||
function balanceOf(address holder)
|
||||
external
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
return _balances[holder];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// solhint-disable space-after-comma
|
||||
contract TestDydxBridge is
|
||||
IDydx,
|
||||
@@ -29,6 +69,8 @@ contract TestDydxBridge is
|
||||
{
|
||||
|
||||
address private constant ALWAYS_REVERT_ADDRESS = address(1);
|
||||
address private _testTokenAddress;
|
||||
bool private _shouldRevertOnOperate;
|
||||
|
||||
event OperateAccount(
|
||||
address owner,
|
||||
@@ -49,6 +91,13 @@ contract TestDydxBridge is
|
||||
bytes data
|
||||
);
|
||||
|
||||
constructor(address[] memory holders)
|
||||
public
|
||||
{
|
||||
// Deploy a test token. This represents the asset being deposited/withdrawn from dydx.
|
||||
_testTokenAddress = address(new TestDydxBridgeToken(holders));
|
||||
}
|
||||
|
||||
/// @dev Simulates `operate` in dydx contract.
|
||||
/// Emits events so that arguments can be validated client-side.
|
||||
function operate(
|
||||
@@ -57,6 +106,10 @@ contract TestDydxBridge is
|
||||
)
|
||||
external
|
||||
{
|
||||
if (_shouldRevertOnOperate) {
|
||||
revert("TestDydxBridge/SHOULD_REVERT_ON_OPERATE");
|
||||
}
|
||||
|
||||
for (uint i = 0; i < accounts.length; ++i) {
|
||||
emit OperateAccount(
|
||||
accounts[i].owner,
|
||||
@@ -78,9 +131,46 @@ contract TestDydxBridge is
|
||||
actions[i].otherAccountId,
|
||||
actions[i].data
|
||||
);
|
||||
|
||||
if (actions[i].actionType == IDydx.ActionType.Withdraw) {
|
||||
require(
|
||||
IERC20Token(_testTokenAddress).transferFrom(
|
||||
address(this),
|
||||
actions[i].otherAddress,
|
||||
actions[i].amount.value
|
||||
),
|
||||
"TestDydxBridge/WITHDRAW_FAILED"
|
||||
);
|
||||
} else if (actions[i].actionType == IDydx.ActionType.Deposit) {
|
||||
require(
|
||||
IERC20Token(_testTokenAddress).transferFrom(
|
||||
actions[i].otherAddress,
|
||||
address(this),
|
||||
actions[i].amount.value
|
||||
),
|
||||
"TestDydxBridge/DEPOSIT_FAILED"
|
||||
);
|
||||
} else {
|
||||
revert("TestDydxBridge/UNSUPPORTED_ACTION");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev If `true` then subsequent calls to `operate` will revert.
|
||||
function setRevertOnOperate(bool shouldRevert)
|
||||
external
|
||||
{
|
||||
_shouldRevertOnOperate = shouldRevert;
|
||||
}
|
||||
|
||||
/// @dev Returns test token.
|
||||
function getTestToken()
|
||||
external
|
||||
returns (address)
|
||||
{
|
||||
return _testTokenAddress;
|
||||
}
|
||||
|
||||
/// @dev overrides `_getDydxAddress()` from `DeploymentConstants` to return this address.
|
||||
function _getDydxAddress()
|
||||
internal
|
||||
|
@@ -67,9 +67,11 @@ interface ITestContract {
|
||||
/// @dev A minimalist ERC20/WETH token.
|
||||
contract TestToken {
|
||||
|
||||
uint8 public decimals;
|
||||
ITestContract private _testContract;
|
||||
|
||||
constructor() public {
|
||||
constructor(uint8 decimals_) public {
|
||||
decimals = decimals_;
|
||||
_testContract = ITestContract(msg.sender);
|
||||
}
|
||||
|
||||
@@ -165,7 +167,7 @@ contract TestKyberBridge is
|
||||
uint256 private _nextFillAmount;
|
||||
|
||||
constructor() public {
|
||||
weth = IEtherToken(address(new TestToken()));
|
||||
weth = IEtherToken(address(new TestToken(18)));
|
||||
}
|
||||
|
||||
/// @dev Implementation of `IKyberNetworkProxy.trade()`
|
||||
@@ -195,11 +197,11 @@ contract TestKyberBridge is
|
||||
return _nextFillAmount;
|
||||
}
|
||||
|
||||
function createToken()
|
||||
function createToken(uint8 decimals)
|
||||
external
|
||||
returns (address tokenAddress)
|
||||
{
|
||||
return address(new TestToken());
|
||||
return address(new TestToken(decimals));
|
||||
}
|
||||
|
||||
function setNextFillAmount(uint256 amount)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-asset-proxy",
|
||||
"version": "3.0.2",
|
||||
"version": "3.1.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -51,12 +51,12 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.0.2",
|
||||
"@0x/contracts-gen": "^2.0.2",
|
||||
"@0x/contracts-test-utils": "^5.0.1",
|
||||
"@0x/contracts-utils": "^4.0.2",
|
||||
"@0x/dev-utils": "^3.0.2",
|
||||
"@0x/sol-compiler": "^4.0.2",
|
||||
"@0x/abi-gen": "^5.0.3",
|
||||
"@0x/contracts-gen": "^2.0.3",
|
||||
"@0x/contracts-test-utils": "^5.1.0",
|
||||
"@0x/contracts-utils": "^4.0.3",
|
||||
"@0x/dev-utils": "^3.1.0",
|
||||
"@0x/sol-compiler": "^4.0.3",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/types": "^3.1.1",
|
||||
@@ -79,16 +79,16 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.0.2",
|
||||
"@0x/contracts-dev-utils": "^1.0.2",
|
||||
"@0x/contracts-erc1155": "^2.0.2",
|
||||
"@0x/contracts-erc20": "^3.0.2",
|
||||
"@0x/contracts-erc721": "^3.0.2",
|
||||
"@0x/contracts-exchange-libs": "^4.0.2",
|
||||
"@0x/order-utils": "^10.0.1",
|
||||
"@0x/base-contract": "^6.0.3",
|
||||
"@0x/contracts-dev-utils": "^1.0.3",
|
||||
"@0x/contracts-erc1155": "^2.0.3",
|
||||
"@0x/contracts-erc20": "^3.0.3",
|
||||
"@0x/contracts-erc721": "^3.0.3",
|
||||
"@0x/contracts-exchange-libs": "^4.0.3",
|
||||
"@0x/order-utils": "^10.1.0",
|
||||
"@0x/typescript-typings": "^5.0.1",
|
||||
"@0x/utils": "^5.1.1",
|
||||
"@0x/web3-wrapper": "^7.0.2",
|
||||
"@0x/utils": "^5.1.2",
|
||||
"@0x/web3-wrapper": "^7.0.3",
|
||||
"ethereum-types": "^3.0.0",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
|
40
contracts/asset-proxy/src/dydx_bridge_encoder.ts
Normal file
40
contracts/asset-proxy/src/dydx_bridge_encoder.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { AbiEncoder, BigNumber } from '@0x/utils';
|
||||
|
||||
export enum DydxBridgeActionType {
|
||||
Deposit,
|
||||
Withdraw,
|
||||
}
|
||||
|
||||
export interface DydxBrigeAction {
|
||||
actionType: DydxBridgeActionType;
|
||||
accountId: BigNumber;
|
||||
marketId: BigNumber;
|
||||
conversionRateNumerator: BigNumber;
|
||||
conversionRateDenominator: BigNumber;
|
||||
}
|
||||
|
||||
export interface DydxBridgeData {
|
||||
accountNumbers: BigNumber[];
|
||||
actions: DydxBrigeAction[];
|
||||
}
|
||||
|
||||
export const dydxBridgeDataEncoder = AbiEncoder.create([
|
||||
{
|
||||
name: 'bridgeData',
|
||||
type: 'tuple',
|
||||
components: [
|
||||
{ name: 'accountNumbers', type: 'uint256[]' },
|
||||
{
|
||||
name: 'actions',
|
||||
type: 'tuple[]',
|
||||
components: [
|
||||
{ name: 'actionType', type: 'uint8' },
|
||||
{ name: 'accountId', type: 'uint256' },
|
||||
{ name: 'marketId', type: 'uint256' },
|
||||
{ name: 'conversionRateNumerator', type: 'uint256' },
|
||||
{ name: 'conversionRateDenominator', type: 'uint256' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
@@ -6,6 +6,7 @@ export {
|
||||
ERC721ProxyContract,
|
||||
Eth2DaiBridgeContract,
|
||||
DydxBridgeContract,
|
||||
TestDydxBridgeContract,
|
||||
IAssetDataContract,
|
||||
IAssetProxyContract,
|
||||
MultiAssetProxyContract,
|
||||
@@ -63,3 +64,4 @@ export {
|
||||
TupleDataItem,
|
||||
StateMutability,
|
||||
} from 'ethereum-types';
|
||||
export * from './dydx_bridge_encoder';
|
||||
|
@@ -1,8 +1,12 @@
|
||||
import { LibMathRevertErrors } from '@0x/contracts-exchange-libs';
|
||||
import { blockchainTests, constants, expect, verifyEventsFromLogs } from '@0x/contracts-test-utils';
|
||||
import { AssetProxyId, RevertReason } from '@0x/types';
|
||||
import { AbiEncoder, BigNumber } from '@0x/utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { DydxBridgeActionType, DydxBridgeData, dydxBridgeDataEncoder } from '../src/dydx_bridge_encoder';
|
||||
import { ERC20BridgeProxyContract, IAssetDataContract } from '../src/wrappers';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
import { TestDydxBridgeContract, TestDydxBridgeEvents } from './wrappers';
|
||||
|
||||
@@ -11,7 +15,24 @@ blockchainTests.resets('DydxBridge unit tests', env => {
|
||||
const marketId = new BigNumber(2);
|
||||
const defaultAmount = new BigNumber(4);
|
||||
const notAuthorized = '0x0000000000000000000000000000000000000001';
|
||||
const defaultDepositAction = {
|
||||
actionType: DydxBridgeActionType.Deposit,
|
||||
accountId: constants.ZERO_AMOUNT,
|
||||
marketId,
|
||||
conversionRateNumerator: constants.ZERO_AMOUNT,
|
||||
conversionRateDenominator: constants.ZERO_AMOUNT,
|
||||
};
|
||||
const defaultWithdrawAction = {
|
||||
actionType: DydxBridgeActionType.Withdraw,
|
||||
accountId: constants.ZERO_AMOUNT,
|
||||
marketId,
|
||||
conversionRateNumerator: constants.ZERO_AMOUNT,
|
||||
conversionRateDenominator: constants.ZERO_AMOUNT,
|
||||
};
|
||||
let testContract: TestDydxBridgeContract;
|
||||
let testProxyContract: ERC20BridgeProxyContract;
|
||||
let assetDataEncoder: IAssetDataContract;
|
||||
let owner: string;
|
||||
let authorized: string;
|
||||
let accountOwner: string;
|
||||
let receiver: string;
|
||||
@@ -19,7 +40,7 @@ blockchainTests.resets('DydxBridge unit tests', env => {
|
||||
before(async () => {
|
||||
// Get accounts
|
||||
const accounts = await env.web3Wrapper.getAvailableAddressesAsync();
|
||||
[, /* owner */ authorized, accountOwner, receiver] = accounts;
|
||||
[owner, authorized, accountOwner, receiver] = accounts;
|
||||
|
||||
// Deploy dydx bridge
|
||||
testContract = await TestDydxBridgeContract.deployFrom0xArtifactAsync(
|
||||
@@ -27,81 +48,57 @@ blockchainTests.resets('DydxBridge unit tests', env => {
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
[accountOwner, receiver],
|
||||
);
|
||||
|
||||
// Deploy test erc20 bridge proxy
|
||||
testProxyContract = await ERC20BridgeProxyContract.deployFrom0xArtifactAsync(
|
||||
artifacts.ERC20BridgeProxy,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
await testProxyContract.addAuthorizedAddress(authorized).awaitTransactionSuccessAsync({ from: owner });
|
||||
|
||||
// Setup asset data encoder
|
||||
assetDataEncoder = new IAssetDataContract(constants.NULL_ADDRESS, env.provider);
|
||||
});
|
||||
|
||||
describe('bridgeTransferFrom()', () => {
|
||||
enum BridgeActionType {
|
||||
Deposit,
|
||||
Withdraw,
|
||||
}
|
||||
interface BrigeAction {
|
||||
actionType: BridgeActionType;
|
||||
accountId: BigNumber;
|
||||
marketId: BigNumber;
|
||||
conversionRateNumerator: BigNumber;
|
||||
conversionRateDenominator: BigNumber;
|
||||
}
|
||||
interface BridgeData {
|
||||
accountNumbers: BigNumber[];
|
||||
actions: BrigeAction[];
|
||||
}
|
||||
const defaultDepositAction = {
|
||||
actionType: BridgeActionType.Deposit as number,
|
||||
accountId: constants.ZERO_AMOUNT,
|
||||
marketId,
|
||||
conversionRateNumerator: constants.ZERO_AMOUNT,
|
||||
conversionRateDenominator: constants.ZERO_AMOUNT,
|
||||
};
|
||||
const defaultWithdrawAction = {
|
||||
actionType: BridgeActionType.Withdraw as number,
|
||||
accountId: constants.ZERO_AMOUNT,
|
||||
marketId,
|
||||
conversionRateNumerator: constants.ZERO_AMOUNT,
|
||||
conversionRateDenominator: constants.ZERO_AMOUNT,
|
||||
};
|
||||
const bridgeDataEncoder = AbiEncoder.create([
|
||||
{
|
||||
name: 'bridgeData',
|
||||
type: 'tuple',
|
||||
components: [
|
||||
{ name: 'accountNumbers', type: 'uint256[]' },
|
||||
{
|
||||
name: 'actions',
|
||||
type: 'tuple[]',
|
||||
components: [
|
||||
{ name: 'actionType', type: 'uint8' },
|
||||
{ name: 'accountId', type: 'uint256' },
|
||||
{ name: 'marketId', type: 'uint256' },
|
||||
{ name: 'conversionRateNumerator', type: 'uint256' },
|
||||
{ name: 'conversionRateDenominator', type: 'uint256' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
const callBridgeTransferFrom = async (
|
||||
from: string,
|
||||
to: string,
|
||||
amount: BigNumber,
|
||||
bridgeData: BridgeData,
|
||||
bridgeData: DydxBridgeData,
|
||||
sender: string,
|
||||
): Promise<string> => {
|
||||
const returnValue = await testContract
|
||||
.bridgeTransferFrom(constants.NULL_ADDRESS, from, to, amount, bridgeDataEncoder.encode({ bridgeData }))
|
||||
.bridgeTransferFrom(
|
||||
constants.NULL_ADDRESS,
|
||||
from,
|
||||
to,
|
||||
amount,
|
||||
dydxBridgeDataEncoder.encode({ bridgeData }),
|
||||
)
|
||||
.callAsync({ from: sender });
|
||||
return returnValue;
|
||||
};
|
||||
const callBridgeTransferFromAndVerifyEvents = async (
|
||||
const executeBridgeTransferFromAndVerifyEvents = async (
|
||||
from: string,
|
||||
to: string,
|
||||
amount: BigNumber,
|
||||
bridgeData: BridgeData,
|
||||
bridgeData: DydxBridgeData,
|
||||
sender: string,
|
||||
): Promise<void> => {
|
||||
// Execute transaction.
|
||||
const txReceipt = await testContract
|
||||
.bridgeTransferFrom(constants.NULL_ADDRESS, from, to, amount, bridgeDataEncoder.encode({ bridgeData }))
|
||||
.bridgeTransferFrom(
|
||||
constants.NULL_ADDRESS,
|
||||
from,
|
||||
to,
|
||||
amount,
|
||||
dydxBridgeDataEncoder.encode({ bridgeData }),
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: sender });
|
||||
|
||||
// Verify `OperateAccount` event.
|
||||
@@ -122,62 +119,139 @@ blockchainTests.resets('DydxBridge unit tests', env => {
|
||||
expectedOperateActionEvents.push({
|
||||
actionType: action.actionType as number,
|
||||
accountId: action.accountId,
|
||||
amountSign: action.actionType === BridgeActionType.Deposit ? true : false,
|
||||
amountSign: action.actionType === DydxBridgeActionType.Deposit ? true : false,
|
||||
amountDenomination: weiDenomination,
|
||||
amountRef: deltaAmountRef,
|
||||
amountValue: action.conversionRateDenominator.gt(0)
|
||||
? amount.times(action.conversionRateNumerator).div(action.conversionRateDenominator)
|
||||
? amount
|
||||
.times(action.conversionRateNumerator)
|
||||
.dividedToIntegerBy(action.conversionRateDenominator)
|
||||
: amount,
|
||||
primaryMarketId: marketId,
|
||||
secondaryMarketId: constants.ZERO_AMOUNT,
|
||||
otherAddress: action.actionType === BridgeActionType.Deposit ? from : to,
|
||||
otherAddress: action.actionType === DydxBridgeActionType.Deposit ? from : to,
|
||||
otherAccountId: constants.ZERO_AMOUNT,
|
||||
data: '0x',
|
||||
});
|
||||
}
|
||||
verifyEventsFromLogs(txReceipt.logs, expectedOperateActionEvents, TestDydxBridgeEvents.OperateAction);
|
||||
};
|
||||
it('succeeds when calling with zero amount', async () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [defaultDepositAction],
|
||||
};
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
constants.ZERO_AMOUNT,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('succeeds when calling with no accounts', async () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [],
|
||||
actions: [defaultDepositAction],
|
||||
};
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('succeeds when calling with no actions', async () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [],
|
||||
};
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('succeeds when calling `operate` with the `deposit` action and a single account', async () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [defaultDepositAction],
|
||||
};
|
||||
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
||||
actions: [defaultDepositAction],
|
||||
};
|
||||
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('succeeds when calling `operate` with the `withdraw` action and a single accuont', async () => {
|
||||
it('succeeds when calling `operate` with the `withdraw` action and a single account', async () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [defaultWithdrawAction],
|
||||
};
|
||||
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('succeeds when calling `operate` with the `withdraw` action and multiple accounts', async () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
||||
actions: [defaultWithdrawAction],
|
||||
};
|
||||
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
||||
actions: [defaultWithdrawAction, defaultDepositAction],
|
||||
};
|
||||
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('succeeds when calling `operate` with multiple actions under a single account', async () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [defaultWithdrawAction, defaultDepositAction],
|
||||
};
|
||||
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('succeeds when scaling the `amount` to deposit', async () => {
|
||||
const conversionRateNumerator = new BigNumber(1);
|
||||
@@ -193,7 +267,13 @@ blockchainTests.resets('DydxBridge unit tests', env => {
|
||||
},
|
||||
],
|
||||
};
|
||||
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('succeeds when scaling the `amount` to withdraw', async () => {
|
||||
const conversionRateNumerator = new BigNumber(1);
|
||||
@@ -209,7 +289,13 @@ blockchainTests.resets('DydxBridge unit tests', env => {
|
||||
},
|
||||
],
|
||||
};
|
||||
await callBridgeTransferFromAndVerifyEvents(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
||||
await executeBridgeTransferFromAndVerifyEvents(
|
||||
accountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
bridgeData,
|
||||
authorized,
|
||||
);
|
||||
});
|
||||
it('reverts if not called by the ERC20 Bridge Proxy', async () => {
|
||||
const bridgeData = {
|
||||
@@ -240,5 +326,74 @@ blockchainTests.resets('DydxBridge unit tests', env => {
|
||||
);
|
||||
expect(returnValue).to.equal(AssetProxyId.ERC20Bridge);
|
||||
});
|
||||
it('should revert when `Operate` reverts', async () => {
|
||||
// Set revert flag.
|
||||
await testContract.setRevertOnOperate(true).awaitTransactionSuccessAsync();
|
||||
|
||||
// Execute transfer.
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [defaultDepositAction],
|
||||
};
|
||||
const tx = callBridgeTransferFrom(accountOwner, receiver, defaultAmount, bridgeData, authorized);
|
||||
const expectedError = 'TestDydxBridge/SHOULD_REVERT_ON_OPERATE';
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
it('should revert when there is a rounding error', async () => {
|
||||
// Setup a rounding error
|
||||
const conversionRateNumerator = new BigNumber(5318);
|
||||
const conversionRateDenominator = new BigNumber(47958);
|
||||
const amount = new BigNumber(9000);
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [
|
||||
defaultDepositAction,
|
||||
{
|
||||
...defaultWithdrawAction,
|
||||
conversionRateNumerator,
|
||||
conversionRateDenominator,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Execute transfer and assert error.
|
||||
const tx = callBridgeTransferFrom(accountOwner, receiver, amount, bridgeData, authorized);
|
||||
const expectedError = new LibMathRevertErrors.RoundingError(
|
||||
conversionRateNumerator,
|
||||
conversionRateDenominator,
|
||||
amount,
|
||||
);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ERC20BridgeProxy.transferFrom()', () => {
|
||||
const bridgeData = {
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [defaultWithdrawAction],
|
||||
};
|
||||
let assetData: string;
|
||||
|
||||
before(async () => {
|
||||
const testTokenAddress = await testContract.getTestToken().callAsync();
|
||||
assetData = assetDataEncoder
|
||||
.ERC20Bridge(testTokenAddress, testContract.address, dydxBridgeDataEncoder.encode({ bridgeData }))
|
||||
.getABIEncodedTransactionData();
|
||||
});
|
||||
|
||||
it('should succeed if `bridgeTransferFrom` succeeds', async () => {
|
||||
await testProxyContract
|
||||
.transferFrom(assetData, accountOwner, receiver, defaultAmount)
|
||||
.awaitTransactionSuccessAsync({ from: authorized });
|
||||
});
|
||||
it('should revert if `bridgeTransferFrom` reverts', async () => {
|
||||
// Set revert flag.
|
||||
await testContract.setRevertOnOperate(true).awaitTransactionSuccessAsync();
|
||||
const tx = testProxyContract
|
||||
.transferFrom(assetData, accountOwner, receiver, defaultAmount)
|
||||
.awaitTransactionSuccessAsync({ from: authorized });
|
||||
const expectedError = 'TestDydxBridge/SHOULD_REVERT_ON_OPERATE';
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -3,6 +3,7 @@ import {
|
||||
constants,
|
||||
expect,
|
||||
getRandomInteger,
|
||||
getRandomPortion,
|
||||
randomAddress,
|
||||
verifyEventsFromLogs,
|
||||
} from '@0x/contracts-test-utils';
|
||||
@@ -17,6 +18,12 @@ import { TestKyberBridgeContract, TestKyberBridgeEvents } from './wrappers';
|
||||
|
||||
blockchainTests.resets('KyberBridge unit tests', env => {
|
||||
const KYBER_ETH_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
|
||||
const FROM_TOKEN_DECIMALS = 6;
|
||||
const TO_TOKEN_DECIMALS = 18;
|
||||
const FROM_TOKEN_BASE = new BigNumber(10).pow(FROM_TOKEN_DECIMALS);
|
||||
const TO_TOKEN_BASE = new BigNumber(10).pow(TO_TOKEN_DECIMALS);
|
||||
const WETH_BASE = new BigNumber(10).pow(18);
|
||||
const KYBER_RATE_BASE = WETH_BASE;
|
||||
let testContract: TestKyberBridgeContract;
|
||||
|
||||
before(async () => {
|
||||
@@ -45,10 +52,10 @@ blockchainTests.resets('KyberBridge unit tests', env => {
|
||||
|
||||
before(async () => {
|
||||
wethAddress = await testContract.weth().callAsync();
|
||||
fromTokenAddress = await testContract.createToken().callAsync();
|
||||
await testContract.createToken().awaitTransactionSuccessAsync();
|
||||
toTokenAddress = await testContract.createToken().callAsync();
|
||||
await testContract.createToken().awaitTransactionSuccessAsync();
|
||||
fromTokenAddress = await testContract.createToken(FROM_TOKEN_DECIMALS).callAsync();
|
||||
await testContract.createToken(FROM_TOKEN_DECIMALS).awaitTransactionSuccessAsync();
|
||||
toTokenAddress = await testContract.createToken(TO_TOKEN_DECIMALS).callAsync();
|
||||
await testContract.createToken(TO_TOKEN_DECIMALS).awaitTransactionSuccessAsync();
|
||||
});
|
||||
|
||||
const STATIC_KYBER_TRADE_ARGS = {
|
||||
@@ -75,13 +82,14 @@ blockchainTests.resets('KyberBridge unit tests', env => {
|
||||
}
|
||||
|
||||
function createTransferFromOpts(opts?: Partial<TransferFromOpts>): TransferFromOpts {
|
||||
const amount = getRandomInteger(1, TO_TOKEN_BASE.times(100));
|
||||
return {
|
||||
fromTokenAddress,
|
||||
toTokenAddress,
|
||||
amount,
|
||||
toAddress: randomAddress(),
|
||||
amount: getRandomInteger(1, 10e18),
|
||||
fillAmount: getRandomInteger(1, 10e18),
|
||||
fromTokenBalance: getRandomInteger(1, 10e18),
|
||||
fillAmount: getRandomPortion(amount),
|
||||
fromTokenBalance: getRandomInteger(1, FROM_TOKEN_BASE.times(100)),
|
||||
...opts,
|
||||
};
|
||||
}
|
||||
@@ -119,9 +127,12 @@ blockchainTests.resets('KyberBridge unit tests', env => {
|
||||
}
|
||||
|
||||
function getMinimumConversionRate(opts: TransferFromOpts): BigNumber {
|
||||
const fromBase = opts.fromTokenAddress === wethAddress ? WETH_BASE : FROM_TOKEN_BASE;
|
||||
const toBase = opts.toTokenAddress === wethAddress ? WETH_BASE : TO_TOKEN_BASE;
|
||||
return opts.amount
|
||||
.times(constants.ONE_ETHER)
|
||||
.div(opts.fromTokenBalance)
|
||||
.div(toBase)
|
||||
.div(opts.fromTokenBalance.div(fromBase))
|
||||
.times(KYBER_RATE_BASE)
|
||||
.integerValue(BigNumber.ROUND_DOWN);
|
||||
}
|
||||
|
||||
|
@@ -176,7 +176,7 @@ blockchainTests.resets('UniswapBridge unit tests', env => {
|
||||
expect(calls[0].exchange).to.eq(exchangeAddress);
|
||||
expect(calls[0].tokensSold).to.bignumber.eq(opts.fromTokenBalance);
|
||||
expect(calls[0].minTokensBought).to.bignumber.eq(opts.amount);
|
||||
expect(calls[0].minEthBought).to.bignumber.eq(0);
|
||||
expect(calls[0].minEthBought).to.bignumber.eq(1);
|
||||
expect(calls[0].deadline).to.bignumber.eq(blockTime);
|
||||
expect(calls[0].recipient).to.eq(opts.toAddress);
|
||||
expect(calls[0].toTokenAddress).to.eq(opts.toTokenAddress);
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1578272714,
|
||||
"version": "3.0.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1576540892,
|
||||
"version": "3.0.2",
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.0.3 - _January 6, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.2 - _December 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-coordinator",
|
||||
"version": "3.0.2",
|
||||
"version": "3.0.3",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -52,19 +52,19 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.0.2",
|
||||
"@0x/contracts-asset-proxy": "^3.0.2",
|
||||
"@0x/contracts-dev-utils": "^1.0.2",
|
||||
"@0x/contracts-erc20": "^3.0.2",
|
||||
"@0x/contracts-exchange": "^3.0.2",
|
||||
"@0x/contracts-gen": "^2.0.2",
|
||||
"@0x/contracts-test-utils": "^5.0.1",
|
||||
"@0x/dev-utils": "^3.0.2",
|
||||
"@0x/order-utils": "^10.0.1",
|
||||
"@0x/sol-compiler": "^4.0.2",
|
||||
"@0x/abi-gen": "^5.0.3",
|
||||
"@0x/contracts-asset-proxy": "^3.1.0",
|
||||
"@0x/contracts-dev-utils": "^1.0.3",
|
||||
"@0x/contracts-erc20": "^3.0.3",
|
||||
"@0x/contracts-exchange": "^3.0.3",
|
||||
"@0x/contracts-gen": "^2.0.3",
|
||||
"@0x/contracts-test-utils": "^5.1.0",
|
||||
"@0x/dev-utils": "^3.1.0",
|
||||
"@0x/order-utils": "^10.1.0",
|
||||
"@0x/sol-compiler": "^4.0.3",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/web3-wrapper": "^7.0.2",
|
||||
"@0x/web3-wrapper": "^7.0.3",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -84,14 +84,14 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/assert": "^3.0.2",
|
||||
"@0x/base-contract": "^6.0.2",
|
||||
"@0x/contract-addresses": "^4.1.0",
|
||||
"@0x/contracts-utils": "^4.0.2",
|
||||
"@0x/json-schemas": "^5.0.2",
|
||||
"@0x/assert": "^3.0.3",
|
||||
"@0x/base-contract": "^6.0.3",
|
||||
"@0x/contract-addresses": "^4.2.0",
|
||||
"@0x/contracts-utils": "^4.0.3",
|
||||
"@0x/json-schemas": "^5.0.3",
|
||||
"@0x/types": "^3.1.1",
|
||||
"@0x/typescript-typings": "^5.0.1",
|
||||
"@0x/utils": "^5.1.1",
|
||||
"@0x/utils": "^5.1.2",
|
||||
"ethereum-types": "^3.0.0",
|
||||
"http-status-codes": "^1.3.2"
|
||||
},
|
||||
|
@@ -1,4 +1,14 @@
|
||||
[
|
||||
{
|
||||
"version": "1.0.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Fixed ERC721 duplicate token ID bug",
|
||||
"pr": 2400
|
||||
}
|
||||
],
|
||||
"timestamp": 1578272714
|
||||
},
|
||||
{
|
||||
"timestamp": 1576540892,
|
||||
"version": "1.0.2",
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.0.3 - _January 6, 2020_
|
||||
|
||||
* Fixed ERC721 duplicate token ID bug (#2400)
|
||||
|
||||
## v1.0.2 - _December 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -7,7 +7,7 @@
|
||||
"evmVersion": "constantinople",
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1666,
|
||||
"runs": 200,
|
||||
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||
},
|
||||
"outputSelection": {
|
||||
|
@@ -35,8 +35,7 @@ contract DevUtils is
|
||||
OrderValidationUtils,
|
||||
LibTransactionDecoder,
|
||||
LibEIP712ExchangeDomain,
|
||||
EthBalanceChecker,
|
||||
OrderTransferSimulationUtils
|
||||
EthBalanceChecker
|
||||
{
|
||||
constructor (address _exchange)
|
||||
public
|
||||
|
@@ -147,7 +147,7 @@ contract LibAssetData {
|
||||
balance = scaledBalance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Balance will be 0 if assetProxyId is unknown
|
||||
return balance;
|
||||
@@ -316,7 +316,7 @@ contract LibAssetData {
|
||||
return (balances, allowances);
|
||||
}
|
||||
|
||||
/// @dev Decode AssetProxy identifier
|
||||
/// @dev Decode AssetProxy identifier
|
||||
/// @param assetData AssetProxy-compliant asset data describing an ERC-20, ERC-721, ERC1155, or MultiAsset asset.
|
||||
/// @return The AssetProxy identifier
|
||||
function decodeAssetProxyId(bytes memory assetData)
|
||||
@@ -353,7 +353,7 @@ contract LibAssetData {
|
||||
|
||||
/// @dev Decode ERC-20 asset data from the format described in the AssetProxy contract specification.
|
||||
/// @param assetData AssetProxy-compliant asset data describing an ERC-20 asset.
|
||||
/// @return The AssetProxy identifier, and the address of the ERC-20
|
||||
/// @return The AssetProxy identifier, and the address of the ERC-20
|
||||
/// contract hosting this asset.
|
||||
function decodeERC20AssetData(bytes memory assetData)
|
||||
public
|
||||
@@ -591,7 +591,7 @@ contract LibAssetData {
|
||||
|
||||
function revertIfInvalidAssetData(bytes memory assetData)
|
||||
public
|
||||
pure
|
||||
pure
|
||||
{
|
||||
bytes4 assetProxyId = assetData.readBytes4(0);
|
||||
|
||||
|
@@ -54,6 +54,51 @@ contract OrderTransferSimulationUtils is
|
||||
_EXCHANGE = IExchange(_exchange);
|
||||
}
|
||||
|
||||
/// @dev Simulates the maker transfers within an order and returns the index of the first failed transfer.
|
||||
/// @param order The order to simulate transfers for.
|
||||
/// @param takerAddress The address of the taker that will fill the order.
|
||||
/// @param takerAssetFillAmount The amount of takerAsset that the taker wished to fill.
|
||||
/// @return The index of the first failed transfer (or 4 if all transfers are successful).
|
||||
function getSimulatedOrderMakerTransferResults(
|
||||
LibOrder.Order memory order,
|
||||
address takerAddress,
|
||||
uint256 takerAssetFillAmount
|
||||
)
|
||||
public
|
||||
returns (OrderTransferResults orderTransferResults)
|
||||
{
|
||||
LibFillResults.FillResults memory fillResults = LibFillResults.calculateFillResults(
|
||||
order,
|
||||
takerAssetFillAmount,
|
||||
_EXCHANGE.protocolFeeMultiplier(),
|
||||
tx.gasprice
|
||||
);
|
||||
|
||||
bytes[] memory assetData = new bytes[](2);
|
||||
address[] memory fromAddresses = new address[](2);
|
||||
address[] memory toAddresses = new address[](2);
|
||||
uint256[] memory amounts = new uint256[](2);
|
||||
|
||||
// Transfer `makerAsset` from maker to taker
|
||||
assetData[0] = order.makerAssetData;
|
||||
fromAddresses[0] = order.makerAddress;
|
||||
toAddresses[0] = takerAddress;
|
||||
amounts[0] = fillResults.makerAssetFilledAmount;
|
||||
|
||||
// Transfer `makerFeeAsset` from maker to feeRecipient
|
||||
assetData[1] = order.makerFeeAssetData;
|
||||
fromAddresses[1] = order.makerAddress;
|
||||
toAddresses[1] = order.feeRecipientAddress;
|
||||
amounts[1] = fillResults.makerFeePaid;
|
||||
|
||||
return _simulateTransferFromCalls(
|
||||
assetData,
|
||||
fromAddresses,
|
||||
toAddresses,
|
||||
amounts
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Simulates all of the transfers within an order and returns the index of the first failed transfer.
|
||||
/// @param order The order to simulate transfers for.
|
||||
/// @param takerAddress The address of the taker that will fill the order.
|
||||
@@ -104,6 +149,54 @@ contract OrderTransferSimulationUtils is
|
||||
toAddresses[3] = order.feeRecipientAddress;
|
||||
amounts[3] = fillResults.makerFeePaid;
|
||||
|
||||
return _simulateTransferFromCalls(
|
||||
assetData,
|
||||
fromAddresses,
|
||||
toAddresses,
|
||||
amounts
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Simulates all of the transfers for each given order and returns the indices of each first failed transfer.
|
||||
/// @param orders Array of orders to individually simulate transfers for.
|
||||
/// @param takerAddresses Array of addresses of takers that will fill each order.
|
||||
/// @param takerAssetFillAmounts Array of amounts of takerAsset that will be filled for each order.
|
||||
/// @return The indices of the first failed transfer (or 4 if all transfers are successful) for each order.
|
||||
function getSimulatedOrdersTransferResults(
|
||||
LibOrder.Order[] memory orders,
|
||||
address[] memory takerAddresses,
|
||||
uint256[] memory takerAssetFillAmounts
|
||||
)
|
||||
public
|
||||
returns (OrderTransferResults[] memory orderTransferResults)
|
||||
{
|
||||
uint256 length = orders.length;
|
||||
orderTransferResults = new OrderTransferResults[](length);
|
||||
for (uint256 i = 0; i != length; i++) {
|
||||
orderTransferResults[i] = getSimulatedOrderTransferResults(
|
||||
orders[i],
|
||||
takerAddresses[i],
|
||||
takerAssetFillAmounts[i]
|
||||
);
|
||||
}
|
||||
return orderTransferResults;
|
||||
}
|
||||
|
||||
/// @dev Makes the simulation call with information about the transfers and processes
|
||||
/// the returndata.
|
||||
/// @param assetData The assetdata to use to make transfers.
|
||||
/// @param fromAddresses The addresses to transfer funds.
|
||||
/// @param toAddresses The addresses that will receive funds
|
||||
/// @param amounts The amounts involved in the transfer.
|
||||
function _simulateTransferFromCalls(
|
||||
bytes[] memory assetData,
|
||||
address[] memory fromAddresses,
|
||||
address[] memory toAddresses,
|
||||
uint256[] memory amounts
|
||||
)
|
||||
internal
|
||||
returns (OrderTransferResults orderTransferResults)
|
||||
{
|
||||
// Encode data for `simulateDispatchTransferFromCalls(assetData, fromAddresses, toAddresses, amounts)`
|
||||
bytes memory simulateDispatchTransferFromCallsData = abi.encodeWithSelector(
|
||||
IExchange(address(0)).simulateDispatchTransferFromCalls.selector,
|
||||
@@ -132,29 +225,4 @@ contract OrderTransferSimulationUtils is
|
||||
revert("UNKNOWN_RETURN_DATA");
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Simulates all of the transfers for each given order and returns the indices of each first failed transfer.
|
||||
/// @param orders Array of orders to individually simulate transfers for.
|
||||
/// @param takerAddresses Array of addresses of takers that will fill each order.
|
||||
/// @param takerAssetFillAmounts Array of amounts of takerAsset that will be filled for each order.
|
||||
/// @return The indices of the first failed transfer (or 4 if all transfers are successful) for each order.
|
||||
function getSimulatedOrdersTransferResults(
|
||||
LibOrder.Order[] memory orders,
|
||||
address[] memory takerAddresses,
|
||||
uint256[] memory takerAssetFillAmounts
|
||||
)
|
||||
public
|
||||
returns (OrderTransferResults[] memory orderTransferResults)
|
||||
{
|
||||
uint256 length = orders.length;
|
||||
orderTransferResults = new OrderTransferResults[](length);
|
||||
for (uint256 i = 0; i != length; i++) {
|
||||
orderTransferResults[i] = getSimulatedOrderTransferResults(
|
||||
orders[i],
|
||||
takerAddresses[i],
|
||||
takerAssetFillAmounts[i]
|
||||
);
|
||||
}
|
||||
return orderTransferResults;
|
||||
}
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||
@@ -25,10 +25,12 @@ import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
import "./LibAssetData.sol";
|
||||
import "./OrderTransferSimulationUtils.sol";
|
||||
|
||||
|
||||
contract OrderValidationUtils is
|
||||
LibAssetData
|
||||
LibAssetData,
|
||||
OrderTransferSimulationUtils
|
||||
{
|
||||
using LibBytes for bytes;
|
||||
using LibSafeMath for uint256;
|
||||
@@ -50,7 +52,6 @@ contract OrderValidationUtils is
|
||||
/// amount of each asset that can be filled.
|
||||
function getOrderRelevantState(LibOrder.Order memory order, bytes memory signature)
|
||||
public
|
||||
view
|
||||
returns (
|
||||
LibOrder.OrderInfo memory orderInfo,
|
||||
uint256 fillableTakerAssetAmount,
|
||||
@@ -99,7 +100,6 @@ contract OrderValidationUtils is
|
||||
} else {
|
||||
// Get the transferable amount of the `makerFeeAsset`
|
||||
uint256 transferableMakerFeeAssetAmount = getTransferableAssetAmount(makerAddress, order.makerFeeAssetData);
|
||||
|
||||
uint256 transferableMakerToTakerAmount = LibMath.getPartialAmountFloor(
|
||||
transferableMakerAssetAmount,
|
||||
order.makerAssetAmount,
|
||||
@@ -120,6 +120,13 @@ contract OrderValidationUtils is
|
||||
transferableTakerAssetAmount
|
||||
);
|
||||
|
||||
// Execute the maker transfers.
|
||||
fillableTakerAssetAmount = getSimulatedOrderMakerTransferResults(
|
||||
order,
|
||||
order.takerAddress,
|
||||
fillableTakerAssetAmount
|
||||
) == OrderTransferResults.TransfersSuccessful ? fillableTakerAssetAmount : 0;
|
||||
|
||||
return (orderInfo, fillableTakerAssetAmount, isValidSignature);
|
||||
}
|
||||
|
||||
@@ -135,7 +142,6 @@ contract OrderValidationUtils is
|
||||
/// the `takerAssetData` to get the final amount of each asset that can be filled.
|
||||
function getOrderRelevantStates(LibOrder.Order[] memory orders, bytes[] memory signatures)
|
||||
public
|
||||
view
|
||||
returns (
|
||||
LibOrder.OrderInfo[] memory ordersInfo,
|
||||
uint256[] memory fillableTakerAssetAmounts,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-dev-utils",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.3",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -8,7 +8,7 @@
|
||||
"main": "lib/src/index.js",
|
||||
"scripts": {
|
||||
"build": "yarn pre_build && tsc -b",
|
||||
"test": "yarn assert_deployable && echo !!! Tests are run via @0x/contracts-tests !!!",
|
||||
"test": "yarn assert_deployable && echo !!! Tests are run via @0x/contracts-integrations !!!",
|
||||
"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",
|
||||
@@ -41,10 +41,10 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/dev-utils/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.0.2",
|
||||
"@0x/assert": "^3.0.2",
|
||||
"@0x/contracts-gen": "^2.0.2",
|
||||
"@0x/sol-compiler": "^4.0.2",
|
||||
"@0x/abi-gen": "^5.0.3",
|
||||
"@0x/assert": "^3.0.3",
|
||||
"@0x/contracts-gen": "^2.0.3",
|
||||
"@0x/sol-compiler": "^4.0.3",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@types/node": "*",
|
||||
@@ -59,7 +59,7 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.0.2"
|
||||
"@0x/base-contract": "^6.0.3"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1578272714,
|
||||
"version": "2.0.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1576540892,
|
||||
"version": "2.0.2",
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v2.0.3 - _January 6, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.2 - _December 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc1155",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.3",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -52,11 +52,11 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.0.2",
|
||||
"@0x/contracts-gen": "^2.0.2",
|
||||
"@0x/contracts-utils": "^4.0.2",
|
||||
"@0x/dev-utils": "^3.0.2",
|
||||
"@0x/sol-compiler": "^4.0.2",
|
||||
"@0x/abi-gen": "^5.0.3",
|
||||
"@0x/contracts-gen": "^2.0.3",
|
||||
"@0x/contracts-utils": "^4.0.3",
|
||||
"@0x/dev-utils": "^3.1.0",
|
||||
"@0x/sol-compiler": "^4.0.3",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/types": "^3.1.1",
|
||||
@@ -80,10 +80,10 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.0.2",
|
||||
"@0x/contracts-test-utils": "^5.0.1",
|
||||
"@0x/utils": "^5.1.1",
|
||||
"@0x/web3-wrapper": "^7.0.2",
|
||||
"@0x/base-contract": "^6.0.3",
|
||||
"@0x/contracts-test-utils": "^5.1.0",
|
||||
"@0x/utils": "^5.1.2",
|
||||
"@0x/web3-wrapper": "^7.0.3",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
@@ -1,10 +1,20 @@
|
||||
[
|
||||
{
|
||||
"version": "1.0.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add gas limits to external quote calls.",
|
||||
"pr": 2405
|
||||
}
|
||||
],
|
||||
"timestamp": 1578272714
|
||||
},
|
||||
{
|
||||
"version": "1.0.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Do not query empty/unsigned orders. Swallow revets on DEX quotes.",
|
||||
"pr": 2365
|
||||
"pr": 2395
|
||||
}
|
||||
],
|
||||
"timestamp": 1576540892
|
||||
|
@@ -5,9 +5,13 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.0.3 - _January 6, 2020_
|
||||
|
||||
* Add gas limits to external quote calls. (#2405)
|
||||
|
||||
## v1.0.2 - _December 17, 2019_
|
||||
|
||||
* Do not query empty/unsigned orders. Swallow revets on DEX quotes. (#2365)
|
||||
* Do not query empty/unsigned orders. Swallow revets on DEX quotes. (#2395)
|
||||
|
||||
## v1.0.1 - _December 9, 2019_
|
||||
|
||||
|
@@ -37,6 +37,9 @@ contract ERC20BridgeSampler is
|
||||
DeploymentConstants
|
||||
{
|
||||
bytes4 constant internal ERC20_PROXY_ID = 0xf47261b0; // bytes4(keccak256("ERC20Token(address)"));
|
||||
uint256 constant internal KYBER_SAMPLE_CALL_GAS = 600e3;
|
||||
uint256 constant internal UNISWAP_SAMPLE_CALL_GAS = 150e3;
|
||||
uint256 constant internal ETH2DAI_SAMPLE_CALL_GAS = 250e3;
|
||||
|
||||
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
|
||||
/// @param orders Native orders to query.
|
||||
@@ -144,7 +147,7 @@ contract ERC20BridgeSampler is
|
||||
);
|
||||
// The fillable amount is zero if the order is not fillable or if the
|
||||
// signature is invalid.
|
||||
if (orderInfo.orderStatus != uint8(LibOrder.OrderStatus.FILLABLE) ||
|
||||
if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE ||
|
||||
!isValidSignature) {
|
||||
orderFillableTakerAssetAmounts[i] = 0;
|
||||
} else {
|
||||
@@ -268,12 +271,13 @@ contract ERC20BridgeSampler is
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
(bool didSucceed, bytes memory resultData) =
|
||||
_getKyberNetworkProxyAddress().staticcall(abi.encodeWithSelector(
|
||||
IKyberNetwork(0).getExpectedRate.selector,
|
||||
_takerToken,
|
||||
_makerToken,
|
||||
takerTokenAmounts[i]
|
||||
));
|
||||
_getKyberNetworkProxyAddress().staticcall.gas(KYBER_SAMPLE_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
IKyberNetwork(0).getExpectedRate.selector,
|
||||
_takerToken,
|
||||
_makerToken,
|
||||
takerTokenAmounts[i]
|
||||
));
|
||||
uint256 rate = 0;
|
||||
if (didSucceed) {
|
||||
rate = abi.decode(resultData, (uint256));
|
||||
@@ -307,12 +311,13 @@ contract ERC20BridgeSampler is
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
(bool didSucceed, bytes memory resultData) =
|
||||
_getEth2DaiAddress().staticcall(abi.encodeWithSelector(
|
||||
IEth2Dai(0).getBuyAmount.selector,
|
||||
makerToken,
|
||||
takerToken,
|
||||
takerTokenAmounts[i]
|
||||
));
|
||||
_getEth2DaiAddress().staticcall.gas(ETH2DAI_SAMPLE_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
IEth2Dai(0).getBuyAmount.selector,
|
||||
makerToken,
|
||||
takerToken,
|
||||
takerTokenAmounts[i]
|
||||
));
|
||||
uint256 buyAmount = 0;
|
||||
if (didSucceed) {
|
||||
buyAmount = abi.decode(resultData, (uint256));
|
||||
@@ -341,12 +346,13 @@ contract ERC20BridgeSampler is
|
||||
takerTokenAmounts = new uint256[](numSamples);
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
(bool didSucceed, bytes memory resultData) =
|
||||
_getEth2DaiAddress().staticcall(abi.encodeWithSelector(
|
||||
IEth2Dai(0).getPayAmount.selector,
|
||||
takerToken,
|
||||
makerToken,
|
||||
makerTokenAmounts[i]
|
||||
));
|
||||
_getEth2DaiAddress().staticcall.gas(ETH2DAI_SAMPLE_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
IEth2Dai(0).getPayAmount.selector,
|
||||
takerToken,
|
||||
makerToken,
|
||||
makerTokenAmounts[i]
|
||||
));
|
||||
uint256 sellAmount = 0;
|
||||
if (didSucceed) {
|
||||
sellAmount = abi.decode(resultData, (uint256));
|
||||
@@ -493,10 +499,11 @@ contract ERC20BridgeSampler is
|
||||
return 0;
|
||||
}
|
||||
(bool didSucceed, bytes memory resultData) =
|
||||
uniswapExchangeAddress.staticcall(abi.encodeWithSelector(
|
||||
functionSelector,
|
||||
inputAmount
|
||||
));
|
||||
uniswapExchangeAddress.staticcall.gas(UNISWAP_SAMPLE_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
functionSelector,
|
||||
inputAmount
|
||||
));
|
||||
if (didSucceed) {
|
||||
outputAmount = abi.decode(resultData, (uint256));
|
||||
}
|
||||
|
@@ -305,6 +305,8 @@ contract TestERC20BridgeSampler is
|
||||
TestERC20BridgeSamplerEth2Dai public eth2Dai;
|
||||
TestERC20BridgeSamplerKyberNetwork public kyber;
|
||||
|
||||
uint8 private constant MAX_ORDER_STATUS = uint8(LibOrder.OrderStatus.CANCELLED) + 1;
|
||||
|
||||
constructor() public {
|
||||
uniswap = new TestERC20BridgeSamplerUniswapExchangeFactory();
|
||||
eth2Dai = new TestERC20BridgeSamplerEth2Dai();
|
||||
@@ -336,9 +338,8 @@ contract TestERC20BridgeSampler is
|
||||
bytes32 orderHash = keccak256(abi.encode(order.salt));
|
||||
// Everything else is derived from the hash.
|
||||
orderInfo.orderHash = orderHash;
|
||||
orderInfo.orderStatus = uint8(uint256(orderHash) % uint8(-1));
|
||||
orderInfo.orderTakerAssetFilledAmount =
|
||||
uint256(orderHash) % order.takerAssetAmount;
|
||||
orderInfo.orderStatus = LibOrder.OrderStatus(uint256(orderHash) % MAX_ORDER_STATUS);
|
||||
orderInfo.orderTakerAssetFilledAmount = uint256(orderHash) % order.takerAssetAmount;
|
||||
fillableTakerAssetAmount =
|
||||
order.takerAssetAmount - orderInfo.orderTakerAssetFilledAmount;
|
||||
isValidSignature = uint256(orderHash) % 2 == 1;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc20-bridge-sampler",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.3",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -50,18 +50,18 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.0.2",
|
||||
"@0x/contracts-asset-proxy": "^3.0.2",
|
||||
"@0x/contracts-erc20": "^3.0.2",
|
||||
"@0x/contracts-exchange": "^3.0.2",
|
||||
"@0x/contracts-exchange-libs": "^4.0.2",
|
||||
"@0x/contracts-gen": "^2.0.2",
|
||||
"@0x/contracts-test-utils": "^5.0.1",
|
||||
"@0x/contracts-utils": "^4.0.2",
|
||||
"@0x/dev-utils": "^3.0.2",
|
||||
"@0x/sol-compiler": "^4.0.2",
|
||||
"@0x/abi-gen": "^5.0.3",
|
||||
"@0x/contracts-asset-proxy": "^3.1.0",
|
||||
"@0x/contracts-erc20": "^3.0.3",
|
||||
"@0x/contracts-exchange": "^3.0.3",
|
||||
"@0x/contracts-exchange-libs": "^4.0.3",
|
||||
"@0x/contracts-gen": "^2.0.3",
|
||||
"@0x/contracts-test-utils": "^5.1.0",
|
||||
"@0x/contracts-utils": "^4.0.3",
|
||||
"@0x/dev-utils": "^3.1.0",
|
||||
"@0x/sol-compiler": "^4.0.3",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/web3-wrapper": "^7.0.2",
|
||||
"@0x/web3-wrapper": "^7.0.3",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -79,10 +79,10 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.0.2",
|
||||
"@0x/base-contract": "^6.0.3",
|
||||
"@0x/types": "^3.1.1",
|
||||
"@0x/typescript-typings": "^5.0.1",
|
||||
"@0x/utils": "^5.1.1",
|
||||
"@0x/utils": "^5.1.2",
|
||||
"ethereum-types": "^3.0.0",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1578272714,
|
||||
"version": "3.0.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1576540892,
|
||||
"version": "3.0.2",
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.0.3 - _January 6, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.2 - _December 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc20",
|
||||
"version": "3.0.2",
|
||||
"version": "3.0.3",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -51,18 +51,18 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.0.2",
|
||||
"@0x/contracts-gen": "^2.0.2",
|
||||
"@0x/contracts-test-utils": "^5.0.1",
|
||||
"@0x/contracts-utils": "^4.0.2",
|
||||
"@0x/dev-utils": "^3.0.2",
|
||||
"@0x/sol-compiler": "^4.0.2",
|
||||
"@0x/abi-gen": "^5.0.3",
|
||||
"@0x/contracts-gen": "^2.0.3",
|
||||
"@0x/contracts-test-utils": "^5.1.0",
|
||||
"@0x/contracts-utils": "^4.0.3",
|
||||
"@0x/dev-utils": "^3.1.0",
|
||||
"@0x/sol-compiler": "^4.0.3",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/types": "^3.1.1",
|
||||
"@0x/typescript-typings": "^5.0.1",
|
||||
"@0x/utils": "^5.1.1",
|
||||
"@0x/web3-wrapper": "^7.0.2",
|
||||
"@0x/utils": "^5.1.2",
|
||||
"@0x/web3-wrapper": "^7.0.3",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -82,7 +82,7 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.0.2"
|
||||
"@0x/base-contract": "^6.0.3"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1578272714,
|
||||
"version": "3.0.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1576540892,
|
||||
"version": "3.0.2",
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.0.3 - _January 6, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.2 - _December 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc721",
|
||||
"version": "3.0.2",
|
||||
"version": "3.0.3",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -52,18 +52,18 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/tokens/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.0.2",
|
||||
"@0x/contracts-gen": "^2.0.2",
|
||||
"@0x/contracts-test-utils": "^5.0.1",
|
||||
"@0x/contracts-utils": "^4.0.2",
|
||||
"@0x/dev-utils": "^3.0.2",
|
||||
"@0x/sol-compiler": "^4.0.2",
|
||||
"@0x/abi-gen": "^5.0.3",
|
||||
"@0x/contracts-gen": "^2.0.3",
|
||||
"@0x/contracts-test-utils": "^5.1.0",
|
||||
"@0x/contracts-utils": "^4.0.3",
|
||||
"@0x/dev-utils": "^3.1.0",
|
||||
"@0x/sol-compiler": "^4.0.3",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/types": "^3.1.1",
|
||||
"@0x/typescript-typings": "^5.0.1",
|
||||
"@0x/utils": "^5.1.1",
|
||||
"@0x/web3-wrapper": "^7.0.2",
|
||||
"@0x/utils": "^5.1.2",
|
||||
"@0x/web3-wrapper": "^7.0.3",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -84,7 +84,7 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.0.2"
|
||||
"@0x/base-contract": "^6.0.3"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1578272714,
|
||||
"version": "4.0.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1576540892,
|
||||
"version": "4.0.2",
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v4.0.3 - _January 6, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.0.2 - _December 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-exchange-forwarder",
|
||||
"version": "4.0.2",
|
||||
"version": "4.0.3",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -52,24 +52,24 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.0.2",
|
||||
"@0x/contracts-asset-proxy": "^3.0.2",
|
||||
"@0x/contracts-dev-utils": "^1.0.2",
|
||||
"@0x/contracts-erc20": "^3.0.2",
|
||||
"@0x/contracts-erc721": "^3.0.2",
|
||||
"@0x/contracts-exchange": "^3.0.2",
|
||||
"@0x/contracts-exchange-libs": "^4.0.2",
|
||||
"@0x/contracts-gen": "^2.0.2",
|
||||
"@0x/contracts-test-utils": "^5.0.1",
|
||||
"@0x/contracts-utils": "^4.0.2",
|
||||
"@0x/dev-utils": "^3.0.2",
|
||||
"@0x/order-utils": "^10.0.1",
|
||||
"@0x/sol-compiler": "^4.0.2",
|
||||
"@0x/abi-gen": "^5.0.3",
|
||||
"@0x/contracts-asset-proxy": "^3.1.0",
|
||||
"@0x/contracts-dev-utils": "^1.0.3",
|
||||
"@0x/contracts-erc20": "^3.0.3",
|
||||
"@0x/contracts-erc721": "^3.0.3",
|
||||
"@0x/contracts-exchange": "^3.0.3",
|
||||
"@0x/contracts-exchange-libs": "^4.0.3",
|
||||
"@0x/contracts-gen": "^2.0.3",
|
||||
"@0x/contracts-test-utils": "^5.1.0",
|
||||
"@0x/contracts-utils": "^4.0.3",
|
||||
"@0x/dev-utils": "^3.1.0",
|
||||
"@0x/order-utils": "^10.1.0",
|
||||
"@0x/sol-compiler": "^4.0.3",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/types": "^3.1.1",
|
||||
"@0x/utils": "^5.1.1",
|
||||
"@0x/web3-wrapper": "^7.0.2",
|
||||
"@0x/utils": "^5.1.2",
|
||||
"@0x/web3-wrapper": "^7.0.3",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -89,7 +89,7 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.0.2",
|
||||
"@0x/base-contract": "^6.0.3",
|
||||
"@0x/typescript-typings": "^5.0.1",
|
||||
"ethereum-types": "^3.0.0"
|
||||
},
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1578272714,
|
||||
"version": "4.0.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1576540892,
|
||||
"version": "4.0.2",
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v4.0.3 - _January 6, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.0.2 - _December 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -29,9 +29,11 @@ contract LibEIP712ExchangeDomain {
|
||||
// EIP712 Exchange Domain Version value
|
||||
string constant internal _EIP712_EXCHANGE_DOMAIN_VERSION = "3.0.0";
|
||||
|
||||
// Hash of the EIP712 Domain Separator data
|
||||
// solhint-disable-next-line var-name-mixedcase
|
||||
// solhint-disable var-name-mixedcase
|
||||
/// @dev Hash of the EIP712 Domain Separator data
|
||||
/// @return 0 Domain hash.
|
||||
bytes32 public EIP712_EXCHANGE_DOMAIN_HASH;
|
||||
// solhint-enable var-name-mixedcase
|
||||
|
||||
/// @param chainId Chain ID of the network this contract is deployed on.
|
||||
/// @param verifyingContractAddressIfExists Address of the verifying contract (null if the address of this contract)
|
||||
|
@@ -60,6 +60,7 @@ library LibOrder {
|
||||
}
|
||||
|
||||
// solhint-disable max-line-length
|
||||
/// @dev Canonical order structure.
|
||||
struct Order {
|
||||
address makerAddress; // Address that created the order.
|
||||
address takerAddress; // Address that is allowed to fill the order. If set to 0, any address is allowed to fill the order.
|
||||
@@ -78,8 +79,9 @@ library LibOrder {
|
||||
}
|
||||
// solhint-enable max-line-length
|
||||
|
||||
/// @dev Order information returned by `getOrderInfo()`.
|
||||
struct OrderInfo {
|
||||
uint8 orderStatus; // Status that describes order's validity and fillability.
|
||||
OrderStatus orderStatus; // Status that describes order's validity and fillability.
|
||||
bytes32 orderHash; // EIP712 typed data hash of the order (see LibOrder.getTypedDataHash).
|
||||
uint256 orderTakerAssetFilledAmount; // Amount of order that has already been filled.
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-exchange-libs",
|
||||
"version": "4.0.2",
|
||||
"version": "4.0.3",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -52,15 +52,15 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/libs/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.0.2",
|
||||
"@0x/contracts-gen": "^2.0.2",
|
||||
"@0x/contracts-test-utils": "^5.0.1",
|
||||
"@0x/dev-utils": "^3.0.2",
|
||||
"@0x/sol-compiler": "^4.0.2",
|
||||
"@0x/subproviders": "^6.0.2",
|
||||
"@0x/abi-gen": "^5.0.3",
|
||||
"@0x/contracts-gen": "^2.0.3",
|
||||
"@0x/contracts-test-utils": "^5.1.0",
|
||||
"@0x/dev-utils": "^3.1.0",
|
||||
"@0x/sol-compiler": "^4.0.3",
|
||||
"@0x/subproviders": "^6.0.3",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/web3-wrapper": "^7.0.2",
|
||||
"@0x/web3-wrapper": "^7.0.3",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -81,12 +81,12 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.0.2",
|
||||
"@0x/contracts-utils": "^4.0.2",
|
||||
"@0x/order-utils": "^10.0.1",
|
||||
"@0x/base-contract": "^6.0.3",
|
||||
"@0x/contracts-utils": "^4.0.3",
|
||||
"@0x/order-utils": "^10.1.0",
|
||||
"@0x/types": "^3.1.1",
|
||||
"@0x/typescript-typings": "^5.0.1",
|
||||
"@0x/utils": "^5.1.1",
|
||||
"@0x/utils": "^5.1.2",
|
||||
"ethereum-types": "^3.0.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1578272714,
|
||||
"version": "3.0.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1576540892,
|
||||
"version": "3.0.2",
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.0.3 - _January 6, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.2 - _December 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -29,6 +29,7 @@ import "./MixinTransferSimulator.sol";
|
||||
// MixinAssetProxyDispatcher, MixinExchangeCore, MixinSignatureValidator,
|
||||
// and MixinTransactions are all inherited via the other Mixins that are
|
||||
// used.
|
||||
/// @dev The 0x Exchange contract.
|
||||
contract Exchange is
|
||||
LibEIP712ExchangeDomain,
|
||||
MixinMatchOrders,
|
||||
|
@@ -62,11 +62,11 @@ contract MixinAssetProxyDispatcher is
|
||||
|
||||
/// @dev Gets an asset proxy.
|
||||
/// @param assetProxyId Id of the asset proxy.
|
||||
/// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered.
|
||||
/// @return assetProxy The asset proxy address registered to assetProxyId. Returns 0x0 if no proxy is registered.
|
||||
function getAssetProxy(bytes4 assetProxyId)
|
||||
external
|
||||
view
|
||||
returns (address)
|
||||
returns (address assetProxy)
|
||||
{
|
||||
return _assetProxies[assetProxyId];
|
||||
}
|
||||
|
@@ -45,14 +45,21 @@ contract MixinExchangeCore is
|
||||
using LibSafeMath for uint256;
|
||||
using LibBytes for bytes;
|
||||
|
||||
// Mapping of orderHash => amount of takerAsset already bought by maker
|
||||
/// @dev Mapping of orderHash => amount of takerAsset already bought by maker
|
||||
/// @param 0 Order hash.
|
||||
/// @return 0 The amount of taker asset filled.
|
||||
mapping (bytes32 => uint256) public filled;
|
||||
|
||||
// Mapping of orderHash => cancelled
|
||||
/// @dev Mapping of orderHash => cancelled
|
||||
/// @param 0 Order hash.
|
||||
/// @return 0 Whether the order was cancelled.
|
||||
mapping (bytes32 => bool) public cancelled;
|
||||
|
||||
// Mapping of makerAddress => senderAddress => lowest salt an order can have in order to be fillable
|
||||
// Orders with specified senderAddress and with a salt less than their epoch are considered cancelled
|
||||
/// @dev Mapping of makerAddress => senderAddress => lowest salt an order can have in order to be fillable
|
||||
/// Orders with specified senderAddress and with a salt less than their epoch are considered cancelled
|
||||
/// @param 0 Address of the order's maker.
|
||||
/// @param 1 Address of the order's sender.
|
||||
/// @return 0 Minimum valid order epoch.
|
||||
mapping (address => mapping (address => uint256)) public orderEpoch;
|
||||
|
||||
/// @dev Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch
|
||||
@@ -94,7 +101,7 @@ contract MixinExchangeCore is
|
||||
/// @param order Order struct containing order specifications.
|
||||
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||
/// @param signature Proof that order has been created by maker.
|
||||
/// @return Amounts filled and fees paid by maker and taker.
|
||||
/// @return fillResults Amounts filled and fees paid by maker and taker.
|
||||
function fillOrder(
|
||||
LibOrder.Order memory order,
|
||||
uint256 takerAssetFillAmount,
|
||||
@@ -125,7 +132,7 @@ contract MixinExchangeCore is
|
||||
|
||||
/// @dev Gets information about an order: status, hash, and amount filled.
|
||||
/// @param order Order to gather information on.
|
||||
/// @return OrderInfo Information about the order and its state.
|
||||
/// @return orderInfo Information about the order and its state.
|
||||
/// See LibOrder.OrderInfo for a complete description.
|
||||
function getOrderInfo(LibOrder.Order memory order)
|
||||
public
|
||||
@@ -140,7 +147,7 @@ contract MixinExchangeCore is
|
||||
// edge cases in the supporting infrastructure because they have
|
||||
// an 'infinite' price when computed by a simple division.
|
||||
if (order.makerAssetAmount == 0) {
|
||||
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.INVALID_MAKER_ASSET_AMOUNT);
|
||||
orderInfo.orderStatus = LibOrder.OrderStatus.INVALID_MAKER_ASSET_AMOUNT;
|
||||
return orderInfo;
|
||||
}
|
||||
|
||||
@@ -149,35 +156,35 @@ contract MixinExchangeCore is
|
||||
// Instead of distinguishing between unfilled and filled zero taker
|
||||
// amount orders, we choose not to support them.
|
||||
if (order.takerAssetAmount == 0) {
|
||||
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.INVALID_TAKER_ASSET_AMOUNT);
|
||||
orderInfo.orderStatus = LibOrder.OrderStatus.INVALID_TAKER_ASSET_AMOUNT;
|
||||
return orderInfo;
|
||||
}
|
||||
|
||||
// Validate order availability
|
||||
if (orderInfo.orderTakerAssetFilledAmount >= order.takerAssetAmount) {
|
||||
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.FULLY_FILLED);
|
||||
orderInfo.orderStatus = LibOrder.OrderStatus.FULLY_FILLED;
|
||||
return orderInfo;
|
||||
}
|
||||
|
||||
// Validate order expiration
|
||||
// solhint-disable-next-line not-rely-on-time
|
||||
if (block.timestamp >= order.expirationTimeSeconds) {
|
||||
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.EXPIRED);
|
||||
orderInfo.orderStatus = LibOrder.OrderStatus.EXPIRED;
|
||||
return orderInfo;
|
||||
}
|
||||
|
||||
// Check if order has been cancelled
|
||||
if (cancelled[orderInfo.orderHash]) {
|
||||
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.CANCELLED);
|
||||
orderInfo.orderStatus = LibOrder.OrderStatus.CANCELLED;
|
||||
return orderInfo;
|
||||
}
|
||||
if (orderEpoch[order.makerAddress][order.senderAddress] > order.salt) {
|
||||
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.CANCELLED);
|
||||
orderInfo.orderStatus = LibOrder.OrderStatus.CANCELLED;
|
||||
return orderInfo;
|
||||
}
|
||||
|
||||
// All other statuses are ruled out: order is Fillable
|
||||
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.FILLABLE);
|
||||
orderInfo.orderStatus = LibOrder.OrderStatus.FILLABLE;
|
||||
return orderInfo;
|
||||
}
|
||||
|
||||
@@ -185,7 +192,7 @@ contract MixinExchangeCore is
|
||||
/// @param order Order struct containing order specifications.
|
||||
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||
/// @param signature Proof that order has been created by maker.
|
||||
/// @return Amounts filled and fees paid by maker and taker.
|
||||
/// @return fillResults Amounts filled and fees paid by maker and taker.
|
||||
function _fillOrder(
|
||||
LibOrder.Order memory order,
|
||||
uint256 takerAssetFillAmount,
|
||||
@@ -255,7 +262,7 @@ contract MixinExchangeCore is
|
||||
_assertValidCancel(order, orderInfo);
|
||||
|
||||
// Noop if order is already unfillable
|
||||
if (orderInfo.orderStatus != uint8(LibOrder.OrderStatus.FILLABLE)) {
|
||||
if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -337,7 +344,7 @@ contract MixinExchangeCore is
|
||||
view
|
||||
{
|
||||
// An order can only be filled if its status is FILLABLE.
|
||||
if (orderInfo.orderStatus != uint8(LibOrder.OrderStatus.FILLABLE)) {
|
||||
if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE) {
|
||||
LibRichErrors.rrevert(LibExchangeRichErrors.OrderStatusError(
|
||||
orderInfo.orderHash,
|
||||
LibOrder.OrderStatus(orderInfo.orderStatus)
|
||||
|
@@ -29,10 +29,12 @@ contract MixinProtocolFees is
|
||||
IProtocolFees,
|
||||
Ownable
|
||||
{
|
||||
// The protocol fee multiplier -- the owner can update this field.
|
||||
/// @dev The protocol fee multiplier -- the owner can update this field.
|
||||
/// @return 0 Gas multplier.
|
||||
uint256 public protocolFeeMultiplier;
|
||||
|
||||
// The address of the registered protocolFeeCollector contract -- the owner can update this field.
|
||||
/// @dev The address of the registered protocolFeeCollector contract -- the owner can update this field.
|
||||
/// @return 0 Contract to forward protocol fees to.
|
||||
address public protocolFeeCollector;
|
||||
|
||||
/// @dev Allows the owner to update the protocol fee multiplier.
|
||||
|
@@ -47,10 +47,16 @@ contract MixinSignatureValidator is
|
||||
// bytes4(keccak256("isValidWalletSignature(bytes32,address,bytes)"))
|
||||
bytes4 private constant LEGACY_WALLET_MAGIC_VALUE = 0xb0671381;
|
||||
|
||||
// Mapping of hash => signer => signed
|
||||
/// @dev Mapping of hash => signer => signed
|
||||
/// @param 0 Order hash.
|
||||
/// @param 1 Signer address.
|
||||
/// @return 0 Whether the hash is presigned.
|
||||
mapping (bytes32 => mapping (address => bool)) public preSigned;
|
||||
|
||||
// Mapping of signer => validator => approved
|
||||
/// @dev Mapping of signer => validator => approved
|
||||
/// @param 0 Signer address.
|
||||
/// @param 1 Signature validator address.
|
||||
/// @return 0 Whether the validator is allowed to validate on behalf of the signer.
|
||||
mapping (address => mapping (address => bool)) public allowedValidators;
|
||||
|
||||
/// @dev Approves a hash on-chain.
|
||||
|
@@ -36,11 +36,14 @@ contract MixinTransactions is
|
||||
{
|
||||
using LibZeroExTransaction for LibZeroExTransaction.ZeroExTransaction;
|
||||
|
||||
// Mapping of transaction hash => executed
|
||||
// This prevents transactions from being executed more than once.
|
||||
/// @dev Mapping of transaction hash => executed
|
||||
/// This prevents transactions from being executed more than once.
|
||||
/// @param 0 The transaction hash.
|
||||
/// @return 0 Whether the transation was executed.
|
||||
mapping (bytes32 => bool) public transactionsExecuted;
|
||||
|
||||
// Address of current transaction signer
|
||||
/// @dev Address of current transaction signer.
|
||||
/// @return 0 The address associated with the the current transaction.
|
||||
address public currentContextAddress;
|
||||
|
||||
/// @dev Executes an Exchange method call in the context of signer.
|
||||
@@ -62,7 +65,7 @@ contract MixinTransactions is
|
||||
/// @dev Executes a batch of Exchange method calls in the context of signer(s).
|
||||
/// @param transactions Array of 0x transaction structures.
|
||||
/// @param signatures Array of proofs that transactions have been signed by signer(s).
|
||||
/// @return Array containing ABI encoded return data for each of the underlying Exchange function calls.
|
||||
/// @return returnData Array containing ABI encoded return data for each of the underlying Exchange function calls.
|
||||
function batchExecuteTransactions(
|
||||
LibZeroExTransaction.ZeroExTransaction[] memory transactions,
|
||||
bytes[] memory signatures
|
||||
@@ -70,10 +73,10 @@ contract MixinTransactions is
|
||||
public
|
||||
payable
|
||||
disableRefundUntilEnd
|
||||
returns (bytes[] memory)
|
||||
returns (bytes[] memory returnData)
|
||||
{
|
||||
uint256 length = transactions.length;
|
||||
bytes[] memory returnData = new bytes[](length);
|
||||
returnData = new bytes[](length);
|
||||
for (uint256 i = 0; i != length; i++) {
|
||||
returnData[i] = _executeTransaction(transactions[i], signatures[i]);
|
||||
}
|
||||
@@ -117,7 +120,7 @@ contract MixinTransactions is
|
||||
_setCurrentContextAddressIfRequired(signerAddress, address(0));
|
||||
|
||||
emit TransactionExecution(transactionHash);
|
||||
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
||||
|
@@ -36,10 +36,11 @@ contract MixinWrapperFunctions is
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
/// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.
|
||||
/// @dev Fills the input order. Reverts if exact `takerAssetFillAmount` not filled.
|
||||
/// @param order Order struct containing order specifications.
|
||||
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||
/// @param signature Proof that order has been created by maker.
|
||||
/// @return fillResults Amounts filled and fees paid.
|
||||
function fillOrKillOrder(
|
||||
LibOrder.Order memory order,
|
||||
uint256 takerAssetFillAmount,
|
||||
@@ -62,7 +63,7 @@ contract MixinWrapperFunctions is
|
||||
/// @param orders Array of order specifications.
|
||||
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
|
||||
/// @param signatures Proofs that orders have been created by makers.
|
||||
/// @return Array of amounts filled and fees paid by makers and taker.
|
||||
/// @return fillResults Array of amounts filled and fees paid by makers and taker.
|
||||
function batchFillOrders(
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256[] memory takerAssetFillAmounts,
|
||||
@@ -89,7 +90,7 @@ contract MixinWrapperFunctions is
|
||||
/// @param orders Array of order specifications.
|
||||
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
|
||||
/// @param signatures Proofs that orders have been created by makers.
|
||||
/// @return Array of amounts filled and fees paid by makers and taker.
|
||||
/// @return fillResults Array of amounts filled and fees paid by makers and taker.
|
||||
function batchFillOrKillOrders(
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256[] memory takerAssetFillAmounts,
|
||||
@@ -116,7 +117,7 @@ contract MixinWrapperFunctions is
|
||||
/// @param orders Array of order specifications.
|
||||
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
|
||||
/// @param signatures Proofs that orders have been created by makers.
|
||||
/// @return Array of amounts filled and fees paid by makers and taker.
|
||||
/// @return fillResults Array of amounts filled and fees paid by makers and taker.
|
||||
function batchFillOrdersNoThrow(
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256[] memory takerAssetFillAmounts,
|
||||
@@ -145,7 +146,7 @@ contract MixinWrapperFunctions is
|
||||
/// @param orders Array of order specifications.
|
||||
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||
/// @param signatures Proofs that orders have been signed by makers.
|
||||
/// @return Amounts filled and fees paid by makers and taker.
|
||||
/// @return fillResults Amounts filled and fees paid by makers and taker.
|
||||
function marketSellOrdersNoThrow(
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256 takerAssetFillAmount,
|
||||
@@ -186,7 +187,7 @@ contract MixinWrapperFunctions is
|
||||
/// @param orders Array of order specifications.
|
||||
/// @param makerAssetFillAmount Desired amount of makerAsset to buy.
|
||||
/// @param signatures Proofs that orders have been signed by makers.
|
||||
/// @return Amounts filled and fees paid by makers and taker.
|
||||
/// @return fillResults Amounts filled and fees paid by makers and taker.
|
||||
function marketBuyOrdersNoThrow(
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256 makerAssetFillAmount,
|
||||
@@ -234,7 +235,7 @@ contract MixinWrapperFunctions is
|
||||
/// @param orders Array of order specifications.
|
||||
/// @param takerAssetFillAmount Minimum amount of takerAsset to sell.
|
||||
/// @param signatures Proofs that orders have been signed by makers.
|
||||
/// @return Amounts filled and fees paid by makers and taker.
|
||||
/// @return fillResults Amounts filled and fees paid by makers and taker.
|
||||
function marketSellOrdersFillOrKill(
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256 takerAssetFillAmount,
|
||||
@@ -259,7 +260,7 @@ contract MixinWrapperFunctions is
|
||||
/// @param orders Array of order specifications.
|
||||
/// @param makerAssetFillAmount Minimum amount of makerAsset to buy.
|
||||
/// @param signatures Proofs that orders have been signed by makers.
|
||||
/// @return Amounts filled and fees paid by makers and taker.
|
||||
/// @return fillResults Amounts filled and fees paid by makers and taker.
|
||||
function marketBuyOrdersFillOrKill(
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256 makerAssetFillAmount,
|
||||
@@ -295,7 +296,7 @@ contract MixinWrapperFunctions is
|
||||
/// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.
|
||||
/// @param order Order struct containing order specifications.
|
||||
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||
/// @param signature Proof that order has been created by maker.
|
||||
/// @param fillResults ignature Proof that order has been created by maker.
|
||||
function _fillOrKillOrder(
|
||||
LibOrder.Order memory order,
|
||||
uint256 takerAssetFillAmount,
|
||||
@@ -324,7 +325,7 @@ contract MixinWrapperFunctions is
|
||||
/// @param order Order struct containing order specifications.
|
||||
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||
/// @param signature Proof that order has been created by maker.
|
||||
/// @return Amounts filled and fees paid by maker and taker.
|
||||
/// @return fillResults Amounts filled and fees paid by maker and taker.
|
||||
function _fillOrderNoThrow(
|
||||
LibOrder.Order memory order,
|
||||
uint256 takerAssetFillAmount,
|
||||
|
@@ -30,7 +30,7 @@ import "../src/Exchange.sol";
|
||||
contract TestWrapperFunctions is
|
||||
Exchange
|
||||
{
|
||||
uint8 internal constant MAX_ORDER_STATUS = uint8(LibOrder.OrderStatus.CANCELLED);
|
||||
LibOrder.OrderStatus internal constant MAX_ORDER_STATUS = LibOrder.OrderStatus.CANCELLED;
|
||||
uint256 internal constant ALWAYS_FAILING_SALT = uint256(-1);
|
||||
string internal constant ALWAYS_FAILING_SALT_REVERT_REASON = "ALWAYS_FAILING_SALT";
|
||||
|
||||
@@ -61,7 +61,7 @@ contract TestWrapperFunctions is
|
||||
// Lower uint128 of `order.salt` is the `orderTakerAssetFilledAmount`.
|
||||
orderInfo.orderTakerAssetFilledAmount = uint128(order.salt);
|
||||
// High byte of `order.salt` is the `orderStatus`.
|
||||
orderInfo.orderStatus = uint8(order.salt >> 248) % (MAX_ORDER_STATUS + 1);
|
||||
orderInfo.orderStatus = LibOrder.OrderStatus(uint8(order.salt >> 248) % (uint8(MAX_ORDER_STATUS) + 1));
|
||||
orderInfo.orderHash = order.getTypedDataHash(EIP712_EXCHANGE_DOMAIN_HASH);
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-exchange",
|
||||
"version": "3.0.2",
|
||||
"version": "3.0.3",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -52,21 +52,21 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.0.2",
|
||||
"@0x/contracts-asset-proxy": "^3.0.2",
|
||||
"@0x/contracts-exchange-libs": "^4.0.2",
|
||||
"@0x/contracts-gen": "^2.0.2",
|
||||
"@0x/contracts-multisig": "^4.0.2",
|
||||
"@0x/contracts-staking": "^2.0.2",
|
||||
"@0x/contracts-test-utils": "^5.0.1",
|
||||
"@0x/contracts-utils": "^4.0.2",
|
||||
"@0x/dev-utils": "^3.0.2",
|
||||
"@0x/sol-compiler": "^4.0.2",
|
||||
"@0x/abi-gen": "^5.0.3",
|
||||
"@0x/contracts-asset-proxy": "^3.1.0",
|
||||
"@0x/contracts-exchange-libs": "^4.0.3",
|
||||
"@0x/contracts-gen": "^2.0.3",
|
||||
"@0x/contracts-multisig": "^4.0.3",
|
||||
"@0x/contracts-staking": "^2.0.3",
|
||||
"@0x/contracts-test-utils": "^5.1.0",
|
||||
"@0x/contracts-utils": "^4.0.3",
|
||||
"@0x/dev-utils": "^3.1.0",
|
||||
"@0x/sol-compiler": "^4.0.3",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/types": "^3.1.1",
|
||||
"@0x/typescript-typings": "^5.0.1",
|
||||
"@0x/web3-wrapper": "^7.0.2",
|
||||
"@0x/web3-wrapper": "^7.0.3",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -88,13 +88,13 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.0.2",
|
||||
"@0x/contracts-dev-utils": "^1.0.2",
|
||||
"@0x/contracts-erc1155": "^2.0.2",
|
||||
"@0x/contracts-erc20": "^3.0.2",
|
||||
"@0x/contracts-erc721": "^3.0.2",
|
||||
"@0x/order-utils": "^10.0.1",
|
||||
"@0x/utils": "^5.1.1",
|
||||
"@0x/base-contract": "^6.0.3",
|
||||
"@0x/contracts-dev-utils": "^1.0.3",
|
||||
"@0x/contracts-erc1155": "^2.0.3",
|
||||
"@0x/contracts-erc20": "^3.0.3",
|
||||
"@0x/contracts-erc721": "^3.0.3",
|
||||
"@0x/order-utils": "^10.1.0",
|
||||
"@0x/utils": "^5.1.2",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1578272714,
|
||||
"version": "5.1.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1576540892,
|
||||
"version": "5.1.1",
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v5.1.2 - _January 6, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.1.1 - _December 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-extensions",
|
||||
"version": "5.1.1",
|
||||
"version": "5.1.2",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -52,24 +52,24 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.0.2",
|
||||
"@0x/contracts-asset-proxy": "^3.0.2",
|
||||
"@0x/contracts-dev-utils": "^1.0.2",
|
||||
"@0x/contracts-erc20": "^3.0.2",
|
||||
"@0x/contracts-erc721": "^3.0.2",
|
||||
"@0x/contracts-exchange": "^3.0.2",
|
||||
"@0x/contracts-exchange-libs": "^4.0.2",
|
||||
"@0x/contracts-gen": "^2.0.2",
|
||||
"@0x/contracts-test-utils": "^5.0.1",
|
||||
"@0x/contracts-utils": "^4.0.2",
|
||||
"@0x/dev-utils": "^3.0.2",
|
||||
"@0x/order-utils": "^10.0.1",
|
||||
"@0x/sol-compiler": "^4.0.2",
|
||||
"@0x/abi-gen": "^5.0.3",
|
||||
"@0x/contracts-asset-proxy": "^3.1.0",
|
||||
"@0x/contracts-dev-utils": "^1.0.3",
|
||||
"@0x/contracts-erc20": "^3.0.3",
|
||||
"@0x/contracts-erc721": "^3.0.3",
|
||||
"@0x/contracts-exchange": "^3.0.3",
|
||||
"@0x/contracts-exchange-libs": "^4.0.3",
|
||||
"@0x/contracts-gen": "^2.0.3",
|
||||
"@0x/contracts-test-utils": "^5.1.0",
|
||||
"@0x/contracts-utils": "^4.0.3",
|
||||
"@0x/dev-utils": "^3.1.0",
|
||||
"@0x/order-utils": "^10.1.0",
|
||||
"@0x/sol-compiler": "^4.0.3",
|
||||
"@0x/ts-doc-gen": "^0.0.22",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/types": "^3.1.1",
|
||||
"@0x/utils": "^5.1.1",
|
||||
"@0x/web3-wrapper": "^7.0.2",
|
||||
"@0x/utils": "^5.1.2",
|
||||
"@0x/web3-wrapper": "^7.0.3",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -91,7 +91,7 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.0.2",
|
||||
"@0x/base-contract": "^6.0.3",
|
||||
"@0x/typescript-typings": "^5.0.1",
|
||||
"ethereum-types": "^3.0.0"
|
||||
},
|
||||
|
@@ -1,4 +1,18 @@
|
||||
[
|
||||
{
|
||||
"version": "2.1.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Integration tests for DydxBridge with (i) Exchange v3 and (ii) Mainnet dYdX SoloMargin contract.",
|
||||
"pr": 2401
|
||||
},
|
||||
{
|
||||
"note": "Add aggregator mainnet tests.",
|
||||
"pr": 2407
|
||||
}
|
||||
],
|
||||
"timestamp": 1578272714
|
||||
},
|
||||
{
|
||||
"timestamp": 1576540892,
|
||||
"version": "2.0.2",
|
||||
|
@@ -5,6 +5,11 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v2.1.0 - _January 6, 2020_
|
||||
|
||||
* Integration tests for DydxBridge with (i) Exchange v3 and (ii) Mainnet dYdX SoloMargin contract. (#2401)
|
||||
* Add aggregator mainnet tests. (#2407)
|
||||
|
||||
## v2.0.2 - _December 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
207
contracts/integrations/contracts/test/TestDydxUser.sol
Normal file
207
contracts/integrations/contracts/test/TestDydxUser.sol
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
|
||||
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 = 0x96DdBa19b69D6EA2549f6a12d005595167414744;
|
||||
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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
|
||||
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-erc20/contracts/src/interfaces/IEtherToken.sol";
|
||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
|
||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
|
||||
|
||||
/// @dev A forwarder contract for filling 0x asset-swapper aggregated orders.
|
||||
/// The forwarder is necessary to purchase taker assets and set up
|
||||
/// approvals in one transaction. Only call the functions on this contract
|
||||
/// in an `eth_call` context or you will lose money!
|
||||
contract TestMainnetAggregatorFills is
|
||||
DeploymentConstants
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
address constant internal EXCHANGE_ADDRESS = 0x61935CbDd02287B511119DDb11Aeb42F1593b7Ef;
|
||||
bytes4 constant internal ERC20_PROXY_ID = 0xf47261b0; // bytes4(keccak256("ERC20Token(address)"));
|
||||
|
||||
struct SimulatedMarketFillResults {
|
||||
uint256 makerAssetBalanceBefore;
|
||||
uint256 takerAssetBalanceBefore;
|
||||
uint256 makerAssetBalanceAfter;
|
||||
uint256 takerAssetBalanceAfter;
|
||||
LibFillResults.FillResults fillResults;
|
||||
}
|
||||
|
||||
// solhint-disable-next-line no-empty-blocks
|
||||
function() external payable {}
|
||||
|
||||
/// @dev Buy taker assets with ETH from `takerOrders` and then perform a
|
||||
/// market buy on `makerOrders`.
|
||||
function marketBuy(
|
||||
address makerTokenAddress,
|
||||
address takerTokenAddress,
|
||||
LibOrder.Order[] memory makerOrders,
|
||||
LibOrder.Order[] memory takerOrders,
|
||||
bytes[] memory makerOrderSignatures,
|
||||
bytes[] memory takerOrderSignatures,
|
||||
uint256 makerAssetBuyAmount
|
||||
)
|
||||
public
|
||||
payable
|
||||
returns (SimulatedMarketFillResults memory results)
|
||||
{
|
||||
_prepareFunds(takerTokenAddress, makerOrders, takerOrders, takerOrderSignatures);
|
||||
results.makerAssetBalanceBefore = IERC20Token(makerTokenAddress).balanceOf(address(this));
|
||||
results.takerAssetBalanceBefore = IERC20Token(takerTokenAddress).balanceOf(address(this));
|
||||
results.fillResults = IExchange(EXCHANGE_ADDRESS)
|
||||
.marketBuyOrdersNoThrow
|
||||
.value(address(this).balance)(
|
||||
makerOrders,
|
||||
makerAssetBuyAmount,
|
||||
makerOrderSignatures
|
||||
);
|
||||
results.makerAssetBalanceAfter = IERC20Token(makerTokenAddress).balanceOf(address(this));
|
||||
results.takerAssetBalanceAfter = IERC20Token(takerTokenAddress).balanceOf(address(this));
|
||||
}
|
||||
|
||||
/// @dev Buy taker assets with ETH from `takerOrders` and then perform a
|
||||
/// market sell on `makerOrders`.
|
||||
function marketSell(
|
||||
address makerTokenAddress,
|
||||
address takerTokenAddress,
|
||||
LibOrder.Order[] memory makerOrders,
|
||||
LibOrder.Order[] memory takerOrders,
|
||||
bytes[] memory makerOrderSignatures,
|
||||
bytes[] memory takerOrderSignatures,
|
||||
uint256 takerAssetSellAmount
|
||||
)
|
||||
public
|
||||
payable
|
||||
returns (SimulatedMarketFillResults memory results)
|
||||
{
|
||||
_prepareFunds(takerTokenAddress, makerOrders, takerOrders, takerOrderSignatures);
|
||||
results.makerAssetBalanceBefore = IERC20Token(makerTokenAddress).balanceOf(address(this));
|
||||
results.takerAssetBalanceBefore = IERC20Token(takerTokenAddress).balanceOf(address(this));
|
||||
results.fillResults = IExchange(EXCHANGE_ADDRESS)
|
||||
.marketSellOrdersNoThrow
|
||||
.value(address(this).balance)(
|
||||
makerOrders,
|
||||
takerAssetSellAmount,
|
||||
makerOrderSignatures
|
||||
);
|
||||
results.makerAssetBalanceAfter = IERC20Token(makerTokenAddress).balanceOf(address(this));
|
||||
results.takerAssetBalanceAfter = IERC20Token(takerTokenAddress).balanceOf(address(this));
|
||||
}
|
||||
|
||||
/// @dev Like `marketSell`, but calls `fillOrder()` individually to detect
|
||||
/// errors.
|
||||
function fillOrders(
|
||||
address makerTokenAddress,
|
||||
address takerTokenAddress,
|
||||
LibOrder.Order[] memory makerOrders,
|
||||
LibOrder.Order[] memory takerOrders,
|
||||
bytes[] memory makerOrderSignatures,
|
||||
bytes[] memory takerOrderSignatures,
|
||||
uint256 takerAssetSellAmount
|
||||
)
|
||||
public
|
||||
payable
|
||||
returns (SimulatedMarketFillResults memory results)
|
||||
{
|
||||
_prepareFunds(takerTokenAddress, makerOrders, takerOrders, takerOrderSignatures);
|
||||
results.makerAssetBalanceBefore = IERC20Token(makerTokenAddress).balanceOf(address(this));
|
||||
results.takerAssetBalanceBefore = IERC20Token(takerTokenAddress).balanceOf(address(this));
|
||||
for (uint256 i = 0; i < makerOrders.length; i++) {
|
||||
if (takerAssetSellAmount == 0) {
|
||||
break;
|
||||
}
|
||||
LibFillResults.FillResults memory fillResults = IExchange(EXCHANGE_ADDRESS)
|
||||
.fillOrder
|
||||
.value(address(this).balance)(
|
||||
makerOrders[i],
|
||||
takerAssetSellAmount,
|
||||
makerOrderSignatures[i]
|
||||
);
|
||||
results.fillResults = LibFillResults.addFillResults(results.fillResults, fillResults);
|
||||
takerAssetSellAmount = takerAssetSellAmount.safeSub(fillResults.takerAssetFilledAmount);
|
||||
}
|
||||
results.makerAssetBalanceAfter = IERC20Token(makerTokenAddress).balanceOf(address(this));
|
||||
results.takerAssetBalanceAfter = IERC20Token(takerTokenAddress).balanceOf(address(this));
|
||||
}
|
||||
|
||||
function _approveAssetProxy(address tokenAddress) private {
|
||||
address assetProxyAddress = IExchange(EXCHANGE_ADDRESS).getAssetProxy(ERC20_PROXY_ID);
|
||||
LibERC20Token.approve(tokenAddress, assetProxyAddress, uint256(-1));
|
||||
}
|
||||
|
||||
/// @dev Buys as much of `takerOrders` as possible with the ETH transferred
|
||||
/// to this contract, leaving enough ETH behind for protocol fees.
|
||||
function _prepareFunds(
|
||||
address takerTokenAddress,
|
||||
LibOrder.Order[] memory makerOrders,
|
||||
LibOrder.Order[] memory takerOrders,
|
||||
bytes[] memory takerOrderSignatures
|
||||
)
|
||||
private
|
||||
{
|
||||
_approveAssetProxy(_getWethAddress());
|
||||
uint256 protocolFee = IExchange(EXCHANGE_ADDRESS).protocolFeeMultiplier() * tx.gasprice;
|
||||
uint256 maxProtocolFees = protocolFee * (takerOrders.length + makerOrders.length);
|
||||
uint256 ethSellAmount = msg.value.safeSub(maxProtocolFees);
|
||||
IEtherToken(_getWethAddress()).deposit.value(ethSellAmount)();
|
||||
if (takerTokenAddress != _getWethAddress()) {
|
||||
IExchange(EXCHANGE_ADDRESS)
|
||||
.marketSellOrdersNoThrow
|
||||
.value(maxProtocolFees)(
|
||||
takerOrders,
|
||||
ethSellAmount,
|
||||
takerOrderSignatures
|
||||
);
|
||||
_approveAssetProxy(takerTokenAddress);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-integrations",
|
||||
"version": "2.0.2",
|
||||
"version": "2.1.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -18,7 +18,7 @@
|
||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
|
||||
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
|
||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 1000000 --bail --exit",
|
||||
"test:fuzz": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/fuzz_tests/*.js' --timeout 0 --bail --exit",
|
||||
"compile": "sol-compiler",
|
||||
"watch": "sol-compiler -w",
|
||||
@@ -38,7 +38,7 @@
|
||||
},
|
||||
"config": {
|
||||
"publicInterfaceContracts": "TestFramework",
|
||||
"abis": "./test/generated-artifacts/@(TestEth2Dai|TestEth2DaiBridge|TestFramework|TestUniswapBridge|TestUniswapExchange|TestUniswapExchangeFactory).json",
|
||||
"abis": "./test/generated-artifacts/@(TestDydxUser|TestEth2Dai|TestEth2DaiBridge|TestFramework|TestMainnetAggregatorFills|TestUniswapBridge|TestUniswapExchange|TestUniswapExchangeFactory).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
@@ -51,22 +51,22 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.0.2",
|
||||
"@0x/contract-addresses": "^4.1.0",
|
||||
"@0x/contract-wrappers": "^13.2.0",
|
||||
"@0x/contracts-coordinator": "^3.0.2",
|
||||
"@0x/contracts-dev-utils": "^1.0.2",
|
||||
"@0x/contracts-exchange-forwarder": "^4.0.2",
|
||||
"@0x/contracts-exchange-libs": "^4.0.2",
|
||||
"@0x/contracts-gen": "^2.0.2",
|
||||
"@0x/contracts-utils": "^4.0.2",
|
||||
"@0x/abi-gen": "^5.0.3",
|
||||
"@0x/contract-addresses": "^4.2.0",
|
||||
"@0x/contract-wrappers": "^13.3.0",
|
||||
"@0x/contracts-coordinator": "^3.0.3",
|
||||
"@0x/contracts-dev-utils": "^1.0.3",
|
||||
"@0x/contracts-exchange-forwarder": "^4.0.3",
|
||||
"@0x/contracts-exchange-libs": "^4.0.3",
|
||||
"@0x/contracts-gen": "^2.0.3",
|
||||
"@0x/contracts-utils": "^4.0.3",
|
||||
"@0x/coordinator-server": "^1.0.5",
|
||||
"@0x/dev-utils": "^3.0.2",
|
||||
"@0x/migrations": "^5.0.2",
|
||||
"@0x/order-utils": "^10.0.1",
|
||||
"@0x/sol-compiler": "^4.0.2",
|
||||
"@0x/dev-utils": "^3.1.0",
|
||||
"@0x/migrations": "^5.1.0",
|
||||
"@0x/order-utils": "^10.1.0",
|
||||
"@0x/sol-compiler": "^4.0.3",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/web3-wrapper": "^7.0.2",
|
||||
"@0x/web3-wrapper": "^7.0.3",
|
||||
"@azure/core-asynciterator-polyfill": "^1.0.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
@@ -89,19 +89,21 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.0.2",
|
||||
"@0x/contracts-asset-proxy": "^3.0.2",
|
||||
"@0x/contracts-erc1155": "^2.0.2",
|
||||
"@0x/contracts-erc20": "^3.0.2",
|
||||
"@0x/contracts-erc721": "^3.0.2",
|
||||
"@0x/contracts-exchange": "^3.0.2",
|
||||
"@0x/contracts-multisig": "^4.0.2",
|
||||
"@0x/contracts-staking": "^2.0.2",
|
||||
"@0x/contracts-test-utils": "^5.0.1",
|
||||
"@0x/asset-swapper": "^3.0.3",
|
||||
"@0x/base-contract": "^6.0.3",
|
||||
"@0x/contracts-asset-proxy": "^3.1.0",
|
||||
"@0x/contracts-erc1155": "^2.0.3",
|
||||
"@0x/contracts-erc20": "^3.0.3",
|
||||
"@0x/contracts-erc721": "^3.0.3",
|
||||
"@0x/contracts-exchange": "^3.0.3",
|
||||
"@0x/contracts-multisig": "^4.0.3",
|
||||
"@0x/contracts-staking": "^2.0.3",
|
||||
"@0x/contracts-test-utils": "^5.1.0",
|
||||
"@0x/types": "^3.1.1",
|
||||
"@0x/typescript-typings": "^5.0.1",
|
||||
"@0x/utils": "^5.1.1",
|
||||
"@0x/utils": "^5.1.2",
|
||||
"ethereum-types": "^3.0.0",
|
||||
"ethereumjs-util": "^6.2.0",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
239
contracts/integrations/test/aggregation/fill_test.ts
Normal file
239
contracts/integrations/test/aggregation/fill_test.ts
Normal file
@@ -0,0 +1,239 @@
|
||||
import { MarketBuySwapQuote, MarketSellSwapQuote, Orderbook, SwapQuoter } from '@0x/asset-swapper';
|
||||
import { blockchainTests, expect, Numberish } from '@0x/contracts-test-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { FillResults, SignedOrder } from '@0x/types';
|
||||
import { BigNumber, logUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { TestMainnetAggregatorFillsContract } from '../wrappers';
|
||||
|
||||
import { tokens } from './tokens';
|
||||
|
||||
blockchainTests.live('Aggregator Mainnet Tests', env => {
|
||||
// Mainnet address of the `TestMainnetAggregatorFills` contract.
|
||||
const TEST_CONTRACT_ADDRESS = '0x37Ca306F42748b7fe105F89FCBb2CD03D27c8146';
|
||||
const TAKER_ADDRESS = '0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B'; // Vitalik
|
||||
const ORDERBOOK_POLLING_MS = 1000;
|
||||
const GAS_PRICE = new BigNumber(1);
|
||||
const TAKER_ASSET_ETH_VALUE = 500e18;
|
||||
const MIN_BALANCE = 500.1e18;
|
||||
const SYMBOLS = ['ETH', 'DAI', 'USDC', 'FOAM'];
|
||||
const TEST_PAIRS = _.flatten(SYMBOLS.map(m => SYMBOLS.filter(t => t !== m).map(t => [m, t])));
|
||||
const FILL_VALUES = [1, 10, 1e2, 1e3, 1e4, 2.5e4, 5e4];
|
||||
|
||||
let testContract: TestMainnetAggregatorFillsContract;
|
||||
let swapQuoter: SwapQuoter;
|
||||
let takerEthBalance: BigNumber;
|
||||
const orderbooks: { [name: string]: Orderbook } = {};
|
||||
|
||||
async function getTakerOrdersAsync(takerAssetSymbol: string): Promise<SignedOrder[]> {
|
||||
if (takerAssetSymbol === 'ETH') {
|
||||
return [];
|
||||
}
|
||||
return getOrdersAsync(takerAssetSymbol, 'ETH');
|
||||
}
|
||||
|
||||
// Fetches ETH -> taker asset orders for the forwarder contract.
|
||||
async function getOrdersAsync(makerAssetSymbol: string, takerAssetSymbol: string): Promise<SignedOrder[]> {
|
||||
const takerTokenAddress = tokens[takerAssetSymbol].address;
|
||||
const makerTokenAddress = tokens[makerAssetSymbol].address;
|
||||
const makerAssetData = assetDataUtils.encodeERC20AssetData(makerTokenAddress);
|
||||
const takerAssetData = assetDataUtils.encodeERC20AssetData(takerTokenAddress);
|
||||
const orders = _.flatten(
|
||||
await Promise.all(
|
||||
Object.keys(orderbooks).map(async name =>
|
||||
getOrdersFromOrderBookAsync(name, makerAssetData, takerAssetData),
|
||||
),
|
||||
),
|
||||
);
|
||||
const uniqueOrders: SignedOrder[] = [];
|
||||
for (const order of orders) {
|
||||
if (!order.makerFee.eq(0) || !order.takerFee.eq(0)) {
|
||||
continue;
|
||||
}
|
||||
if (uniqueOrders.findIndex(o => isSameOrder(order, o)) === -1) {
|
||||
uniqueOrders.push(order);
|
||||
}
|
||||
}
|
||||
return uniqueOrders;
|
||||
}
|
||||
|
||||
async function getOrdersFromOrderBookAsync(
|
||||
name: string,
|
||||
makerAssetData: string,
|
||||
takerAssetData: string,
|
||||
): Promise<SignedOrder[]> {
|
||||
try {
|
||||
return (await orderbooks[name].getOrdersAsync(makerAssetData, takerAssetData)).map(r => r.order);
|
||||
} catch (err) {
|
||||
logUtils.warn(`Failed to retrieve orders from orderbook "${name}".`);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function isSameOrder(a: SignedOrder, b: SignedOrder): boolean {
|
||||
for (const [k, v] of Object.entries(a)) {
|
||||
if (k in (b as any)) {
|
||||
if (BigNumber.isBigNumber(v) && !v.eq((b as any)[k])) {
|
||||
return false;
|
||||
}
|
||||
if (v !== (b as any)[k]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function toTokenUnits(symbol: string, weis: Numberish): BigNumber {
|
||||
return new BigNumber(weis).div(new BigNumber(10).pow(tokens[symbol].decimals));
|
||||
}
|
||||
|
||||
function fromTokenUnits(symbol: string, units: Numberish): BigNumber {
|
||||
return new BigNumber(units)
|
||||
.times(new BigNumber(10).pow(tokens[symbol].decimals))
|
||||
.integerValue(BigNumber.ROUND_DOWN);
|
||||
}
|
||||
|
||||
interface MarketOperationResult {
|
||||
makerAssetBalanceBefore: BigNumber;
|
||||
takerAssetBalanceBefore: BigNumber;
|
||||
makerAssetBalanceAfter: BigNumber;
|
||||
takerAssetBalanceAfter: BigNumber;
|
||||
fillResults: FillResults;
|
||||
}
|
||||
|
||||
// Liquidity is low right now so it's possible we didn't have
|
||||
// enough taker assets to cover the orders, so occasionally we'll get incomplete
|
||||
// fills. This function will catch those cases.
|
||||
// TODO(dorothy-zbornak): Remove this special case when liquidity is up.
|
||||
function checkHadEnoughTakerAsset(
|
||||
quote: MarketBuySwapQuote | MarketSellSwapQuote,
|
||||
result: MarketOperationResult,
|
||||
): boolean {
|
||||
if (result.takerAssetBalanceBefore.gte(quote.worstCaseQuoteInfo.takerAssetAmount)) {
|
||||
return true;
|
||||
}
|
||||
const takerAssetPct = result.takerAssetBalanceBefore
|
||||
.div(quote.worstCaseQuoteInfo.takerAssetAmount)
|
||||
.times(100)
|
||||
.toNumber()
|
||||
.toFixed(1);
|
||||
logUtils.warn(`Could not acquire enough taker asset to complete the fill: ${takerAssetPct}%`);
|
||||
expect(result.fillResults.makerAssetFilledAmount).to.bignumber.lt(quote.worstCaseQuoteInfo.makerAssetAmount);
|
||||
return false;
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
testContract = new TestMainnetAggregatorFillsContract(TEST_CONTRACT_ADDRESS, env.provider, {
|
||||
...env.txDefaults,
|
||||
gasPrice: GAS_PRICE,
|
||||
gas: 10e6,
|
||||
});
|
||||
swapQuoter = SwapQuoter.getSwapQuoterForStandardRelayerAPIUrl(env.provider, 'https://api.0x.org/sra');
|
||||
// Pool orderbooks because we're desperate for liquidity.
|
||||
orderbooks.swapQuoter = swapQuoter.orderbook;
|
||||
orderbooks.bamboo = Orderbook.getOrderbookForPollingProvider({
|
||||
httpEndpoint: 'https://sra.bamboorelay.com/0x/v3',
|
||||
pollingIntervalMs: ORDERBOOK_POLLING_MS,
|
||||
});
|
||||
// TODO(dorothy-zbornak): Uncomment when radar's SRA is up.
|
||||
// orderbooks.radar = Orderbook.getOrderbookForPollingProvider({
|
||||
// httpEndpoint: 'https://api-v3.radarrelay.com/v3',
|
||||
// pollingIntervalMs: ORDERBOOK_POLLING_MS,
|
||||
// });
|
||||
takerEthBalance = await env.web3Wrapper.getBalanceInWeiAsync(TAKER_ADDRESS);
|
||||
});
|
||||
|
||||
it('taker has minimum ETH', async () => {
|
||||
expect(takerEthBalance).to.bignumber.gte(MIN_BALANCE);
|
||||
});
|
||||
|
||||
describe('market sells', () => {
|
||||
for (const [makerSymbol, takerSymbol] of TEST_PAIRS) {
|
||||
for (const fillValue of FILL_VALUES) {
|
||||
const fillAmount = fromTokenUnits(takerSymbol, new BigNumber(fillValue).div(tokens[takerSymbol].price));
|
||||
it(`sell ${toTokenUnits(takerSymbol, fillAmount)} ${takerSymbol} for ${makerSymbol}`, async () => {
|
||||
const [quote, takerOrders] = await Promise.all([
|
||||
swapQuoter.getMarketSellSwapQuoteAsync(
|
||||
tokens[makerSymbol].address,
|
||||
tokens[takerSymbol].address,
|
||||
fillAmount,
|
||||
{ gasPrice: GAS_PRICE },
|
||||
),
|
||||
getTakerOrdersAsync(takerSymbol),
|
||||
]);
|
||||
// Buy taker assets from `takerOrders` and and perform a
|
||||
// market sell on the bridge orders.
|
||||
const fill = await testContract
|
||||
.marketSell(
|
||||
tokens[makerSymbol].address,
|
||||
tokens[takerSymbol].address,
|
||||
quote.orders,
|
||||
takerOrders,
|
||||
quote.orders.map(o => o.signature),
|
||||
takerOrders.map(o => o.signature),
|
||||
quote.takerAssetFillAmount,
|
||||
)
|
||||
.callAsync({
|
||||
value: quote.worstCaseQuoteInfo.protocolFeeInWeiAmount.plus(TAKER_ASSET_ETH_VALUE),
|
||||
from: TAKER_ADDRESS,
|
||||
gasPrice: quote.gasPrice,
|
||||
});
|
||||
if (checkHadEnoughTakerAsset(quote, fill)) {
|
||||
expect(fill.fillResults.makerAssetFilledAmount, 'makerAssetFilledAmount').to.bignumber.gte(
|
||||
quote.worstCaseQuoteInfo.makerAssetAmount,
|
||||
);
|
||||
expect(fill.fillResults.takerAssetFilledAmount, 'takerAssetFilledAmount').to.bignumber.lte(
|
||||
quote.takerAssetFillAmount,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
describe('market buys', () => {
|
||||
for (const [makerSymbol, takerSymbol] of TEST_PAIRS) {
|
||||
for (const fillValue of FILL_VALUES) {
|
||||
const fillAmount = fromTokenUnits(makerSymbol, new BigNumber(fillValue).div(tokens[makerSymbol].price));
|
||||
it(`buy ${toTokenUnits(makerSymbol, fillAmount)} ${makerSymbol} with ${takerSymbol}`, async () => {
|
||||
const [quote, takerOrders] = await Promise.all([
|
||||
swapQuoter.getMarketBuySwapQuoteAsync(
|
||||
tokens[makerSymbol].address,
|
||||
tokens[takerSymbol].address,
|
||||
fillAmount,
|
||||
{ gasPrice: GAS_PRICE },
|
||||
),
|
||||
getTakerOrdersAsync(takerSymbol),
|
||||
]);
|
||||
// Buy taker assets from `takerOrders` and and perform a
|
||||
// market buy on the bridge orders.
|
||||
const fill = await testContract
|
||||
.marketBuy(
|
||||
tokens[makerSymbol].address,
|
||||
tokens[takerSymbol].address,
|
||||
quote.orders,
|
||||
takerOrders,
|
||||
quote.orders.map(o => o.signature),
|
||||
takerOrders.map(o => o.signature),
|
||||
quote.makerAssetFillAmount,
|
||||
)
|
||||
.callAsync({
|
||||
value: quote.worstCaseQuoteInfo.protocolFeeInWeiAmount.plus(TAKER_ASSET_ETH_VALUE),
|
||||
from: TAKER_ADDRESS,
|
||||
gasPrice: quote.gasPrice,
|
||||
});
|
||||
if (checkHadEnoughTakerAsset(quote, fill)) {
|
||||
expect(fill.fillResults.takerAssetFilledAmount, 'takerAssetFilledAmount').to.bignumber.lte(
|
||||
quote.worstCaseQuoteInfo.takerAssetAmount,
|
||||
);
|
||||
expect(fill.fillResults.makerAssetFilledAmount, 'makerAssetFilledAmount').to.bignumber.gte(
|
||||
quote.makerAssetFillAmount,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
77
contracts/integrations/test/aggregation/tokens.ts
Normal file
77
contracts/integrations/test/aggregation/tokens.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
export const tokens: { [symbol: string]: { address: string; decimals: number; price: number } } = {
|
||||
ETH: {
|
||||
address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
|
||||
decimals: 18,
|
||||
price: 133,
|
||||
},
|
||||
SAI: {
|
||||
address: '0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359',
|
||||
decimals: 18,
|
||||
price: 1,
|
||||
},
|
||||
DAI: {
|
||||
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
|
||||
decimals: 18,
|
||||
price: 1,
|
||||
},
|
||||
USDC: {
|
||||
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
||||
decimals: 6,
|
||||
price: 1,
|
||||
},
|
||||
WBTC: {
|
||||
address: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
|
||||
decimals: 8,
|
||||
price: 6900,
|
||||
},
|
||||
MKR: {
|
||||
address: '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2',
|
||||
decimals: 18,
|
||||
price: 454,
|
||||
},
|
||||
BAT: {
|
||||
address: '0x0D8775F648430679A709E98d2b0Cb6250d2887EF',
|
||||
decimals: 18,
|
||||
price: 0.17,
|
||||
},
|
||||
OMG: {
|
||||
address: '0xd26114cd6EE289AccF82350c8d8487fedB8A0C07',
|
||||
decimals: 18,
|
||||
price: 0.65,
|
||||
},
|
||||
ZRX: {
|
||||
address: '0xE41d2489571d322189246DaFA5ebDe1F4699F498',
|
||||
decimals: 18,
|
||||
price: 0.19,
|
||||
},
|
||||
ZIL: {
|
||||
address: '0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27',
|
||||
decimals: 12,
|
||||
price: 0.004,
|
||||
},
|
||||
FOAM: {
|
||||
address: '0x4946Fcea7C692606e8908002e55A582af44AC121',
|
||||
decimals: 18,
|
||||
price: 0.004,
|
||||
},
|
||||
USDT: {
|
||||
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
||||
decimals: 6,
|
||||
price: 0.019,
|
||||
},
|
||||
REP: {
|
||||
address: '0x1985365e9f78359a9B6AD760e32412f4a445E862',
|
||||
decimals: 18,
|
||||
price: 8.9,
|
||||
},
|
||||
MANA: {
|
||||
address: '0x0F5D2fB29fb7d3CFeE444a200298f468908cC942',
|
||||
decimals: 18,
|
||||
price: 0.025,
|
||||
},
|
||||
LINK: {
|
||||
address: '0x514910771AF9Ca656af840dff83E8264EcF986CA',
|
||||
decimals: 18,
|
||||
price: 1.8,
|
||||
},
|
||||
};
|
@@ -5,16 +5,20 @@
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as TestDydxUser from '../test/generated-artifacts/TestDydxUser.json';
|
||||
import * as TestEth2Dai from '../test/generated-artifacts/TestEth2Dai.json';
|
||||
import * as TestEth2DaiBridge from '../test/generated-artifacts/TestEth2DaiBridge.json';
|
||||
import * as TestFramework from '../test/generated-artifacts/TestFramework.json';
|
||||
import * as TestMainnetAggregatorFills from '../test/generated-artifacts/TestMainnetAggregatorFills.json';
|
||||
import * as TestUniswapBridge from '../test/generated-artifacts/TestUniswapBridge.json';
|
||||
import * as TestUniswapExchange from '../test/generated-artifacts/TestUniswapExchange.json';
|
||||
import * as TestUniswapExchangeFactory from '../test/generated-artifacts/TestUniswapExchangeFactory.json';
|
||||
export const artifacts = {
|
||||
TestDydxUser: TestDydxUser as ContractArtifact,
|
||||
TestEth2Dai: TestEth2Dai as ContractArtifact,
|
||||
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
|
||||
TestFramework: TestFramework as ContractArtifact,
|
||||
TestMainnetAggregatorFills: TestMainnetAggregatorFills as ContractArtifact,
|
||||
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
|
||||
TestUniswapExchange: TestUniswapExchange as ContractArtifact,
|
||||
TestUniswapExchangeFactory: TestUniswapExchangeFactory as ContractArtifact,
|
||||
|
1002
contracts/integrations/test/bridges/abi/dydxEvents.ts
Normal file
1002
contracts/integrations/test/bridges/abi/dydxEvents.ts
Normal file
File diff suppressed because it is too large
Load Diff
22
contracts/integrations/test/bridges/deploy_dydx_bridge.ts
Normal file
22
contracts/integrations/test/bridges/deploy_dydx_bridge.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { artifacts as assetProxyArtifacts, TestDydxBridgeContract } from '@0x/contracts-asset-proxy';
|
||||
import { BlockchainTestsEnvironment } from '@0x/contracts-test-utils';
|
||||
|
||||
import { DeploymentManager } from '../framework/deployment_manager';
|
||||
|
||||
/**
|
||||
* Deploys test DydxBridge contract configured to work alongside the provided `deployment`.
|
||||
*/
|
||||
export async function deployDydxBridgeAsync(
|
||||
deployment: DeploymentManager,
|
||||
environment: BlockchainTestsEnvironment,
|
||||
): Promise<TestDydxBridgeContract> {
|
||||
const tokenHolders = deployment.accounts;
|
||||
const dydxBridge = await TestDydxBridgeContract.deployFrom0xArtifactAsync(
|
||||
assetProxyArtifacts.TestDydxBridge,
|
||||
environment.provider,
|
||||
deployment.txDefaults,
|
||||
assetProxyArtifacts,
|
||||
tokenHolders,
|
||||
);
|
||||
return dydxBridge;
|
||||
}
|
159
contracts/integrations/test/bridges/dydx_bridge_mainnet_test.ts
Normal file
159
contracts/integrations/test/bridges/dydx_bridge_mainnet_test.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
import {
|
||||
artifacts as assetProxyArtifacts,
|
||||
DydxBridgeActionType,
|
||||
DydxBridgeContract,
|
||||
DydxBridgeData,
|
||||
dydxBridgeDataEncoder,
|
||||
} from '@0x/contracts-asset-proxy';
|
||||
import { artifacts as erc20Artifacts } from '@0x/contracts-erc20';
|
||||
import { blockchainTests, constants, describe, expect, toBaseUnitAmount } from '@0x/contracts-test-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { DecodedLogArgs, LogWithDecodedArgs } from 'ethereum-types';
|
||||
|
||||
import { contractAddresses, dydxAccountOwner } from '../mainnet_fork_utils';
|
||||
|
||||
import { dydxEvents } from './abi/dydxEvents';
|
||||
|
||||
blockchainTests.fork.resets('Mainnet dydx bridge tests', env => {
|
||||
let testContract: DydxBridgeContract;
|
||||
// random account to receive tokens from dydx
|
||||
const receiver = '0x986ccf5234d9cfbb25246f1a5bfa51f4ccfcb308';
|
||||
const defaultAccountNumber = new BigNumber(0);
|
||||
const daiMarketId = new BigNumber(3);
|
||||
const defaultAmount = toBaseUnitAmount(0.01);
|
||||
const defaultDepositAction = {
|
||||
actionType: DydxBridgeActionType.Deposit as number,
|
||||
accountId: constants.ZERO_AMOUNT,
|
||||
marketId: daiMarketId,
|
||||
conversionRateNumerator: constants.ZERO_AMOUNT,
|
||||
conversionRateDenominator: constants.ZERO_AMOUNT,
|
||||
};
|
||||
const defaultWithdrawAction = {
|
||||
actionType: DydxBridgeActionType.Withdraw as number,
|
||||
accountId: constants.ZERO_AMOUNT,
|
||||
marketId: daiMarketId,
|
||||
// This ratio must be less than the `1` to account
|
||||
// for interest in dydx balances because the test
|
||||
// account has an initial dydx balance of zero.
|
||||
conversionRateNumerator: new BigNumber(1),
|
||||
conversionRateDenominator: new BigNumber(2),
|
||||
};
|
||||
before(async () => {
|
||||
testContract = new DydxBridgeContract(contractAddresses.dydxBridge, env.provider, env.txDefaults, {
|
||||
DydxBridge: assetProxyArtifacts.DydxBridge.compilerOutput.abi,
|
||||
ERC20: erc20Artifacts.ERC20Token.compilerOutput.abi,
|
||||
Dydx: dydxEvents.abi,
|
||||
});
|
||||
});
|
||||
|
||||
describe('bridgeTransferFrom()', () => {
|
||||
const callAndVerifyDydxEvents = async (bridgeData: DydxBridgeData): Promise<void> => {
|
||||
const txReceipt = await testContract
|
||||
.bridgeTransferFrom(
|
||||
constants.NULL_ADDRESS,
|
||||
dydxAccountOwner,
|
||||
receiver,
|
||||
defaultAmount,
|
||||
dydxBridgeDataEncoder.encode({ bridgeData }),
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: contractAddresses.erc20BridgeProxy, gasPrice: 0 });
|
||||
|
||||
// Construct expected events
|
||||
const expectedDepositEvents = [];
|
||||
const expectedWithdrawEvents = [];
|
||||
for (const action of bridgeData.actions) {
|
||||
const scaledAmount = action.conversionRateDenominator.gt(0)
|
||||
? defaultAmount
|
||||
.times(action.conversionRateNumerator)
|
||||
.dividedToIntegerBy(action.conversionRateDenominator)
|
||||
: defaultAmount;
|
||||
switch (action.actionType) {
|
||||
case DydxBridgeActionType.Deposit:
|
||||
expectedDepositEvents.push({
|
||||
accountOwner: dydxAccountOwner,
|
||||
accountNumber: bridgeData.accountNumbers[action.accountId.toNumber()],
|
||||
market: action.marketId,
|
||||
update: [[true, scaledAmount]],
|
||||
from: dydxAccountOwner,
|
||||
});
|
||||
break;
|
||||
|
||||
case DydxBridgeActionType.Withdraw:
|
||||
expectedWithdrawEvents.push({
|
||||
accountOwner: dydxAccountOwner,
|
||||
accountNumber: bridgeData.accountNumbers[action.accountId.toNumber()],
|
||||
market: action.marketId,
|
||||
update: [[false, scaledAmount]],
|
||||
to: receiver,
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Unrecognized Action: ${action.actionType}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify events
|
||||
let nextExpectedDepositEventIdx = 0;
|
||||
let nextExpectedWithdrawEventIdx = 0;
|
||||
for (const rawLog of txReceipt.logs) {
|
||||
// tslint:disable-next-line no-unnecessary-type-assertion
|
||||
const log = rawLog as LogWithDecodedArgs<DecodedLogArgs>;
|
||||
if (log.event !== 'LogDeposit' && log.event !== 'LogWithdraw') {
|
||||
continue;
|
||||
}
|
||||
const expectedEvent =
|
||||
log.event === 'LogDeposit'
|
||||
? expectedDepositEvents[nextExpectedDepositEventIdx++]
|
||||
: expectedWithdrawEvents[nextExpectedWithdrawEventIdx++];
|
||||
expect(log.args.accountOwner, 'accountOwner').to.equal(expectedEvent.accountOwner);
|
||||
expect(log.args.accountNumber, 'accountNumber').to.bignumber.equal(expectedEvent.accountNumber);
|
||||
expect(log.args.market, 'market').to.bignumber.equal(expectedEvent.market);
|
||||
expect(log.args.from, 'from').to.equal(expectedEvent.from);
|
||||
// We only check the first update field because it's the delta balance (amount deposited).
|
||||
// The next field is the new total, which depends on interest rates at the time of execution.
|
||||
expect(log.args.update[0][0], 'update sign').to.equal(expectedEvent.update[0][0]);
|
||||
const updateValueHex = log.args.update[0][1]._hex;
|
||||
const updateValueBn = new BigNumber(updateValueHex, 16);
|
||||
expect(updateValueBn, 'update value').to.bignumber.equal(expectedEvent.update[0][1]);
|
||||
}
|
||||
};
|
||||
|
||||
it('succeeds when calling `operate` with the `deposit` action and a single account', async () => {
|
||||
await callAndVerifyDydxEvents({
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [defaultDepositAction],
|
||||
});
|
||||
});
|
||||
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
|
||||
await callAndVerifyDydxEvents({
|
||||
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
||||
actions: [defaultDepositAction],
|
||||
});
|
||||
});
|
||||
it('succeeds when calling `operate` with the `withdraw` action and a single account', async () => {
|
||||
await callAndVerifyDydxEvents({
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [defaultWithdrawAction],
|
||||
});
|
||||
});
|
||||
it('succeeds when calling `operate` with the `withdraw` action and multiple accounts', async () => {
|
||||
await callAndVerifyDydxEvents({
|
||||
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
||||
actions: [defaultWithdrawAction],
|
||||
});
|
||||
});
|
||||
it('succeeds when calling `operate` with the `deposit` action and multiple accounts', async () => {
|
||||
await callAndVerifyDydxEvents({
|
||||
accountNumbers: [defaultAccountNumber, defaultAccountNumber.plus(1)],
|
||||
actions: [defaultWithdrawAction, defaultDepositAction],
|
||||
});
|
||||
});
|
||||
it('succeeds when calling `operate` with multiple actions under a single account', async () => {
|
||||
await callAndVerifyDydxEvents({
|
||||
accountNumbers: [defaultAccountNumber],
|
||||
actions: [defaultWithdrawAction, defaultDepositAction],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
54
contracts/integrations/test/dev-utils/get_order_hash.ts
Normal file
54
contracts/integrations/test/dev-utils/get_order_hash.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { artifacts, DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import { artifacts as exchangeArtifacts, ExchangeContract } from '@0x/contracts-exchange';
|
||||
import { blockchainTests, constants, expect, orderHashUtils } from '@0x/contracts-test-utils';
|
||||
import { Order } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
blockchainTests('DevUtils.getOrderHash', env => {
|
||||
let devUtils: DevUtilsContract;
|
||||
let exchange: ExchangeContract;
|
||||
let chainId: number;
|
||||
|
||||
before(async () => {
|
||||
chainId = await env.getChainIdAsync();
|
||||
|
||||
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
||||
exchangeArtifacts.Exchange,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
exchangeArtifacts,
|
||||
new BigNumber(chainId),
|
||||
);
|
||||
devUtils = await DevUtilsContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DevUtils,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
exchange.address,
|
||||
);
|
||||
});
|
||||
|
||||
it('should return the order hash', async () => {
|
||||
const order: Order = {
|
||||
makerAddress: constants.NULL_ADDRESS,
|
||||
takerAddress: constants.NULL_ADDRESS,
|
||||
senderAddress: constants.NULL_ADDRESS,
|
||||
feeRecipientAddress: constants.NULL_ADDRESS,
|
||||
makerAssetData: constants.NULL_ADDRESS,
|
||||
takerAssetData: constants.NULL_ADDRESS,
|
||||
makerFeeAssetData: constants.NULL_ADDRESS,
|
||||
takerFeeAssetData: constants.NULL_ADDRESS,
|
||||
salt: new BigNumber(0),
|
||||
makerFee: new BigNumber(0),
|
||||
takerFee: new BigNumber(0),
|
||||
makerAssetAmount: new BigNumber(0),
|
||||
takerAssetAmount: new BigNumber(0),
|
||||
expirationTimeSeconds: new BigNumber(0),
|
||||
exchangeAddress: exchange.address,
|
||||
chainId,
|
||||
};
|
||||
expect(await devUtils.getOrderHash(order, new BigNumber(chainId), exchange.address).callAsync()).to.be.equal(
|
||||
orderHashUtils.getOrderHashHex(order),
|
||||
);
|
||||
});
|
||||
});
|
@@ -1,6 +1,5 @@
|
||||
import * as chai from 'chai';
|
||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
import * as crypto from 'crypto';
|
||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
|
||||
import {
|
||||
artifacts as proxyArtifacts,
|
||||
@@ -19,19 +18,12 @@ import {
|
||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { artifacts as erc721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||
import { artifacts as exchangeArtifacts, ExchangeContract } from '@0x/contracts-exchange';
|
||||
import { chaiSetup, constants, LogDecoder, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { blockchainTests, constants, expect, LogDecoder } from '@0x/contracts-test-utils';
|
||||
import { AssetProxyId } from '@0x/types';
|
||||
import { BigNumber, providerUtils, StringRevertError, LibBytesRevertErrors } from '@0x/utils';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
import { BigNumber, hexUtils, LibBytesRevertErrors, StringRevertError } from '@0x/utils';
|
||||
|
||||
import { artifacts, LibAssetDataContract } from '@0x/contracts-dev-utils';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
const KNOWN_ERC20_ENCODING = {
|
||||
address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48',
|
||||
assetData: '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48',
|
||||
@@ -69,7 +61,8 @@ const KNOWN_STATIC_CALL_ENCODING = {
|
||||
'0xc339d10a0000000000000000000000006dfff22588be9b3ef8cf0ad6dc9b84796f9fb45f0000000000000000000000000000000000000000000000000000000000000060b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60000000000000000000000000000000000000000000000000000000000000024ed2cfc9c000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000',
|
||||
};
|
||||
|
||||
describe('LibAssetData', () => {
|
||||
// TODO(jalextowle): This file could really be cleaned up by using the DeploymentManager tool.
|
||||
blockchainTests.resets('LibAssetData', env => {
|
||||
let exchange: ExchangeContract;
|
||||
let erc20Proxy: ERC20ProxyContract;
|
||||
let erc721Proxy: ERC721ProxyContract;
|
||||
@@ -93,44 +86,43 @@ describe('LibAssetData', () => {
|
||||
let erc1155TokenId: BigNumber;
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
const chainId = await providerUtils.getChainIdAsync(provider);
|
||||
const chainId = await env.getChainIdAsync();
|
||||
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
||||
exchangeArtifacts.Exchange,
|
||||
provider,
|
||||
txDefaults,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
{},
|
||||
new BigNumber(chainId),
|
||||
);
|
||||
|
||||
erc20Proxy = await ERC20ProxyContract.deployFrom0xArtifactAsync(
|
||||
proxyArtifacts.ERC20Proxy,
|
||||
provider,
|
||||
txDefaults,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
erc721Proxy = await ERC721ProxyContract.deployFrom0xArtifactAsync(
|
||||
proxyArtifacts.ERC721Proxy,
|
||||
provider,
|
||||
txDefaults,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
erc1155Proxy = await ERC1155ProxyContract.deployFrom0xArtifactAsync(
|
||||
proxyArtifacts.ERC1155Proxy,
|
||||
provider,
|
||||
txDefaults,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
multiAssetProxy = await MultiAssetProxyContract.deployFrom0xArtifactAsync(
|
||||
proxyArtifacts.MultiAssetProxy,
|
||||
provider,
|
||||
txDefaults,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
staticCallProxy = await StaticCallProxyContract.deployFrom0xArtifactAsync(
|
||||
proxyArtifacts.StaticCallProxy,
|
||||
provider,
|
||||
txDefaults,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
|
||||
@@ -142,25 +134,25 @@ describe('LibAssetData', () => {
|
||||
|
||||
libAssetData = await LibAssetDataContract.deployFrom0xArtifactAsync(
|
||||
artifacts.LibAssetData,
|
||||
provider,
|
||||
txDefaults,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
exchange.address,
|
||||
);
|
||||
|
||||
staticCallTarget = await TestStaticCallTargetContract.deployFrom0xArtifactAsync(
|
||||
proxyArtifacts.TestStaticCallTarget,
|
||||
provider,
|
||||
txDefaults,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
|
||||
[tokenOwnerAddress] = await web3Wrapper.getAvailableAddressesAsync();
|
||||
[tokenOwnerAddress] = await env.getAccountAddressesAsync();
|
||||
|
||||
erc20Token = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
||||
erc20Artifacts.DummyERC20Token,
|
||||
provider,
|
||||
txDefaults,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
'Dummy',
|
||||
'DUM',
|
||||
@@ -170,8 +162,8 @@ describe('LibAssetData', () => {
|
||||
|
||||
erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync(
|
||||
erc721Artifacts.DummyERC721Token,
|
||||
provider,
|
||||
txDefaults,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
'Dummy',
|
||||
'DUM',
|
||||
@@ -187,12 +179,12 @@ describe('LibAssetData', () => {
|
||||
|
||||
erc1155Token = await ERC1155MintableContract.deployFrom0xArtifactAsync(
|
||||
erc1155Artifacts.ERC1155Mintable,
|
||||
provider,
|
||||
txDefaults,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
|
||||
const logDecoder = new LogDecoder(web3Wrapper, erc1155Artifacts);
|
||||
const logDecoder = new LogDecoder(env.web3Wrapper, erc1155Artifacts);
|
||||
const transactionReceipt = await logDecoder.getTxWithDecodedLogsAsync(
|
||||
await erc1155Token.create('uri:Dummy', /*isNonFungible:*/ false).sendTransactionAsync(),
|
||||
);
|
||||
@@ -204,17 +196,6 @@ describe('LibAssetData', () => {
|
||||
.awaitTransactionSuccessAsync();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
it('should have a deployed-to address', () => {
|
||||
expect(libAssetData.address.slice(0, 2)).to.equal('0x');
|
||||
});
|
||||
@@ -345,7 +326,7 @@ describe('LibAssetData', () => {
|
||||
});
|
||||
|
||||
it('should revert for invalid assetProxyId', async () => {
|
||||
const badAssetData = '0x' + crypto.randomBytes(4).toString('hex') + constants.NULL_ADDRESS;
|
||||
const badAssetData = `0x${crypto.randomBytes(4).toString('hex')}${constants.NULL_ADDRESS}`;
|
||||
await expect(libAssetData.revertIfInvalidAssetData(badAssetData).callAsync()).to.eventually.be.rejectedWith(
|
||||
StringRevertError,
|
||||
);
|
||||
@@ -441,8 +422,7 @@ describe('LibAssetData', () => {
|
||||
|
||||
it('should return a balance of MAX_UINT256 if the the StaticCallProxy assetData contains data for a successful staticcall', async () => {
|
||||
const staticCallData = staticCallTarget.isOddNumber(new BigNumber(1)).getABIEncodedTransactionData();
|
||||
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
||||
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
||||
const expectedResultHash = hexUtils.hash(hexUtils.leftPad(1));
|
||||
const assetData = await libAssetData
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
||||
@@ -452,8 +432,7 @@ describe('LibAssetData', () => {
|
||||
|
||||
it('should return a balance of 0 if the the StaticCallProxy assetData contains data for an unsuccessful staticcall', async () => {
|
||||
const staticCallData = staticCallTarget.isOddNumber(new BigNumber(0)).getABIEncodedTransactionData();
|
||||
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
|
||||
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
|
||||
const expectedResultHash = hexUtils.hash(hexUtils.leftPad(1));
|
||||
const assetData = await libAssetData
|
||||
.encodeStaticCallAssetData(staticCallTarget.address, staticCallData, expectedResultHash)
|
||||
.callAsync();
|
@@ -1,161 +1,92 @@
|
||||
import {
|
||||
artifacts as proxyArtifacts,
|
||||
ERC20ProxyContract,
|
||||
ERC20Wrapper,
|
||||
ERC721ProxyContract,
|
||||
ERC721Wrapper,
|
||||
MultiAssetProxyContract,
|
||||
} from '@0x/contracts-asset-proxy';
|
||||
import { ERC20ProxyContract } from '@0x/contracts-asset-proxy';
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||
import { artifacts as exchangeArtifacts, ExchangeContract } from '@0x/contracts-exchange';
|
||||
import {
|
||||
chaiSetup,
|
||||
constants,
|
||||
orderHashUtils,
|
||||
OrderFactory,
|
||||
OrderStatus,
|
||||
provider,
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { ExchangeContract } from '@0x/contracts-exchange';
|
||||
import { blockchainTests, constants, expect, orderHashUtils, OrderStatus } from '@0x/contracts-test-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { OrderTransferResults, SignedOrder } from '@0x/types';
|
||||
import { BigNumber, providerUtils } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
import { artifacts, DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import { Maker } from '../framework/actors/maker';
|
||||
import { DeploymentManager } from '../framework/deployment_manager';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
let makerAddress: string;
|
||||
// TODO(jalextowle): This can be cleaned up by using the actors more.
|
||||
blockchainTests.resets('OrderValidationUtils/OrderTransferSimulatorUtils', env => {
|
||||
let takerAddress: string;
|
||||
let owner: string;
|
||||
let erc20AssetData: string;
|
||||
let erc20AssetData2: string;
|
||||
let erc721AssetData: string;
|
||||
let feeAssetData: string;
|
||||
|
||||
let maker: Maker;
|
||||
let devUtils: DevUtilsContract;
|
||||
let erc20Token: DummyERC20TokenContract;
|
||||
let erc20Token2: DummyERC20TokenContract;
|
||||
let feeErc20Token: DummyERC20TokenContract;
|
||||
let erc721Token: DummyERC721TokenContract;
|
||||
let exchange: ExchangeContract;
|
||||
let devUtils: DevUtilsContract;
|
||||
let erc20Proxy: ERC20ProxyContract;
|
||||
let erc721Proxy: ERC721ProxyContract;
|
||||
let multiAssetProxy: MultiAssetProxyContract;
|
||||
let exchange: ExchangeContract;
|
||||
let deployment: DeploymentManager;
|
||||
|
||||
let erc20AssetData: string;
|
||||
let erc20AssetData2: string;
|
||||
let feeAssetData: string;
|
||||
let erc721AssetData: string;
|
||||
|
||||
let signedOrder: SignedOrder;
|
||||
let orderFactory: OrderFactory;
|
||||
|
||||
const tokenId = new BigNumber(123456789);
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
[takerAddress, owner] = await env.getAccountAddressesAsync();
|
||||
|
||||
before(async () => {
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
const usedAddresses = ([owner, makerAddress, takerAddress] = accounts.slice(0, 3));
|
||||
const chainId = await providerUtils.getChainIdAsync(provider);
|
||||
deployment = await DeploymentManager.deployAsync(env, {
|
||||
numErc20TokensToDeploy: 3,
|
||||
numErc721TokensToDeploy: 1,
|
||||
owner,
|
||||
});
|
||||
erc20Token = deployment.tokens.erc20[0];
|
||||
erc20Token2 = deployment.tokens.erc20[1];
|
||||
feeErc20Token = deployment.tokens.erc20[2];
|
||||
erc20Proxy = deployment.assetProxies.erc20Proxy;
|
||||
devUtils = deployment.devUtils;
|
||||
exchange = deployment.exchange;
|
||||
|
||||
const erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
|
||||
const erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
|
||||
erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
|
||||
erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20Token2.address);
|
||||
feeAssetData = assetDataUtils.encodeERC20AssetData(feeErc20Token.address);
|
||||
|
||||
const numDummyErc20ToDeploy = 3;
|
||||
[erc20Token, erc20Token2, feeErc20Token] = await erc20Wrapper.deployDummyTokensAsync(
|
||||
numDummyErc20ToDeploy,
|
||||
constants.DUMMY_TOKEN_DECIMALS,
|
||||
);
|
||||
erc20Proxy = await erc20Wrapper.deployProxyAsync();
|
||||
|
||||
[erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
|
||||
erc721Proxy = await erc721Wrapper.deployProxyAsync();
|
||||
|
||||
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
||||
exchangeArtifacts.Exchange,
|
||||
provider,
|
||||
txDefaults,
|
||||
{},
|
||||
new BigNumber(chainId),
|
||||
);
|
||||
|
||||
multiAssetProxy = await MultiAssetProxyContract.deployFrom0xArtifactAsync(
|
||||
proxyArtifacts.MultiAssetProxy,
|
||||
provider,
|
||||
txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
|
||||
await exchange.registerAssetProxy(erc20Proxy.address).awaitTransactionSuccessAsync({ from: owner });
|
||||
await exchange.registerAssetProxy(erc721Proxy.address).awaitTransactionSuccessAsync({ from: owner });
|
||||
await exchange.registerAssetProxy(multiAssetProxy.address).awaitTransactionSuccessAsync({ from: owner });
|
||||
await erc20Proxy.addAuthorizedAddress(exchange.address).awaitTransactionSuccessAsync({ from: owner });
|
||||
await erc721Proxy.addAuthorizedAddress(exchange.address).awaitTransactionSuccessAsync({ from: owner });
|
||||
|
||||
devUtils = await DevUtilsContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DevUtils,
|
||||
provider,
|
||||
txDefaults,
|
||||
artifacts,
|
||||
exchange.address,
|
||||
);
|
||||
|
||||
feeAssetData = await devUtils.encodeERC20AssetData(feeErc20Token.address).callAsync();
|
||||
erc20AssetData = await devUtils.encodeERC20AssetData(erc20Token.address).callAsync();
|
||||
erc20AssetData2 = await devUtils.encodeERC20AssetData(erc20Token2.address).callAsync();
|
||||
erc721AssetData = await devUtils.encodeERC721AssetData(erc721Token.address, tokenId).callAsync();
|
||||
const defaultOrderParams = {
|
||||
...constants.STATIC_ORDER_PARAMS,
|
||||
makerAddress,
|
||||
feeRecipientAddress: constants.NULL_ADDRESS,
|
||||
makerAssetData: erc20AssetData,
|
||||
takerAssetData: erc20AssetData2,
|
||||
makerFeeAssetData: feeAssetData,
|
||||
takerFeeAssetData: feeAssetData,
|
||||
exchangeAddress: exchange.address,
|
||||
chainId,
|
||||
};
|
||||
const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
|
||||
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
maker = new Maker({
|
||||
name: 'Maker',
|
||||
deployment,
|
||||
orderConfig: {
|
||||
makerAssetData: erc20AssetData,
|
||||
takerAssetData: erc20AssetData2,
|
||||
makerFeeAssetData: feeAssetData,
|
||||
takerFeeAssetData: feeAssetData,
|
||||
feeRecipientAddress: constants.NULL_ADDRESS,
|
||||
},
|
||||
});
|
||||
const [tokenID] = await maker.configureERC721TokenAsync(deployment.tokens.erc721[0]);
|
||||
erc721AssetData = assetDataUtils.encodeERC721AssetData(deployment.tokens.erc721[0].address, tokenID);
|
||||
});
|
||||
|
||||
describe('getTransferableAssetAmount', () => {
|
||||
it('should return the balance when balance < allowance', async () => {
|
||||
const balance = new BigNumber(123);
|
||||
const allowance = new BigNumber(456);
|
||||
await erc20Token.setBalance(makerAddress, balance).awaitTransactionSuccessAsync();
|
||||
await erc20Token.setBalance(maker.address, balance).awaitTransactionSuccessAsync();
|
||||
await erc20Token.approve(erc20Proxy.address, allowance).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
const transferableAmount = await devUtils
|
||||
.getTransferableAssetAmount(makerAddress, erc20AssetData)
|
||||
.getTransferableAssetAmount(maker.address, erc20AssetData)
|
||||
.callAsync();
|
||||
expect(transferableAmount).to.bignumber.equal(balance);
|
||||
});
|
||||
it('should return the allowance when allowance < balance', async () => {
|
||||
const balance = new BigNumber(456);
|
||||
const allowance = new BigNumber(123);
|
||||
await erc20Token.setBalance(makerAddress, balance).awaitTransactionSuccessAsync();
|
||||
await erc20Token.setBalance(maker.address, balance).awaitTransactionSuccessAsync();
|
||||
await erc20Token.approve(erc20Proxy.address, allowance).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
const transferableAmount = await devUtils
|
||||
.getTransferableAssetAmount(makerAddress, erc20AssetData)
|
||||
.getTransferableAssetAmount(maker.address, erc20AssetData)
|
||||
.callAsync();
|
||||
expect(transferableAmount).to.bignumber.equal(allowance);
|
||||
});
|
||||
@@ -165,23 +96,23 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
.callAsync();
|
||||
const transferableAmount1 = new BigNumber(10);
|
||||
const transferableAmount2 = new BigNumber(5);
|
||||
await erc20Token.setBalance(makerAddress, transferableAmount1).awaitTransactionSuccessAsync();
|
||||
await erc20Token.setBalance(maker.address, transferableAmount1).awaitTransactionSuccessAsync();
|
||||
await erc20Token.approve(erc20Proxy.address, transferableAmount1).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
await erc20Token2.setBalance(makerAddress, transferableAmount2).awaitTransactionSuccessAsync();
|
||||
await erc20Token2.setBalance(maker.address, transferableAmount2).awaitTransactionSuccessAsync();
|
||||
await erc20Token2.approve(erc20Proxy.address, transferableAmount2).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
const transferableAmount = await devUtils
|
||||
.getTransferableAssetAmount(makerAddress, multiAssetData)
|
||||
.getTransferableAssetAmount(maker.address, multiAssetData)
|
||||
.callAsync();
|
||||
expect(transferableAmount).to.bignumber.equal(transferableAmount2);
|
||||
});
|
||||
});
|
||||
describe('getOrderRelevantState', () => {
|
||||
beforeEach(async () => {
|
||||
signedOrder = await orderFactory.newSignedOrderAsync();
|
||||
signedOrder = await maker.signOrderAsync({});
|
||||
});
|
||||
it('should return the correct orderInfo when the order is valid', async () => {
|
||||
const [orderInfo] = await devUtils.getOrderRelevantState(signedOrder, signedOrder.signature).callAsync();
|
||||
@@ -209,9 +140,9 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
it('should return a fillableTakerAssetAmount of 0 when fee balances/allowances are insufficient', async () => {
|
||||
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
|
||||
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
|
||||
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
const [, fillableTakerAssetAmount] = await devUtils
|
||||
.getOrderRelevantState(signedOrder, signedOrder.signature)
|
||||
@@ -222,10 +153,44 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
const multiAssetData = await devUtils
|
||||
.encodeMultiAssetData([new BigNumber(1), new BigNumber(1)], [erc20AssetData, erc20AssetData2])
|
||||
.callAsync();
|
||||
signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetData: multiAssetData });
|
||||
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
|
||||
signedOrder = await maker.signOrderAsync({ makerAssetData: multiAssetData });
|
||||
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
|
||||
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
const [, fillableTakerAssetAmount] = await devUtils
|
||||
.getOrderRelevantState(signedOrder, signedOrder.signature)
|
||||
.callAsync();
|
||||
expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
it('should return a fillableTakerAssetAmount of 0 when an erc721 asset is duplicated in the maker side of a multi-asset proxy order', async () => {
|
||||
const multiAssetData = await devUtils
|
||||
.encodeMultiAssetData([new BigNumber(1), new BigNumber(1)], [erc721AssetData, erc721AssetData])
|
||||
.callAsync();
|
||||
signedOrder = await maker.signOrderAsync({
|
||||
makerAssetData: multiAssetData,
|
||||
makerAssetAmount: new BigNumber(1),
|
||||
takerAssetData: erc721AssetData,
|
||||
takerAssetAmount: new BigNumber(1),
|
||||
makerFee: constants.ZERO_AMOUNT,
|
||||
takerFee: constants.ZERO_AMOUNT,
|
||||
});
|
||||
const [, fillableTakerAssetAmount] = await devUtils
|
||||
.getOrderRelevantState(signedOrder, signedOrder.signature)
|
||||
.callAsync();
|
||||
expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
it('should return a fillableTakerAssetAmount of 0 when an erc721 asset is duplicated in the taker side of a multi-asset proxy order', async () => {
|
||||
const multiAssetData = await devUtils
|
||||
.encodeMultiAssetData([new BigNumber(1), new BigNumber(1)], [erc721AssetData, erc721AssetData])
|
||||
.callAsync();
|
||||
signedOrder = await maker.signOrderAsync({
|
||||
makerAssetData: erc721AssetData,
|
||||
makerAssetAmount: new BigNumber(1),
|
||||
takerAssetData: multiAssetData,
|
||||
takerAssetAmount: new BigNumber(1),
|
||||
makerFee: constants.ZERO_AMOUNT,
|
||||
takerFee: constants.ZERO_AMOUNT,
|
||||
});
|
||||
const [, fillableTakerAssetAmount] = await devUtils
|
||||
.getOrderRelevantState(signedOrder, signedOrder.signature)
|
||||
@@ -233,16 +198,16 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
it('should return the correct fillableTakerAssetAmount when fee balances/allowances are partially sufficient', async () => {
|
||||
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
|
||||
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
|
||||
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
const divisor = 4;
|
||||
await feeErc20Token
|
||||
.setBalance(makerAddress, signedOrder.makerFee.dividedToIntegerBy(divisor))
|
||||
.setBalance(maker.address, signedOrder.makerFee.dividedToIntegerBy(divisor))
|
||||
.awaitTransactionSuccessAsync();
|
||||
await feeErc20Token.approve(erc20Proxy.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
const [, fillableTakerAssetAmount] = await devUtils
|
||||
.getOrderRelevantState(signedOrder, signedOrder.signature)
|
||||
@@ -254,14 +219,14 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
it('should return the correct fillableTakerAssetAmount when non-fee balances/allowances are partially sufficient', async () => {
|
||||
const divisor = 4;
|
||||
await erc20Token
|
||||
.setBalance(makerAddress, signedOrder.makerAssetAmount.dividedToIntegerBy(divisor))
|
||||
.setBalance(maker.address, signedOrder.makerAssetAmount.dividedToIntegerBy(divisor))
|
||||
.awaitTransactionSuccessAsync();
|
||||
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
await feeErc20Token.setBalance(makerAddress, signedOrder.makerFee).awaitTransactionSuccessAsync();
|
||||
await feeErc20Token.setBalance(maker.address, signedOrder.makerFee).awaitTransactionSuccessAsync();
|
||||
await feeErc20Token.approve(erc20Proxy.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
const [, fillableTakerAssetAmount] = await devUtils
|
||||
.getOrderRelevantState(signedOrder, signedOrder.signature)
|
||||
@@ -274,23 +239,23 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
const multiAssetData = await devUtils
|
||||
.encodeMultiAssetData([new BigNumber(1), new BigNumber(1)], [erc20AssetData, erc20AssetData2])
|
||||
.callAsync();
|
||||
signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetData: multiAssetData });
|
||||
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
|
||||
signedOrder = await maker.signOrderAsync({ makerAssetData: multiAssetData });
|
||||
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
|
||||
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
await feeErc20Token.setBalance(makerAddress, signedOrder.makerFee).awaitTransactionSuccessAsync();
|
||||
await feeErc20Token.setBalance(maker.address, signedOrder.makerFee).awaitTransactionSuccessAsync();
|
||||
await feeErc20Token.approve(erc20Proxy.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
const divisor = 4;
|
||||
await erc20Token2
|
||||
.setBalance(makerAddress, signedOrder.makerAssetAmount.dividedToIntegerBy(divisor))
|
||||
.setBalance(maker.address, signedOrder.makerAssetAmount.dividedToIntegerBy(divisor))
|
||||
.awaitTransactionSuccessAsync();
|
||||
await erc20Token2
|
||||
.approve(erc20Proxy.address, signedOrder.makerAssetAmount.dividedToIntegerBy(divisor))
|
||||
.awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
const [, fillableTakerAssetAmount] = await devUtils
|
||||
.getOrderRelevantState(signedOrder, signedOrder.signature)
|
||||
@@ -300,9 +265,9 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
);
|
||||
});
|
||||
it('should return a fillableTakerAssetAmount of 0 when non-fee balances/allowances are insufficient', async () => {
|
||||
await feeErc20Token.setBalance(makerAddress, signedOrder.makerFee).awaitTransactionSuccessAsync();
|
||||
await feeErc20Token.setBalance(maker.address, signedOrder.makerFee).awaitTransactionSuccessAsync();
|
||||
await feeErc20Token.approve(erc20Proxy.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
const [, fillableTakerAssetAmount] = await devUtils
|
||||
.getOrderRelevantState(signedOrder, signedOrder.signature)
|
||||
@@ -310,13 +275,13 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
});
|
||||
it('should return a fillableTakerAssetAmount equal to the takerAssetAmount when the order is unfilled and balances/allowances are sufficient', async () => {
|
||||
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
|
||||
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
|
||||
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
await feeErc20Token.setBalance(makerAddress, signedOrder.makerFee).awaitTransactionSuccessAsync();
|
||||
await feeErc20Token.setBalance(maker.address, signedOrder.makerFee).awaitTransactionSuccessAsync();
|
||||
await feeErc20Token.approve(erc20Proxy.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
const [, fillableTakerAssetAmount] = await devUtils
|
||||
.getOrderRelevantState(signedOrder, signedOrder.signature)
|
||||
@@ -324,16 +289,16 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
expect(fillableTakerAssetAmount).to.bignumber.equal(signedOrder.takerAssetAmount);
|
||||
});
|
||||
it('should return the correct fillableTakerAssetAmount when balances/allowances are partially sufficient and makerAsset=makerFeeAsset', async () => {
|
||||
signedOrder = await orderFactory.newSignedOrderAsync({
|
||||
signedOrder = await maker.signOrderAsync({
|
||||
makerAssetData: feeAssetData,
|
||||
makerAssetAmount: new BigNumber(10),
|
||||
takerAssetAmount: new BigNumber(20),
|
||||
makerFee: new BigNumber(40),
|
||||
});
|
||||
const transferableMakerAssetAmount = new BigNumber(10);
|
||||
await feeErc20Token.setBalance(makerAddress, transferableMakerAssetAmount).awaitTransactionSuccessAsync();
|
||||
await feeErc20Token.setBalance(maker.address, transferableMakerAssetAmount).awaitTransactionSuccessAsync();
|
||||
await feeErc20Token.approve(erc20Proxy.address, transferableMakerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
const expectedFillableTakerAssetAmount = transferableMakerAssetAmount
|
||||
.times(signedOrder.takerAssetAmount)
|
||||
@@ -344,10 +309,10 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
expect(fillableTakerAssetAmount).to.bignumber.equal(expectedFillableTakerAssetAmount);
|
||||
});
|
||||
it('should return the correct fillabeTakerassetAmount when makerAsset balances/allowances are sufficient and there are no maker fees', async () => {
|
||||
signedOrder = await orderFactory.newSignedOrderAsync({ makerFee: constants.ZERO_AMOUNT });
|
||||
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
|
||||
signedOrder = await maker.signOrderAsync({ makerFee: constants.ZERO_AMOUNT });
|
||||
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
|
||||
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
const [, fillableTakerAssetAmount] = await devUtils
|
||||
.getOrderRelevantState(signedOrder, signedOrder.signature)
|
||||
@@ -355,13 +320,13 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
expect(fillableTakerAssetAmount).to.bignumber.equal(signedOrder.takerAssetAmount);
|
||||
});
|
||||
it('should return a fillableTakerAssetAmount when the remaining takerAssetAmount is less than the transferable amount', async () => {
|
||||
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
|
||||
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
|
||||
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
await feeErc20Token.setBalance(makerAddress, signedOrder.makerFee).awaitTransactionSuccessAsync();
|
||||
await feeErc20Token.setBalance(maker.address, signedOrder.makerFee).awaitTransactionSuccessAsync();
|
||||
await feeErc20Token.approve(erc20Proxy.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
await erc20Token2.setBalance(takerAddress, signedOrder.takerAssetAmount).awaitTransactionSuccessAsync();
|
||||
await erc20Token2.approve(erc20Proxy.address, signedOrder.takerAssetAmount).awaitTransactionSuccessAsync({
|
||||
@@ -375,7 +340,7 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
const takerAssetFillAmount = signedOrder.takerAssetAmount.dividedToIntegerBy(4);
|
||||
await exchange
|
||||
.fillOrder(signedOrder, takerAssetFillAmount, signedOrder.signature)
|
||||
.awaitTransactionSuccessAsync({ from: takerAddress });
|
||||
.awaitTransactionSuccessAsync({ from: takerAddress, value: DeploymentManager.protocolFee });
|
||||
const [, fillableTakerAssetAmount] = await devUtils
|
||||
.getOrderRelevantState(signedOrder, signedOrder.signature)
|
||||
.callAsync();
|
||||
@@ -384,15 +349,15 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
);
|
||||
});
|
||||
it('should return correct info even when there are no fees specified', async () => {
|
||||
signedOrder = await orderFactory.newSignedOrderAsync({
|
||||
signedOrder = await maker.signOrderAsync({
|
||||
makerFee: new BigNumber(0),
|
||||
takerFee: new BigNumber(0),
|
||||
makerFeeAssetData: '0x',
|
||||
takerFeeAssetData: '0x',
|
||||
});
|
||||
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
|
||||
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
|
||||
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
const [orderInfo, fillableTakerAssetAmount, isValidSignature] = await devUtils
|
||||
.getOrderRelevantState(signedOrder, signedOrder.signature)
|
||||
@@ -406,18 +371,21 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
});
|
||||
describe('getOrderRelevantStates', async () => {
|
||||
it('should return the correct information for multiple orders', async () => {
|
||||
signedOrder = await orderFactory.newSignedOrderAsync();
|
||||
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
|
||||
signedOrder = await maker.signOrderAsync();
|
||||
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync();
|
||||
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
await feeErc20Token.setBalance(makerAddress, signedOrder.makerFee).awaitTransactionSuccessAsync();
|
||||
await feeErc20Token.setBalance(maker.address, signedOrder.makerFee).awaitTransactionSuccessAsync();
|
||||
await feeErc20Token.approve(erc20Proxy.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
const signedOrder2 = await maker.signOrderAsync({
|
||||
makerAssetData: erc721AssetData,
|
||||
makerAssetAmount: new BigNumber(1),
|
||||
});
|
||||
const signedOrder2 = await orderFactory.newSignedOrderAsync({ makerAssetData: erc721AssetData });
|
||||
const invalidSignature = '0x01';
|
||||
await exchange.cancelOrder(signedOrder2).awaitTransactionSuccessAsync({ from: makerAddress });
|
||||
await exchange.cancelOrder(signedOrder2).awaitTransactionSuccessAsync({ from: maker.address });
|
||||
const [ordersInfo, fillableTakerAssetAmounts, isValidSignature] = await devUtils
|
||||
.getOrderRelevantStates([signedOrder, signedOrder2], [signedOrder.signature, invalidSignature])
|
||||
.callAsync();
|
||||
@@ -435,7 +403,7 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
});
|
||||
describe('getSimulatedOrderTransferResults', () => {
|
||||
beforeEach(async () => {
|
||||
signedOrder = await orderFactory.newSignedOrderAsync();
|
||||
signedOrder = await maker.signOrderAsync();
|
||||
});
|
||||
it('should return TakerAssetDataFailed if the takerAsset transfer fails', async () => {
|
||||
const orderTransferResults = await devUtils
|
||||
@@ -462,11 +430,11 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
await erc20Token2.approve(erc20Proxy.address, signedOrder.takerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: takerAddress,
|
||||
});
|
||||
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
});
|
||||
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
const orderTransferResults = await devUtils
|
||||
.getSimulatedOrderTransferResults(signedOrder, takerAddress, signedOrder.takerAssetAmount)
|
||||
@@ -480,11 +448,11 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
await erc20Token2.approve(erc20Proxy.address, signedOrder.takerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: takerAddress,
|
||||
});
|
||||
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
});
|
||||
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
await feeErc20Token.setBalance(takerAddress, signedOrder.takerFee).awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
@@ -504,11 +472,11 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
await erc20Token2.approve(erc20Proxy.address, signedOrder.takerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: takerAddress,
|
||||
});
|
||||
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
});
|
||||
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
await feeErc20Token.setBalance(takerAddress, signedOrder.takerFee).awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
@@ -516,11 +484,11 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
await feeErc20Token.approve(erc20Proxy.address, signedOrder.takerFee).awaitTransactionSuccessAsync({
|
||||
from: takerAddress,
|
||||
});
|
||||
await feeErc20Token.setBalance(makerAddress, signedOrder.makerFee).awaitTransactionSuccessAsync({
|
||||
await feeErc20Token.setBalance(maker.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
});
|
||||
await feeErc20Token.approve(erc20Proxy.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
const orderTransferResults = await devUtils
|
||||
.getSimulatedOrderTransferResults(signedOrder, takerAddress, signedOrder.takerAssetAmount)
|
||||
@@ -536,11 +504,11 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
await erc20Token2.approve(erc20Proxy.address, signedOrder.takerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: takerAddress,
|
||||
});
|
||||
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
});
|
||||
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
await feeErc20Token.setBalance(takerAddress, signedOrder.takerFee).awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
@@ -548,11 +516,11 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
await feeErc20Token.approve(erc20Proxy.address, signedOrder.takerFee).awaitTransactionSuccessAsync({
|
||||
from: takerAddress,
|
||||
});
|
||||
await feeErc20Token.setBalance(makerAddress, signedOrder.makerFee).awaitTransactionSuccessAsync({
|
||||
await feeErc20Token.setBalance(maker.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
});
|
||||
await feeErc20Token.approve(erc20Proxy.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
const orderTransferResults = await devUtils
|
||||
.getSimulatedOrderTransferResults(signedOrder, takerAddress, signedOrder.takerAssetAmount.dividedBy(2))
|
||||
@@ -569,11 +537,11 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
await erc20Token2.approve(erc20Proxy.address, signedOrder.takerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: takerAddress,
|
||||
});
|
||||
await erc20Token.setBalance(makerAddress, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
await erc20Token.setBalance(maker.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
});
|
||||
await erc20Token.approve(erc20Proxy.address, signedOrder.makerAssetAmount).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
await feeErc20Token.setBalance(takerAddress, signedOrder.takerFee).awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
@@ -581,11 +549,11 @@ describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => {
|
||||
await feeErc20Token.approve(erc20Proxy.address, signedOrder.takerFee).awaitTransactionSuccessAsync({
|
||||
from: takerAddress,
|
||||
});
|
||||
await feeErc20Token.setBalance(makerAddress, signedOrder.makerFee).awaitTransactionSuccessAsync({
|
||||
await feeErc20Token.setBalance(maker.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
|
||||
from: owner,
|
||||
});
|
||||
await feeErc20Token.approve(erc20Proxy.address, signedOrder.makerFee).awaitTransactionSuccessAsync({
|
||||
from: makerAddress,
|
||||
from: maker.address,
|
||||
});
|
||||
const [orderTransferResults1, orderTransferResults2] = await devUtils
|
||||
.getSimulatedOrdersTransferResults(
|
278
contracts/integrations/test/exchange/fill_dydx_order_test.ts
Normal file
278
contracts/integrations/test/exchange/fill_dydx_order_test.ts
Normal file
@@ -0,0 +1,278 @@
|
||||
import { DydxBridgeActionType, dydxBridgeDataEncoder, TestDydxBridgeContract } from '@0x/contracts-asset-proxy';
|
||||
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { LibMathRevertErrors } from '@0x/contracts-exchange-libs';
|
||||
import { blockchainTests, constants, describe, expect, toBaseUnitAmount } from '@0x/contracts-test-utils';
|
||||
import { SignedOrder } from '@0x/order-utils';
|
||||
import { BigNumber, RevertError } from '@0x/utils';
|
||||
import { DecodedLogArgs, LogWithDecodedArgs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { deployDydxBridgeAsync } from '../bridges/deploy_dydx_bridge';
|
||||
import { Actor } from '../framework/actors/base';
|
||||
import { Maker } from '../framework/actors/maker';
|
||||
import { Taker } from '../framework/actors/taker';
|
||||
import { DeploymentManager } from '../framework/deployment_manager';
|
||||
|
||||
blockchainTests.resets('Exchange fills dydx orders', env => {
|
||||
let testContract: TestDydxBridgeContract;
|
||||
let makerToken: DummyERC20TokenContract;
|
||||
let takerToken: DummyERC20TokenContract;
|
||||
const marketId = new BigNumber(3);
|
||||
const dydxConversionRateNumerator = toBaseUnitAmount(6);
|
||||
const dydxConversionRateDenominator = toBaseUnitAmount(1);
|
||||
let maker: Maker;
|
||||
let taker: Taker;
|
||||
let orderConfig: Partial<SignedOrder>;
|
||||
let dydxBridgeProxyAssetData: string;
|
||||
let deployment: DeploymentManager;
|
||||
let testTokenAddress: string;
|
||||
const defaultDepositAction = {
|
||||
actionType: DydxBridgeActionType.Deposit as number,
|
||||
accountId: constants.ZERO_AMOUNT,
|
||||
marketId,
|
||||
conversionRateNumerator: dydxConversionRateNumerator,
|
||||
conversionRateDenominator: dydxConversionRateDenominator,
|
||||
};
|
||||
const defaultWithdrawAction = {
|
||||
actionType: DydxBridgeActionType.Withdraw as number,
|
||||
accountId: constants.ZERO_AMOUNT,
|
||||
marketId,
|
||||
conversionRateNumerator: constants.ZERO_AMOUNT,
|
||||
conversionRateDenominator: constants.ZERO_AMOUNT,
|
||||
};
|
||||
const bridgeData = {
|
||||
accountNumbers: [new BigNumber(0)],
|
||||
actions: [defaultDepositAction, defaultWithdrawAction],
|
||||
};
|
||||
|
||||
before(async () => {
|
||||
// Deploy contracts
|
||||
deployment = await DeploymentManager.deployAsync(env, {
|
||||
numErc20TokensToDeploy: 2,
|
||||
});
|
||||
testContract = await deployDydxBridgeAsync(deployment, env);
|
||||
const encodedBridgeData = dydxBridgeDataEncoder.encode({ bridgeData });
|
||||
testTokenAddress = await testContract.getTestToken().callAsync();
|
||||
dydxBridgeProxyAssetData = deployment.assetDataEncoder
|
||||
.ERC20Bridge(testTokenAddress, testContract.address, encodedBridgeData)
|
||||
.getABIEncodedTransactionData();
|
||||
[makerToken, takerToken] = deployment.tokens.erc20;
|
||||
|
||||
// Configure default order parameters.
|
||||
orderConfig = {
|
||||
makerAssetAmount: toBaseUnitAmount(1),
|
||||
takerAssetAmount: toBaseUnitAmount(1),
|
||||
makerAssetData: deployment.assetDataEncoder.ERC20Token(makerToken.address).getABIEncodedTransactionData(),
|
||||
takerAssetData: deployment.assetDataEncoder.ERC20Token(takerToken.address).getABIEncodedTransactionData(),
|
||||
// Not important for this test.
|
||||
feeRecipientAddress: constants.NULL_ADDRESS,
|
||||
makerFeeAssetData: deployment.assetDataEncoder
|
||||
.ERC20Token(makerToken.address)
|
||||
.getABIEncodedTransactionData(),
|
||||
takerFeeAssetData: deployment.assetDataEncoder
|
||||
.ERC20Token(takerToken.address)
|
||||
.getABIEncodedTransactionData(),
|
||||
makerFee: toBaseUnitAmount(1),
|
||||
takerFee: toBaseUnitAmount(1),
|
||||
};
|
||||
|
||||
// Configure maker.
|
||||
maker = new Maker({
|
||||
name: 'Maker',
|
||||
deployment,
|
||||
orderConfig,
|
||||
});
|
||||
await maker.configureERC20TokenAsync(makerToken, deployment.assetProxies.erc20Proxy.address);
|
||||
|
||||
// Configure taker.
|
||||
taker = new Taker({
|
||||
name: 'Taker',
|
||||
deployment,
|
||||
});
|
||||
await taker.configureERC20TokenAsync(takerToken, deployment.assetProxies.erc20Proxy.address);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
Actor.reset();
|
||||
});
|
||||
|
||||
describe('fillOrder', () => {
|
||||
interface DydxFillResults {
|
||||
makerAssetFilledAmount: BigNumber;
|
||||
takerAssetFilledAmount: BigNumber;
|
||||
makerFeePaid: BigNumber;
|
||||
takerFeePaid: BigNumber;
|
||||
amountDepositedIntoDydx: BigNumber;
|
||||
amountWithdrawnFromDydx: BigNumber;
|
||||
}
|
||||
const fillOrder = async (
|
||||
signedOrder: SignedOrder,
|
||||
customTakerAssetFillAmount?: BigNumber,
|
||||
): Promise<DydxFillResults> => {
|
||||
// Fill order
|
||||
const takerAssetFillAmount =
|
||||
customTakerAssetFillAmount !== undefined ? customTakerAssetFillAmount : signedOrder.takerAssetAmount;
|
||||
const tx = await taker.fillOrderAsync(signedOrder, takerAssetFillAmount);
|
||||
|
||||
// Extract values from fill event.
|
||||
// tslint:disable no-unnecessary-type-assertion
|
||||
const fillEvent = _.find(tx.logs, log => {
|
||||
return (log as any).event === 'Fill';
|
||||
}) as LogWithDecodedArgs<DecodedLogArgs>;
|
||||
|
||||
// Extract amount deposited into dydx from maker.
|
||||
const dydxDepositEvent = _.find(tx.logs, log => {
|
||||
return (
|
||||
(log as any).event === 'OperateAction' &&
|
||||
(log as any).args.actionType === DydxBridgeActionType.Deposit
|
||||
);
|
||||
}) as LogWithDecodedArgs<DecodedLogArgs>;
|
||||
|
||||
// Extract amount withdrawn from dydx to taker.
|
||||
const dydxWithdrawEvent = _.find(tx.logs, log => {
|
||||
return (
|
||||
(log as any).event === 'OperateAction' &&
|
||||
(log as any).args.actionType === DydxBridgeActionType.Withdraw
|
||||
);
|
||||
}) as LogWithDecodedArgs<DecodedLogArgs>;
|
||||
|
||||
// Return values of interest for assertions.
|
||||
return {
|
||||
makerAssetFilledAmount: fillEvent.args.makerAssetFilledAmount,
|
||||
takerAssetFilledAmount: fillEvent.args.takerAssetFilledAmount,
|
||||
makerFeePaid: fillEvent.args.makerFeePaid,
|
||||
takerFeePaid: fillEvent.args.takerFeePaid,
|
||||
amountDepositedIntoDydx: dydxDepositEvent.args.amountValue,
|
||||
amountWithdrawnFromDydx: dydxWithdrawEvent.args.amountValue,
|
||||
};
|
||||
};
|
||||
it('should successfully fill a dydx order (DydxBridge used in makerAssetData)', async () => {
|
||||
const signedOrder = await maker.signOrderAsync({
|
||||
// Invert the dydx conversion rate when using the bridge in the makerAssetData.
|
||||
makerAssetData: dydxBridgeProxyAssetData,
|
||||
makerAssetAmount: dydxConversionRateDenominator,
|
||||
takerAssetAmount: dydxConversionRateNumerator,
|
||||
});
|
||||
const dydxFillResults = await fillOrder(signedOrder);
|
||||
expect(
|
||||
dydxFillResults.makerAssetFilledAmount,
|
||||
'makerAssetFilledAmount should equal amountWithdrawnFromDydx',
|
||||
).to.bignumber.equal(dydxFillResults.amountWithdrawnFromDydx);
|
||||
expect(
|
||||
dydxFillResults.takerAssetFilledAmount,
|
||||
'takerAssetFilledAmount should equal amountDepositedIntoDydx',
|
||||
).to.bignumber.equal(dydxFillResults.amountDepositedIntoDydx);
|
||||
});
|
||||
it('should successfully fill a dydx order (DydxBridge used in takerAssetData)', async () => {
|
||||
const signedOrder = await maker.signOrderAsync({
|
||||
// Match the dydx conversion rate when using the bridge in the takerAssetData.
|
||||
takerAssetData: dydxBridgeProxyAssetData,
|
||||
makerAssetAmount: dydxConversionRateNumerator,
|
||||
takerAssetAmount: dydxConversionRateDenominator,
|
||||
});
|
||||
const dydxFillResults = await fillOrder(signedOrder);
|
||||
expect(
|
||||
dydxFillResults.makerAssetFilledAmount,
|
||||
'makerAssetFilledAmount should equal amountDepositedIntoDydx',
|
||||
).to.bignumber.equal(dydxFillResults.amountDepositedIntoDydx);
|
||||
expect(
|
||||
dydxFillResults.takerAssetFilledAmount,
|
||||
'takerAssetFilledAmount should equal amountWithdrawnFromDydx',
|
||||
).to.bignumber.equal(dydxFillResults.amountWithdrawnFromDydx);
|
||||
});
|
||||
it('should successfully fill a dydx order (DydxBridge used in makerFeeAssetData)', async () => {
|
||||
const signedOrder = await maker.signOrderAsync({
|
||||
// Invert the dydx conversion rate when using the bridge in the makerFeeAssetData.
|
||||
makerFeeAssetData: dydxBridgeProxyAssetData,
|
||||
makerFee: dydxConversionRateDenominator,
|
||||
takerFee: dydxConversionRateNumerator,
|
||||
});
|
||||
const dydxFillResults = await fillOrder(signedOrder);
|
||||
expect(
|
||||
dydxFillResults.makerFeePaid,
|
||||
'makerFeePaid should equal amountWithdrawnFromDydx',
|
||||
).to.bignumber.equal(dydxFillResults.amountWithdrawnFromDydx);
|
||||
expect(
|
||||
dydxFillResults.takerFeePaid,
|
||||
'takerFeePaid should equal amountDepositedIntoDydx',
|
||||
).to.bignumber.equal(dydxFillResults.amountDepositedIntoDydx);
|
||||
});
|
||||
it('should successfully fill a dydx order (DydxBridge used in takerFeeAssetData)', async () => {
|
||||
const signedOrder = await maker.signOrderAsync({
|
||||
// Match the dydx conversion rate when using the bridge in the takerFeeAssetData.
|
||||
takerFeeAssetData: dydxBridgeProxyAssetData,
|
||||
makerFee: dydxConversionRateNumerator,
|
||||
takerFee: dydxConversionRateDenominator,
|
||||
});
|
||||
const dydxFillResults = await fillOrder(signedOrder);
|
||||
expect(
|
||||
dydxFillResults.makerFeePaid,
|
||||
'makerFeePaid should equal amountDepositedIntoDydx',
|
||||
).to.bignumber.equal(dydxFillResults.amountDepositedIntoDydx);
|
||||
expect(
|
||||
dydxFillResults.takerFeePaid,
|
||||
'takerFeePaid should equal amountWithdrawnFromDydx',
|
||||
).to.bignumber.equal(dydxFillResults.amountWithdrawnFromDydx);
|
||||
});
|
||||
it('should partially fill a dydx order (DydxBridge used in makerAssetData)', async () => {
|
||||
const signedOrder = await maker.signOrderAsync({
|
||||
// Invert the dydx conversion rate when using the bridge in the makerAssetData.
|
||||
makerAssetData: dydxBridgeProxyAssetData,
|
||||
makerAssetAmount: dydxConversionRateDenominator,
|
||||
takerAssetAmount: dydxConversionRateNumerator,
|
||||
});
|
||||
const dydxFillResults = await fillOrder(signedOrder, signedOrder.takerAssetAmount.div(2));
|
||||
expect(
|
||||
dydxFillResults.makerAssetFilledAmount,
|
||||
'makerAssetFilledAmount should equal amountWithdrawnFromDydx',
|
||||
).to.bignumber.equal(dydxFillResults.amountWithdrawnFromDydx);
|
||||
expect(
|
||||
dydxFillResults.takerAssetFilledAmount,
|
||||
'takerAssetFilledAmount should equal amountDepositedIntoDydx',
|
||||
).to.bignumber.equal(dydxFillResults.amountDepositedIntoDydx);
|
||||
});
|
||||
it('should revert in DydxBridge when there is a rounding error', async () => {
|
||||
// This order will not trigger the rounding error in the Exchange,
|
||||
// but will trigger one in the DydxBridge.
|
||||
const badDepositAction = {
|
||||
...defaultDepositAction,
|
||||
conversionRateNumerator: new BigNumber(5318),
|
||||
conversionRateDenominator: new BigNumber(47958),
|
||||
};
|
||||
const badBridgeData = {
|
||||
accountNumbers: [new BigNumber(0)],
|
||||
actions: [badDepositAction],
|
||||
};
|
||||
const encodedBridgeData = dydxBridgeDataEncoder.encode({ bridgeData: badBridgeData });
|
||||
const badDydxBridgeProxyAssetData = deployment.assetDataEncoder
|
||||
.ERC20Bridge(testTokenAddress, testContract.address, encodedBridgeData)
|
||||
.getABIEncodedTransactionData();
|
||||
const signedOrder = await maker.signOrderAsync({
|
||||
makerAssetData: badDydxBridgeProxyAssetData,
|
||||
makerAssetAmount: badDepositAction.conversionRateDenominator,
|
||||
takerAssetAmount: badDepositAction.conversionRateNumerator,
|
||||
});
|
||||
const takerAssetFillAmount = new BigNumber(998);
|
||||
|
||||
// Compute expected error.
|
||||
const target = takerAssetFillAmount
|
||||
.multipliedBy(signedOrder.makerAssetAmount)
|
||||
.dividedToIntegerBy(signedOrder.takerAssetAmount);
|
||||
const expectedAssetProxyError = new LibMathRevertErrors.RoundingError(
|
||||
signedOrder.takerAssetAmount,
|
||||
signedOrder.makerAssetAmount,
|
||||
target,
|
||||
);
|
||||
|
||||
// Call fillOrder and assert the rounding error generated by the DydxBridge.
|
||||
const txPromise = taker.fillOrderAsync(signedOrder, takerAssetFillAmount);
|
||||
let assetProxyError;
|
||||
try {
|
||||
await txPromise.catch();
|
||||
} catch (e) {
|
||||
assetProxyError = RevertError.decode(e.values.errorData);
|
||||
}
|
||||
expect(assetProxyError).to.deep.equal(expectedAssetProxyError);
|
||||
});
|
||||
});
|
||||
});
|
@@ -111,7 +111,7 @@ export function MakerMixin<TBase extends Constructor>(Base: TBase): TBase & Cons
|
||||
await (owner as Actor).configureERC20TokenAsync(token as DummyERC20TokenContract);
|
||||
balance = balanceStore.balances.erc20[owner.address][token.address] =
|
||||
constants.INITIAL_ERC20_BALANCE;
|
||||
return Pseudorandom.integer(balance.dividedToIntegerBy(2));
|
||||
return Pseudorandom.integer(0, balance.dividedToIntegerBy(2));
|
||||
}),
|
||||
);
|
||||
// Encode asset data
|
||||
|
@@ -6,7 +6,7 @@ import * as _ from 'lodash';
|
||||
import { validCreateStakingPoolAssertion } from '../assertions/createStakingPool';
|
||||
import { validDecreaseStakingPoolOperatorShareAssertion } from '../assertions/decreaseStakingPoolOperatorShare';
|
||||
import { AssertionResult } from '../assertions/function_assertion';
|
||||
import { Pseudorandom } from '../utils/pseudorandom';
|
||||
import { Distributions, Pseudorandom } from '../utils/pseudorandom';
|
||||
|
||||
import { Actor, Constructor } from './base';
|
||||
|
||||
@@ -83,7 +83,11 @@ export function PoolOperatorMixin<TBase extends Constructor>(Base: TBase): TBase
|
||||
private async *_validCreateStakingPool(): AsyncIterableIterator<AssertionResult> {
|
||||
const assertion = validCreateStakingPoolAssertion(this.actor.deployment, this.actor.simulationEnvironment!);
|
||||
while (true) {
|
||||
const operatorShare = Pseudorandom.integer(constants.PPM).toNumber();
|
||||
const operatorShare = Pseudorandom.integer(
|
||||
0,
|
||||
constants.PPM,
|
||||
Distributions.Kumaraswamy(0.2, 0.2),
|
||||
).toNumber();
|
||||
yield assertion.executeAsync([operatorShare, false], { from: this.actor.address });
|
||||
}
|
||||
}
|
||||
@@ -96,7 +100,11 @@ export function PoolOperatorMixin<TBase extends Constructor>(Base: TBase): TBase
|
||||
if (poolId === undefined) {
|
||||
yield undefined;
|
||||
} else {
|
||||
const operatorShare = Pseudorandom.integer(stakingPools[poolId].operatorShare).toNumber();
|
||||
const operatorShare = Pseudorandom.integer(
|
||||
0,
|
||||
stakingPools[poolId].operatorShare,
|
||||
Distributions.Kumaraswamy(0.2, 0.2),
|
||||
).toNumber();
|
||||
yield assertion.executeAsync([poolId, operatorShare], { from: this.actor.address });
|
||||
}
|
||||
}
|
||||
|
@@ -79,7 +79,7 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
|
||||
while (true) {
|
||||
await balanceStore.updateErc20BalancesAsync();
|
||||
const zrxBalance = balanceStore.balances.erc20[this.actor.address][zrx.address];
|
||||
const amount = Pseudorandom.integer(zrxBalance);
|
||||
const amount = Pseudorandom.integer(0, zrxBalance);
|
||||
yield assertion.executeAsync([amount], { from: this.actor.address });
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
|
||||
undelegatedStake.currentEpochBalance,
|
||||
undelegatedStake.nextEpochBalance,
|
||||
);
|
||||
const amount = Pseudorandom.integer(withdrawableStake);
|
||||
const amount = Pseudorandom.integer(0, withdrawableStake);
|
||||
yield assertion.executeAsync([amount], { from: this.actor.address });
|
||||
}
|
||||
}
|
||||
@@ -118,7 +118,10 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
|
||||
const fromStatus =
|
||||
fromPoolId === undefined || stakingPools[fromPoolId].lastFinalized.isLessThan(currentEpoch.minus(1))
|
||||
? StakeStatus.Undelegated
|
||||
: (Pseudorandom.sample([StakeStatus.Undelegated, StakeStatus.Delegated]) as StakeStatus);
|
||||
: (Pseudorandom.sample(
|
||||
[StakeStatus.Undelegated, StakeStatus.Delegated],
|
||||
[0.2, 0.8], // 20% chance of `Undelegated`, 80% chance of `Delegated`
|
||||
) as StakeStatus);
|
||||
const from = new StakeInfo(fromStatus, fromPoolId);
|
||||
|
||||
// Pick a random pool to move the stake to
|
||||
@@ -128,7 +131,10 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
|
||||
const toStatus =
|
||||
toPoolId === undefined || stakingPools[toPoolId].lastFinalized.isLessThan(currentEpoch.minus(1))
|
||||
? StakeStatus.Undelegated
|
||||
: (Pseudorandom.sample([StakeStatus.Undelegated, StakeStatus.Delegated]) as StakeStatus);
|
||||
: (Pseudorandom.sample(
|
||||
[StakeStatus.Undelegated, StakeStatus.Delegated],
|
||||
[0.2, 0.8], // 20% chance of `Undelegated`, 80% chance of `Delegated`
|
||||
) as StakeStatus);
|
||||
const to = new StakeInfo(toStatus, toPoolId);
|
||||
|
||||
// The next epoch balance of the `from` stake is the amount that can be moved
|
||||
@@ -136,7 +142,7 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
|
||||
from.status === StakeStatus.Undelegated
|
||||
? this.stake[StakeStatus.Undelegated].nextEpochBalance
|
||||
: this.stake[StakeStatus.Delegated][from.poolId].nextEpochBalance;
|
||||
const amount = Pseudorandom.integer(moveableStake);
|
||||
const amount = Pseudorandom.integer(0, moveableStake);
|
||||
|
||||
yield assertion.executeAsync([from, to, amount], { from: this.actor.address });
|
||||
}
|
||||
|
@@ -75,12 +75,12 @@ export function TakerMixin<TBase extends Constructor>(Base: TBase): TBase & Cons
|
||||
// Maker creates and signs a fillable order
|
||||
const order = await maker.createFillableOrderAsync(this.actor);
|
||||
// Taker fills the order by a random amount (up to the order's takerAssetAmount)
|
||||
const fillAmount = Pseudorandom.integer(order.takerAssetAmount);
|
||||
const fillAmount = Pseudorandom.integer(0, order.takerAssetAmount);
|
||||
// Taker executes the fill with a random msg.value, so that sometimes the
|
||||
// protocol fee is paid in ETH and other times it's paid in WETH.
|
||||
yield assertion.executeAsync([order, fillAmount, order.signature], {
|
||||
from: this.actor.address,
|
||||
value: Pseudorandom.integer(DeploymentManager.protocolFee.times(2)),
|
||||
value: Pseudorandom.integer(0, DeploymentManager.protocolFee.times(2)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@
|
||||
"extends": ["@0x/tslint-config"],
|
||||
"rules": {
|
||||
"max-classes-per-file": false,
|
||||
"no-non-null-assertion": false
|
||||
"no-non-null-assertion": false,
|
||||
"custom-no-magic-numbers": false
|
||||
}
|
||||
}
|
||||
|
@@ -13,11 +13,11 @@ import { FunctionAssertion, FunctionResult } from './function_assertion';
|
||||
export function validDecreaseStakingPoolOperatorShareAssertion(
|
||||
deployment: DeploymentManager,
|
||||
pools: StakingPoolById,
|
||||
): FunctionAssertion<[string, number], {}, void> {
|
||||
): FunctionAssertion<[string, number], void, void> {
|
||||
const { stakingWrapper } = deployment.staking;
|
||||
|
||||
return new FunctionAssertion<[string, number], {}, void>(stakingWrapper, 'decreaseStakingPoolOperatorShare', {
|
||||
after: async (_beforeInfo, result: FunctionResult, args: [string, number], _txData: Partial<TxData>) => {
|
||||
return new FunctionAssertion<[string, number], void, void>(stakingWrapper, 'decreaseStakingPoolOperatorShare', {
|
||||
after: async (_beforeInfo: void, result: FunctionResult, args: [string, number], _txData: Partial<TxData>) => {
|
||||
// Ensure that the tx succeeded.
|
||||
expect(result.success, `Error: ${result.data}`).to.be.true();
|
||||
|
||||
|
@@ -8,7 +8,7 @@ import {
|
||||
MultiAssetProxyContract,
|
||||
StaticCallProxyContract,
|
||||
} from '@0x/contracts-asset-proxy';
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import { artifacts as devUtilsArtifacts, DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import { artifacts as ERC1155Artifacts, ERC1155MintableContract } from '@0x/contracts-erc1155';
|
||||
import { artifacts as ERC20Artifacts, DummyERC20TokenContract, WETH9Contract } from '@0x/contracts-erc20';
|
||||
import { artifacts as ERC721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||
@@ -145,7 +145,7 @@ export class DeploymentManager {
|
||||
exchangeArtifacts.Exchange,
|
||||
environment.provider,
|
||||
txDefaults,
|
||||
{ ...ERC20Artifacts, ...exchangeArtifacts, ...stakingArtifacts },
|
||||
{ ...ERC20Artifacts, ...exchangeArtifacts, ...stakingArtifacts, ...assetProxyArtifacts },
|
||||
new BigNumber(chainId),
|
||||
);
|
||||
const governor = await ZeroExGovernorContract.deployFrom0xArtifactAsync(
|
||||
@@ -196,7 +196,13 @@ export class DeploymentManager {
|
||||
staking.stakingProxy,
|
||||
]);
|
||||
|
||||
const devUtils = new DevUtilsContract(constants.NULL_ADDRESS, environment.provider);
|
||||
const devUtils = await DevUtilsContract.deployFrom0xArtifactAsync(
|
||||
devUtilsArtifacts.DevUtils,
|
||||
environment.provider,
|
||||
environment.txDefaults,
|
||||
devUtilsArtifacts,
|
||||
exchange.address,
|
||||
);
|
||||
const assetDataEncoder = new IAssetDataContract(constants.NULL_ADDRESS, environment.provider);
|
||||
|
||||
// Construct the new instance and return it.
|
||||
|
@@ -1,54 +1,87 @@
|
||||
import { Numberish } from '@0x/contracts-test-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
import * as seedrandom from 'seedrandom';
|
||||
|
||||
class PRNGWrapper {
|
||||
public readonly seed = process.env.SEED || Math.random().toString();
|
||||
private readonly _rng = seedrandom(this.seed);
|
||||
public readonly rng = seedrandom(this.seed);
|
||||
|
||||
/*
|
||||
* Pseudorandom version of _.sample. Picks an element of the given array with uniform probability.
|
||||
* Return undefined if the array is empty.
|
||||
* Pseudorandom version of _.sample. Picks an element of the given array. If an array of weights
|
||||
* is provided, elements of `arr` are weighted according to the value in the corresponding index
|
||||
* of `weights`. Otherwise, the samples are chosen uniformly at random. Return undefined if the
|
||||
* array is empty.
|
||||
*/
|
||||
public sample<T>(arr: T[]): T | undefined {
|
||||
public sample<T>(arr: T[], weights?: number[]): T | undefined {
|
||||
if (arr.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const index = Math.abs(this._rng.int32()) % arr.length;
|
||||
|
||||
let index: number;
|
||||
if (weights !== undefined) {
|
||||
const cdf = weights.map((_weight, i) => _.sum(weights.slice(0, i + 1)) / _.sum(weights));
|
||||
const x = this.rng();
|
||||
index = cdf.findIndex(value => value > x);
|
||||
} else {
|
||||
index = Math.abs(this.rng.int32()) % arr.length;
|
||||
}
|
||||
return arr[index];
|
||||
}
|
||||
|
||||
/*
|
||||
* Pseudorandom version of _.sampleSize. Returns an array of `n` samples from the given array
|
||||
* (with replacement), chosen with uniform probability. Return undefined if the array is empty.
|
||||
* (with replacement). If an array of weights is provided, elements of `arr` are weighted
|
||||
* according to the value in the corresponding index of `weights`. Otherwise, the samples are
|
||||
* chosen uniformly at random. Return undefined if the array is empty.
|
||||
*/
|
||||
public sampleSize<T>(arr: T[], n: number): T[] | undefined {
|
||||
public sampleSize<T>(arr: T[], n: number, weights?: number[]): T[] | undefined {
|
||||
if (arr.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const samples = [];
|
||||
for (let i = 0; i < n; i++) {
|
||||
samples.push(this.sample(arr) as T);
|
||||
samples.push(this.sample(arr, weights) as T);
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
// tslint:disable:unified-signatures
|
||||
/*
|
||||
* Pseudorandom version of getRandomPortion/getRandomInteger. If two arguments are provided,
|
||||
* treats those arguments as the min and max (inclusive) of the desired range. If only one
|
||||
* argument is provided, picks an integer between 0 and the argument.
|
||||
* Pseudorandom version of getRandomPortion/getRandomInteger. If no distribution is provided,
|
||||
* samples an integer between the min and max uniformly at random. If a distribution is
|
||||
* provided, samples an integer from the given distribution (assumed to be defined on the
|
||||
* interval [0, 1]) scaled to [min, max].
|
||||
*/
|
||||
public integer(max: Numberish): BigNumber;
|
||||
public integer(min: Numberish, max: Numberish): BigNumber;
|
||||
public integer(a: Numberish, b?: Numberish): BigNumber {
|
||||
if (b === undefined) {
|
||||
return new BigNumber(this._rng()).times(a).integerValue(BigNumber.ROUND_HALF_UP);
|
||||
} else {
|
||||
const range = new BigNumber(b).minus(a);
|
||||
return this.integer(range).plus(a);
|
||||
}
|
||||
public integer(min: Numberish, max: Numberish, distribution: () => Numberish = this.rng): BigNumber {
|
||||
const range = new BigNumber(max).minus(min);
|
||||
return new BigNumber(distribution())
|
||||
.times(range)
|
||||
.integerValue(BigNumber.ROUND_HALF_UP)
|
||||
.plus(min);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a function that produces samples from the Kumaraswamy distribution parameterized by
|
||||
* the given alpha and beta. The Kumaraswamy distribution is like the beta distribution, but
|
||||
* with a nice closed form. More info:
|
||||
* https://en.wikipedia.org/wiki/Kumaraswamy_distribution
|
||||
* https://www.johndcook.com/blog/2009/11/24/kumaraswamy-distribution/
|
||||
* Alpha and beta default to 0.2, so that the distribution favors the extremes of the domain.
|
||||
* The PDF for alpha=0.2, beta=0.2:
|
||||
* https://www.wolframalpha.com/input/?i=0.2*0.2*x%5E%280.2-1%29*%281-x%5E0.2%29%5E%280.2-1%29+from+0+to+1
|
||||
*/
|
||||
public kumaraswamy(this: PRNGWrapper, alpha: Numberish = 0.2, beta: Numberish = 0.2): () => BigNumber {
|
||||
const ONE = new BigNumber(1);
|
||||
return () => {
|
||||
const u = new BigNumber(this.rng()).modulo(ONE); // u ~ Uniform(0, 1)
|
||||
// Evaluate the inverse CDF at `u` to obtain a sample from Kumaraswamy(alpha, beta)
|
||||
return ONE.minus(ONE.minus(u).exponentiatedBy(ONE.dividedBy(beta))).exponentiatedBy(ONE.dividedBy(alpha));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const Pseudorandom = new PRNGWrapper();
|
||||
export const Distributions = {
|
||||
Uniform: Pseudorandom.rng,
|
||||
Kumaraswamy: Pseudorandom.kumaraswamy.bind(Pseudorandom),
|
||||
};
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { blockchainTests } from '@0x/contracts-test-utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { Actor } from '../framework/actors/base';
|
||||
import { PoolOperator } from '../framework/actors/pool_operator';
|
||||
@@ -14,12 +15,14 @@ export class PoolManagementSimulation extends Simulation {
|
||||
const { actors } = this.environment;
|
||||
const operators = filterActorsByRole(actors, PoolOperator);
|
||||
|
||||
const actions = [
|
||||
...operators.map(operator => operator.simulationActions.validCreateStakingPool),
|
||||
...operators.map(operator => operator.simulationActions.validDecreaseStakingPoolOperatorShare),
|
||||
];
|
||||
const [actions, weights] = _.unzip([
|
||||
// 40% chance of executing validCreateStakingPool assertion for a random operator
|
||||
...operators.map(operator => [operator.simulationActions.validCreateStakingPool, 0.4]),
|
||||
// 60% chance of executing validDecreaseStakingPoolOperatorShare for a random operator
|
||||
...operators.map(operator => [operator.simulationActions.validDecreaseStakingPoolOperatorShare, 0.6]),
|
||||
]) as [Array<AsyncIterableIterator<AssertionResult | void>>, number[]];
|
||||
while (true) {
|
||||
const action = Pseudorandom.sample(actions);
|
||||
const action = Pseudorandom.sample(actions, weights);
|
||||
yield (await action!.next()).value; // tslint:disable-line:no-non-null-assertion
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { blockchainTests } from '@0x/contracts-test-utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { Actor } from '../framework/actors/base';
|
||||
import { MakerTaker } from '../framework/actors/hybrids';
|
||||
@@ -22,14 +23,17 @@ export class PoolMembershipSimulation extends Simulation {
|
||||
|
||||
const poolManagement = new PoolManagementSimulation(this.environment);
|
||||
|
||||
const actions = [
|
||||
...makers.map(maker => maker.simulationActions.validJoinStakingPool),
|
||||
...takers.map(taker => taker.simulationActions.validFillOrder),
|
||||
poolManagement.generator,
|
||||
];
|
||||
const [actions, weights] = _.unzip([
|
||||
// 20% chance of executing validJoinStakingPool for a random maker
|
||||
...makers.map(maker => [maker.simulationActions.validJoinStakingPool, 0.2 / makers.length]),
|
||||
// 60% chance of executing validFillOrder for a random taker
|
||||
...takers.map(taker => [taker.simulationActions.validFillOrder, 0.6 / takers.length]),
|
||||
// 20% chance of executing an assertion generated from the pool management simulation
|
||||
[poolManagement.generator, 0.2],
|
||||
]) as [Array<AsyncIterableIterator<AssertionResult | void>>, number[]];
|
||||
|
||||
while (true) {
|
||||
const action = Pseudorandom.sample(actions);
|
||||
const action = Pseudorandom.sample(actions, weights);
|
||||
yield (await action!.next()).value; // tslint:disable-line:no-non-null-assertion
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { blockchainTests } from '@0x/contracts-test-utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { Actor } from '../framework/actors/base';
|
||||
import { StakerOperator } from '../framework/actors/hybrids';
|
||||
@@ -20,14 +21,19 @@ export class StakeManagementSimulation extends Simulation {
|
||||
|
||||
const poolManagement = new PoolManagementSimulation(this.environment);
|
||||
|
||||
const actions = [
|
||||
...stakers.map(staker => staker.simulationActions.validStake),
|
||||
...stakers.map(staker => staker.simulationActions.validUnstake),
|
||||
...stakers.map(staker => staker.simulationActions.validMoveStake),
|
||||
poolManagement.generator,
|
||||
];
|
||||
const [actions, weights] = _.unzip([
|
||||
// 30% chance of executing validStake for a random staker
|
||||
...stakers.map(staker => [staker.simulationActions.validStake, 0.3 / stakers.length]),
|
||||
// 20% chance of executing validUnstake for a random staker
|
||||
...stakers.map(staker => [staker.simulationActions.validUnstake, 0.2 / stakers.length]),
|
||||
// 30% chance of executing validMoveStake for a random staker
|
||||
...stakers.map(staker => [staker.simulationActions.validMoveStake, 0.3 / stakers.length]),
|
||||
// 20% chance of executing an assertion generated from the pool management simulation
|
||||
[poolManagement.generator, 0.2],
|
||||
]) as [Array<AsyncIterableIterator<AssertionResult | void>>, number[]];
|
||||
|
||||
while (true) {
|
||||
const action = Pseudorandom.sample(actions);
|
||||
const action = Pseudorandom.sample(actions, weights);
|
||||
yield (await action!.next()).value; // tslint:disable-line:no-non-null-assertion
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { blockchainTests } from '@0x/contracts-test-utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { Actor } from '../framework/actors/base';
|
||||
import {
|
||||
@@ -32,15 +33,20 @@ export class StakingRewardsSimulation extends Simulation {
|
||||
const poolMembership = new PoolMembershipSimulation(this.environment);
|
||||
const stakeManagement = new StakeManagementSimulation(this.environment);
|
||||
|
||||
const actions = [
|
||||
...stakers.map(staker => staker.simulationActions.validWithdrawDelegatorRewards),
|
||||
...keepers.map(keeper => keeper.simulationActions.validFinalizePool),
|
||||
...keepers.map(keeper => keeper.simulationActions.validEndEpoch),
|
||||
poolMembership.generator,
|
||||
stakeManagement.generator,
|
||||
];
|
||||
const [actions, weights] = _.unzip([
|
||||
// 10% chance of executing validWithdrawDelegatorRewards for a random staker
|
||||
...stakers.map(staker => [staker.simulationActions.validWithdrawDelegatorRewards, 0.1 / stakers.length]),
|
||||
// 10% chance of executing validFinalizePool for a random keeper
|
||||
...keepers.map(keeper => [keeper.simulationActions.validFinalizePool, 0.1 / keepers.length]),
|
||||
// 10% chance of executing validEndEpoch for a random keeper
|
||||
...keepers.map(keeper => [keeper.simulationActions.validEndEpoch, 0.1 / keepers.length]),
|
||||
// 50% chance of executing an assertion generated from the pool membership simulation
|
||||
[poolMembership.generator, 0.5],
|
||||
// 20% chance of executing an assertion generated from the stake management simulation
|
||||
[stakeManagement.generator, 0.2],
|
||||
]) as [Array<AsyncIterableIterator<AssertionResult | void>>, number[]];
|
||||
while (true) {
|
||||
const action = Pseudorandom.sample(actions);
|
||||
const action = Pseudorandom.sample(actions, weights);
|
||||
yield (await action!.next()).value; // tslint:disable-line:no-non-null-assertion
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"extends": ["@0x/tslint-config"],
|
||||
"rules": {
|
||||
"no-invalid-this": false
|
||||
"no-invalid-this": false,
|
||||
"custom-no-magic-numbers": false
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +1,19 @@
|
||||
import { ContractWrappers } from '@0x/contract-wrappers';
|
||||
import { ERC20ProxyContract, MultiAssetProxyContract } from '@0x/contracts-asset-proxy';
|
||||
import { StakingProxyContract, ZrxVaultContract } from '@0x/contracts-staking';
|
||||
import { blockchainTests, describe, expect } from '@0x/contracts-test-utils';
|
||||
import { AssetProxyId } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
import { contractAddresses, contractWrappers } from './mainnet_fork_utils';
|
||||
import { contractAddresses, getContractwrappers } from './mainnet_fork_utils';
|
||||
|
||||
blockchainTests.fork.resets('Mainnet configs tests', env => {
|
||||
let contractWrappers: ContractWrappers;
|
||||
|
||||
before(async () => {
|
||||
contractWrappers = getContractwrappers(env.provider);
|
||||
});
|
||||
|
||||
blockchainTests.resets.fork('Mainnet configs tests', env => {
|
||||
describe('Exchange', () => {
|
||||
it('should be owned by the ZeroExGovernor ', async () => {
|
||||
const owner = await contractWrappers.exchange.owner().callAsync();
|
||||
|
@@ -1,9 +1,14 @@
|
||||
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||
import { ContractWrappers } from '@0x/contract-wrappers';
|
||||
import { provider } from '@0x/contracts-test-utils';
|
||||
import { Web3ProviderEngine } from '@0x/dev-utils';
|
||||
|
||||
const chainId = 1;
|
||||
const contractAddresses = getContractAddressesForChainOrThrow(chainId);
|
||||
const contractWrappers = new ContractWrappers(provider, { chainId, contractAddresses });
|
||||
export const dydxAccountOwner = '0xeb58c2caa96f39626dcceb74fdbb7a9a8b54ec18';
|
||||
export const contractAddresses = getContractAddressesForChainOrThrow(chainId);
|
||||
|
||||
export { contractAddresses, contractWrappers };
|
||||
/**
|
||||
* Create contract wrappers for the mainnet given a mainnet/forked provider.
|
||||
*/
|
||||
export function getContractwrappers(provider: Web3ProviderEngine): ContractWrappers {
|
||||
return new ContractWrappers(provider, { chainId, contractAddresses });
|
||||
}
|
||||
|
@@ -3,9 +3,11 @@
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../test/generated-wrappers/test_dydx_user';
|
||||
export * from '../test/generated-wrappers/test_eth2_dai';
|
||||
export * from '../test/generated-wrappers/test_eth2_dai_bridge';
|
||||
export * from '../test/generated-wrappers/test_framework';
|
||||
export * from '../test/generated-wrappers/test_mainnet_aggregator_fills';
|
||||
export * from '../test/generated-wrappers/test_uniswap_bridge';
|
||||
export * from '../test/generated-wrappers/test_uniswap_exchange';
|
||||
export * from '../test/generated-wrappers/test_uniswap_exchange_factory';
|
||||
|
@@ -4,9 +4,11 @@
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": [
|
||||
"generated-artifacts/TestFramework.json",
|
||||
"test/generated-artifacts/TestDydxUser.json",
|
||||
"test/generated-artifacts/TestEth2Dai.json",
|
||||
"test/generated-artifacts/TestEth2DaiBridge.json",
|
||||
"test/generated-artifacts/TestFramework.json",
|
||||
"test/generated-artifacts/TestMainnetAggregatorFills.json",
|
||||
"test/generated-artifacts/TestUniswapBridge.json",
|
||||
"test/generated-artifacts/TestUniswapExchange.json",
|
||||
"test/generated-artifacts/TestUniswapExchangeFactory.json"
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1578272714,
|
||||
"version": "4.0.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1576540892,
|
||||
"version": "4.0.2",
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v4.0.3 - _January 6, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.0.2 - _December 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-multisig",
|
||||
"version": "4.0.2",
|
||||
"version": "4.0.3",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -49,18 +49,18 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/multisig/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.0.2",
|
||||
"@0x/contracts-asset-proxy": "^3.0.2",
|
||||
"@0x/contracts-erc20": "^3.0.2",
|
||||
"@0x/contracts-gen": "^2.0.2",
|
||||
"@0x/contracts-test-utils": "^5.0.1",
|
||||
"@0x/contracts-utils": "^4.0.2",
|
||||
"@0x/dev-utils": "^3.0.2",
|
||||
"@0x/sol-compiler": "^4.0.2",
|
||||
"@0x/abi-gen": "^5.0.3",
|
||||
"@0x/contracts-asset-proxy": "^3.1.0",
|
||||
"@0x/contracts-erc20": "^3.0.3",
|
||||
"@0x/contracts-gen": "^2.0.3",
|
||||
"@0x/contracts-test-utils": "^5.1.0",
|
||||
"@0x/contracts-utils": "^4.0.3",
|
||||
"@0x/dev-utils": "^3.1.0",
|
||||
"@0x/sol-compiler": "^4.0.3",
|
||||
"@0x/tslint-config": "^4.0.0",
|
||||
"@0x/types": "^3.1.1",
|
||||
"@0x/utils": "^5.1.1",
|
||||
"@0x/web3-wrapper": "^7.0.2",
|
||||
"@0x/utils": "^5.1.2",
|
||||
"@0x/web3-wrapper": "^7.0.3",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
@@ -78,7 +78,7 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.0.2",
|
||||
"@0x/base-contract": "^6.0.3",
|
||||
"@0x/typescript-typings": "^5.0.1",
|
||||
"ethereum-types": "^3.0.0"
|
||||
},
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1578272714,
|
||||
"version": "2.0.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1576540892,
|
||||
"version": "2.0.2",
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user