Compare commits
11 Commits
@0x/contra
...
protocol@1
Author | SHA1 | Date | |
---|---|---|---|
|
15a508f3ea | ||
|
b3c20ff909 | ||
|
682c07cb73 | ||
|
602605ab4b | ||
|
0eff2548d5 | ||
|
93ee681204 | ||
|
d7bea98075 | ||
|
437a3b048d | ||
|
f55a9454b5 | ||
|
3b03ad0db4 | ||
|
27d679e1f1 |
@@ -1,4 +1,14 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "3.7.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Fix Bancor support of ETH",
|
||||||
|
"pr": 88
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1608105788
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1607485227,
|
"timestamp": 1607485227,
|
||||||
"version": "3.6.9",
|
"version": "3.6.9",
|
||||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v3.7.0 - _December 16, 2020_
|
||||||
|
|
||||||
|
* Fix Bancor support of ETH (#88)
|
||||||
|
|
||||||
## v3.6.9 - _December 9, 2020_
|
## v3.6.9 - _December 9, 2020_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
@@ -21,6 +21,7 @@ pragma solidity ^0.5.9;
|
|||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
||||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||||
@@ -36,6 +37,20 @@ contract BancorBridge is
|
|||||||
struct TransferState {
|
struct TransferState {
|
||||||
address bancorNetworkAddress;
|
address bancorNetworkAddress;
|
||||||
address[] path;
|
address[] path;
|
||||||
|
IEtherToken weth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Bancor ETH pseudo-address.
|
||||||
|
address constant public BANCOR_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||||
|
|
||||||
|
// solhint-disable no-empty-blocks
|
||||||
|
/// @dev Payable fallback to receive ETH from Bancor/WETH.
|
||||||
|
function ()
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
{
|
||||||
|
// Poor man's receive in 0.5.9
|
||||||
|
require(msg.data.length == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of
|
/// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of
|
||||||
@@ -60,7 +75,6 @@ contract BancorBridge is
|
|||||||
{
|
{
|
||||||
// hold variables to get around stack depth limitations
|
// hold variables to get around stack depth limitations
|
||||||
TransferState memory state;
|
TransferState memory state;
|
||||||
|
|
||||||
// Decode the bridge data.
|
// Decode the bridge data.
|
||||||
(
|
(
|
||||||
state.path,
|
state.path,
|
||||||
@@ -68,34 +82,42 @@ contract BancorBridge is
|
|||||||
// solhint-disable indent
|
// solhint-disable indent
|
||||||
) = abi.decode(bridgeData, (address[], address));
|
) = abi.decode(bridgeData, (address[], address));
|
||||||
// solhint-enable indent
|
// solhint-enable indent
|
||||||
|
state.weth = IEtherToken(_getWethAddress());
|
||||||
|
|
||||||
require(state.path.length > 0, "BancorBridge/PATH_MUST_EXIST");
|
require(state.path.length >= 2, "BancorBridge/PATH_LENGTH_MUST_BE_GREATER_THAN_TWO");
|
||||||
// Just transfer the tokens if they're the same.
|
|
||||||
if (state.path[0] == toTokenAddress) {
|
// Grant an allowance to the Bancor Network to spend `fromTokenAddress` token.
|
||||||
LibERC20Token.transfer(state.path[0], to, amount);
|
uint256 fromTokenBalance;
|
||||||
return BRIDGE_SUCCESS;
|
uint256 payableAmount = 0;
|
||||||
|
// If it's ETH in the path then withdraw from WETH
|
||||||
|
// The Bancor path will have ETH as the 0xeee address
|
||||||
|
// Bancor expects to be paid in ETH not WETH
|
||||||
|
if (state.path[0] == BANCOR_ETH_ADDRESS) {
|
||||||
|
fromTokenBalance = state.weth.balanceOf(address(this));
|
||||||
|
state.weth.withdraw(fromTokenBalance);
|
||||||
|
payableAmount = fromTokenBalance;
|
||||||
|
} else {
|
||||||
|
fromTokenBalance = IERC20Token(state.path[0]).balanceOf(address(this));
|
||||||
|
LibERC20Token.approveIfBelow(state.path[0], state.bancorNetworkAddress, fromTokenBalance);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise use Bancor to convert
|
|
||||||
require(state.path.length > 2, "BancorBridge/PATH_LENGTH_MUST_BE_GREATER_THAN_TWO");
|
|
||||||
require(state.path[state.path.length - 1] == toTokenAddress, "BancorBridge/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN");
|
|
||||||
|
|
||||||
// // Grant an allowance to the Bancor Network to spend `fromTokenAddress` token.
|
|
||||||
uint256 fromTokenBalance = IERC20Token(state.path[0]).balanceOf(address(this));
|
|
||||||
LibERC20Token.approveIfBelow(state.path[0], state.bancorNetworkAddress, fromTokenBalance);
|
|
||||||
|
|
||||||
// Convert the tokens
|
// Convert the tokens
|
||||||
uint256 boughtAmount = IBancorNetwork(state.bancorNetworkAddress).convertByPath(
|
uint256 boughtAmount = IBancorNetwork(state.bancorNetworkAddress).convertByPath.value(payableAmount)(
|
||||||
state.path, // path originating with source token and terminating in destination token
|
state.path, // path originating with source token and terminating in destination token
|
||||||
fromTokenBalance, // amount of source token to trade
|
fromTokenBalance, // amount of source token to trade
|
||||||
amount, // minimum amount of destination token expected to receive
|
amount, // minimum amount of destination token expected to receive
|
||||||
to, // beneficiary
|
state.path[state.path.length-1] == BANCOR_ETH_ADDRESS ? address(this) : to, // beneficiary
|
||||||
address(0), // affiliateAccount; no fee paid
|
address(0), // affiliateAccount; no fee paid
|
||||||
0 // affiliateFee; no fee paid
|
0 // affiliateFee; no fee paid
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (state.path[state.path.length-1] == BANCOR_ETH_ADDRESS) {
|
||||||
|
state.weth.deposit.value(boughtAmount)();
|
||||||
|
state.weth.transfer(to, boughtAmount);
|
||||||
|
}
|
||||||
|
|
||||||
emit ERC20BridgeTransfer(
|
emit ERC20BridgeTransfer(
|
||||||
state.path[0], // fromTokenAddress
|
state.path[0] == BANCOR_ETH_ADDRESS ? address(state.weth) : state.path[0],
|
||||||
toTokenAddress,
|
toTokenAddress,
|
||||||
fromTokenBalance,
|
fromTokenBalance,
|
||||||
boughtAmount,
|
boughtAmount,
|
||||||
@@ -118,5 +140,5 @@ contract BancorBridge is
|
|||||||
{
|
{
|
||||||
return LEGACY_WALLET_MAGIC_VALUE;
|
return LEGACY_WALLET_MAGIC_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -56,11 +56,14 @@ contract KyberBridge is
|
|||||||
uint256 constant private KYBER_RATE_BASE = 10 ** 18;
|
uint256 constant private KYBER_RATE_BASE = 10 ** 18;
|
||||||
|
|
||||||
// solhint-disable no-empty-blocks
|
// solhint-disable no-empty-blocks
|
||||||
/// @dev Payable fallback to receive ETH from Kyber.
|
/// @dev Payable fallback to receive ETH from Kyber/WETH.
|
||||||
function ()
|
function ()
|
||||||
external
|
external
|
||||||
payable
|
payable
|
||||||
{}
|
{
|
||||||
|
// Poor man's receive in 0.5.9
|
||||||
|
require(msg.data.length == 0);
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Callback for `IKyberBridge`. Tries to buy `amount` of
|
/// @dev Callback for `IKyberBridge`. Tries to buy `amount` of
|
||||||
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
|
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-asset-proxy",
|
"name": "@0x/contracts-asset-proxy",
|
||||||
"version": "3.6.9",
|
"version": "3.7.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
|
@@ -12,13 +12,11 @@ import { DecodedLogs } from 'ethereum-types';
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { artifacts } from './artifacts';
|
import { artifacts } from './artifacts';
|
||||||
|
|
||||||
import { TestBancorBridgeContract } from './generated-wrappers/test_bancor_bridge';
|
import { TestBancorBridgeContract } from './generated-wrappers/test_bancor_bridge';
|
||||||
import {
|
import {
|
||||||
TestBancorBridgeConvertByPathInputEventArgs as ConvertByPathArgs,
|
TestBancorBridgeConvertByPathInputEventArgs as ConvertByPathArgs,
|
||||||
TestBancorBridgeEvents as ContractEvents,
|
TestBancorBridgeEvents as ContractEvents,
|
||||||
TestBancorBridgeTokenApproveEventArgs as TokenApproveArgs,
|
TestBancorBridgeTokenApproveEventArgs as TokenApproveArgs,
|
||||||
TestBancorBridgeTokenTransferEventArgs as TokenTransferArgs,
|
|
||||||
} from './wrappers';
|
} from './wrappers';
|
||||||
|
|
||||||
blockchainTests.resets('Bancor unit tests', env => {
|
blockchainTests.resets('Bancor unit tests', env => {
|
||||||
@@ -128,24 +126,6 @@ blockchainTests.resets('Bancor unit tests', env => {
|
|||||||
expect(result).to.eq(AssetProxyId.ERC20Bridge);
|
expect(result).to.eq(AssetProxyId.ERC20Bridge);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('performs transfer when both tokens are the same', async () => {
|
|
||||||
const createTokenFn = testContract.createToken(constants.NULL_ADDRESS);
|
|
||||||
const tokenAddress = await createTokenFn.callAsync();
|
|
||||||
await createTokenFn.awaitTransactionSuccessAsync();
|
|
||||||
|
|
||||||
const { opts, result, logs } = await transferFromAsync({
|
|
||||||
tokenAddressesPath: [tokenAddress, tokenAddress],
|
|
||||||
});
|
|
||||||
expect(result).to.eq(AssetProxyId.ERC20Bridge, 'asset proxy id');
|
|
||||||
const transfers = filterLogsToArguments<TokenTransferArgs>(logs, ContractEvents.TokenTransfer);
|
|
||||||
|
|
||||||
expect(transfers.length).to.eq(1);
|
|
||||||
expect(transfers[0].token).to.eq(tokenAddress, 'input token address');
|
|
||||||
expect(transfers[0].from).to.eq(testContract.address);
|
|
||||||
expect(transfers[0].to).to.eq(opts.toAddress, 'recipient address');
|
|
||||||
expect(transfers[0].amount).to.bignumber.eq(opts.amount, 'amount');
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('token -> token', async () => {
|
describe('token -> token', async () => {
|
||||||
it('calls BancorNetwork.convertByPath()', async () => {
|
it('calls BancorNetwork.convertByPath()', async () => {
|
||||||
const { opts, result, logs } = await transferFromAsync();
|
const { opts, result, logs } = await transferFromAsync();
|
||||||
|
@@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1608105788,
|
||||||
|
"version": "1.1.18",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1607485227,
|
"timestamp": 1607485227,
|
||||||
"version": "1.1.17",
|
"version": "1.1.17",
|
||||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v1.1.18 - _December 16, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v1.1.17 - _December 9, 2020_
|
## v1.1.17 - _December 9, 2020_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-broker",
|
"name": "@0x/contracts-broker",
|
||||||
"version": "1.1.17",
|
"version": "1.1.18",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -52,10 +52,10 @@
|
|||||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
|
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.4.13",
|
"@0x/abi-gen": "^5.4.13",
|
||||||
"@0x/contracts-asset-proxy": "^3.6.9",
|
"@0x/contracts-asset-proxy": "^3.7.0",
|
||||||
"@0x/contracts-erc20": "^3.2.12",
|
"@0x/contracts-erc20": "^3.2.12",
|
||||||
"@0x/contracts-erc721": "^3.1.18",
|
"@0x/contracts-erc721": "^3.1.18",
|
||||||
"@0x/contracts-exchange": "^3.2.18",
|
"@0x/contracts-exchange": "^3.2.19",
|
||||||
"@0x/contracts-exchange-libs": "^4.3.18",
|
"@0x/contracts-exchange-libs": "^4.3.18",
|
||||||
"@0x/contracts-gen": "^2.0.24",
|
"@0x/contracts-gen": "^2.0.24",
|
||||||
"@0x/contracts-test-utils": "^5.3.15",
|
"@0x/contracts-test-utils": "^5.3.15",
|
||||||
|
@@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1608105788,
|
||||||
|
"version": "3.1.19",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1607485227,
|
"timestamp": 1607485227,
|
||||||
"version": "3.1.18",
|
"version": "3.1.18",
|
||||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v3.1.19 - _December 16, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v3.1.18 - _December 9, 2020_
|
## v3.1.18 - _December 9, 2020_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-coordinator",
|
"name": "@0x/contracts-coordinator",
|
||||||
"version": "3.1.18",
|
"version": "3.1.19",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -53,8 +53,8 @@
|
|||||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
|
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.4.13",
|
"@0x/abi-gen": "^5.4.13",
|
||||||
"@0x/contracts-asset-proxy": "^3.6.9",
|
"@0x/contracts-asset-proxy": "^3.7.0",
|
||||||
"@0x/contracts-dev-utils": "^1.3.16",
|
"@0x/contracts-dev-utils": "^1.3.17",
|
||||||
"@0x/contracts-erc20": "^3.2.12",
|
"@0x/contracts-erc20": "^3.2.12",
|
||||||
"@0x/contracts-gen": "^2.0.24",
|
"@0x/contracts-gen": "^2.0.24",
|
||||||
"@0x/dev-utils": "^4.1.3",
|
"@0x/dev-utils": "^4.1.3",
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
"@0x/assert": "^3.0.19",
|
"@0x/assert": "^3.0.19",
|
||||||
"@0x/base-contract": "^6.2.14",
|
"@0x/base-contract": "^6.2.14",
|
||||||
"@0x/contract-addresses": "^5.6.0",
|
"@0x/contract-addresses": "^5.6.0",
|
||||||
"@0x/contracts-exchange": "^3.2.18",
|
"@0x/contracts-exchange": "^3.2.19",
|
||||||
"@0x/contracts-test-utils": "^5.3.15",
|
"@0x/contracts-test-utils": "^5.3.15",
|
||||||
"@0x/contracts-utils": "^4.6.3",
|
"@0x/contracts-utils": "^4.6.3",
|
||||||
"@0x/json-schemas": "^5.3.4",
|
"@0x/json-schemas": "^5.3.4",
|
||||||
|
@@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1608105788,
|
||||||
|
"version": "1.3.17",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1607485227,
|
"timestamp": 1607485227,
|
||||||
"version": "1.3.16",
|
"version": "1.3.16",
|
||||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v1.3.17 - _December 16, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v1.3.16 - _December 9, 2020_
|
## v1.3.16 - _December 9, 2020_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-dev-utils",
|
"name": "@0x/contracts-dev-utils",
|
||||||
"version": "1.3.16",
|
"version": "1.3.17",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.4.13",
|
"@0x/abi-gen": "^5.4.13",
|
||||||
"@0x/assert": "^3.0.19",
|
"@0x/assert": "^3.0.19",
|
||||||
"@0x/contracts-asset-proxy": "^3.6.9",
|
"@0x/contracts-asset-proxy": "^3.7.0",
|
||||||
"@0x/contracts-erc20": "^3.2.12",
|
"@0x/contracts-erc20": "^3.2.12",
|
||||||
"@0x/contracts-gen": "^2.0.24",
|
"@0x/contracts-gen": "^2.0.24",
|
||||||
"@0x/contracts-test-utils": "^5.3.15",
|
"@0x/contracts-test-utils": "^5.3.15",
|
||||||
|
@@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1608105788,
|
||||||
|
"version": "4.2.19",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1607485227,
|
"timestamp": 1607485227,
|
||||||
"version": "4.2.18",
|
"version": "4.2.18",
|
||||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v4.2.19 - _December 16, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v4.2.18 - _December 9, 2020_
|
## v4.2.18 - _December 9, 2020_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-exchange-forwarder",
|
"name": "@0x/contracts-exchange-forwarder",
|
||||||
"version": "4.2.18",
|
"version": "4.2.19",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -53,12 +53,12 @@
|
|||||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
|
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.4.13",
|
"@0x/abi-gen": "^5.4.13",
|
||||||
"@0x/contracts-asset-proxy": "^3.6.9",
|
"@0x/contracts-asset-proxy": "^3.7.0",
|
||||||
"@0x/contracts-dev-utils": "^1.3.16",
|
"@0x/contracts-dev-utils": "^1.3.17",
|
||||||
"@0x/contracts-erc1155": "^2.1.18",
|
"@0x/contracts-erc1155": "^2.1.18",
|
||||||
"@0x/contracts-erc20": "^3.2.12",
|
"@0x/contracts-erc20": "^3.2.12",
|
||||||
"@0x/contracts-erc721": "^3.1.18",
|
"@0x/contracts-erc721": "^3.1.18",
|
||||||
"@0x/contracts-exchange": "^3.2.18",
|
"@0x/contracts-exchange": "^3.2.19",
|
||||||
"@0x/contracts-exchange-libs": "^4.3.18",
|
"@0x/contracts-exchange-libs": "^4.3.18",
|
||||||
"@0x/contracts-gen": "^2.0.24",
|
"@0x/contracts-gen": "^2.0.24",
|
||||||
"@0x/contracts-test-utils": "^5.3.15",
|
"@0x/contracts-test-utils": "^5.3.15",
|
||||||
|
@@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1608105788,
|
||||||
|
"version": "3.2.19",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1607485227,
|
"timestamp": 1607485227,
|
||||||
"version": "3.2.18",
|
"version": "3.2.18",
|
||||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v3.2.19 - _December 16, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v3.2.18 - _December 9, 2020_
|
## v3.2.18 - _December 9, 2020_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-exchange",
|
"name": "@0x/contracts-exchange",
|
||||||
"version": "3.2.18",
|
"version": "3.2.19",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -53,11 +53,11 @@
|
|||||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/protocol",
|
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/protocol",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.4.13",
|
"@0x/abi-gen": "^5.4.13",
|
||||||
"@0x/contracts-asset-proxy": "^3.6.9",
|
"@0x/contracts-asset-proxy": "^3.7.0",
|
||||||
"@0x/contracts-exchange-libs": "^4.3.18",
|
"@0x/contracts-exchange-libs": "^4.3.18",
|
||||||
"@0x/contracts-gen": "^2.0.24",
|
"@0x/contracts-gen": "^2.0.24",
|
||||||
"@0x/contracts-multisig": "^4.1.18",
|
"@0x/contracts-multisig": "^4.1.19",
|
||||||
"@0x/contracts-staking": "^2.0.25",
|
"@0x/contracts-staking": "^2.0.26",
|
||||||
"@0x/contracts-test-utils": "^5.3.15",
|
"@0x/contracts-test-utils": "^5.3.15",
|
||||||
"@0x/contracts-utils": "^4.6.3",
|
"@0x/contracts-utils": "^4.6.3",
|
||||||
"@0x/dev-utils": "^4.1.3",
|
"@0x/dev-utils": "^4.1.3",
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^6.2.14",
|
"@0x/base-contract": "^6.2.14",
|
||||||
"@0x/contracts-dev-utils": "^1.3.16",
|
"@0x/contracts-dev-utils": "^1.3.17",
|
||||||
"@0x/contracts-erc1155": "^2.1.18",
|
"@0x/contracts-erc1155": "^2.1.18",
|
||||||
"@0x/contracts-erc20": "^3.2.12",
|
"@0x/contracts-erc20": "^3.2.12",
|
||||||
"@0x/contracts-erc721": "^3.1.18",
|
"@0x/contracts-erc721": "^3.1.18",
|
||||||
|
@@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1608105788,
|
||||||
|
"version": "6.2.13",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1607485227,
|
"timestamp": 1607485227,
|
||||||
"version": "6.2.12",
|
"version": "6.2.12",
|
||||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v6.2.13 - _December 16, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v6.2.12 - _December 9, 2020_
|
## v6.2.12 - _December 9, 2020_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-extensions",
|
"name": "@0x/contracts-extensions",
|
||||||
"version": "6.2.12",
|
"version": "6.2.13",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -53,11 +53,11 @@
|
|||||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
|
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.4.13",
|
"@0x/abi-gen": "^5.4.13",
|
||||||
"@0x/contracts-asset-proxy": "^3.6.9",
|
"@0x/contracts-asset-proxy": "^3.7.0",
|
||||||
"@0x/contracts-dev-utils": "^1.3.16",
|
"@0x/contracts-dev-utils": "^1.3.17",
|
||||||
"@0x/contracts-erc20": "^3.2.12",
|
"@0x/contracts-erc20": "^3.2.12",
|
||||||
"@0x/contracts-erc721": "^3.1.18",
|
"@0x/contracts-erc721": "^3.1.18",
|
||||||
"@0x/contracts-exchange": "^3.2.18",
|
"@0x/contracts-exchange": "^3.2.19",
|
||||||
"@0x/contracts-exchange-libs": "^4.3.18",
|
"@0x/contracts-exchange-libs": "^4.3.18",
|
||||||
"@0x/contracts-gen": "^2.0.24",
|
"@0x/contracts-gen": "^2.0.24",
|
||||||
"@0x/contracts-utils": "^4.6.3",
|
"@0x/contracts-utils": "^4.6.3",
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-integrations",
|
"name": "@0x/contracts-integrations",
|
||||||
"version": "2.7.15",
|
"version": "2.7.17",
|
||||||
"private": true,
|
"private": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
@@ -55,19 +55,19 @@
|
|||||||
"@0x/abi-gen": "^5.4.13",
|
"@0x/abi-gen": "^5.4.13",
|
||||||
"@0x/contract-addresses": "^5.6.0",
|
"@0x/contract-addresses": "^5.6.0",
|
||||||
"@0x/contract-wrappers": "^13.11.0",
|
"@0x/contract-wrappers": "^13.11.0",
|
||||||
"@0x/contracts-broker": "^1.1.17",
|
"@0x/contracts-broker": "^1.1.18",
|
||||||
"@0x/contracts-coordinator": "^3.1.18",
|
"@0x/contracts-coordinator": "^3.1.19",
|
||||||
"@0x/contracts-dev-utils": "^1.3.16",
|
"@0x/contracts-dev-utils": "^1.3.17",
|
||||||
"@0x/contracts-exchange-forwarder": "^4.2.18",
|
"@0x/contracts-exchange-forwarder": "^4.2.19",
|
||||||
"@0x/contracts-exchange-libs": "^4.3.18",
|
"@0x/contracts-exchange-libs": "^4.3.18",
|
||||||
"@0x/contracts-extensions": "^6.2.12",
|
"@0x/contracts-extensions": "^6.2.13",
|
||||||
"@0x/contracts-gen": "^2.0.24",
|
"@0x/contracts-gen": "^2.0.24",
|
||||||
"@0x/contracts-utils": "^4.6.3",
|
"@0x/contracts-utils": "^4.6.3",
|
||||||
"@0x/coordinator-server": "^1.0.5",
|
"@0x/coordinator-server": "^1.0.5",
|
||||||
"@0x/dev-utils": "^4.1.3",
|
"@0x/dev-utils": "^4.1.3",
|
||||||
"@0x/migrations": "^6.5.4",
|
"@0x/migrations": "^6.5.6",
|
||||||
"@0x/order-utils": "^10.4.10",
|
"@0x/order-utils": "^10.4.10",
|
||||||
"@0x/protocol-utils": "^1.0.1",
|
"@0x/protocol-utils": "^1.1.0",
|
||||||
"@0x/sol-compiler": "^4.4.1",
|
"@0x/sol-compiler": "^4.4.1",
|
||||||
"@0x/tslint-config": "^4.1.3",
|
"@0x/tslint-config": "^4.1.3",
|
||||||
"@0x/web3-wrapper": "^7.3.0",
|
"@0x/web3-wrapper": "^7.3.0",
|
||||||
@@ -93,17 +93,17 @@
|
|||||||
"typescript": "3.0.1"
|
"typescript": "3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/asset-swapper": "^5.4.2",
|
"@0x/asset-swapper": "^5.5.1",
|
||||||
"@0x/base-contract": "^6.2.14",
|
"@0x/base-contract": "^6.2.14",
|
||||||
"@0x/contracts-asset-proxy": "^3.6.9",
|
"@0x/contracts-asset-proxy": "^3.7.0",
|
||||||
"@0x/contracts-erc1155": "^2.1.18",
|
"@0x/contracts-erc1155": "^2.1.18",
|
||||||
"@0x/contracts-erc20": "^3.2.12",
|
"@0x/contracts-erc20": "^3.2.12",
|
||||||
"@0x/contracts-erc721": "^3.1.18",
|
"@0x/contracts-erc721": "^3.1.18",
|
||||||
"@0x/contracts-exchange": "^3.2.18",
|
"@0x/contracts-exchange": "^3.2.19",
|
||||||
"@0x/contracts-multisig": "^4.1.18",
|
"@0x/contracts-multisig": "^4.1.19",
|
||||||
"@0x/contracts-staking": "^2.0.25",
|
"@0x/contracts-staking": "^2.0.26",
|
||||||
"@0x/contracts-test-utils": "^5.3.15",
|
"@0x/contracts-test-utils": "^5.3.15",
|
||||||
"@0x/contracts-zero-ex": "^0.12.0",
|
"@0x/contracts-zero-ex": "^0.14.0",
|
||||||
"@0x/subproviders": "^6.2.3",
|
"@0x/subproviders": "^6.2.3",
|
||||||
"@0x/types": "^3.3.1",
|
"@0x/types": "^3.3.1",
|
||||||
"@0x/typescript-typings": "^5.1.6",
|
"@0x/typescript-typings": "^5.1.6",
|
||||||
|
@@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1608105788,
|
||||||
|
"version": "4.1.19",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1607485227,
|
"timestamp": 1607485227,
|
||||||
"version": "4.1.18",
|
"version": "4.1.18",
|
||||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v4.1.19 - _December 16, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v4.1.18 - _December 9, 2020_
|
## v4.1.18 - _December 9, 2020_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-multisig",
|
"name": "@0x/contracts-multisig",
|
||||||
"version": "4.1.18",
|
"version": "4.1.19",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/multisig",
|
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/multisig",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.4.13",
|
"@0x/abi-gen": "^5.4.13",
|
||||||
"@0x/contracts-asset-proxy": "^3.6.9",
|
"@0x/contracts-asset-proxy": "^3.7.0",
|
||||||
"@0x/contracts-erc20": "^3.2.12",
|
"@0x/contracts-erc20": "^3.2.12",
|
||||||
"@0x/contracts-gen": "^2.0.24",
|
"@0x/contracts-gen": "^2.0.24",
|
||||||
"@0x/contracts-test-utils": "^5.3.15",
|
"@0x/contracts-test-utils": "^5.3.15",
|
||||||
|
@@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1608105788,
|
||||||
|
"version": "2.0.26",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1607485227,
|
"timestamp": 1607485227,
|
||||||
"version": "2.0.25",
|
"version": "2.0.25",
|
||||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v2.0.26 - _December 16, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v2.0.25 - _December 9, 2020_
|
## v2.0.25 - _December 9, 2020_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-staking",
|
"name": "@0x/contracts-staking",
|
||||||
"version": "2.0.25",
|
"version": "2.0.26",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -54,8 +54,8 @@
|
|||||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens",
|
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.4.13",
|
"@0x/abi-gen": "^5.4.13",
|
||||||
"@0x/contracts-asset-proxy": "^3.6.9",
|
"@0x/contracts-asset-proxy": "^3.7.0",
|
||||||
"@0x/contracts-dev-utils": "^1.3.16",
|
"@0x/contracts-dev-utils": "^1.3.17",
|
||||||
"@0x/contracts-erc20": "^3.2.12",
|
"@0x/contracts-erc20": "^3.2.12",
|
||||||
"@0x/contracts-exchange-libs": "^4.3.18",
|
"@0x/contracts-exchange-libs": "^4.3.18",
|
||||||
"@0x/contracts-gen": "^2.0.24",
|
"@0x/contracts-gen": "^2.0.24",
|
||||||
|
@@ -8,3 +8,4 @@
|
|||||||
# Blacklist tests in lib
|
# Blacklist tests in lib
|
||||||
/lib/test/*
|
/lib/test/*
|
||||||
# Package specific ignore
|
# Package specific ignore
|
||||||
|
/lib/scripts/*
|
||||||
|
@@ -1,4 +1,32 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "0.14.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Use the `MetaTransaction` class from `@0x/protocol-utils` in tests.",
|
||||||
|
"pr": 90
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1608149382
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "0.13.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Address audit feedback in UniswapFeature",
|
||||||
|
"pr": 82
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Always transfer `msg.value` to the liquidity provider contract in LiquidityProviderFeature to",
|
||||||
|
"pr": 82
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Remove backwards compatibility with old PLP/bridge interface in `LiquidityProviderFeature` and `MixinZeroExBridge`",
|
||||||
|
"pr": 85
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1608105788
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "0.12.0",
|
"version": "0.12.0",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@@ -5,6 +5,16 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v0.14.0 - _December 16, 2020_
|
||||||
|
|
||||||
|
* Use the `MetaTransaction` class from `@0x/protocol-utils` in tests. (#90)
|
||||||
|
|
||||||
|
## v0.13.0 - _December 16, 2020_
|
||||||
|
|
||||||
|
* Address audit feedback in UniswapFeature (#82)
|
||||||
|
* Always transfer `msg.value` to the liquidity provider contract in LiquidityProviderFeature to (#82)
|
||||||
|
* Remove backwards compatibility with old PLP/bridge interface in `LiquidityProviderFeature` and `MixinZeroExBridge` (#85)
|
||||||
|
|
||||||
## v0.12.0 - _December 9, 2020_
|
## v0.12.0 - _December 9, 2020_
|
||||||
|
|
||||||
* Add test for selector collisions on the proxy (#74)
|
* Add test for selector collisions on the proxy (#74)
|
||||||
|
@@ -57,7 +57,6 @@ contract FeeCollector is AuthorizableV06 {
|
|||||||
external
|
external
|
||||||
onlyAuthorized
|
onlyAuthorized
|
||||||
{
|
{
|
||||||
// Leave 1 wei behind to avoid expensive zero-->non-zero state change.
|
|
||||||
if (address(this).balance > 0) {
|
if (address(this).balance > 0) {
|
||||||
weth.deposit{value: address(this).balance}();
|
weth.deposit{value: address(this).balance}();
|
||||||
}
|
}
|
||||||
|
@@ -68,21 +68,13 @@ contract LiquidityProviderSandbox is
|
|||||||
onlyOwner
|
onlyOwner
|
||||||
override
|
override
|
||||||
{
|
{
|
||||||
try ILiquidityProvider(provider).sellTokenForToken(
|
ILiquidityProvider(provider).sellTokenForToken(
|
||||||
inputToken,
|
inputToken,
|
||||||
outputToken,
|
outputToken,
|
||||||
recipient,
|
recipient,
|
||||||
minBuyAmount,
|
minBuyAmount,
|
||||||
auxiliaryData
|
auxiliaryData
|
||||||
) {} catch {
|
);
|
||||||
IERC20Bridge(provider).bridgeTransferFrom(
|
|
||||||
outputToken,
|
|
||||||
provider,
|
|
||||||
recipient,
|
|
||||||
minBuyAmount,
|
|
||||||
auxiliaryData
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Calls `sellEthForToken` on the given `provider` contract to
|
/// @dev Calls `sellEthForToken` on the given `provider` contract to
|
||||||
|
@@ -44,7 +44,7 @@ contract LiquidityProviderFeature is
|
|||||||
/// @dev Name of this feature.
|
/// @dev Name of this feature.
|
||||||
string public constant override FEATURE_NAME = "LiquidityProviderFeature";
|
string public constant override FEATURE_NAME = "LiquidityProviderFeature";
|
||||||
/// @dev Version of this feature.
|
/// @dev Version of this feature.
|
||||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 1);
|
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 2);
|
||||||
|
|
||||||
/// @dev ETH pseudo-token address.
|
/// @dev ETH pseudo-token address.
|
||||||
address constant internal ETH_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
address constant internal ETH_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||||
@@ -111,9 +111,13 @@ contract LiquidityProviderFeature is
|
|||||||
recipient = msg.sender;
|
recipient = msg.sender;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputToken == ETH_TOKEN_ADDRESS) {
|
// Forward all attached ETH to the provider.
|
||||||
provider.transfer(sellAmount);
|
if (msg.value > 0) {
|
||||||
} else {
|
provider.transfer(msg.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputToken != ETH_TOKEN_ADDRESS) {
|
||||||
|
// Transfer input ERC20 tokens to the provider.
|
||||||
_transferERC20Tokens(
|
_transferERC20Tokens(
|
||||||
IERC20TokenV06(inputToken),
|
IERC20TokenV06(inputToken),
|
||||||
msg.sender,
|
msg.sender,
|
||||||
|
@@ -37,7 +37,7 @@ contract UniswapFeature is
|
|||||||
/// @dev Name of this feature.
|
/// @dev Name of this feature.
|
||||||
string public constant override FEATURE_NAME = "UniswapFeature";
|
string public constant override FEATURE_NAME = "UniswapFeature";
|
||||||
/// @dev Version of this feature.
|
/// @dev Version of this feature.
|
||||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 0);
|
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 1);
|
||||||
/// @dev A bloom filter for tokens that consume all gas when `transferFrom()` fails.
|
/// @dev A bloom filter for tokens that consume all gas when `transferFrom()` fails.
|
||||||
bytes32 public immutable GREEDY_TOKENS_BLOOM_FILTER;
|
bytes32 public immutable GREEDY_TOKENS_BLOOM_FILTER;
|
||||||
/// @dev WETH contract.
|
/// @dev WETH contract.
|
||||||
@@ -167,8 +167,13 @@ contract UniswapFeature is
|
|||||||
}
|
}
|
||||||
|
|
||||||
if iszero(i) {
|
if iszero(i) {
|
||||||
|
// This is the first token in the path.
|
||||||
switch eq(sellToken, ETH_TOKEN_ADDRESS_32)
|
switch eq(sellToken, ETH_TOKEN_ADDRESS_32)
|
||||||
case 0 { // Not selling ETH. Selling an ERC20 instead.
|
case 0 { // Not selling ETH. Selling an ERC20 instead.
|
||||||
|
// Make sure ETH was not attached to the call.
|
||||||
|
if gt(callvalue(), 0) {
|
||||||
|
revert(0, 0)
|
||||||
|
}
|
||||||
// For the first pair we need to transfer sellTokens into the
|
// For the first pair we need to transfer sellTokens into the
|
||||||
// pair contract.
|
// pair contract.
|
||||||
moveTakerTokensTo(sellToken, pair, sellAmount)
|
moveTakerTokensTo(sellToken, pair, sellAmount)
|
||||||
@@ -203,6 +208,10 @@ contract UniswapFeature is
|
|||||||
if iszero(staticcall(gas(), pair, 0xB00, 0x4, 0xC00, 0x40)) {
|
if iszero(staticcall(gas(), pair, 0xB00, 0x4, 0xC00, 0x40)) {
|
||||||
bubbleRevert()
|
bubbleRevert()
|
||||||
}
|
}
|
||||||
|
// Revert if the pair contract does not return two words.
|
||||||
|
if iszero(eq(returndatasize(), 0x40)) {
|
||||||
|
revert(0,0)
|
||||||
|
}
|
||||||
|
|
||||||
// Sell amount for this hop is the previous buy amount.
|
// Sell amount for this hop is the previous buy amount.
|
||||||
let pairSellAmount := buyAmount
|
let pairSellAmount := buyAmount
|
||||||
@@ -374,11 +383,15 @@ contract UniswapFeature is
|
|||||||
mstore(0xB00, ALLOWANCE_CALL_SELECTOR_32)
|
mstore(0xB00, ALLOWANCE_CALL_SELECTOR_32)
|
||||||
mstore(0xB04, caller())
|
mstore(0xB04, caller())
|
||||||
mstore(0xB24, address())
|
mstore(0xB24, address())
|
||||||
let success := call(gas(), token, 0, 0xB00, 0x44, 0xC00, 0x20)
|
let success := staticcall(gas(), token, 0xB00, 0x44, 0xC00, 0x20)
|
||||||
if iszero(success) {
|
if iszero(success) {
|
||||||
// Call to allowance() failed.
|
// Call to allowance() failed.
|
||||||
bubbleRevert()
|
bubbleRevert()
|
||||||
}
|
}
|
||||||
|
// Make sure the allowance call returned a single word.
|
||||||
|
if iszero(eq(returndatasize(), 0x20)) {
|
||||||
|
revert(0, 0)
|
||||||
|
}
|
||||||
// Call succeeded.
|
// Call succeeded.
|
||||||
// Result is stored in 0xC00-0xC20.
|
// Result is stored in 0xC00-0xC20.
|
||||||
if lt(mload(0xC00), amount) {
|
if lt(mload(0xC00), amount) {
|
||||||
@@ -397,8 +410,6 @@ contract UniswapFeature is
|
|||||||
mstore(0xB44, amount)
|
mstore(0xB44, amount)
|
||||||
|
|
||||||
let success := call(
|
let success := call(
|
||||||
// Cap the gas limit to prvent all gas being consumed
|
|
||||||
// if the token reverts.
|
|
||||||
gas(),
|
gas(),
|
||||||
token,
|
token,
|
||||||
0,
|
0,
|
||||||
|
@@ -22,7 +22,6 @@ import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
|||||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||||
import "../../../vendor/ILiquidityProvider.sol";
|
import "../../../vendor/ILiquidityProvider.sol";
|
||||||
import "../../../vendor/v3/IERC20Bridge.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract MixinZeroExBridge {
|
contract MixinZeroExBridge {
|
||||||
@@ -61,32 +60,20 @@ contract MixinZeroExBridge {
|
|||||||
bridgeAddress,
|
bridgeAddress,
|
||||||
sellAmount
|
sellAmount
|
||||||
);
|
);
|
||||||
try ILiquidityProvider(bridgeAddress).sellTokenForToken(
|
boughtAmount = ILiquidityProvider(bridgeAddress).sellTokenForToken(
|
||||||
address(sellToken),
|
address(sellToken),
|
||||||
address(buyToken),
|
address(buyToken),
|
||||||
address(this), // recipient
|
address(this), // recipient
|
||||||
1, // minBuyAmount
|
1, // minBuyAmount
|
||||||
bridgeData
|
bridgeData
|
||||||
) returns (uint256 _boughtAmount) {
|
);
|
||||||
boughtAmount = _boughtAmount;
|
emit ERC20BridgeTransfer(
|
||||||
emit ERC20BridgeTransfer(
|
sellToken,
|
||||||
sellToken,
|
buyToken,
|
||||||
buyToken,
|
sellAmount,
|
||||||
sellAmount,
|
boughtAmount,
|
||||||
boughtAmount,
|
bridgeAddress,
|
||||||
bridgeAddress,
|
address(this)
|
||||||
address(this)
|
);
|
||||||
);
|
|
||||||
} catch {
|
|
||||||
uint256 balanceBefore = buyToken.balanceOf(address(this));
|
|
||||||
IERC20Bridge(bridgeAddress).bridgeTransferFrom(
|
|
||||||
address(buyToken),
|
|
||||||
bridgeAddress,
|
|
||||||
address(this), // recipient
|
|
||||||
1, // minBuyAmount
|
|
||||||
bridgeData
|
|
||||||
);
|
|
||||||
boughtAmount = buyToken.balanceOf(address(this)).safeSub(balanceBefore);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-zero-ex",
|
"name": "@0x/contracts-zero-ex",
|
||||||
"version": "0.12.0",
|
"version": "0.14.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -37,7 +37,8 @@
|
|||||||
"compile:truffle": "truffle compile",
|
"compile:truffle": "truffle compile",
|
||||||
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
|
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
|
||||||
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES",
|
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES",
|
||||||
"publish:private": "yarn build && gitpkg publish"
|
"publish:private": "yarn build && gitpkg publish",
|
||||||
|
"rollback": "node ./lib/scripts/rollback.js"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IAllowanceTarget,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITokenSpenderFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,TokenSpenderFeature,AffiliateFeeTransformer,SignatureValidatorFeature,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector",
|
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IAllowanceTarget,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITokenSpenderFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,TokenSpenderFeature,AffiliateFeeTransformer,SignatureValidatorFeature,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector",
|
||||||
@@ -55,18 +56,24 @@
|
|||||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/zero-ex",
|
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/zero-ex",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.4.13",
|
"@0x/abi-gen": "^5.4.13",
|
||||||
|
"@0x/contract-addresses": "^5.6.0",
|
||||||
"@0x/contracts-erc20": "^3.2.12",
|
"@0x/contracts-erc20": "^3.2.12",
|
||||||
"@0x/contracts-gen": "^2.0.24",
|
"@0x/contracts-gen": "^2.0.24",
|
||||||
"@0x/contracts-test-utils": "^5.3.15",
|
"@0x/contracts-test-utils": "^5.3.15",
|
||||||
"@0x/dev-utils": "^4.1.3",
|
"@0x/dev-utils": "^4.1.3",
|
||||||
|
"@0x/order-utils": "^10.4.10",
|
||||||
"@0x/sol-compiler": "^4.4.1",
|
"@0x/sol-compiler": "^4.4.1",
|
||||||
"@0x/ts-doc-gen": "^0.0.28",
|
"@0x/ts-doc-gen": "^0.0.28",
|
||||||
"@0x/tslint-config": "^4.1.3",
|
"@0x/tslint-config": "^4.1.3",
|
||||||
|
"@types/isomorphic-fetch": "^0.0.35",
|
||||||
"@types/lodash": "4.14.104",
|
"@types/lodash": "4.14.104",
|
||||||
"@types/mocha": "^5.2.7",
|
"@types/mocha": "^5.2.7",
|
||||||
|
"@types/prompts": "^2.0.9",
|
||||||
|
"isomorphic-fetch": "^3.0.0",
|
||||||
"lodash": "^4.17.11",
|
"lodash": "^4.17.11",
|
||||||
"mocha": "^6.2.0",
|
"mocha": "^6.2.0",
|
||||||
"npm-run-all": "^4.1.2",
|
"npm-run-all": "^4.1.2",
|
||||||
|
"prompts": "^2.4.0",
|
||||||
"shx": "^0.2.2",
|
"shx": "^0.2.2",
|
||||||
"solhint": "^1.4.1",
|
"solhint": "^1.4.1",
|
||||||
"truffle": "^5.0.32",
|
"truffle": "^5.0.32",
|
||||||
@@ -76,8 +83,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^6.2.14",
|
"@0x/base-contract": "^6.2.14",
|
||||||
"@0x/order-utils": "^10.4.10",
|
"@0x/protocol-utils": "^1.1.0",
|
||||||
"@0x/protocol-utils": "^1.0.1",
|
|
||||||
"@0x/subproviders": "^6.2.3",
|
"@0x/subproviders": "^6.2.3",
|
||||||
"@0x/types": "^3.3.1",
|
"@0x/types": "^3.3.1",
|
||||||
"@0x/typescript-typings": "^5.1.6",
|
"@0x/typescript-typings": "^5.1.6",
|
||||||
|
420
contracts/zero-ex/scripts/rollback.ts
Normal file
420
contracts/zero-ex/scripts/rollback.ts
Normal file
@@ -0,0 +1,420 @@
|
|||||||
|
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||||
|
import { constants } from '@0x/contracts-test-utils';
|
||||||
|
import { RPCSubprovider, SupportedProvider, Web3ProviderEngine } from '@0x/subproviders';
|
||||||
|
import { AbiEncoder, BigNumber, logUtils, providerUtils } from '@0x/utils';
|
||||||
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||||
|
import { MethodAbi } from 'ethereum-types';
|
||||||
|
import * as fetch from 'isomorphic-fetch';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import * as prompts from 'prompts';
|
||||||
|
|
||||||
|
import * as wrappers from '../src/wrappers';
|
||||||
|
|
||||||
|
const SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/mzhu25/zeroex-migrations';
|
||||||
|
|
||||||
|
const ownableFeature = new wrappers.OwnableFeatureContract(constants.NULL_ADDRESS, new Web3ProviderEngine());
|
||||||
|
const simpleFunctionRegistryFeature = new wrappers.SimpleFunctionRegistryFeatureContract(
|
||||||
|
constants.NULL_ADDRESS,
|
||||||
|
new Web3ProviderEngine(),
|
||||||
|
);
|
||||||
|
const DO_NOT_ROLLBACK = [
|
||||||
|
ownableFeature.getSelector('migrate'),
|
||||||
|
ownableFeature.getSelector('transferOwnership'),
|
||||||
|
simpleFunctionRegistryFeature.getSelector('rollback'),
|
||||||
|
simpleFunctionRegistryFeature.getSelector('extend'),
|
||||||
|
];
|
||||||
|
|
||||||
|
const governorEncoder = AbiEncoder.create('(bytes[], address[], uint256[])');
|
||||||
|
|
||||||
|
const selectorToSignature: { [selector: string]: string } = {};
|
||||||
|
for (const wrapper of Object.values(wrappers)) {
|
||||||
|
if (typeof wrapper === 'function') {
|
||||||
|
const contract = new wrapper(constants.NULL_ADDRESS, new Web3ProviderEngine());
|
||||||
|
contract.abi
|
||||||
|
.filter(abiDef => abiDef.type === 'function')
|
||||||
|
.map(method => {
|
||||||
|
const methodName = (method as MethodAbi).name;
|
||||||
|
const selector = contract.getSelector(methodName);
|
||||||
|
const signature = contract.getFunctionSignature(methodName);
|
||||||
|
selectorToSignature[selector] = signature;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProxyFunctionEntity {
|
||||||
|
id: string;
|
||||||
|
currentImpl: string;
|
||||||
|
fullHistory: Array<{ impl: string; timestamp: string }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Deployment {
|
||||||
|
time: string;
|
||||||
|
updates: Array<{ selector: string; signature?: string; previousImpl: string; newImpl: string }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function querySubgraphAsync(): Promise<ProxyFunctionEntity[]> {
|
||||||
|
const query = `
|
||||||
|
{
|
||||||
|
proxyFunctions {
|
||||||
|
id
|
||||||
|
currentImpl
|
||||||
|
fullHistory {
|
||||||
|
impl
|
||||||
|
timestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const response = await fetch(SUBGRAPH_URL, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
query,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const {
|
||||||
|
data: { proxyFunctions },
|
||||||
|
} = await response.json();
|
||||||
|
// Sort the history in chronological order
|
||||||
|
proxyFunctions.map((fn: ProxyFunctionEntity) =>
|
||||||
|
fn.fullHistory.sort((a, b) => Number.parseInt(a.timestamp, 10) - Number.parseInt(b.timestamp, 10)),
|
||||||
|
);
|
||||||
|
return proxyFunctions;
|
||||||
|
}
|
||||||
|
|
||||||
|
function reconstructDeployments(proxyFunctions: ProxyFunctionEntity[]): Deployment[] {
|
||||||
|
const deploymentsByTimestamp: { [timestamp: string]: Deployment } = {};
|
||||||
|
proxyFunctions.map(fn => {
|
||||||
|
fn.fullHistory.map((update, i) => {
|
||||||
|
const { updates } = (deploymentsByTimestamp[update.timestamp] = deploymentsByTimestamp[
|
||||||
|
update.timestamp
|
||||||
|
] || { time: timestampToUTC(update.timestamp), updates: [] });
|
||||||
|
updates.push({
|
||||||
|
selector: fn.id,
|
||||||
|
signature: selectorToSignature[fn.id],
|
||||||
|
previousImpl: i > 0 ? fn.fullHistory[i - 1].impl : constants.NULL_ADDRESS,
|
||||||
|
newImpl: update.impl,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return Object.keys(deploymentsByTimestamp)
|
||||||
|
.sort()
|
||||||
|
.map(timestamp => deploymentsByTimestamp[timestamp]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function timestampToUTC(timestamp: string): string {
|
||||||
|
return new Date(Number.parseInt(timestamp, 10) * 1000).toUTCString();
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CommandLineActions {
|
||||||
|
History = 'History',
|
||||||
|
Function = 'Function',
|
||||||
|
Current = 'Current',
|
||||||
|
Rollback = 'Rollback',
|
||||||
|
Emergency = 'Emergency',
|
||||||
|
Exit = 'Exit',
|
||||||
|
}
|
||||||
|
|
||||||
|
async function confirmRollbackAsync(
|
||||||
|
rollbackTargets: { [selector: string]: string },
|
||||||
|
proxyFunctions: ProxyFunctionEntity[],
|
||||||
|
): Promise<boolean> {
|
||||||
|
const { confirmed } = await prompts({
|
||||||
|
type: 'confirm',
|
||||||
|
name: 'confirmed',
|
||||||
|
message: `Are these the correct rollbacks?\n${Object.entries(rollbackTargets)
|
||||||
|
.map(
|
||||||
|
([selector, target]) =>
|
||||||
|
`[${selector}] ${selectorToSignature[selector] || '(function signature not found)'} \n ${
|
||||||
|
proxyFunctions.find(fn => fn.id === selector)!.currentImpl
|
||||||
|
} => ${target}`,
|
||||||
|
)
|
||||||
|
.join('\n')}`,
|
||||||
|
});
|
||||||
|
return confirmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function printRollbackCalldataAsync(
|
||||||
|
rollbackTargets: { [selector: string]: string },
|
||||||
|
zeroEx: wrappers.IZeroExContract,
|
||||||
|
): Promise<void> {
|
||||||
|
const numRollbacks = Object.keys(rollbackTargets).length;
|
||||||
|
const { numTxns } = await prompts({
|
||||||
|
type: 'number',
|
||||||
|
name: 'numTxns',
|
||||||
|
message:
|
||||||
|
'To avoid limitations on calldata size, the full rollback can be split into multiple transactions. How many transactions would you like to split it into?',
|
||||||
|
initial: 1,
|
||||||
|
style: 'default',
|
||||||
|
min: 1,
|
||||||
|
max: numRollbacks,
|
||||||
|
});
|
||||||
|
for (let i = 0; i < numTxns; i++) {
|
||||||
|
const startIndex = i * Math.trunc(numRollbacks / numTxns);
|
||||||
|
const endIndex = startIndex + Math.trunc(numRollbacks / numTxns) + (i < numRollbacks % numTxns ? 1 : 0);
|
||||||
|
const rollbacks = Object.entries(rollbackTargets).slice(startIndex, endIndex);
|
||||||
|
const rollbackCallData = governorEncoder.encode([
|
||||||
|
rollbacks.map(([selector, target]) => zeroEx.rollback(selector, target).getABIEncodedTransactionData()),
|
||||||
|
new Array(rollbacks.length).fill(zeroEx.address),
|
||||||
|
new Array(rollbacks.length).fill(constants.ZERO_AMOUNT),
|
||||||
|
]);
|
||||||
|
if (numTxns > 1) {
|
||||||
|
logUtils.log(`======================== Governor Calldata #${i + 1} ========================`);
|
||||||
|
}
|
||||||
|
logUtils.log(rollbackCallData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deploymentHistoryAsync(deployments: Deployment[], proxyFunctions: ProxyFunctionEntity[]): Promise<void> {
|
||||||
|
const { index } = await prompts({
|
||||||
|
type: 'select',
|
||||||
|
name: 'index',
|
||||||
|
message: 'Choose a deployment:',
|
||||||
|
choices: deployments.map((deployment, i) => ({
|
||||||
|
title: deployment.time,
|
||||||
|
value: i,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
|
||||||
|
const { action } = await prompts({
|
||||||
|
type: 'select',
|
||||||
|
name: 'action',
|
||||||
|
message: 'What would you like to do?',
|
||||||
|
choices: [
|
||||||
|
{ title: 'Deployment info', value: 'info' },
|
||||||
|
{ title: 'Rollback this deployment', value: 'rollback' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (action === 'info') {
|
||||||
|
logUtils.log(
|
||||||
|
deployments[index].updates.map(update => ({
|
||||||
|
selector: update.selector,
|
||||||
|
signature: update.signature || '(function signature not found)',
|
||||||
|
update: `${update.previousImpl} => ${update.newImpl}`,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const zeroEx = await getMainnetContractAsync();
|
||||||
|
const rollbackTargets: { [selector: string]: string } = {};
|
||||||
|
for (const update of deployments[index].updates) {
|
||||||
|
rollbackTargets[update.selector] = update.previousImpl;
|
||||||
|
const rollbackLength = (await zeroEx.getRollbackLength(update.selector).callAsync()).toNumber();
|
||||||
|
for (let i = rollbackLength - 1; i >= 0; i--) {
|
||||||
|
const entry = await zeroEx.getRollbackEntryAtIndex(update.selector, new BigNumber(i)).callAsync();
|
||||||
|
if (entry === update.previousImpl) {
|
||||||
|
break;
|
||||||
|
} else if (i === 0) {
|
||||||
|
logUtils.log(
|
||||||
|
'Cannot rollback this deployment. The following update from this deployment cannot be rolled back:',
|
||||||
|
);
|
||||||
|
logUtils.log(`\t[${update.selector}] ${update.signature || '(function signature not found)'}`);
|
||||||
|
logUtils.log(`\t${update.previousImpl} => ${update.newImpl}`);
|
||||||
|
logUtils.log(
|
||||||
|
`Cannot find ${
|
||||||
|
update.previousImpl
|
||||||
|
} in the selector's rollback history. It itself may have been previously rolled back.`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const isConfirmed = await confirmRollbackAsync(rollbackTargets, proxyFunctions);
|
||||||
|
if (isConfirmed) {
|
||||||
|
await printRollbackCalldataAsync(rollbackTargets, zeroEx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function functionHistoryAsync(proxyFunctions: ProxyFunctionEntity[]): Promise<void> {
|
||||||
|
const { fnSelector } = await prompts({
|
||||||
|
type: 'autocomplete',
|
||||||
|
name: 'fnSelector',
|
||||||
|
message: 'Enter the selector or name of the function:',
|
||||||
|
choices: [
|
||||||
|
..._.flatMap(Object.entries(selectorToSignature), ([selector, signature]) => [
|
||||||
|
{ title: selector, value: selector, description: signature },
|
||||||
|
{ title: signature, value: selector, description: selector },
|
||||||
|
]),
|
||||||
|
...proxyFunctions
|
||||||
|
.filter(fn => !Object.keys(selectorToSignature).includes(fn.id))
|
||||||
|
.map(fn => ({ title: fn.id, value: fn.id, description: '(function signature not found)' })),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const functionEntity = proxyFunctions.find(fn => fn.id === fnSelector);
|
||||||
|
if (functionEntity === undefined) {
|
||||||
|
logUtils.log(`Couldn't find deployment history for selector ${fnSelector}`);
|
||||||
|
} else {
|
||||||
|
logUtils.log(
|
||||||
|
functionEntity.fullHistory.map(update => ({
|
||||||
|
date: timestampToUTC(update.timestamp),
|
||||||
|
impl: update.impl,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function currentFunctionsAsync(proxyFunctions: ProxyFunctionEntity[]): Promise<void> {
|
||||||
|
const currentFunctions: {
|
||||||
|
[selector: string]: { signature: string; impl: string; lastUpdated: string };
|
||||||
|
} = {};
|
||||||
|
proxyFunctions
|
||||||
|
.filter(fn => fn.currentImpl !== constants.NULL_ADDRESS)
|
||||||
|
.map(fn => {
|
||||||
|
currentFunctions[fn.id] = {
|
||||||
|
signature: selectorToSignature[fn.id] || '(function signature not found)',
|
||||||
|
impl: fn.currentImpl,
|
||||||
|
lastUpdated: timestampToUTC(fn.fullHistory.slice(-1)[0].timestamp),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
logUtils.log(currentFunctions);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateRollbackAsync(proxyFunctions: ProxyFunctionEntity[]): Promise<void> {
|
||||||
|
const zeroEx = await getMainnetContractAsync();
|
||||||
|
const { selected } = await prompts({
|
||||||
|
type: 'autocompleteMultiselect',
|
||||||
|
name: 'selected',
|
||||||
|
message: 'Select the functions to rollback:',
|
||||||
|
choices: _.flatMap(proxyFunctions.filter(fn => fn.currentImpl !== constants.NULL_ADDRESS), fn => [
|
||||||
|
{
|
||||||
|
title: [
|
||||||
|
`[${fn.id}]`,
|
||||||
|
`Implemented @ ${fn.currentImpl}`,
|
||||||
|
selectorToSignature[fn.id] || '(function signature not found)',
|
||||||
|
].join('\n\t\t\t\t'),
|
||||||
|
value: fn.id,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
});
|
||||||
|
const rollbackTargets: { [selector: string]: string } = {};
|
||||||
|
for (const selector of selected) {
|
||||||
|
const rollbackLength = (await zeroEx.getRollbackLength(selector).callAsync()).toNumber();
|
||||||
|
const rollbackHistory = await Promise.all(
|
||||||
|
_.range(rollbackLength).map(async i =>
|
||||||
|
zeroEx.getRollbackEntryAtIndex(selector, new BigNumber(i)).callAsync(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
const fullHistory = proxyFunctions.find(fn => fn.id === selector)!.fullHistory;
|
||||||
|
const previousImpl = rollbackHistory[rollbackLength - 1];
|
||||||
|
const { target } = await prompts({
|
||||||
|
type: 'select',
|
||||||
|
name: 'target',
|
||||||
|
message: 'Select the implementation to rollback to',
|
||||||
|
hint: `[${selector}] ${selectorToSignature[selector] || '(function signature not found)'}`,
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
title: 'DISABLE',
|
||||||
|
value: constants.NULL_ADDRESS,
|
||||||
|
description: 'Rolls back to address(0)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'PREVIOUS',
|
||||||
|
value: previousImpl,
|
||||||
|
description: `${previousImpl} (${timestampToUTC(
|
||||||
|
_.findLast(fullHistory, update => update.impl === previousImpl)!.timestamp,
|
||||||
|
)})`,
|
||||||
|
},
|
||||||
|
...[...new Set(rollbackHistory)]
|
||||||
|
.filter(impl => impl !== constants.NULL_ADDRESS)
|
||||||
|
.map(impl => ({
|
||||||
|
title: impl,
|
||||||
|
value: impl,
|
||||||
|
description: timestampToUTC(_.findLast(fullHistory, update => update.impl === impl)!.timestamp),
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
rollbackTargets[selector] = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isConfirmed = await confirmRollbackAsync(rollbackTargets, proxyFunctions);
|
||||||
|
if (isConfirmed) {
|
||||||
|
await printRollbackCalldataAsync(rollbackTargets, zeroEx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateEmergencyRollbackAsync(proxyFunctions: ProxyFunctionEntity[]): Promise<void> {
|
||||||
|
const zeroEx = new wrappers.IZeroExContract(
|
||||||
|
getContractAddressesForChainOrThrow(1).exchangeProxy,
|
||||||
|
new Web3ProviderEngine(),
|
||||||
|
);
|
||||||
|
const allSelectors = proxyFunctions
|
||||||
|
.filter(fn => fn.currentImpl !== constants.NULL_ADDRESS && !DO_NOT_ROLLBACK.includes(fn.id))
|
||||||
|
.map(fn => fn.id);
|
||||||
|
await printRollbackCalldataAsync(
|
||||||
|
_.zipObject(allSelectors, new Array(allSelectors.length).fill(constants.NULL_ADDRESS)),
|
||||||
|
zeroEx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let provider: SupportedProvider | undefined = process.env.RPC_URL ? createWeb3Provider(process.env.RPC_URL) : undefined;
|
||||||
|
|
||||||
|
function createWeb3Provider(rpcUrl: string): SupportedProvider {
|
||||||
|
const providerEngine = new Web3ProviderEngine();
|
||||||
|
providerEngine.addProvider(new RPCSubprovider(rpcUrl));
|
||||||
|
providerUtils.startProviderEngine(providerEngine);
|
||||||
|
return providerEngine;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getMainnetContractAsync(): Promise<wrappers.IZeroExContract> {
|
||||||
|
if (provider === undefined) {
|
||||||
|
const { rpcUrl } = await prompts({
|
||||||
|
type: 'text',
|
||||||
|
name: 'rpcUrl',
|
||||||
|
message: 'Enter an RPC endpoint:',
|
||||||
|
});
|
||||||
|
provider = createWeb3Provider(rpcUrl);
|
||||||
|
}
|
||||||
|
const chainId = await new Web3Wrapper(provider).getChainIdAsync();
|
||||||
|
const { exchangeProxy } = getContractAddressesForChainOrThrow(chainId);
|
||||||
|
return new wrappers.IZeroExContract(exchangeProxy, provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const proxyFunctions = await querySubgraphAsync();
|
||||||
|
const deployments = reconstructDeployments(proxyFunctions);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { action } = await prompts({
|
||||||
|
type: 'select',
|
||||||
|
name: 'action',
|
||||||
|
message: 'What would you like to do?',
|
||||||
|
choices: [
|
||||||
|
{ title: '🚢 Deployment history', value: CommandLineActions.History },
|
||||||
|
{ title: '📜 Function history', value: CommandLineActions.Function },
|
||||||
|
{ title: '🗺️ Currently registered functions', value: CommandLineActions.Current },
|
||||||
|
{ title: '🔙 Generate rollback calldata', value: CommandLineActions.Rollback },
|
||||||
|
{ title: '🚨 Emergency shutdown calldata', value: CommandLineActions.Emergency },
|
||||||
|
{ title: '👋 Exit', value: CommandLineActions.Exit },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case CommandLineActions.History:
|
||||||
|
await deploymentHistoryAsync(deployments, proxyFunctions);
|
||||||
|
break;
|
||||||
|
case CommandLineActions.Function:
|
||||||
|
await functionHistoryAsync(proxyFunctions);
|
||||||
|
break;
|
||||||
|
case CommandLineActions.Current:
|
||||||
|
await currentFunctionsAsync(proxyFunctions);
|
||||||
|
break;
|
||||||
|
case CommandLineActions.Rollback:
|
||||||
|
await generateRollbackAsync(proxyFunctions);
|
||||||
|
break;
|
||||||
|
case CommandLineActions.Emergency:
|
||||||
|
await generateEmergencyRollbackAsync(proxyFunctions);
|
||||||
|
break;
|
||||||
|
case CommandLineActions.Exit:
|
||||||
|
default:
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})().catch(err => {
|
||||||
|
logUtils.log(err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
@@ -9,8 +9,6 @@ import { abis } from '../utils/abis';
|
|||||||
import { fullMigrateAsync } from '../utils/migration';
|
import { fullMigrateAsync } from '../utils/migration';
|
||||||
import {
|
import {
|
||||||
LiquidityProviderSandboxContract,
|
LiquidityProviderSandboxContract,
|
||||||
TestBridgeContract,
|
|
||||||
TestBridgeEvents,
|
|
||||||
TestLiquidityProviderContract,
|
TestLiquidityProviderContract,
|
||||||
TestLiquidityProviderEvents,
|
TestLiquidityProviderEvents,
|
||||||
TestWethContract,
|
TestWethContract,
|
||||||
@@ -148,41 +146,6 @@ blockchainTests('LiquidityProvider feature', env => {
|
|||||||
TestLiquidityProviderEvents.SellTokenForToken,
|
TestLiquidityProviderEvents.SellTokenForToken,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('Successfully executes an ERC20-ERC20 swap (backwards-compatibility)', async () => {
|
|
||||||
const bridge = await TestBridgeContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.TestBridge,
|
|
||||||
env.provider,
|
|
||||||
env.txDefaults,
|
|
||||||
artifacts,
|
|
||||||
weth.address,
|
|
||||||
token.address,
|
|
||||||
);
|
|
||||||
const tx = await feature
|
|
||||||
.sellToLiquidityProvider(
|
|
||||||
token.address,
|
|
||||||
weth.address,
|
|
||||||
bridge.address,
|
|
||||||
constants.NULL_ADDRESS,
|
|
||||||
constants.ONE_ETHER,
|
|
||||||
constants.ZERO_AMOUNT,
|
|
||||||
constants.NULL_BYTES,
|
|
||||||
)
|
|
||||||
.awaitTransactionSuccessAsync({ from: taker });
|
|
||||||
verifyEventsFromLogs(
|
|
||||||
tx.logs,
|
|
||||||
[
|
|
||||||
{
|
|
||||||
inputToken: token.address,
|
|
||||||
outputToken: weth.address,
|
|
||||||
inputTokenAmount: constants.ONE_ETHER,
|
|
||||||
outputTokenAmount: constants.ZERO_AMOUNT,
|
|
||||||
from: bridge.address,
|
|
||||||
to: taker,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
TestBridgeEvents.ERC20BridgeTransfer,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('Reverts if cannot fulfill the minimum buy amount', async () => {
|
it('Reverts if cannot fulfill the minimum buy amount', async () => {
|
||||||
const minBuyAmount = new BigNumber(1);
|
const minBuyAmount = new BigNumber(1);
|
||||||
const tx = feature
|
const tx = feature
|
||||||
|
@@ -6,9 +6,7 @@ import {
|
|||||||
randomAddress,
|
randomAddress,
|
||||||
verifyEventsFromLogs,
|
verifyEventsFromLogs,
|
||||||
} from '@0x/contracts-test-utils';
|
} from '@0x/contracts-test-utils';
|
||||||
import { getExchangeProxyMetaTransactionHash, signatureUtils } from '@0x/order-utils';
|
import { MetaTransaction, MetaTransactionFields } from '@0x/protocol-utils';
|
||||||
import { Signature } from '@0x/protocol-utils';
|
|
||||||
import { ExchangeProxyMetaTransaction } from '@0x/types';
|
|
||||||
import { BigNumber, hexUtils, StringRevertError, ZeroExRevertErrors } from '@0x/utils';
|
import { BigNumber, hexUtils, StringRevertError, ZeroExRevertErrors } from '@0x/utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
@@ -31,6 +29,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
let owner: string;
|
let owner: string;
|
||||||
let maker: string;
|
let maker: string;
|
||||||
let sender: string;
|
let sender: string;
|
||||||
|
let notSigner: string;
|
||||||
let signers: string[];
|
let signers: string[];
|
||||||
let zeroEx: IZeroExContract;
|
let zeroEx: IZeroExContract;
|
||||||
let feature: MetaTransactionsFeatureContract;
|
let feature: MetaTransactionsFeatureContract;
|
||||||
@@ -45,7 +44,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
const REENTRANCY_FLAG_MTX = 0x1;
|
const REENTRANCY_FLAG_MTX = 0x1;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
[owner, maker, sender, ...signers] = await env.getAccountAddressesAsync();
|
[owner, maker, sender, notSigner, ...signers] = await env.getAccountAddressesAsync();
|
||||||
transformERC20Feature = await TestMetaTransactionsTransformERC20FeatureContract.deployFrom0xArtifactAsync(
|
transformERC20Feature = await TestMetaTransactionsTransformERC20FeatureContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.TestMetaTransactionsTransformERC20Feature,
|
artifacts.TestMetaTransactionsTransformERC20Feature,
|
||||||
env.provider,
|
env.provider,
|
||||||
@@ -83,19 +82,8 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
function sigstruct(signature: string): Signature {
|
function getRandomMetaTransaction(fields: Partial<MetaTransactionFields> = {}): MetaTransaction {
|
||||||
return {
|
return new MetaTransaction({
|
||||||
v: parseInt(hexUtils.slice(signature, 0, 1), 16),
|
|
||||||
signatureType: parseInt(hexUtils.slice(signature, 65, 66), 16),
|
|
||||||
r: hexUtils.slice(signature, 1, 33),
|
|
||||||
s: hexUtils.slice(signature, 33, 65),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRandomMetaTransaction(
|
|
||||||
fields: Partial<ExchangeProxyMetaTransaction> = {},
|
|
||||||
): ExchangeProxyMetaTransaction {
|
|
||||||
return {
|
|
||||||
signer: _.sampleSize(signers)[0],
|
signer: _.sampleSize(signers)[0],
|
||||||
sender,
|
sender,
|
||||||
minGasPrice: getRandomInteger('2', '1e9'),
|
minGasPrice: getRandomInteger('2', '1e9'),
|
||||||
@@ -106,28 +94,16 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
value: getRandomInteger(1, '1e18'),
|
value: getRandomInteger(1, '1e18'),
|
||||||
feeToken: feeToken.address,
|
feeToken: feeToken.address,
|
||||||
feeAmount: getRandomInteger(1, MAX_FEE_AMOUNT),
|
feeAmount: getRandomInteger(1, MAX_FEE_AMOUNT),
|
||||||
domain: {
|
chainId: 1, // Ganache's `chainid` opcode is hardcoded as 1
|
||||||
chainId: 1, // Ganache's `chainid` opcode is hardcoded as 1
|
verifyingContract: zeroEx.address,
|
||||||
verifyingContract: zeroEx.address,
|
|
||||||
},
|
|
||||||
...fields,
|
...fields,
|
||||||
};
|
});
|
||||||
}
|
|
||||||
|
|
||||||
async function signMetaTransactionAsync(mtx: ExchangeProxyMetaTransaction, signer?: string): Promise<Signature> {
|
|
||||||
return sigstruct(
|
|
||||||
await signatureUtils.ecSignHashAsync(
|
|
||||||
env.provider,
|
|
||||||
getExchangeProxyMetaTransactionHash(mtx),
|
|
||||||
signer || mtx.signer,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('getMetaTransactionHash()', () => {
|
describe('getMetaTransactionHash()', () => {
|
||||||
it('generates the correct hash', async () => {
|
it('generates the correct hash', async () => {
|
||||||
const mtx = getRandomMetaTransaction();
|
const mtx = getRandomMetaTransaction();
|
||||||
const expected = getExchangeProxyMetaTransactionHash(mtx);
|
const expected = mtx.getHash();
|
||||||
const actual = await feature.getMetaTransactionHash(mtx).callAsync();
|
const actual = await feature.getMetaTransactionHash(mtx).callAsync();
|
||||||
expect(actual).to.eq(expected);
|
expect(actual).to.eq(expected);
|
||||||
});
|
});
|
||||||
@@ -163,7 +139,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
const mtx = getRandomMetaTransaction({
|
const mtx = getRandomMetaTransaction({
|
||||||
callData: nativeOrdersFeature.fillLimitOrder(order, sig, fillAmount).getABIEncodedTransactionData(),
|
callData: nativeOrdersFeature.fillLimitOrder(order, sig, fillAmount).getABIEncodedTransactionData(),
|
||||||
});
|
});
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.minGasPrice,
|
gasPrice: mtx.minGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -198,7 +174,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
callData: nativeOrdersFeature.fillRfqOrder(order, sig, fillAmount).getABIEncodedTransactionData(),
|
callData: nativeOrdersFeature.fillRfqOrder(order, sig, fillAmount).getABIEncodedTransactionData(),
|
||||||
value: ZERO_AMOUNT,
|
value: ZERO_AMOUNT,
|
||||||
});
|
});
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.minGasPrice,
|
gasPrice: mtx.minGasPrice,
|
||||||
value: 0,
|
value: 0,
|
||||||
@@ -237,7 +213,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
)
|
)
|
||||||
.getABIEncodedTransactionData(),
|
.getABIEncodedTransactionData(),
|
||||||
});
|
});
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.minGasPrice,
|
gasPrice: mtx.minGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -275,7 +251,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
)
|
)
|
||||||
.getABIEncodedTransactionData();
|
.getABIEncodedTransactionData();
|
||||||
const mtx = getRandomMetaTransaction({ callData });
|
const mtx = getRandomMetaTransaction({ callData });
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.minGasPrice,
|
gasPrice: mtx.minGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -315,7 +291,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
)
|
)
|
||||||
.getABIEncodedTransactionData(),
|
.getABIEncodedTransactionData(),
|
||||||
});
|
});
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.minGasPrice,
|
gasPrice: mtx.minGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -340,7 +316,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
)
|
)
|
||||||
.getABIEncodedTransactionData(),
|
.getABIEncodedTransactionData(),
|
||||||
});
|
});
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.minGasPrice,
|
gasPrice: mtx.minGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -363,8 +339,8 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
)
|
)
|
||||||
.getABIEncodedTransactionData(),
|
.getABIEncodedTransactionData(),
|
||||||
});
|
});
|
||||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
const mtxHash = mtx.getHash();
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.minGasPrice,
|
gasPrice: mtx.minGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -393,8 +369,8 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
const mtx = getRandomMetaTransaction({
|
const mtx = getRandomMetaTransaction({
|
||||||
callData: transformERC20Feature.createTransformWallet().getABIEncodedTransactionData(),
|
callData: transformERC20Feature.createTransformWallet().getABIEncodedTransactionData(),
|
||||||
});
|
});
|
||||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
const mtxHash = mtx.getHash();
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.minGasPrice,
|
gasPrice: mtx.minGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -421,8 +397,8 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
)
|
)
|
||||||
.getABIEncodedTransactionData(),
|
.getABIEncodedTransactionData(),
|
||||||
});
|
});
|
||||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
const mtxHash = mtx.getHash();
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.minGasPrice,
|
gasPrice: mtx.minGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -439,8 +415,8 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
|
|
||||||
it('fails if not enough ETH provided', async () => {
|
it('fails if not enough ETH provided', async () => {
|
||||||
const mtx = getRandomMetaTransaction();
|
const mtx = getRandomMetaTransaction();
|
||||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
const mtxHash = mtx.getHash();
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.minGasPrice,
|
gasPrice: mtx.minGasPrice,
|
||||||
value: mtx.value.minus(1),
|
value: mtx.value.minus(1),
|
||||||
@@ -457,8 +433,8 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
|
|
||||||
it('fails if gas price too low', async () => {
|
it('fails if gas price too low', async () => {
|
||||||
const mtx = getRandomMetaTransaction();
|
const mtx = getRandomMetaTransaction();
|
||||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
const mtxHash = mtx.getHash();
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.minGasPrice.minus(1),
|
gasPrice: mtx.minGasPrice.minus(1),
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -476,8 +452,8 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
|
|
||||||
it('fails if gas price too high', async () => {
|
it('fails if gas price too high', async () => {
|
||||||
const mtx = getRandomMetaTransaction();
|
const mtx = getRandomMetaTransaction();
|
||||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
const mtxHash = mtx.getHash();
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.maxGasPrice.plus(1),
|
gasPrice: mtx.maxGasPrice.plus(1),
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -497,8 +473,8 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
const mtx = getRandomMetaTransaction({
|
const mtx = getRandomMetaTransaction({
|
||||||
expirationTimeSeconds: new BigNumber(Math.floor(_.now() / 1000 - 60)),
|
expirationTimeSeconds: new BigNumber(Math.floor(_.now() / 1000 - 60)),
|
||||||
});
|
});
|
||||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
const mtxHash = mtx.getHash();
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.maxGasPrice,
|
gasPrice: mtx.maxGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -518,8 +494,8 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
const mtx = getRandomMetaTransaction({
|
const mtx = getRandomMetaTransaction({
|
||||||
sender: requiredSender,
|
sender: requiredSender,
|
||||||
});
|
});
|
||||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
const mtxHash = mtx.getHash();
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.maxGasPrice,
|
gasPrice: mtx.maxGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -536,8 +512,8 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
|
|
||||||
it('fails if signature is wrong', async () => {
|
it('fails if signature is wrong', async () => {
|
||||||
const mtx = getRandomMetaTransaction({ signer: signers[0] });
|
const mtx = getRandomMetaTransaction({ signer: signers[0] });
|
||||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
const mtxHash = mtx.getHash();
|
||||||
const signature = await signMetaTransactionAsync(mtx, signers[1]);
|
const signature = await mtx.clone({ signer: notSigner }).getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.maxGasPrice,
|
gasPrice: mtx.maxGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -567,8 +543,8 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
.getABIEncodedTransactionData(),
|
.getABIEncodedTransactionData(),
|
||||||
value: TRANSFORM_ERC20_REENTER_VALUE,
|
value: TRANSFORM_ERC20_REENTER_VALUE,
|
||||||
});
|
});
|
||||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
const mtxHash = mtx.getHash();
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.maxGasPrice,
|
gasPrice: mtx.maxGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -600,8 +576,8 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
.getABIEncodedTransactionData(),
|
.getABIEncodedTransactionData(),
|
||||||
value: TRANSFORM_ERC20_BATCH_REENTER_VALUE,
|
value: TRANSFORM_ERC20_BATCH_REENTER_VALUE,
|
||||||
});
|
});
|
||||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
const mtxHash = mtx.getHash();
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.maxGasPrice,
|
gasPrice: mtx.maxGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -633,8 +609,8 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
.getABIEncodedTransactionData(),
|
.getABIEncodedTransactionData(),
|
||||||
value: TRANSFORM_ERC20_REENTER_VALUE,
|
value: TRANSFORM_ERC20_REENTER_VALUE,
|
||||||
});
|
});
|
||||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
const mtxHash = mtx.getHash();
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.maxGasPrice,
|
gasPrice: mtx.maxGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -666,8 +642,8 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
.getABIEncodedTransactionData(),
|
.getABIEncodedTransactionData(),
|
||||||
value: TRANSFORM_ERC20_BATCH_REENTER_VALUE,
|
value: TRANSFORM_ERC20_BATCH_REENTER_VALUE,
|
||||||
});
|
});
|
||||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
const mtxHash = mtx.getHash();
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.maxGasPrice,
|
gasPrice: mtx.maxGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -703,7 +679,9 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
.getABIEncodedTransactionData(),
|
.getABIEncodedTransactionData(),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
const signatures = await Promise.all(mtxs.map(async mtx => signMetaTransactionAsync(mtx)));
|
const signatures = await Promise.all(
|
||||||
|
mtxs.map(async mtx => mtx.getSignatureWithProviderAsync(env.provider)),
|
||||||
|
);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: BigNumber.max(...mtxs.map(mtx => mtx.minGasPrice)),
|
gasPrice: BigNumber.max(...mtxs.map(mtx => mtx.minGasPrice)),
|
||||||
value: BigNumber.sum(...mtxs.map(mtx => mtx.value)),
|
value: BigNumber.sum(...mtxs.map(mtx => mtx.value)),
|
||||||
@@ -728,9 +706,9 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
.getABIEncodedTransactionData(),
|
.getABIEncodedTransactionData(),
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
const mtxHash = mtx.getHash();
|
||||||
const mtxs = _.times(2, () => mtx);
|
const mtxs = _.times(2, () => mtx);
|
||||||
const signatures = await Promise.all(mtxs.map(async m => signMetaTransactionAsync(m)));
|
const signatures = await Promise.all(mtxs.map(async m => m.getSignatureWithProviderAsync(env.provider)));
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: BigNumber.max(...mtxs.map(m => m.minGasPrice)),
|
gasPrice: BigNumber.max(...mtxs.map(m => m.minGasPrice)),
|
||||||
value: BigNumber.sum(...mtxs.map(m => m.value)),
|
value: BigNumber.sum(...mtxs.map(m => m.value)),
|
||||||
@@ -756,8 +734,8 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
)
|
)
|
||||||
.getABIEncodedTransactionData(),
|
.getABIEncodedTransactionData(),
|
||||||
});
|
});
|
||||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
const mtxHash = mtx.getHash();
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.minGasPrice,
|
gasPrice: mtx.minGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -786,8 +764,8 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
.getABIEncodedTransactionData(),
|
.getABIEncodedTransactionData(),
|
||||||
value: TRANSFORM_ERC20_REENTER_VALUE,
|
value: TRANSFORM_ERC20_REENTER_VALUE,
|
||||||
});
|
});
|
||||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
const mtxHash = mtx.getHash();
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.maxGasPrice,
|
gasPrice: mtx.maxGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -819,8 +797,8 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
.getABIEncodedTransactionData(),
|
.getABIEncodedTransactionData(),
|
||||||
value: TRANSFORM_ERC20_BATCH_REENTER_VALUE,
|
value: TRANSFORM_ERC20_BATCH_REENTER_VALUE,
|
||||||
});
|
});
|
||||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
const mtxHash = mtx.getHash();
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.maxGasPrice,
|
gasPrice: mtx.maxGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -859,7 +837,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
)
|
)
|
||||||
.getABIEncodedTransactionData(),
|
.getABIEncodedTransactionData(),
|
||||||
});
|
});
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.minGasPrice,
|
gasPrice: mtx.minGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
@@ -873,7 +851,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
describe('getMetaTransactionHashExecutedBlock()', () => {
|
describe('getMetaTransactionHashExecutedBlock()', () => {
|
||||||
it('returns zero for an unexecuted mtx', async () => {
|
it('returns zero for an unexecuted mtx', async () => {
|
||||||
const mtx = getRandomMetaTransaction();
|
const mtx = getRandomMetaTransaction();
|
||||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
const mtxHash = mtx.getHash();
|
||||||
const block = await feature.getMetaTransactionHashExecutedBlock(mtxHash).callAsync();
|
const block = await feature.getMetaTransactionHashExecutedBlock(mtxHash).callAsync();
|
||||||
expect(block).to.bignumber.eq(0);
|
expect(block).to.bignumber.eq(0);
|
||||||
});
|
});
|
||||||
@@ -891,13 +869,13 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
)
|
)
|
||||||
.getABIEncodedTransactionData(),
|
.getABIEncodedTransactionData(),
|
||||||
});
|
});
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.minGasPrice,
|
gasPrice: mtx.minGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
};
|
};
|
||||||
const receipt = await feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
const receipt = await feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
||||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
const mtxHash = mtx.getHash();
|
||||||
const block = await feature.getMetaTransactionHashExecutedBlock(mtxHash).callAsync();
|
const block = await feature.getMetaTransactionHashExecutedBlock(mtxHash).callAsync();
|
||||||
expect(block).to.bignumber.eq(receipt.blockNumber);
|
expect(block).to.bignumber.eq(receipt.blockNumber);
|
||||||
});
|
});
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig",
|
"extends": "../../tsconfig",
|
||||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*", "./scripts/**/*"],
|
||||||
"files": [
|
"files": [
|
||||||
"generated-artifacts/AffiliateFeeTransformer.json",
|
"generated-artifacts/AffiliateFeeTransformer.json",
|
||||||
"generated-artifacts/BridgeAdapter.json",
|
"generated-artifacts/BridgeAdapter.json",
|
||||||
|
@@ -137,4 +137,34 @@ In both cases, the ``@0x/protocol-utils`` package simplifies generating these si
|
|||||||
|
|
||||||
The Orderbook
|
The Orderbook
|
||||||
=======================
|
=======================
|
||||||
Orders are shared through a decentralized and permissionless network, called `0x Mesh <https://0x.org/mesh>`_. The simplest way to post and discover orders is through `0x API <https://0x.org/api>`_. See `this guide <https://0x.org/docs/guides/market-making-on-0x>`_ tailored for Market Makers.
|
Orders are shared through a decentralized and permissionless network, called `0x Mesh <https://0x.org/mesh>`_. The simplest way to post and discover orders is through `0x API <https://0x.org/api>`_. See `this guide <https://0x.org/docs/guides/market-making-on-0x>`_ tailored for Market Makers.
|
||||||
|
|
||||||
|
Orders are usually represented as a JSON object off-chain. Below is a table represention and example of how orders should be formatted off-chain.
|
||||||
|
|
||||||
|
JSON representation of RFQ Orders
|
||||||
|
*********************************
|
||||||
|
|
||||||
|
A ``RFQOrder`` should be serialized to JSON as following:
|
||||||
|
|
||||||
|
.. code-block:: typescript
|
||||||
|
|
||||||
|
interface RfqOrderJson {
|
||||||
|
"maker": string,
|
||||||
|
"taker": string,
|
||||||
|
"makerToken": string,
|
||||||
|
"takerToken": string,
|
||||||
|
"makerAmount": string,
|
||||||
|
"takerAmount": string,
|
||||||
|
"txOrigin": string,
|
||||||
|
"pool": string,
|
||||||
|
"expiry": number,
|
||||||
|
"salt": string,
|
||||||
|
"chainId": number, // Ethereum Chain Id where the transaction is submitted.
|
||||||
|
"verifyingContract": string, // Address of the contract where the transaction should be sent.
|
||||||
|
"signature": {
|
||||||
|
"signatureType": number,
|
||||||
|
"v": number,
|
||||||
|
"s": string,
|
||||||
|
"r": string,
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,23 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1608149382,
|
||||||
|
"version": "5.5.1",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "5.5.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Bancor now supported in all pairs",
|
||||||
|
"pr": 88
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1608105788
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1607485227,
|
"timestamp": 1607485227,
|
||||||
"version": "5.4.2",
|
"version": "5.4.2",
|
||||||
|
@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v5.5.1 - _December 16, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v5.5.0 - _December 16, 2020_
|
||||||
|
|
||||||
|
* Bancor now supported in all pairs (#88)
|
||||||
|
|
||||||
## v5.4.2 - _December 9, 2020_
|
## v5.4.2 - _December 9, 2020_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
131
packages/asset-swapper/contracts/src/BancorSampler.sol
Normal file
131
packages/asset-swapper/contracts/src/BancorSampler.sol
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2020 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.6;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "./DeploymentConstants.sol";
|
||||||
|
import "./interfaces/IBancor.sol";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
contract BancorSampler is
|
||||||
|
DeploymentConstants
|
||||||
|
{
|
||||||
|
|
||||||
|
/// @dev Base gas limit for Bancor calls.
|
||||||
|
uint256 constant private BANCOR_CALL_GAS = 300e3; // 300k
|
||||||
|
address constant private BANCOR_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||||
|
|
||||||
|
/// @dev Sample sell quotes from Bancor.
|
||||||
|
/// @param paths The paths to check for Bancor. Only the best is used
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
|
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||||
|
/// @return bancorNetwork the Bancor Network address
|
||||||
|
/// @return path the selected conversion path from bancor
|
||||||
|
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||||
|
/// amount.
|
||||||
|
function sampleSellsFromBancor(
|
||||||
|
address[][] memory paths,
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (address bancorNetwork, address[] memory path, uint256[] memory makerTokenAmounts)
|
||||||
|
{
|
||||||
|
bancorNetwork = _getBancorNetwork();
|
||||||
|
if (paths.length == 0) {
|
||||||
|
return (bancorNetwork, path, makerTokenAmounts);
|
||||||
|
}
|
||||||
|
uint256 maxBoughtAmount = 0;
|
||||||
|
// Find the best path by selling the largest taker amount
|
||||||
|
for (uint256 i = 0; i < paths.length; i++) {
|
||||||
|
if (paths[i].length < 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
IBancorNetwork(bancorNetwork)
|
||||||
|
.rateByPath
|
||||||
|
{gas: BANCOR_CALL_GAS}
|
||||||
|
(paths[i], takerTokenAmounts[takerTokenAmounts.length-1])
|
||||||
|
returns (uint256 amount)
|
||||||
|
{
|
||||||
|
if (amount > maxBoughtAmount) {
|
||||||
|
maxBoughtAmount = amount;
|
||||||
|
path = paths[i];
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Swallow failures, leaving all results as zero.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
try
|
||||||
|
IBancorNetwork(bancorNetwork)
|
||||||
|
.rateByPath
|
||||||
|
{gas: BANCOR_CALL_GAS}
|
||||||
|
(path, takerTokenAmounts[i])
|
||||||
|
returns (uint256 amount)
|
||||||
|
{
|
||||||
|
makerTokenAmounts[i] = amount;
|
||||||
|
} catch {
|
||||||
|
// Swallow failures, leaving all results as zero.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (bancorNetwork, path, makerTokenAmounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Sample buy quotes from Bancor. Unimplemented
|
||||||
|
/// @param paths The paths to check for Bancor. Only the best is used
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
|
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||||
|
/// @return bancorNetwork the Bancor Network address
|
||||||
|
/// @return path the selected conversion path from bancor
|
||||||
|
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||||
|
/// amount.
|
||||||
|
function sampleBuysFromBancor(
|
||||||
|
address[][] memory paths,
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory makerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (address bancorNetwork, address[] memory path, uint256[] memory takerTokenAmounts)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getBancorNetwork()
|
||||||
|
private
|
||||||
|
view
|
||||||
|
returns (address)
|
||||||
|
{
|
||||||
|
IBancorRegistry registry = IBancorRegistry(_getBancorRegistryAddress());
|
||||||
|
return registry.getAddress(registry.BANCOR_NETWORK());
|
||||||
|
}
|
||||||
|
}
|
@@ -62,6 +62,8 @@ contract DeploymentConstants {
|
|||||||
address constant private DODO_REGISTRY = 0x3A97247DF274a17C59A3bd12735ea3FcDFb49950;
|
address constant private DODO_REGISTRY = 0x3A97247DF274a17C59A3bd12735ea3FcDFb49950;
|
||||||
/// @dev Mainnet address of the DODO Helper contract
|
/// @dev Mainnet address of the DODO Helper contract
|
||||||
address constant private DODO_HELPER = 0x533dA777aeDCE766CEAe696bf90f8541A4bA80Eb;
|
address constant private DODO_HELPER = 0x533dA777aeDCE766CEAe696bf90f8541A4bA80Eb;
|
||||||
|
/// @dev Mainnet address of the Bancor Registry contract
|
||||||
|
address constant private BANCOR_REGISTRY = 0x52Ae12ABe5D8BD778BD5397F99cA900624CfADD4;
|
||||||
|
|
||||||
// // Ropsten addresses ///////////////////////////////////////////////////////
|
// // Ropsten addresses ///////////////////////////////////////////////////////
|
||||||
// /// @dev Mainnet address of the WETH contract.
|
// /// @dev Mainnet address of the WETH contract.
|
||||||
@@ -337,4 +339,14 @@ contract DeploymentConstants {
|
|||||||
{
|
{
|
||||||
return DODO_HELPER;
|
return DODO_HELPER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev An overridable way to retrieve the Bancor Registry contract address.
|
||||||
|
/// @return registry The Bancor registry contract address.
|
||||||
|
function _getBancorRegistryAddress()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (address registry)
|
||||||
|
{
|
||||||
|
return BANCOR_REGISTRY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,7 @@ pragma solidity ^0.6;
|
|||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "./BalancerSampler.sol";
|
import "./BalancerSampler.sol";
|
||||||
|
import "./BancorSampler.sol";
|
||||||
import "./CurveSampler.sol";
|
import "./CurveSampler.sol";
|
||||||
import "./DODOSampler.sol";
|
import "./DODOSampler.sol";
|
||||||
import "./Eth2DaiSampler.sol";
|
import "./Eth2DaiSampler.sol";
|
||||||
@@ -38,6 +39,7 @@ import "./UniswapV2Sampler.sol";
|
|||||||
|
|
||||||
contract ERC20BridgeSampler is
|
contract ERC20BridgeSampler is
|
||||||
BalancerSampler,
|
BalancerSampler,
|
||||||
|
BancorSampler,
|
||||||
CurveSampler,
|
CurveSampler,
|
||||||
DODOSampler,
|
DODOSampler,
|
||||||
Eth2DaiSampler,
|
Eth2DaiSampler,
|
||||||
|
32
packages/asset-swapper/contracts/src/interfaces/IBancor.sol
Normal file
32
packages/asset-swapper/contracts/src/interfaces/IBancor.sol
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2020 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.6;
|
||||||
|
|
||||||
|
|
||||||
|
interface IBancor {}
|
||||||
|
|
||||||
|
interface IBancorNetwork {
|
||||||
|
function conversionPath(address _sourceToken, address _targetToken) external view returns (address[] memory);
|
||||||
|
function rateByPath(address[] memory _path, uint256 _amount) external view returns (uint256);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IBancorRegistry {
|
||||||
|
function getAddress(bytes32 _contractName) external view returns (address);
|
||||||
|
function BANCOR_NETWORK() external view returns (bytes32);
|
||||||
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/asset-swapper",
|
"name": "@0x/asset-swapper",
|
||||||
"version": "5.4.2",
|
"version": "5.5.1",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
"config": {
|
"config": {
|
||||||
"publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker",
|
"publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker",
|
||||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||||
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|CurveSampler|DODOSampler|DeploymentConstants|DummyLiquidityProvider|ERC20BridgeSampler|Eth2DaiSampler|IBalancer|ICurve|IEth2Dai|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SushiSwapSampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler).json",
|
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BancorSampler|CurveSampler|DODOSampler|DeploymentConstants|DummyLiquidityProvider|ERC20BridgeSampler|Eth2DaiSampler|IBalancer|IBancor|ICurve|IEth2Dai|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SushiSwapSampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler).json",
|
||||||
"postpublish": {
|
"postpublish": {
|
||||||
"assets": []
|
"assets": []
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
"@0x/utils": "^6.1.1",
|
"@0x/utils": "^6.1.1",
|
||||||
"@0x/web3-wrapper": "^7.3.0",
|
"@0x/web3-wrapper": "^7.3.0",
|
||||||
"@balancer-labs/sor": "0.3.2",
|
"@balancer-labs/sor": "0.3.2",
|
||||||
"@bancor/sdk": "^0.2.9",
|
"@bancor/sdk": "0.2.9",
|
||||||
"@ethersproject/abi": "^5.0.1",
|
"@ethersproject/abi": "^5.0.1",
|
||||||
"@ethersproject/address": "^5.0.1",
|
"@ethersproject/address": "^5.0.1",
|
||||||
"@ethersproject/contracts": "^5.0.1",
|
"@ethersproject/contracts": "^5.0.1",
|
||||||
@@ -87,16 +87,16 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/base-contract": "^6.2.14",
|
"@0x/base-contract": "^6.2.14",
|
||||||
"@0x/contracts-asset-proxy": "^3.6.9",
|
"@0x/contracts-asset-proxy": "^3.7.0",
|
||||||
"@0x/contracts-erc20": "^3.2.12",
|
"@0x/contracts-erc20": "^3.2.12",
|
||||||
"@0x/contracts-exchange": "^3.2.18",
|
"@0x/contracts-exchange": "^3.2.19",
|
||||||
"@0x/contracts-exchange-libs": "^4.3.18",
|
"@0x/contracts-exchange-libs": "^4.3.18",
|
||||||
"@0x/contracts-gen": "^2.0.24",
|
"@0x/contracts-gen": "^2.0.24",
|
||||||
"@0x/contracts-test-utils": "^5.3.15",
|
"@0x/contracts-test-utils": "^5.3.15",
|
||||||
"@0x/contracts-utils": "^4.6.3",
|
"@0x/contracts-utils": "^4.6.3",
|
||||||
"@0x/contracts-zero-ex": "^0.12.0",
|
"@0x/contracts-zero-ex": "^0.14.0",
|
||||||
"@0x/mesh-rpc-client": "^9.4.2",
|
"@0x/mesh-rpc-client": "^9.4.2",
|
||||||
"@0x/migrations": "^6.5.4",
|
"@0x/migrations": "^6.5.6",
|
||||||
"@0x/sol-compiler": "^4.4.1",
|
"@0x/sol-compiler": "^4.4.1",
|
||||||
"@0x/subproviders": "^6.2.3",
|
"@0x/subproviders": "^6.2.3",
|
||||||
"@0x/ts-doc-gen": "^0.0.28",
|
"@0x/ts-doc-gen": "^0.0.28",
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
import { ChainId, getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||||
import { DevUtilsContract } from '@0x/contract-wrappers';
|
import { DevUtilsContract } from '@0x/contract-wrappers';
|
||||||
import { schemas } from '@0x/json-schemas';
|
import { schemas } from '@0x/json-schemas';
|
||||||
import { assetDataUtils, SignedOrder } from '@0x/order-utils';
|
import { assetDataUtils, SignedOrder } from '@0x/order-utils';
|
||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
import { assert } from './utils/assert';
|
import { assert } from './utils/assert';
|
||||||
import { calculateLiquidity } from './utils/calculate_liquidity';
|
import { calculateLiquidity } from './utils/calculate_liquidity';
|
||||||
import { MarketOperationUtils } from './utils/market_operation_utils';
|
import { MarketOperationUtils } from './utils/market_operation_utils';
|
||||||
|
import { BancorService } from './utils/market_operation_utils/bancor_service';
|
||||||
import { createDummyOrderForSampler } from './utils/market_operation_utils/orders';
|
import { createDummyOrderForSampler } from './utils/market_operation_utils/orders';
|
||||||
import { DexOrderSampler } from './utils/market_operation_utils/sampler';
|
import { DexOrderSampler } from './utils/market_operation_utils/sampler';
|
||||||
import { SourceFilters } from './utils/market_operation_utils/source_filters';
|
import { SourceFilters } from './utils/market_operation_utils/source_filters';
|
||||||
@@ -208,16 +209,18 @@ export class SwapQuoter {
|
|||||||
gas: samplerGasLimit,
|
gas: samplerGasLimit,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
this._marketOperationUtils = new MarketOperationUtils(
|
this._marketOperationUtils = new MarketOperationUtils(
|
||||||
new DexOrderSampler(
|
new DexOrderSampler(
|
||||||
samplerContract,
|
samplerContract,
|
||||||
samplerOverrides,
|
samplerOverrides,
|
||||||
provider,
|
undefined, // balancer pool cache
|
||||||
undefined,
|
undefined, // cream pool cache
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
tokenAdjacencyGraph,
|
tokenAdjacencyGraph,
|
||||||
liquidityProviderRegistry,
|
liquidityProviderRegistry,
|
||||||
|
this.chainId === ChainId.Mainnet // Enable Bancor only on Mainnet
|
||||||
|
? async () => BancorService.createAsync(provider)
|
||||||
|
: async () => undefined,
|
||||||
),
|
),
|
||||||
this._contractAddresses,
|
this._contractAddresses,
|
||||||
{
|
{
|
||||||
|
@@ -1,26 +1,17 @@
|
|||||||
import { SupportedProvider } from '@0x/dev-utils';
|
import { SupportedProvider } from '@0x/dev-utils';
|
||||||
import { BigNumber } from '@0x/utils';
|
|
||||||
import { SDK } from '@bancor/sdk';
|
import { SDK } from '@bancor/sdk';
|
||||||
import { Ethereum, getDecimals } from '@bancor/sdk/dist/blockchains/ethereum';
|
import { Ethereum } from '@bancor/sdk/dist/blockchains/ethereum';
|
||||||
import { fromWei, toWei } from '@bancor/sdk/dist/helpers';
|
import { BlockchainType } from '@bancor/sdk/dist/types';
|
||||||
import { BlockchainType, Token } from '@bancor/sdk/dist/types';
|
|
||||||
|
|
||||||
import { BancorFillData, Quote } from './types';
|
import { TOKENS } from './constants';
|
||||||
|
|
||||||
/**
|
const findToken = (tokenAddress: string, graph: object): string =>
|
||||||
* Converts an address to a Bancor Token type
|
// If we're looking for WETH it is stored by Bancor as the 0xeee address
|
||||||
*/
|
tokenAddress.toLowerCase() === TOKENS.WETH.toLowerCase()
|
||||||
export function token(address: string, blockchainType: BlockchainType = BlockchainType.Ethereum): Token {
|
? '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'
|
||||||
return {
|
: Object.keys(graph).filter(k => k.toLowerCase() === tokenAddress.toLowerCase())[0];
|
||||||
blockchainType,
|
|
||||||
blockchainId: address,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export class BancorService {
|
export class BancorService {
|
||||||
// Bancor recommends setting this value to 2% under the expected return amount
|
|
||||||
public minReturnAmountBufferPercentage = 0.99;
|
|
||||||
|
|
||||||
public static async createAsync(provider: SupportedProvider): Promise<BancorService> {
|
public static async createAsync(provider: SupportedProvider): Promise<BancorService> {
|
||||||
const sdk = await SDK.create({ ethereumNodeEndpoint: provider });
|
const sdk = await SDK.create({ ethereumNodeEndpoint: provider });
|
||||||
const service = new BancorService(sdk);
|
const service = new BancorService(sdk);
|
||||||
@@ -28,38 +19,16 @@ export class BancorService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(public sdk: SDK) {}
|
constructor(public sdk: SDK) {}
|
||||||
|
public getPaths(_fromToken: string, _toToken: string): string[][] {
|
||||||
public async getQuotesAsync(
|
// HACK: We reach into the blockchain object and pull in it's cache of tokens
|
||||||
fromToken: string,
|
// and we use it's internal non-async getPathsFunc
|
||||||
toToken: string,
|
try {
|
||||||
amounts: BigNumber[],
|
const blockchain = this.sdk._core.blockchains[BlockchainType.Ethereum] as Ethereum;
|
||||||
): Promise<Array<Quote<BancorFillData>>> {
|
const fromToken = findToken(_fromToken, blockchain.graph);
|
||||||
const sdk = this.sdk;
|
const toToken = findToken(_toToken, blockchain.graph);
|
||||||
const blockchain = sdk._core.blockchains[BlockchainType.Ethereum] as Ethereum;
|
return blockchain.getPathsFunc.bind(blockchain)(fromToken, toToken);
|
||||||
const sourceDecimals = await getDecimals(blockchain, fromToken);
|
} catch (e) {
|
||||||
const quotes = await sdk.pricing.getPathAndRates(
|
return [];
|
||||||
token(fromToken),
|
}
|
||||||
token(toToken),
|
|
||||||
amounts.map(amt => fromWei(amt.toString(), sourceDecimals)),
|
|
||||||
);
|
|
||||||
const targetDecimals = await getDecimals(blockchain, toToken);
|
|
||||||
const networkAddress = this.getBancorNetworkAddress();
|
|
||||||
|
|
||||||
return quotes.map(quote => {
|
|
||||||
const { path, rate } = quote;
|
|
||||||
const output = toWei(rate, targetDecimals);
|
|
||||||
return {
|
|
||||||
amount: new BigNumber(output).multipliedBy(this.minReturnAmountBufferPercentage).dp(0),
|
|
||||||
fillData: {
|
|
||||||
path: path.map(p => p.blockchainId),
|
|
||||||
networkAddress,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public getBancorNetworkAddress(): string {
|
|
||||||
const blockchain = this.sdk._core.blockchains[BlockchainType.Ethereum] as Ethereum;
|
|
||||||
return blockchain.bancorNetwork._address;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ import { BridgeContractAddresses } from '../../types';
|
|||||||
|
|
||||||
import { SourceFilters } from './source_filters';
|
import { SourceFilters } from './source_filters';
|
||||||
import {
|
import {
|
||||||
|
BancorFillData,
|
||||||
CurveFillData,
|
CurveFillData,
|
||||||
CurveFunctionSelectors,
|
CurveFunctionSelectors,
|
||||||
CurveInfo,
|
CurveInfo,
|
||||||
@@ -34,8 +35,7 @@ export const SELL_SOURCE_FILTER = new SourceFilters([
|
|||||||
ERC20BridgeSource.Kyber,
|
ERC20BridgeSource.Kyber,
|
||||||
ERC20BridgeSource.Curve,
|
ERC20BridgeSource.Curve,
|
||||||
ERC20BridgeSource.Balancer,
|
ERC20BridgeSource.Balancer,
|
||||||
// Bancor is sampled off-chain, but this list should only include on-chain sources (used in ERC20BridgeSampler)
|
ERC20BridgeSource.Bancor,
|
||||||
// ERC20BridgeSource.Bancor,
|
|
||||||
ERC20BridgeSource.MStable,
|
ERC20BridgeSource.MStable,
|
||||||
ERC20BridgeSource.Mooniswap,
|
ERC20BridgeSource.Mooniswap,
|
||||||
ERC20BridgeSource.Swerve,
|
ERC20BridgeSource.Swerve,
|
||||||
@@ -60,7 +60,7 @@ export const BUY_SOURCE_FILTER = new SourceFilters([
|
|||||||
ERC20BridgeSource.Kyber,
|
ERC20BridgeSource.Kyber,
|
||||||
ERC20BridgeSource.Curve,
|
ERC20BridgeSource.Curve,
|
||||||
ERC20BridgeSource.Balancer,
|
ERC20BridgeSource.Balancer,
|
||||||
// ERC20BridgeSource.Bancor, // FIXME: Disabled until Bancor SDK supports buy quotes
|
// ERC20BridgeSource.Bancor, // FIXME: Bancor Buys not implemented in Sampler
|
||||||
ERC20BridgeSource.MStable,
|
ERC20BridgeSource.MStable,
|
||||||
ERC20BridgeSource.Mooniswap,
|
ERC20BridgeSource.Mooniswap,
|
||||||
ERC20BridgeSource.Shell,
|
ERC20BridgeSource.Shell,
|
||||||
@@ -410,7 +410,7 @@ export const BRIDGE_ADDRESSES_BY_CHAIN: { [chainId in ChainId]: BridgeContractAd
|
|||||||
curveBridge: '0x1796cd592d19e3bcd744fbb025bb61a6d8cb2c09',
|
curveBridge: '0x1796cd592d19e3bcd744fbb025bb61a6d8cb2c09',
|
||||||
multiBridge: '0xc03117a8c9bde203f70aa911cb64a7a0df5ba1e1',
|
multiBridge: '0xc03117a8c9bde203f70aa911cb64a7a0df5ba1e1',
|
||||||
balancerBridge: '0xfe01821ca163844203220cd08e4f2b2fb43ae4e4',
|
balancerBridge: '0xfe01821ca163844203220cd08e4f2b2fb43ae4e4',
|
||||||
bancorBridge: '0x259897d9699553edbdf8538599242354e957fb94',
|
bancorBridge: '0xc880c252db7c51f74161633338a3bdafa8e65276',
|
||||||
mStableBridge: '0x2bf04fcea05f0989a14d9afa37aa376baca6b2b3',
|
mStableBridge: '0x2bf04fcea05f0989a14d9afa37aa376baca6b2b3',
|
||||||
mooniswapBridge: '0x02b7eca484ad960fca3f7709e0b2ac81eec3069c',
|
mooniswapBridge: '0x02b7eca484ad960fca3f7709e0b2ac81eec3069c',
|
||||||
sushiswapBridge: '0x47ed0262a0b688dcb836d254c6a2e96b6c48a9f5',
|
sushiswapBridge: '0x47ed0262a0b688dcb836d254c6a2e96b6c48a9f5',
|
||||||
@@ -539,7 +539,14 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
|
|||||||
throw new Error('Unrecognized SnowSwap address');
|
throw new Error('Unrecognized SnowSwap address');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[ERC20BridgeSource.Bancor]: () => 300e3,
|
[ERC20BridgeSource.Bancor]: (fillData?: FillData) => {
|
||||||
|
let gas = 200e3;
|
||||||
|
const path = (fillData as BancorFillData).path;
|
||||||
|
if (path.length > 2) {
|
||||||
|
gas += (path.length - 2) * 60e3; // +60k for each hop.
|
||||||
|
}
|
||||||
|
return gas;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_FEE_SCHEDULE: Required<FeeSchedule> = Object.assign(
|
export const DEFAULT_FEE_SCHEDULE: Required<FeeSchedule> = Object.assign(
|
||||||
|
@@ -81,13 +81,12 @@ export class MarketOperationUtils {
|
|||||||
private readonly _feeSources = new SourceFilters(FEE_QUOTE_SOURCES);
|
private readonly _feeSources = new SourceFilters(FEE_QUOTE_SOURCES);
|
||||||
|
|
||||||
private static _computeQuoteReport(
|
private static _computeQuoteReport(
|
||||||
nativeOrders: SignedOrder[],
|
|
||||||
quoteRequestor: QuoteRequestor | undefined,
|
quoteRequestor: QuoteRequestor | undefined,
|
||||||
marketSideLiquidity: MarketSideLiquidity,
|
marketSideLiquidity: MarketSideLiquidity,
|
||||||
optimizerResult: OptimizerResult,
|
optimizerResult: OptimizerResult,
|
||||||
comparisonPrice?: BigNumber | undefined,
|
comparisonPrice?: BigNumber | undefined,
|
||||||
): QuoteReport {
|
): QuoteReport {
|
||||||
const { side, dexQuotes, twoHopQuotes, orderFillableAmounts } = marketSideLiquidity;
|
const { side, dexQuotes, twoHopQuotes, orderFillableAmounts, nativeOrders } = marketSideLiquidity;
|
||||||
const { liquidityDelivered } = optimizerResult;
|
const { liquidityDelivered } = optimizerResult;
|
||||||
return generateQuoteReport(
|
return generateQuoteReport(
|
||||||
side,
|
side,
|
||||||
@@ -204,23 +203,12 @@ export class MarketOperationUtils {
|
|||||||
? this._sampler.getCreamSellQuotesOffChainAsync(makerToken, takerToken, sampleAmounts)
|
? this._sampler.getCreamSellQuotesOffChainAsync(makerToken, takerToken, sampleAmounts)
|
||||||
: Promise.resolve([]);
|
: Promise.resolve([]);
|
||||||
|
|
||||||
const offChainBancorPromise = quoteSourceFilters.isAllowed(ERC20BridgeSource.Bancor)
|
|
||||||
? this._sampler.getBancorSellQuotesOffChainAsync(makerToken, takerToken, [takerAmount])
|
|
||||||
: Promise.resolve([]);
|
|
||||||
|
|
||||||
const [
|
const [
|
||||||
[tokenDecimals, orderFillableAmounts, ethToMakerAssetRate, ethToTakerAssetRate, dexQuotes, twoHopQuotes],
|
[tokenDecimals, orderFillableAmounts, ethToMakerAssetRate, ethToTakerAssetRate, dexQuotes, twoHopQuotes],
|
||||||
rfqtIndicativeQuotes,
|
rfqtIndicativeQuotes,
|
||||||
offChainBalancerQuotes,
|
offChainBalancerQuotes,
|
||||||
offChainCreamQuotes,
|
offChainCreamQuotes,
|
||||||
offChainBancorQuotes,
|
] = await Promise.all([samplerPromise, rfqtPromise, offChainBalancerPromise, offChainCreamPromise]);
|
||||||
] = await Promise.all([
|
|
||||||
samplerPromise,
|
|
||||||
rfqtPromise,
|
|
||||||
offChainBalancerPromise,
|
|
||||||
offChainCreamPromise,
|
|
||||||
offChainBancorPromise,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const [makerTokenDecimals, takerTokenDecimals] = tokenDecimals;
|
const [makerTokenDecimals, takerTokenDecimals] = tokenDecimals;
|
||||||
return {
|
return {
|
||||||
@@ -228,7 +216,7 @@ export class MarketOperationUtils {
|
|||||||
inputAmount: takerAmount,
|
inputAmount: takerAmount,
|
||||||
inputToken: takerToken,
|
inputToken: takerToken,
|
||||||
outputToken: makerToken,
|
outputToken: makerToken,
|
||||||
dexQuotes: dexQuotes.concat([...offChainBalancerQuotes, ...offChainCreamQuotes, offChainBancorQuotes]),
|
dexQuotes: dexQuotes.concat([...offChainBalancerQuotes, ...offChainCreamQuotes]),
|
||||||
nativeOrders,
|
nativeOrders,
|
||||||
orderFillableAmounts,
|
orderFillableAmounts,
|
||||||
ethToOutputRate: ethToMakerAssetRate,
|
ethToOutputRate: ethToMakerAssetRate,
|
||||||
@@ -630,7 +618,7 @@ export class MarketOperationUtils {
|
|||||||
side === MarketOperation.Sell
|
side === MarketOperation.Sell
|
||||||
? this.getMarketSellLiquidityAsync.bind(this)
|
? this.getMarketSellLiquidityAsync.bind(this)
|
||||||
: this.getMarketBuyLiquidityAsync.bind(this);
|
: this.getMarketBuyLiquidityAsync.bind(this);
|
||||||
const marketSideLiquidity: MarketSideLiquidity = await marketLiquidityFnAsync(nativeOrders, amount, _opts);
|
let marketSideLiquidity: MarketSideLiquidity = await marketLiquidityFnAsync(nativeOrders, amount, _opts);
|
||||||
let optimizerResult: OptimizerResult | undefined;
|
let optimizerResult: OptimizerResult | undefined;
|
||||||
try {
|
try {
|
||||||
optimizerResult = await this._generateOptimizedOrdersAsync(marketSideLiquidity, optimizerOpts);
|
optimizerResult = await this._generateOptimizedOrdersAsync(marketSideLiquidity, optimizerOpts);
|
||||||
@@ -673,13 +661,11 @@ export class MarketOperationUtils {
|
|||||||
);
|
);
|
||||||
// Re-run optimizer with the new indicative quote
|
// Re-run optimizer with the new indicative quote
|
||||||
if (indicativeQuotes.length > 0) {
|
if (indicativeQuotes.length > 0) {
|
||||||
optimizerResult = await this._generateOptimizedOrdersAsync(
|
marketSideLiquidity = {
|
||||||
{
|
...marketSideLiquidity,
|
||||||
...marketSideLiquidity,
|
rfqtIndicativeQuotes: indicativeQuotes,
|
||||||
rfqtIndicativeQuotes: indicativeQuotes,
|
};
|
||||||
},
|
optimizerResult = await this._generateOptimizedOrdersAsync(marketSideLiquidity, optimizerOpts);
|
||||||
optimizerOpts,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else if (!rfqt.isIndicative && isFirmPriceAwareEnabled) {
|
} else if (!rfqt.isIndicative && isFirmPriceAwareEnabled) {
|
||||||
// A firm quote is being requested, and firm quotes price-aware enabled. Ensure that `intentOnFilling` is enabled.
|
// A firm quote is being requested, and firm quotes price-aware enabled. Ensure that `intentOnFilling` is enabled.
|
||||||
@@ -707,19 +693,18 @@ export class MarketOperationUtils {
|
|||||||
? firmQuoteSignedOrders.map(signedOrder => signedOrder.takerAssetAmount)
|
? firmQuoteSignedOrders.map(signedOrder => signedOrder.takerAssetAmount)
|
||||||
: await rfqt.firmQuoteValidator.getRfqtTakerFillableAmountsAsync(firmQuoteSignedOrders);
|
: await rfqt.firmQuoteValidator.getRfqtTakerFillableAmountsAsync(firmQuoteSignedOrders);
|
||||||
|
|
||||||
|
marketSideLiquidity = {
|
||||||
|
...marketSideLiquidity,
|
||||||
|
nativeOrders: marketSideLiquidity.nativeOrders.concat(firmQuoteSignedOrders),
|
||||||
|
orderFillableAmounts: marketSideLiquidity.orderFillableAmounts.concat(
|
||||||
|
rfqOrderFillableAmounts,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
// Re-run optimizer with the new firm quote. This is the second and last time
|
// Re-run optimizer with the new firm quote. This is the second and last time
|
||||||
// we run the optimized in a block of code. In this case, we don't catch a potential `NoOptimalPath` exception
|
// we run the optimized in a block of code. In this case, we don't catch a potential `NoOptimalPath` exception
|
||||||
// and we let it bubble up if it happens.
|
// and we let it bubble up if it happens.
|
||||||
optimizerResult = await this._generateOptimizedOrdersAsync(
|
optimizerResult = await this._generateOptimizedOrdersAsync(marketSideLiquidity, optimizerOpts);
|
||||||
{
|
|
||||||
...marketSideLiquidity,
|
|
||||||
nativeOrders: marketSideLiquidity.nativeOrders.concat(firmQuoteSignedOrders),
|
|
||||||
orderFillableAmounts: marketSideLiquidity.orderFillableAmounts.concat(
|
|
||||||
rfqOrderFillableAmounts,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
optimizerOpts,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -735,7 +720,6 @@ export class MarketOperationUtils {
|
|||||||
let quoteReport: QuoteReport | undefined;
|
let quoteReport: QuoteReport | undefined;
|
||||||
if (_opts.shouldGenerateQuoteReport) {
|
if (_opts.shouldGenerateQuoteReport) {
|
||||||
quoteReport = MarketOperationUtils._computeQuoteReport(
|
quoteReport = MarketOperationUtils._computeQuoteReport(
|
||||||
nativeOrders,
|
|
||||||
_opts.rfqt ? _opts.rfqt.quoteRequestor : undefined,
|
_opts.rfqt ? _opts.rfqt.quoteRequestor : undefined,
|
||||||
marketSideLiquidity,
|
marketSideLiquidity,
|
||||||
optimizerResult,
|
optimizerResult,
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import { SupportedProvider } from '@0x/dev-utils';
|
|
||||||
import { BigNumber, NULL_BYTES } from '@0x/utils';
|
import { BigNumber, NULL_BYTES } from '@0x/utils';
|
||||||
|
|
||||||
import { SamplerOverrides } from '../../types';
|
import { SamplerOverrides } from '../../types';
|
||||||
@@ -36,21 +35,19 @@ export class DexOrderSampler extends SamplerOperations {
|
|||||||
constructor(
|
constructor(
|
||||||
_samplerContract: ERC20BridgeSamplerContract,
|
_samplerContract: ERC20BridgeSamplerContract,
|
||||||
private readonly _samplerOverrides?: SamplerOverrides,
|
private readonly _samplerOverrides?: SamplerOverrides,
|
||||||
provider?: SupportedProvider,
|
|
||||||
balancerPoolsCache?: BalancerPoolsCache,
|
balancerPoolsCache?: BalancerPoolsCache,
|
||||||
creamPoolsCache?: CreamPoolsCache,
|
creamPoolsCache?: CreamPoolsCache,
|
||||||
getBancorServiceFn?: () => BancorService,
|
|
||||||
tokenAdjacencyGraph?: TokenAdjacencyGraph,
|
tokenAdjacencyGraph?: TokenAdjacencyGraph,
|
||||||
liquidityProviderRegistry?: LiquidityProviderRegistry,
|
liquidityProviderRegistry?: LiquidityProviderRegistry,
|
||||||
|
bancorServiceFn: () => Promise<BancorService | undefined> = async () => undefined,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
_samplerContract,
|
_samplerContract,
|
||||||
provider,
|
|
||||||
balancerPoolsCache,
|
balancerPoolsCache,
|
||||||
creamPoolsCache,
|
creamPoolsCache,
|
||||||
getBancorServiceFn,
|
|
||||||
tokenAdjacencyGraph,
|
tokenAdjacencyGraph,
|
||||||
liquidityProviderRegistry,
|
liquidityProviderRegistry,
|
||||||
|
bancorServiceFn,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import { SupportedProvider } from '@0x/dev-utils';
|
|
||||||
import { SignedOrder } from '@0x/types';
|
import { SignedOrder } from '@0x/types';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
@@ -81,25 +80,16 @@ export class SamplerOperations {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected readonly _samplerContract: ERC20BridgeSamplerContract,
|
protected readonly _samplerContract: ERC20BridgeSamplerContract,
|
||||||
public readonly provider?: SupportedProvider,
|
|
||||||
public readonly balancerPoolsCache: BalancerPoolsCache = new BalancerPoolsCache(),
|
public readonly balancerPoolsCache: BalancerPoolsCache = new BalancerPoolsCache(),
|
||||||
public readonly creamPoolsCache: CreamPoolsCache = new CreamPoolsCache(),
|
public readonly creamPoolsCache: CreamPoolsCache = new CreamPoolsCache(),
|
||||||
protected readonly getBancorServiceFn?: () => BancorService, // for dependency injection in tests
|
|
||||||
protected readonly tokenAdjacencyGraph: TokenAdjacencyGraph = { default: [] },
|
protected readonly tokenAdjacencyGraph: TokenAdjacencyGraph = { default: [] },
|
||||||
public readonly liquidityProviderRegistry: LiquidityProviderRegistry = LIQUIDITY_PROVIDER_REGISTRY,
|
public readonly liquidityProviderRegistry: LiquidityProviderRegistry = LIQUIDITY_PROVIDER_REGISTRY,
|
||||||
) {}
|
bancorServiceFn: () => Promise<BancorService | undefined> = async () => undefined,
|
||||||
|
) {
|
||||||
public async getBancorServiceAsync(): Promise<BancorService> {
|
// Initialize the Bancor service, fetching paths in the background
|
||||||
if (this.getBancorServiceFn !== undefined) {
|
bancorServiceFn()
|
||||||
return this.getBancorServiceFn();
|
.then(service => (this._bancorService = service))
|
||||||
}
|
.catch(/* do nothing */);
|
||||||
if (this.provider === undefined) {
|
|
||||||
throw new Error('Cannot sample liquidity from Bancor; no provider supplied.');
|
|
||||||
}
|
|
||||||
if (this._bancorService === undefined) {
|
|
||||||
this._bancorService = await BancorService.createAsync(this.provider);
|
|
||||||
}
|
|
||||||
return this._bancorService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTokenDecimals(makerTokenAddress: string, takerTokenAddress: string): BatchedOperation<BigNumber[]> {
|
public getTokenDecimals(makerTokenAddress: string, takerTokenAddress: string): BatchedOperation<BigNumber[]> {
|
||||||
@@ -588,28 +578,48 @@ export class SamplerOperations {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getBancorSellQuotesOffChainAsync(
|
public getBancorSellQuotes(
|
||||||
makerToken: string,
|
makerToken: string,
|
||||||
takerToken: string,
|
takerToken: string,
|
||||||
takerFillAmounts: BigNumber[],
|
takerFillAmounts: BigNumber[],
|
||||||
): Promise<Array<DexSample<BancorFillData>>> {
|
): SourceQuoteOperation<BancorFillData> {
|
||||||
const bancorService = await this.getBancorServiceAsync();
|
const paths = this._bancorService ? this._bancorService.getPaths(takerToken, makerToken) : [];
|
||||||
try {
|
return new SamplerContractOperation({
|
||||||
const quotes = await bancorService.getQuotesAsync(takerToken, makerToken, takerFillAmounts);
|
source: ERC20BridgeSource.Bancor,
|
||||||
return quotes.map((quote, i) => ({
|
contract: this._samplerContract,
|
||||||
source: ERC20BridgeSource.Bancor,
|
function: this._samplerContract.sampleSellsFromBancor,
|
||||||
output: quote.amount,
|
params: [paths, takerToken, makerToken, takerFillAmounts],
|
||||||
input: takerFillAmounts[i],
|
callback: (callResults: string, fillData: BancorFillData): BigNumber[] => {
|
||||||
fillData: quote.fillData,
|
const [networkAddress, path, samples] = this._samplerContract.getABIDecodedReturnData<
|
||||||
}));
|
[string, string[], BigNumber[]]
|
||||||
} catch (e) {
|
>('sampleSellsFromBancor', callResults);
|
||||||
return takerFillAmounts.map(input => ({
|
fillData.networkAddress = networkAddress;
|
||||||
source: ERC20BridgeSource.Bancor,
|
fillData.path = path;
|
||||||
output: ZERO_AMOUNT,
|
return samples;
|
||||||
input,
|
},
|
||||||
fillData: { path: [], networkAddress: '' },
|
});
|
||||||
}));
|
}
|
||||||
}
|
|
||||||
|
// Unimplemented
|
||||||
|
public getBancorBuyQuotes(
|
||||||
|
makerToken: string,
|
||||||
|
takerToken: string,
|
||||||
|
makerFillAmounts: BigNumber[],
|
||||||
|
): SourceQuoteOperation<BancorFillData> {
|
||||||
|
return new SamplerContractOperation({
|
||||||
|
source: ERC20BridgeSource.Bancor,
|
||||||
|
contract: this._samplerContract,
|
||||||
|
function: this._samplerContract.sampleBuysFromBancor,
|
||||||
|
params: [[], takerToken, makerToken, makerFillAmounts],
|
||||||
|
callback: (callResults: string, fillData: BancorFillData): BigNumber[] => {
|
||||||
|
const [networkAddress, path, samples] = this._samplerContract.getABIDecodedReturnData<
|
||||||
|
[string, string[], BigNumber[]]
|
||||||
|
>('sampleSellsFromBancor', callResults);
|
||||||
|
fillData.networkAddress = networkAddress;
|
||||||
|
fillData.path = path;
|
||||||
|
return samples;
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMooniswapSellQuotes(
|
public getMooniswapSellQuotes(
|
||||||
@@ -1114,6 +1124,8 @@ export class SamplerOperations {
|
|||||||
);
|
);
|
||||||
case ERC20BridgeSource.Dodo:
|
case ERC20BridgeSource.Dodo:
|
||||||
return this.getDODOSellQuotes(makerToken, takerToken, takerFillAmounts);
|
return this.getDODOSellQuotes(makerToken, takerToken, takerFillAmounts);
|
||||||
|
case ERC20BridgeSource.Bancor:
|
||||||
|
return this.getBancorSellQuotes(makerToken, takerToken, takerFillAmounts);
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unsupported sell sample source: ${source}`);
|
throw new Error(`Unsupported sell sample source: ${source}`);
|
||||||
}
|
}
|
||||||
@@ -1237,6 +1249,8 @@ export class SamplerOperations {
|
|||||||
);
|
);
|
||||||
case ERC20BridgeSource.Dodo:
|
case ERC20BridgeSource.Dodo:
|
||||||
return this.getDODOBuyQuotes(makerToken, takerToken, makerFillAmounts);
|
return this.getDODOBuyQuotes(makerToken, takerToken, makerFillAmounts);
|
||||||
|
case ERC20BridgeSource.Bancor:
|
||||||
|
return this.getBancorBuyQuotes(makerToken, takerToken, makerFillAmounts);
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unsupported buy sample source: ${source}`);
|
throw new Error(`Unsupported buy sample source: ${source}`);
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,7 @@ import { ContractArtifact } from 'ethereum-types';
|
|||||||
import * as ApproximateBuys from '../test/generated-artifacts/ApproximateBuys.json';
|
import * as ApproximateBuys from '../test/generated-artifacts/ApproximateBuys.json';
|
||||||
import * as BalanceChecker from '../test/generated-artifacts/BalanceChecker.json';
|
import * as BalanceChecker from '../test/generated-artifacts/BalanceChecker.json';
|
||||||
import * as BalancerSampler from '../test/generated-artifacts/BalancerSampler.json';
|
import * as BalancerSampler from '../test/generated-artifacts/BalancerSampler.json';
|
||||||
|
import * as BancorSampler from '../test/generated-artifacts/BancorSampler.json';
|
||||||
import * as CurveSampler from '../test/generated-artifacts/CurveSampler.json';
|
import * as CurveSampler from '../test/generated-artifacts/CurveSampler.json';
|
||||||
import * as DeploymentConstants from '../test/generated-artifacts/DeploymentConstants.json';
|
import * as DeploymentConstants from '../test/generated-artifacts/DeploymentConstants.json';
|
||||||
import * as DODOSampler from '../test/generated-artifacts/DODOSampler.json';
|
import * as DODOSampler from '../test/generated-artifacts/DODOSampler.json';
|
||||||
@@ -15,6 +16,7 @@ import * as DummyLiquidityProvider from '../test/generated-artifacts/DummyLiquid
|
|||||||
import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json';
|
import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json';
|
||||||
import * as Eth2DaiSampler from '../test/generated-artifacts/Eth2DaiSampler.json';
|
import * as Eth2DaiSampler from '../test/generated-artifacts/Eth2DaiSampler.json';
|
||||||
import * as IBalancer from '../test/generated-artifacts/IBalancer.json';
|
import * as IBalancer from '../test/generated-artifacts/IBalancer.json';
|
||||||
|
import * as IBancor from '../test/generated-artifacts/IBancor.json';
|
||||||
import * as ICurve from '../test/generated-artifacts/ICurve.json';
|
import * as ICurve from '../test/generated-artifacts/ICurve.json';
|
||||||
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
|
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
|
||||||
import * as IKyberNetwork from '../test/generated-artifacts/IKyberNetwork.json';
|
import * as IKyberNetwork from '../test/generated-artifacts/IKyberNetwork.json';
|
||||||
@@ -42,6 +44,7 @@ export const artifacts = {
|
|||||||
ApproximateBuys: ApproximateBuys as ContractArtifact,
|
ApproximateBuys: ApproximateBuys as ContractArtifact,
|
||||||
BalanceChecker: BalanceChecker as ContractArtifact,
|
BalanceChecker: BalanceChecker as ContractArtifact,
|
||||||
BalancerSampler: BalancerSampler as ContractArtifact,
|
BalancerSampler: BalancerSampler as ContractArtifact,
|
||||||
|
BancorSampler: BancorSampler as ContractArtifact,
|
||||||
CurveSampler: CurveSampler as ContractArtifact,
|
CurveSampler: CurveSampler as ContractArtifact,
|
||||||
DODOSampler: DODOSampler as ContractArtifact,
|
DODOSampler: DODOSampler as ContractArtifact,
|
||||||
DeploymentConstants: DeploymentConstants as ContractArtifact,
|
DeploymentConstants: DeploymentConstants as ContractArtifact,
|
||||||
@@ -60,6 +63,7 @@ export const artifacts = {
|
|||||||
UniswapSampler: UniswapSampler as ContractArtifact,
|
UniswapSampler: UniswapSampler as ContractArtifact,
|
||||||
UniswapV2Sampler: UniswapV2Sampler as ContractArtifact,
|
UniswapV2Sampler: UniswapV2Sampler as ContractArtifact,
|
||||||
IBalancer: IBalancer as ContractArtifact,
|
IBalancer: IBalancer as ContractArtifact,
|
||||||
|
IBancor: IBancor as ContractArtifact,
|
||||||
ICurve: ICurve as ContractArtifact,
|
ICurve: ICurve as ContractArtifact,
|
||||||
IEth2Dai: IEth2Dai as ContractArtifact,
|
IEth2Dai: IEth2Dai as ContractArtifact,
|
||||||
IKyberNetwork: IKyberNetwork as ContractArtifact,
|
IKyberNetwork: IKyberNetwork as ContractArtifact,
|
||||||
|
@@ -1,72 +0,0 @@
|
|||||||
import { web3Factory, Web3ProviderEngine } from '@0x/dev-utils';
|
|
||||||
import { Ethereum, getDecimals } from '@bancor/sdk/dist/blockchains/ethereum';
|
|
||||||
import { fromWei, toWei } from '@bancor/sdk/dist/helpers';
|
|
||||||
import { BlockchainType } from '@bancor/sdk/dist/types';
|
|
||||||
import * as chai from 'chai';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
import 'mocha';
|
|
||||||
|
|
||||||
import { BancorFillData, BigNumber } from '../src';
|
|
||||||
import { BancorService, token } from '../src/utils/market_operation_utils/bancor_service';
|
|
||||||
|
|
||||||
import { chaiSetup } from './utils/chai_setup';
|
|
||||||
|
|
||||||
chaiSetup.configure();
|
|
||||||
const expect = chai.expect;
|
|
||||||
|
|
||||||
const ADDRESS_REGEX = /^(0x)?[0-9a-f]{40}$/i;
|
|
||||||
const RPC_URL = `https://mainnet.infura.io/v3/${process.env.INFURA_PROJECT_ID}`;
|
|
||||||
|
|
||||||
const provider: Web3ProviderEngine = web3Factory.getRpcProvider({ rpcUrl: RPC_URL });
|
|
||||||
// tslint:disable:custom-no-magic-numbers
|
|
||||||
|
|
||||||
let bancorService: BancorService;
|
|
||||||
|
|
||||||
// These tests test the bancor SDK against mainnet
|
|
||||||
// TODO (xianny): After we move asset-swapper out of the monorepo, we should add an env variable to circle CI to run this test
|
|
||||||
describe.skip('Bancor Service', () => {
|
|
||||||
const eth = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
|
|
||||||
const bnt = '0x1f573d6fb3f13d689ff844b4ce37794d79a7ff1c';
|
|
||||||
it('should retrieve the bancor network address', async () => {
|
|
||||||
bancorService = await BancorService.createAsync(provider);
|
|
||||||
const networkAddress = bancorService.getBancorNetworkAddress();
|
|
||||||
expect(networkAddress).to.match(ADDRESS_REGEX);
|
|
||||||
});
|
|
||||||
it('should retrieve a quote', async () => {
|
|
||||||
const amt = new BigNumber(10e18);
|
|
||||||
const quotes = await bancorService.getQuotesAsync(eth, bnt, [amt]);
|
|
||||||
const fillData = quotes[0].fillData as BancorFillData;
|
|
||||||
|
|
||||||
// get rate from the bancor sdk
|
|
||||||
const blockchain = bancorService.sdk._core.blockchains[BlockchainType.Ethereum] as Ethereum;
|
|
||||||
const sourceDecimals = await getDecimals(blockchain, token(eth));
|
|
||||||
const rate = await bancorService.sdk.pricing.getRateByPath(
|
|
||||||
fillData.path.map(p => token(p)),
|
|
||||||
fromWei(amt.toString(), sourceDecimals),
|
|
||||||
);
|
|
||||||
const expectedRate = toWei(rate, await getDecimals(blockchain, token(bnt)));
|
|
||||||
|
|
||||||
expect(fillData.networkAddress).to.match(ADDRESS_REGEX);
|
|
||||||
expect(fillData.path[0].toLowerCase()).to.eq(eth);
|
|
||||||
expect(fillData.path[2].toLowerCase()).to.eq(bnt);
|
|
||||||
expect(fillData.path.length).to.eq(3); // eth -> bnt should be single hop!
|
|
||||||
expect(quotes[0].amount.dp(0)).to.bignumber.eq(
|
|
||||||
new BigNumber(expectedRate).multipliedBy(bancorService.minReturnAmountBufferPercentage).dp(0),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
// HACK (xianny): for exploring SDK results
|
|
||||||
it('should retrieve multiple quotes', async () => {
|
|
||||||
const amts = [1, 10, 100, 1000].map(a => new BigNumber(a).multipliedBy(10e18));
|
|
||||||
const quotes = await bancorService.getQuotesAsync(eth, bnt, amts);
|
|
||||||
quotes.map((q, i) => {
|
|
||||||
// tslint:disable:no-console
|
|
||||||
const fillData = q.fillData as BancorFillData;
|
|
||||||
console.log(
|
|
||||||
`Input ${amts[i].toExponential()}; Output: ${q.amount}; Ratio: ${q.amount.dividedBy(amts[i])}, Path: ${
|
|
||||||
fillData.path.length
|
|
||||||
}\nPath: ${JSON.stringify(fillData.path)}`,
|
|
||||||
);
|
|
||||||
// tslint:enable:no-console
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@@ -17,9 +17,7 @@ import { DexOrderSampler, getSampleAmounts } from '../src/utils/market_operation
|
|||||||
import { ERC20BridgeSource, TokenAdjacencyGraph } from '../src/utils/market_operation_utils/types';
|
import { ERC20BridgeSource, TokenAdjacencyGraph } from '../src/utils/market_operation_utils/types';
|
||||||
|
|
||||||
import { MockBalancerPoolsCache } from './utils/mock_balancer_pools_cache';
|
import { MockBalancerPoolsCache } from './utils/mock_balancer_pools_cache';
|
||||||
import { MockBancorService } from './utils/mock_bancor_service';
|
|
||||||
import { MockSamplerContract } from './utils/mock_sampler_contract';
|
import { MockSamplerContract } from './utils/mock_sampler_contract';
|
||||||
import { provider } from './utils/web3_wrapper';
|
|
||||||
|
|
||||||
const CHAIN_ID = 1;
|
const CHAIN_ID = 1;
|
||||||
// tslint:disable: custom-no-magic-numbers
|
// tslint:disable: custom-no-magic-numbers
|
||||||
@@ -104,7 +102,15 @@ describe('DexSampler tests', () => {
|
|||||||
return expectedFillableAmounts;
|
return expectedFillableAmounts;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const dexOrderSampler = new DexOrderSampler(sampler);
|
const dexOrderSampler = new DexOrderSampler(
|
||||||
|
sampler,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
async () => undefined,
|
||||||
|
);
|
||||||
const [fillableAmounts] = await dexOrderSampler.executeAsync(
|
const [fillableAmounts] = await dexOrderSampler.executeAsync(
|
||||||
dexOrderSampler.getOrderFillableMakerAmounts(ORDERS, exchangeAddress),
|
dexOrderSampler.getOrderFillableMakerAmounts(ORDERS, exchangeAddress),
|
||||||
);
|
);
|
||||||
@@ -120,7 +126,15 @@ describe('DexSampler tests', () => {
|
|||||||
return expectedFillableAmounts;
|
return expectedFillableAmounts;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const dexOrderSampler = new DexOrderSampler(sampler);
|
const dexOrderSampler = new DexOrderSampler(
|
||||||
|
sampler,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
async () => undefined,
|
||||||
|
);
|
||||||
const [fillableAmounts] = await dexOrderSampler.executeAsync(
|
const [fillableAmounts] = await dexOrderSampler.executeAsync(
|
||||||
dexOrderSampler.getOrderFillableTakerAmounts(ORDERS, exchangeAddress),
|
dexOrderSampler.getOrderFillableTakerAmounts(ORDERS, exchangeAddress),
|
||||||
);
|
);
|
||||||
@@ -140,7 +154,15 @@ describe('DexSampler tests', () => {
|
|||||||
return ['0x', expectedMakerFillAmounts];
|
return ['0x', expectedMakerFillAmounts];
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const dexOrderSampler = new DexOrderSampler(sampler);
|
const dexOrderSampler = new DexOrderSampler(
|
||||||
|
sampler,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
async () => undefined,
|
||||||
|
);
|
||||||
const [fillableAmounts] = await dexOrderSampler.executeAsync(
|
const [fillableAmounts] = await dexOrderSampler.executeAsync(
|
||||||
dexOrderSampler.getKyberSellQuotes(
|
dexOrderSampler.getKyberSellQuotes(
|
||||||
'0x',
|
'0x',
|
||||||
@@ -171,9 +193,10 @@ describe('DexSampler tests', () => {
|
|||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
{
|
||||||
undefined,
|
[poolAddress]: { tokens: [expectedMakerToken, expectedTakerToken], gasCost },
|
||||||
{ [poolAddress]: { tokens: [expectedMakerToken, expectedTakerToken], gasCost } },
|
},
|
||||||
|
async () => undefined,
|
||||||
);
|
);
|
||||||
const [result] = await dexOrderSampler.executeAsync(
|
const [result] = await dexOrderSampler.executeAsync(
|
||||||
dexOrderSampler.getSellQuotes(
|
dexOrderSampler.getSellQuotes(
|
||||||
@@ -214,9 +237,10 @@ describe('DexSampler tests', () => {
|
|||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
{
|
||||||
undefined,
|
[poolAddress]: { tokens: [expectedMakerToken, expectedTakerToken], gasCost },
|
||||||
{ [poolAddress]: { tokens: [expectedMakerToken, expectedTakerToken], gasCost } },
|
},
|
||||||
|
async () => undefined,
|
||||||
);
|
);
|
||||||
const [result] = await dexOrderSampler.executeAsync(
|
const [result] = await dexOrderSampler.executeAsync(
|
||||||
dexOrderSampler.getBuyQuotes(
|
dexOrderSampler.getBuyQuotes(
|
||||||
@@ -251,7 +275,15 @@ describe('DexSampler tests', () => {
|
|||||||
return expectedMakerFillAmounts;
|
return expectedMakerFillAmounts;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const dexOrderSampler = new DexOrderSampler(sampler);
|
const dexOrderSampler = new DexOrderSampler(
|
||||||
|
sampler,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
async () => undefined,
|
||||||
|
);
|
||||||
const [fillableAmounts] = await dexOrderSampler.executeAsync(
|
const [fillableAmounts] = await dexOrderSampler.executeAsync(
|
||||||
dexOrderSampler.getEth2DaiSellQuotes(expectedMakerToken, expectedTakerToken, expectedTakerFillAmounts),
|
dexOrderSampler.getEth2DaiSellQuotes(expectedMakerToken, expectedTakerToken, expectedTakerFillAmounts),
|
||||||
);
|
);
|
||||||
@@ -271,7 +303,15 @@ describe('DexSampler tests', () => {
|
|||||||
return expectedMakerFillAmounts;
|
return expectedMakerFillAmounts;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const dexOrderSampler = new DexOrderSampler(sampler);
|
const dexOrderSampler = new DexOrderSampler(
|
||||||
|
sampler,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
async () => undefined,
|
||||||
|
);
|
||||||
const [fillableAmounts] = await dexOrderSampler.executeAsync(
|
const [fillableAmounts] = await dexOrderSampler.executeAsync(
|
||||||
dexOrderSampler.getUniswapSellQuotes(expectedMakerToken, expectedTakerToken, expectedTakerFillAmounts),
|
dexOrderSampler.getUniswapSellQuotes(expectedMakerToken, expectedTakerToken, expectedTakerFillAmounts),
|
||||||
);
|
);
|
||||||
@@ -290,7 +330,15 @@ describe('DexSampler tests', () => {
|
|||||||
return expectedMakerFillAmounts;
|
return expectedMakerFillAmounts;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const dexOrderSampler = new DexOrderSampler(sampler);
|
const dexOrderSampler = new DexOrderSampler(
|
||||||
|
sampler,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
async () => undefined,
|
||||||
|
);
|
||||||
const [fillableAmounts] = await dexOrderSampler.executeAsync(
|
const [fillableAmounts] = await dexOrderSampler.executeAsync(
|
||||||
dexOrderSampler.getUniswapV2SellQuotes(
|
dexOrderSampler.getUniswapV2SellQuotes(
|
||||||
[expectedMakerToken, expectedTakerToken],
|
[expectedMakerToken, expectedTakerToken],
|
||||||
@@ -313,7 +361,15 @@ describe('DexSampler tests', () => {
|
|||||||
return expectedTakerFillAmounts;
|
return expectedTakerFillAmounts;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const dexOrderSampler = new DexOrderSampler(sampler);
|
const dexOrderSampler = new DexOrderSampler(
|
||||||
|
sampler,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
async () => undefined,
|
||||||
|
);
|
||||||
const [fillableAmounts] = await dexOrderSampler.executeAsync(
|
const [fillableAmounts] = await dexOrderSampler.executeAsync(
|
||||||
dexOrderSampler.getEth2DaiBuyQuotes(expectedMakerToken, expectedTakerToken, expectedMakerFillAmounts),
|
dexOrderSampler.getEth2DaiBuyQuotes(expectedMakerToken, expectedTakerToken, expectedMakerFillAmounts),
|
||||||
);
|
);
|
||||||
@@ -333,7 +389,15 @@ describe('DexSampler tests', () => {
|
|||||||
return expectedTakerFillAmounts;
|
return expectedTakerFillAmounts;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const dexOrderSampler = new DexOrderSampler(sampler);
|
const dexOrderSampler = new DexOrderSampler(
|
||||||
|
sampler,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
async () => undefined,
|
||||||
|
);
|
||||||
const [fillableAmounts] = await dexOrderSampler.executeAsync(
|
const [fillableAmounts] = await dexOrderSampler.executeAsync(
|
||||||
dexOrderSampler.getUniswapBuyQuotes(expectedMakerToken, expectedTakerToken, expectedMakerFillAmounts),
|
dexOrderSampler.getUniswapBuyQuotes(expectedMakerToken, expectedTakerToken, expectedMakerFillAmounts),
|
||||||
);
|
);
|
||||||
@@ -385,9 +449,9 @@ describe('DexSampler tests', () => {
|
|||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
tokenAdjacencyGraph,
|
tokenAdjacencyGraph,
|
||||||
|
undefined,
|
||||||
|
async () => undefined,
|
||||||
);
|
);
|
||||||
const [quotes] = await dexOrderSampler.executeAsync(
|
const [quotes] = await dexOrderSampler.executeAsync(
|
||||||
dexOrderSampler.getSellQuotes(
|
dexOrderSampler.getSellQuotes(
|
||||||
@@ -441,8 +505,11 @@ describe('DexSampler tests', () => {
|
|||||||
const dexOrderSampler = new DexOrderSampler(
|
const dexOrderSampler = new DexOrderSampler(
|
||||||
new MockSamplerContract({}),
|
new MockSamplerContract({}),
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
|
||||||
balancerPoolsCache,
|
balancerPoolsCache,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
async () => undefined,
|
||||||
);
|
);
|
||||||
const quotes = await dexOrderSampler.getBalancerSellQuotesOffChainAsync(
|
const quotes = await dexOrderSampler.getBalancerSellQuotesOffChainAsync(
|
||||||
expectedMakerToken,
|
expectedMakerToken,
|
||||||
@@ -451,45 +518,6 @@ describe('DexSampler tests', () => {
|
|||||||
);
|
);
|
||||||
expect(quotes).to.have.lengthOf(0);
|
expect(quotes).to.have.lengthOf(0);
|
||||||
});
|
});
|
||||||
it('getSellQuotes() uses samples from Bancor', async () => {
|
|
||||||
const expectedTakerToken = randomAddress();
|
|
||||||
const expectedMakerToken = randomAddress();
|
|
||||||
const networkAddress = randomAddress();
|
|
||||||
const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 3);
|
|
||||||
const rate = getRandomFloat(0, 100);
|
|
||||||
const bancorService = await MockBancorService.createMockAsync({
|
|
||||||
getQuotesAsync: async (fromToken: string, toToken: string, amounts: BigNumber[]) => {
|
|
||||||
expect(fromToken).equal(expectedTakerToken);
|
|
||||||
expect(toToken).equal(expectedMakerToken);
|
|
||||||
return Promise.resolve(
|
|
||||||
amounts.map(a => ({
|
|
||||||
fillData: { path: [fromToken, toToken], networkAddress },
|
|
||||||
amount: a.multipliedBy(rate),
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const dexOrderSampler = new DexOrderSampler(
|
|
||||||
new MockSamplerContract({}),
|
|
||||||
undefined, // sampler overrides
|
|
||||||
provider,
|
|
||||||
undefined, // balancer cache
|
|
||||||
undefined, // cream cache
|
|
||||||
() => bancorService,
|
|
||||||
);
|
|
||||||
const quotes = await dexOrderSampler.getBancorSellQuotesOffChainAsync(
|
|
||||||
expectedMakerToken,
|
|
||||||
expectedTakerToken,
|
|
||||||
expectedTakerFillAmounts,
|
|
||||||
);
|
|
||||||
const expectedQuotes = expectedTakerFillAmounts.map(a => ({
|
|
||||||
source: ERC20BridgeSource.Bancor,
|
|
||||||
input: a,
|
|
||||||
output: a.multipliedBy(rate),
|
|
||||||
fillData: { path: [expectedTakerToken, expectedMakerToken], networkAddress },
|
|
||||||
}));
|
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
|
||||||
});
|
|
||||||
it('getBuyQuotes()', async () => {
|
it('getBuyQuotes()', async () => {
|
||||||
const expectedTakerToken = randomAddress();
|
const expectedTakerToken = randomAddress();
|
||||||
const expectedMakerToken = randomAddress();
|
const expectedMakerToken = randomAddress();
|
||||||
@@ -530,9 +558,9 @@ describe('DexSampler tests', () => {
|
|||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
tokenAdjacencyGraph,
|
tokenAdjacencyGraph,
|
||||||
|
undefined,
|
||||||
|
async () => undefined,
|
||||||
);
|
);
|
||||||
const [quotes] = await dexOrderSampler.executeAsync(
|
const [quotes] = await dexOrderSampler.executeAsync(
|
||||||
dexOrderSampler.getBuyQuotes(sources, expectedMakerToken, expectedTakerToken, expectedMakerFillAmounts),
|
dexOrderSampler.getBuyQuotes(sources, expectedMakerToken, expectedTakerToken, expectedMakerFillAmounts),
|
||||||
@@ -577,8 +605,11 @@ describe('DexSampler tests', () => {
|
|||||||
const dexOrderSampler = new DexOrderSampler(
|
const dexOrderSampler = new DexOrderSampler(
|
||||||
new MockSamplerContract({}),
|
new MockSamplerContract({}),
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
|
||||||
balancerPoolsCache,
|
balancerPoolsCache,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
async () => undefined,
|
||||||
);
|
);
|
||||||
const quotes = await dexOrderSampler.getBalancerBuyQuotesOffChainAsync(
|
const quotes = await dexOrderSampler.getBalancerBuyQuotesOffChainAsync(
|
||||||
expectedMakerToken,
|
expectedMakerToken,
|
||||||
@@ -605,7 +636,15 @@ describe('DexSampler tests', () => {
|
|||||||
return expectedFillableTakerAmounts;
|
return expectedFillableTakerAmounts;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const dexOrderSampler = new DexOrderSampler(sampler);
|
const dexOrderSampler = new DexOrderSampler(
|
||||||
|
sampler,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
async () => undefined,
|
||||||
|
);
|
||||||
const [fillableMakerAmounts, fillableTakerAmounts] = await dexOrderSampler.executeAsync(
|
const [fillableMakerAmounts, fillableTakerAmounts] = await dexOrderSampler.executeAsync(
|
||||||
dexOrderSampler.getOrderFillableMakerAmounts(ORDERS, exchangeAddress),
|
dexOrderSampler.getOrderFillableMakerAmounts(ORDERS, exchangeAddress),
|
||||||
dexOrderSampler.getOrderFillableTakerAmounts(ORDERS, exchangeAddress),
|
dexOrderSampler.getOrderFillableTakerAmounts(ORDERS, exchangeAddress),
|
||||||
|
@@ -1,33 +0,0 @@
|
|||||||
import { BigNumber } from '@0x/utils';
|
|
||||||
import { SDK } from '@bancor/sdk';
|
|
||||||
|
|
||||||
import { BancorService } from '../../src/utils/market_operation_utils/bancor_service';
|
|
||||||
import { BancorFillData, Quote } from '../../src/utils/market_operation_utils/types';
|
|
||||||
|
|
||||||
export interface Handlers {
|
|
||||||
getQuotesAsync: (fromToken: string, toToken: string, amount: BigNumber[]) => Promise<Array<Quote<BancorFillData>>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MockBancorService extends BancorService {
|
|
||||||
// Bancor recommends setting this value to 2% under the expected return amount
|
|
||||||
public minReturnAmountBufferPercentage = 0.98;
|
|
||||||
|
|
||||||
public static async createMockAsync(handlers: Partial<Handlers>): Promise<MockBancorService> {
|
|
||||||
const sdk = new SDK();
|
|
||||||
return new MockBancorService(sdk, handlers);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(sdk: SDK, public handlers: Partial<Handlers>) {
|
|
||||||
super(sdk);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getQuotesAsync(
|
|
||||||
fromToken: string,
|
|
||||||
toToken: string,
|
|
||||||
amounts: BigNumber[],
|
|
||||||
): Promise<Array<Quote<BancorFillData>>> {
|
|
||||||
return this.handlers.getQuotesAsync
|
|
||||||
? this.handlers.getQuotesAsync(fromToken, toToken, amounts)
|
|
||||||
: super.getQuotesAsync(fromToken, toToken, amounts);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -6,6 +6,7 @@
|
|||||||
export * from '../test/generated-wrappers/approximate_buys';
|
export * from '../test/generated-wrappers/approximate_buys';
|
||||||
export * from '../test/generated-wrappers/balance_checker';
|
export * from '../test/generated-wrappers/balance_checker';
|
||||||
export * from '../test/generated-wrappers/balancer_sampler';
|
export * from '../test/generated-wrappers/balancer_sampler';
|
||||||
|
export * from '../test/generated-wrappers/bancor_sampler';
|
||||||
export * from '../test/generated-wrappers/curve_sampler';
|
export * from '../test/generated-wrappers/curve_sampler';
|
||||||
export * from '../test/generated-wrappers/d_o_d_o_sampler';
|
export * from '../test/generated-wrappers/d_o_d_o_sampler';
|
||||||
export * from '../test/generated-wrappers/deployment_constants';
|
export * from '../test/generated-wrappers/deployment_constants';
|
||||||
@@ -13,6 +14,7 @@ export * from '../test/generated-wrappers/dummy_liquidity_provider';
|
|||||||
export * from '../test/generated-wrappers/erc20_bridge_sampler';
|
export * from '../test/generated-wrappers/erc20_bridge_sampler';
|
||||||
export * from '../test/generated-wrappers/eth2_dai_sampler';
|
export * from '../test/generated-wrappers/eth2_dai_sampler';
|
||||||
export * from '../test/generated-wrappers/i_balancer';
|
export * from '../test/generated-wrappers/i_balancer';
|
||||||
|
export * from '../test/generated-wrappers/i_bancor';
|
||||||
export * from '../test/generated-wrappers/i_curve';
|
export * from '../test/generated-wrappers/i_curve';
|
||||||
export * from '../test/generated-wrappers/i_eth2_dai';
|
export * from '../test/generated-wrappers/i_eth2_dai';
|
||||||
export * from '../test/generated-wrappers/i_kyber_network';
|
export * from '../test/generated-wrappers/i_kyber_network';
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
"test/generated-artifacts/ApproximateBuys.json",
|
"test/generated-artifacts/ApproximateBuys.json",
|
||||||
"test/generated-artifacts/BalanceChecker.json",
|
"test/generated-artifacts/BalanceChecker.json",
|
||||||
"test/generated-artifacts/BalancerSampler.json",
|
"test/generated-artifacts/BalancerSampler.json",
|
||||||
|
"test/generated-artifacts/BancorSampler.json",
|
||||||
"test/generated-artifacts/CurveSampler.json",
|
"test/generated-artifacts/CurveSampler.json",
|
||||||
"test/generated-artifacts/DODOSampler.json",
|
"test/generated-artifacts/DODOSampler.json",
|
||||||
"test/generated-artifacts/DeploymentConstants.json",
|
"test/generated-artifacts/DeploymentConstants.json",
|
||||||
@@ -15,6 +16,7 @@
|
|||||||
"test/generated-artifacts/ERC20BridgeSampler.json",
|
"test/generated-artifacts/ERC20BridgeSampler.json",
|
||||||
"test/generated-artifacts/Eth2DaiSampler.json",
|
"test/generated-artifacts/Eth2DaiSampler.json",
|
||||||
"test/generated-artifacts/IBalancer.json",
|
"test/generated-artifacts/IBalancer.json",
|
||||||
|
"test/generated-artifacts/IBancor.json",
|
||||||
"test/generated-artifacts/ICurve.json",
|
"test/generated-artifacts/ICurve.json",
|
||||||
"test/generated-artifacts/IEth2Dai.json",
|
"test/generated-artifacts/IEth2Dai.json",
|
||||||
"test/generated-artifacts/IKyberNetwork.json",
|
"test/generated-artifacts/IKyberNetwork.json",
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contract-wrappers-test",
|
"name": "@0x/contract-wrappers-test",
|
||||||
"version": "12.2.28",
|
"version": "12.2.30",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
"@0x/contract-wrappers": "^13.11.0",
|
"@0x/contract-wrappers": "^13.11.0",
|
||||||
"@0x/contracts-test-utils": "^5.3.15",
|
"@0x/contracts-test-utils": "^5.3.15",
|
||||||
"@0x/dev-utils": "^4.1.3",
|
"@0x/dev-utils": "^4.1.3",
|
||||||
"@0x/migrations": "^6.5.4",
|
"@0x/migrations": "^6.5.6",
|
||||||
"@0x/order-utils": "^10.4.10",
|
"@0x/order-utils": "^10.4.10",
|
||||||
"@0x/subproviders": "^6.2.3",
|
"@0x/subproviders": "^6.2.3",
|
||||||
"@0x/ts-doc-gen": "^0.0.28",
|
"@0x/ts-doc-gen": "^0.0.28",
|
||||||
|
@@ -1,4 +1,22 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1608149382,
|
||||||
|
"version": "6.5.6",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1608105788,
|
||||||
|
"version": "6.5.5",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1607485227,
|
"timestamp": 1607485227,
|
||||||
"version": "6.5.4",
|
"version": "6.5.4",
|
||||||
|
@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v6.5.6 - _December 16, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v6.5.5 - _December 16, 2020_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v6.5.4 - _December 9, 2020_
|
## v6.5.4 - _December 9, 2020_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/migrations",
|
"name": "@0x/migrations",
|
||||||
"version": "6.5.4",
|
"version": "6.5.6",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -69,19 +69,19 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^6.2.14",
|
"@0x/base-contract": "^6.2.14",
|
||||||
"@0x/contract-addresses": "^5.6.0",
|
"@0x/contract-addresses": "^5.6.0",
|
||||||
"@0x/contracts-asset-proxy": "^3.6.9",
|
"@0x/contracts-asset-proxy": "^3.7.0",
|
||||||
"@0x/contracts-coordinator": "^3.1.18",
|
"@0x/contracts-coordinator": "^3.1.19",
|
||||||
"@0x/contracts-dev-utils": "^1.3.16",
|
"@0x/contracts-dev-utils": "^1.3.17",
|
||||||
"@0x/contracts-erc1155": "^2.1.18",
|
"@0x/contracts-erc1155": "^2.1.18",
|
||||||
"@0x/contracts-erc20": "^3.2.12",
|
"@0x/contracts-erc20": "^3.2.12",
|
||||||
"@0x/contracts-erc721": "^3.1.18",
|
"@0x/contracts-erc721": "^3.1.18",
|
||||||
"@0x/contracts-exchange": "^3.2.18",
|
"@0x/contracts-exchange": "^3.2.19",
|
||||||
"@0x/contracts-exchange-forwarder": "^4.2.18",
|
"@0x/contracts-exchange-forwarder": "^4.2.19",
|
||||||
"@0x/contracts-extensions": "^6.2.12",
|
"@0x/contracts-extensions": "^6.2.13",
|
||||||
"@0x/contracts-multisig": "^4.1.18",
|
"@0x/contracts-multisig": "^4.1.19",
|
||||||
"@0x/contracts-staking": "^2.0.25",
|
"@0x/contracts-staking": "^2.0.26",
|
||||||
"@0x/contracts-utils": "^4.6.3",
|
"@0x/contracts-utils": "^4.6.3",
|
||||||
"@0x/contracts-zero-ex": "^0.12.0",
|
"@0x/contracts-zero-ex": "^0.14.0",
|
||||||
"@0x/sol-compiler": "^4.4.1",
|
"@0x/sol-compiler": "^4.4.1",
|
||||||
"@0x/subproviders": "^6.2.3",
|
"@0x/subproviders": "^6.2.3",
|
||||||
"@0x/typescript-typings": "^5.1.6",
|
"@0x/typescript-typings": "^5.1.6",
|
||||||
|
@@ -1,4 +1,14 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "1.1.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Add the `MetaTransaction` class for EP mtxs",
|
||||||
|
"pr": 90
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1608149382
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1607485227,
|
"timestamp": 1607485227,
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v1.1.0 - _December 16, 2020_
|
||||||
|
|
||||||
|
* Add the `MetaTransaction` class for EP mtxs (#90)
|
||||||
|
|
||||||
## v1.0.1 - _December 9, 2020_
|
## v1.0.1 - _December 9, 2020_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/protocol-utils",
|
"name": "@0x/protocol-utils",
|
||||||
"version": "1.0.1",
|
"version": "1.1.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
|
@@ -1 +1,4 @@
|
|||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
export const ETH_TOKEN_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
|
export const ETH_TOKEN_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
|
||||||
|
export const ZERO = new BigNumber(0);
|
||||||
|
@@ -7,6 +7,8 @@ export interface EIP712Domain {
|
|||||||
verifyingContract: string;
|
verifyingContract: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type EIP712_STRUCT_ABI = Array<{ type: string; name: string }>;
|
||||||
|
|
||||||
export const EIP712_DOMAIN_PARAMETERS = [
|
export const EIP712_DOMAIN_PARAMETERS = [
|
||||||
{ name: 'name', type: 'string' },
|
{ name: 'name', type: 'string' },
|
||||||
{ name: 'version', type: 'string' },
|
{ name: 'version', type: 'string' },
|
||||||
@@ -68,3 +70,12 @@ export function getExchangeProxyEIP712Hash(structHash: string, chainId?: number,
|
|||||||
hexUtils.concat('0x1901', getExchangeProxyEIP712DomainHash(chainId, verifyingContract), structHash),
|
hexUtils.concat('0x1901', getExchangeProxyEIP712DomainHash(chainId, verifyingContract), structHash),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the type hash of an EIP712 struct given its ABI.
|
||||||
|
*/
|
||||||
|
export function getTypeHash(structName: string, abi: EIP712_STRUCT_ABI): string {
|
||||||
|
return hexUtils.hash(
|
||||||
|
hexUtils.toHex(Buffer.from([`${structName}(`, abi.map(a => `${a.type} ${a.name}`).join(','), ')'].join(''))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@@ -4,6 +4,7 @@ export const RevertError = _RevertErrors.RevertError;
|
|||||||
|
|
||||||
export * from './eip712_utils';
|
export * from './eip712_utils';
|
||||||
export * from './orders';
|
export * from './orders';
|
||||||
|
export * from './meta_transactions';
|
||||||
export * from './signature_utils';
|
export * from './signature_utils';
|
||||||
export * from './transformer_utils';
|
export * from './transformer_utils';
|
||||||
export * from './constants';
|
export * from './constants';
|
||||||
|
171
packages/protocol-utils/src/meta_transactions.ts
Normal file
171
packages/protocol-utils/src/meta_transactions.ts
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||||
|
import { SupportedProvider } from '@0x/subproviders';
|
||||||
|
import { EIP712TypedData } from '@0x/types';
|
||||||
|
import { BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils';
|
||||||
|
|
||||||
|
import { ZERO } from './constants';
|
||||||
|
import {
|
||||||
|
createExchangeProxyEIP712Domain,
|
||||||
|
EIP712_DOMAIN_PARAMETERS,
|
||||||
|
getExchangeProxyEIP712Hash,
|
||||||
|
getTypeHash,
|
||||||
|
} from './eip712_utils';
|
||||||
|
import {
|
||||||
|
eip712SignTypedDataWithKey,
|
||||||
|
eip712SignTypedDataWithProviderAsync,
|
||||||
|
ethSignHashWithKey,
|
||||||
|
ethSignHashWithProviderAsync,
|
||||||
|
Signature,
|
||||||
|
SignatureType,
|
||||||
|
} from './signature_utils';
|
||||||
|
|
||||||
|
const MTX_DEFAULT_VALUES = {
|
||||||
|
signer: NULL_ADDRESS,
|
||||||
|
sender: NULL_ADDRESS,
|
||||||
|
minGasPrice: ZERO,
|
||||||
|
maxGasPrice: ZERO,
|
||||||
|
expirationTimeSeconds: ZERO,
|
||||||
|
salt: ZERO,
|
||||||
|
callData: hexUtils.leftPad(0),
|
||||||
|
value: ZERO,
|
||||||
|
feeToken: NULL_ADDRESS,
|
||||||
|
feeAmount: ZERO,
|
||||||
|
chainId: 1,
|
||||||
|
verifyingContract: getContractAddressesForChainOrThrow(1).exchangeProxy,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MetaTransactionFields = typeof MTX_DEFAULT_VALUES;
|
||||||
|
|
||||||
|
export class MetaTransaction {
|
||||||
|
public static readonly STRUCT_NAME = 'MetaTransactionData';
|
||||||
|
public static readonly STRUCT_ABI = [
|
||||||
|
{ type: 'address', name: 'signer' },
|
||||||
|
{ type: 'address', name: 'sender' },
|
||||||
|
{ type: 'uint256', name: 'minGasPrice' },
|
||||||
|
{ type: 'uint256', name: 'maxGasPrice' },
|
||||||
|
{ type: 'uint256', name: 'expirationTimeSeconds' },
|
||||||
|
{ type: 'uint256', name: 'salt' },
|
||||||
|
{ type: 'bytes', name: 'callData' },
|
||||||
|
{ type: 'uint256', name: 'value' },
|
||||||
|
{ type: 'address', name: 'feeToken' },
|
||||||
|
{ type: 'uint256', name: 'feeAmount' },
|
||||||
|
];
|
||||||
|
public static readonly TYPE_HASH = getTypeHash(MetaTransaction.STRUCT_NAME, MetaTransaction.STRUCT_ABI);
|
||||||
|
|
||||||
|
public signer: string;
|
||||||
|
public sender: string;
|
||||||
|
public minGasPrice: BigNumber;
|
||||||
|
public maxGasPrice: BigNumber;
|
||||||
|
public expirationTimeSeconds: BigNumber;
|
||||||
|
public salt: BigNumber;
|
||||||
|
public callData: string;
|
||||||
|
public value: BigNumber;
|
||||||
|
public feeToken: string;
|
||||||
|
public feeAmount: BigNumber;
|
||||||
|
public chainId: number;
|
||||||
|
public verifyingContract: string;
|
||||||
|
|
||||||
|
public constructor(fields: Partial<MetaTransactionFields> = {}) {
|
||||||
|
const _fields = { ...MTX_DEFAULT_VALUES, ...fields };
|
||||||
|
this.signer = _fields.signer;
|
||||||
|
this.sender = _fields.sender;
|
||||||
|
this.minGasPrice = _fields.minGasPrice;
|
||||||
|
this.maxGasPrice = _fields.maxGasPrice;
|
||||||
|
this.expirationTimeSeconds = _fields.expirationTimeSeconds;
|
||||||
|
this.salt = _fields.salt;
|
||||||
|
this.callData = _fields.callData;
|
||||||
|
this.value = _fields.value;
|
||||||
|
this.feeToken = _fields.feeToken;
|
||||||
|
this.feeAmount = _fields.feeAmount;
|
||||||
|
this.chainId = _fields.chainId;
|
||||||
|
this.verifyingContract = _fields.verifyingContract;
|
||||||
|
}
|
||||||
|
|
||||||
|
public clone(fields: Partial<MetaTransactionFields> = {}): MetaTransaction {
|
||||||
|
return new MetaTransaction({
|
||||||
|
signer: this.signer,
|
||||||
|
sender: this.sender,
|
||||||
|
minGasPrice: this.minGasPrice,
|
||||||
|
maxGasPrice: this.maxGasPrice,
|
||||||
|
expirationTimeSeconds: this.expirationTimeSeconds,
|
||||||
|
salt: this.salt,
|
||||||
|
callData: this.callData,
|
||||||
|
value: this.value,
|
||||||
|
feeToken: this.feeToken,
|
||||||
|
feeAmount: this.feeAmount,
|
||||||
|
chainId: this.chainId,
|
||||||
|
verifyingContract: this.verifyingContract,
|
||||||
|
...fields,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getStructHash(): string {
|
||||||
|
return hexUtils.hash(
|
||||||
|
hexUtils.concat(
|
||||||
|
hexUtils.leftPad(MetaTransaction.TYPE_HASH),
|
||||||
|
hexUtils.leftPad(this.signer),
|
||||||
|
hexUtils.leftPad(this.sender),
|
||||||
|
hexUtils.leftPad(this.minGasPrice),
|
||||||
|
hexUtils.leftPad(this.maxGasPrice),
|
||||||
|
hexUtils.leftPad(this.expirationTimeSeconds),
|
||||||
|
hexUtils.leftPad(this.salt),
|
||||||
|
hexUtils.hash(this.callData),
|
||||||
|
hexUtils.leftPad(this.value),
|
||||||
|
hexUtils.leftPad(this.feeToken),
|
||||||
|
hexUtils.leftPad(this.feeAmount),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getEIP712TypedData(): EIP712TypedData {
|
||||||
|
return {
|
||||||
|
types: {
|
||||||
|
EIP712Domain: EIP712_DOMAIN_PARAMETERS,
|
||||||
|
[MetaTransaction.STRUCT_NAME]: MetaTransaction.STRUCT_ABI,
|
||||||
|
},
|
||||||
|
domain: createExchangeProxyEIP712Domain(this.chainId, this.verifyingContract) as any,
|
||||||
|
primaryType: MetaTransaction.STRUCT_NAME,
|
||||||
|
message: {
|
||||||
|
signer: this.signer,
|
||||||
|
sender: this.sender,
|
||||||
|
minGasPrice: this.minGasPrice.toString(10),
|
||||||
|
maxGasPrice: this.maxGasPrice.toString(10),
|
||||||
|
expirationTimeSeconds: this.expirationTimeSeconds.toString(10),
|
||||||
|
salt: this.salt.toString(10),
|
||||||
|
callData: this.callData,
|
||||||
|
value: this.value.toString(10),
|
||||||
|
feeToken: this.feeToken,
|
||||||
|
feeAmount: this.feeAmount.toString(10),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public getHash(): string {
|
||||||
|
return getExchangeProxyEIP712Hash(this.getStructHash(), this.chainId, this.verifyingContract);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getSignatureWithProviderAsync(
|
||||||
|
provider: SupportedProvider,
|
||||||
|
type: SignatureType = SignatureType.EthSign,
|
||||||
|
): Promise<Signature> {
|
||||||
|
switch (type) {
|
||||||
|
case SignatureType.EIP712:
|
||||||
|
return eip712SignTypedDataWithProviderAsync(this.getEIP712TypedData(), this.signer, provider);
|
||||||
|
case SignatureType.EthSign:
|
||||||
|
return ethSignHashWithProviderAsync(this.getHash(), this.signer, provider);
|
||||||
|
default:
|
||||||
|
throw new Error(`Cannot sign with signature type: ${type}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSignatureWithKey(key: string, type: SignatureType = SignatureType.EthSign): Signature {
|
||||||
|
switch (type) {
|
||||||
|
case SignatureType.EIP712:
|
||||||
|
return eip712SignTypedDataWithKey(this.getEIP712TypedData(), key);
|
||||||
|
case SignatureType.EthSign:
|
||||||
|
return ethSignHashWithKey(this.getHash(), key);
|
||||||
|
default:
|
||||||
|
throw new Error(`Cannot sign with signature type: ${type}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,8 +1,15 @@
|
|||||||
|
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||||
import { SupportedProvider } from '@0x/subproviders';
|
import { SupportedProvider } from '@0x/subproviders';
|
||||||
import { EIP712TypedData } from '@0x/types';
|
import { EIP712TypedData } from '@0x/types';
|
||||||
import { BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils';
|
import { BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils';
|
||||||
|
|
||||||
import { createExchangeProxyEIP712Domain, EIP712_DOMAIN_PARAMETERS, getExchangeProxyEIP712Hash } from './eip712_utils';
|
import { ZERO } from './constants';
|
||||||
|
import {
|
||||||
|
createExchangeProxyEIP712Domain,
|
||||||
|
EIP712_DOMAIN_PARAMETERS,
|
||||||
|
getExchangeProxyEIP712Hash,
|
||||||
|
getTypeHash,
|
||||||
|
} from './eip712_utils';
|
||||||
import {
|
import {
|
||||||
eip712SignTypedDataWithKey,
|
eip712SignTypedDataWithKey,
|
||||||
eip712SignTypedDataWithProviderAsync,
|
eip712SignTypedDataWithProviderAsync,
|
||||||
@@ -12,7 +19,6 @@ import {
|
|||||||
SignatureType,
|
SignatureType,
|
||||||
} from './signature_utils';
|
} from './signature_utils';
|
||||||
|
|
||||||
const ZERO = new BigNumber(0);
|
|
||||||
const COMMON_ORDER_DEFAULT_VALUES = {
|
const COMMON_ORDER_DEFAULT_VALUES = {
|
||||||
makerToken: NULL_ADDRESS,
|
makerToken: NULL_ADDRESS,
|
||||||
takerToken: NULL_ADDRESS,
|
takerToken: NULL_ADDRESS,
|
||||||
@@ -24,7 +30,7 @@ const COMMON_ORDER_DEFAULT_VALUES = {
|
|||||||
expiry: ZERO,
|
expiry: ZERO,
|
||||||
salt: ZERO,
|
salt: ZERO,
|
||||||
chainId: 1,
|
chainId: 1,
|
||||||
verifyingContract: '0xdef1c0ded9bec7f1a1670819833240f027b25eff',
|
verifyingContract: getContractAddressesForChainOrThrow(1).exchangeProxy,
|
||||||
};
|
};
|
||||||
const LIMIT_ORDER_DEFAULT_VALUES = {
|
const LIMIT_ORDER_DEFAULT_VALUES = {
|
||||||
...COMMON_ORDER_DEFAULT_VALUES,
|
...COMMON_ORDER_DEFAULT_VALUES,
|
||||||
@@ -117,30 +123,22 @@ export abstract class OrderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class LimitOrder extends OrderBase {
|
export class LimitOrder extends OrderBase {
|
||||||
public static readonly TYPE_HASH = hexUtils.hash(
|
public static readonly STRUCT_NAME = 'LimitOrder';
|
||||||
hexUtils.toHex(
|
public static readonly STRUCT_ABI = [
|
||||||
Buffer.from(
|
{ type: 'address', name: 'makerToken' },
|
||||||
[
|
{ type: 'address', name: 'takerToken' },
|
||||||
'LimitOrder(',
|
{ type: 'uint128', name: 'makerAmount' },
|
||||||
[
|
{ type: 'uint128', name: 'takerAmount' },
|
||||||
'address makerToken',
|
{ type: 'uint128', name: 'takerTokenFeeAmount' },
|
||||||
'address takerToken',
|
{ type: 'address', name: 'maker' },
|
||||||
'uint128 makerAmount',
|
{ type: 'address', name: 'taker' },
|
||||||
'uint128 takerAmount',
|
{ type: 'address', name: 'sender' },
|
||||||
'uint128 takerTokenFeeAmount',
|
{ type: 'address', name: 'feeRecipient' },
|
||||||
'address maker',
|
{ type: 'bytes32', name: 'pool' },
|
||||||
'address taker',
|
{ type: 'uint64', name: 'expiry' },
|
||||||
'address sender',
|
{ type: 'uint256', name: 'salt' },
|
||||||
'address feeRecipient',
|
];
|
||||||
'bytes32 pool',
|
public static readonly TYPE_HASH = getTypeHash(LimitOrder.STRUCT_NAME, LimitOrder.STRUCT_ABI);
|
||||||
'uint64 expiry',
|
|
||||||
'uint256 salt',
|
|
||||||
].join(','),
|
|
||||||
')',
|
|
||||||
].join(''),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
public takerTokenFeeAmount: BigNumber;
|
public takerTokenFeeAmount: BigNumber;
|
||||||
public sender: string;
|
public sender: string;
|
||||||
@@ -198,23 +196,10 @@ export class LimitOrder extends OrderBase {
|
|||||||
return {
|
return {
|
||||||
types: {
|
types: {
|
||||||
EIP712Domain: EIP712_DOMAIN_PARAMETERS,
|
EIP712Domain: EIP712_DOMAIN_PARAMETERS,
|
||||||
LimitOrder: [
|
[LimitOrder.STRUCT_NAME]: LimitOrder.STRUCT_ABI,
|
||||||
{ type: 'address', name: 'makerToken' },
|
|
||||||
{ type: 'address', name: 'takerToken' },
|
|
||||||
{ type: 'uint128', name: 'makerAmount' },
|
|
||||||
{ type: 'uint128', name: 'takerAmount' },
|
|
||||||
{ type: 'uint128', name: 'takerTokenFeeAmount' },
|
|
||||||
{ type: 'address', name: 'maker' },
|
|
||||||
{ type: 'address', name: 'taker' },
|
|
||||||
{ type: 'address', name: 'sender' },
|
|
||||||
{ type: 'address', name: 'feeRecipient' },
|
|
||||||
{ type: 'bytes32', name: 'pool' },
|
|
||||||
{ type: 'uint64', name: 'expiry' },
|
|
||||||
{ type: 'uint256', name: 'salt' },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
domain: createExchangeProxyEIP712Domain(this.chainId, this.verifyingContract) as any,
|
domain: createExchangeProxyEIP712Domain(this.chainId, this.verifyingContract) as any,
|
||||||
primaryType: 'LimitOrder',
|
primaryType: LimitOrder.STRUCT_NAME,
|
||||||
message: {
|
message: {
|
||||||
makerToken: this.makerToken,
|
makerToken: this.makerToken,
|
||||||
takerToken: this.takerToken,
|
takerToken: this.takerToken,
|
||||||
@@ -234,28 +219,20 @@ export class LimitOrder extends OrderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class RfqOrder extends OrderBase {
|
export class RfqOrder extends OrderBase {
|
||||||
public static readonly TYPE_HASH = hexUtils.hash(
|
public static readonly STRUCT_NAME = 'RfqOrder';
|
||||||
hexUtils.toHex(
|
public static readonly STRUCT_ABI = [
|
||||||
Buffer.from(
|
{ type: 'address', name: 'makerToken' },
|
||||||
[
|
{ type: 'address', name: 'takerToken' },
|
||||||
'RfqOrder(',
|
{ type: 'uint128', name: 'makerAmount' },
|
||||||
[
|
{ type: 'uint128', name: 'takerAmount' },
|
||||||
'address makerToken',
|
{ type: 'address', name: 'maker' },
|
||||||
'address takerToken',
|
{ type: 'address', name: 'taker' },
|
||||||
'uint128 makerAmount',
|
{ type: 'address', name: 'txOrigin' },
|
||||||
'uint128 takerAmount',
|
{ type: 'bytes32', name: 'pool' },
|
||||||
'address maker',
|
{ type: 'uint64', name: 'expiry' },
|
||||||
'address taker',
|
{ type: 'uint256', name: 'salt' },
|
||||||
'address txOrigin',
|
];
|
||||||
'bytes32 pool',
|
public static readonly TYPE_HASH = getTypeHash(RfqOrder.STRUCT_NAME, RfqOrder.STRUCT_ABI);
|
||||||
'uint64 expiry',
|
|
||||||
'uint256 salt',
|
|
||||||
].join(','),
|
|
||||||
')',
|
|
||||||
].join(''),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
public txOrigin: string;
|
public txOrigin: string;
|
||||||
|
|
||||||
@@ -305,21 +282,10 @@ export class RfqOrder extends OrderBase {
|
|||||||
return {
|
return {
|
||||||
types: {
|
types: {
|
||||||
EIP712Domain: EIP712_DOMAIN_PARAMETERS,
|
EIP712Domain: EIP712_DOMAIN_PARAMETERS,
|
||||||
RfqOrder: [
|
[RfqOrder.STRUCT_NAME]: RfqOrder.STRUCT_ABI,
|
||||||
{ type: 'address', name: 'makerToken' },
|
|
||||||
{ type: 'address', name: 'takerToken' },
|
|
||||||
{ type: 'uint128', name: 'makerAmount' },
|
|
||||||
{ type: 'uint128', name: 'takerAmount' },
|
|
||||||
{ type: 'address', name: 'maker' },
|
|
||||||
{ type: 'address', name: 'taker' },
|
|
||||||
{ type: 'address', name: 'txOrigin' },
|
|
||||||
{ type: 'bytes32', name: 'pool' },
|
|
||||||
{ type: 'uint64', name: 'expiry' },
|
|
||||||
{ type: 'uint256', name: 'salt' },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
domain: createExchangeProxyEIP712Domain(this.chainId, this.verifyingContract) as any,
|
domain: createExchangeProxyEIP712Domain(this.chainId, this.verifyingContract) as any,
|
||||||
primaryType: 'RfqOrder',
|
primaryType: RfqOrder.STRUCT_NAME,
|
||||||
message: {
|
message: {
|
||||||
makerToken: this.makerToken,
|
makerToken: this.makerToken,
|
||||||
takerToken: this.takerToken,
|
takerToken: this.takerToken,
|
||||||
|
84
packages/protocol-utils/test/meta_transactions_test.ts
Normal file
84
packages/protocol-utils/test/meta_transactions_test.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import { chaiSetup, web3Factory, Web3Wrapper } from '@0x/dev-utils';
|
||||||
|
import { Web3ProviderEngine } from '@0x/subproviders';
|
||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import * as ethjs from 'ethereumjs-util';
|
||||||
|
|
||||||
|
import { MetaTransaction } from '../src/meta_transactions';
|
||||||
|
import { SignatureType } from '../src/signature_utils';
|
||||||
|
|
||||||
|
chaiSetup.configure();
|
||||||
|
|
||||||
|
describe('mtxs', () => {
|
||||||
|
let provider: Web3ProviderEngine;
|
||||||
|
let providerMaker: string;
|
||||||
|
const key = '0xee094b79aa0315914955f2f09be9abe541dcdc51f0aae5bec5453e9f73a471a6';
|
||||||
|
const keyMaker = ethjs.bufferToHex(ethjs.privateToAddress(ethjs.toBuffer(key)));
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
provider = web3Factory.getRpcProvider({ shouldUseInProcessGanache: true });
|
||||||
|
[providerMaker] = await new Web3Wrapper(provider).getAvailableAddressesAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('MetaTransaction', () => {
|
||||||
|
const mtx = new MetaTransaction({
|
||||||
|
signer: '0x349e8d89e8b37214d9ce3949fc5754152c525bc3',
|
||||||
|
sender: '0x83c62b2e67dea0df2a27be0def7a22bd7102642c',
|
||||||
|
minGasPrice: new BigNumber(1234),
|
||||||
|
maxGasPrice: new BigNumber(5678),
|
||||||
|
expirationTimeSeconds: new BigNumber(9101112),
|
||||||
|
salt: new BigNumber(2001),
|
||||||
|
callData: '0x12345678',
|
||||||
|
value: new BigNumber(1001),
|
||||||
|
feeToken: '0xcc3c7ea403427154ec908203ba6c418bd699f7ce',
|
||||||
|
feeAmount: new BigNumber(9101112),
|
||||||
|
chainId: 8008,
|
||||||
|
verifyingContract: '0x6701704d2421c64ee9aa93ec7f96ede81c4be77d',
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can get the struct hash', () => {
|
||||||
|
const actual = mtx.getStructHash();
|
||||||
|
const expected = '0x164b8bfaed3718d233d4cc87501d0d8fa0a72ed7deeb8e591524133f17867180';
|
||||||
|
expect(actual).to.eq(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can get the EIP712 hash', () => {
|
||||||
|
const actual = mtx.getHash();
|
||||||
|
const expected = '0x068f2f98836e489070608461768bfd3331128787d09278d38869c2b56bfc34a4';
|
||||||
|
expect(actual).to.eq(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can get an EthSign signature with a provider', async () => {
|
||||||
|
const actual = await mtx.clone({ signer: providerMaker }).getSignatureWithProviderAsync(provider);
|
||||||
|
const expected = {
|
||||||
|
signatureType: SignatureType.EthSign,
|
||||||
|
r: '0xbb831776a2d6639d4e4d1641f158773ce202881bac74dddb2672d5ff5521ef5c',
|
||||||
|
s: '0x746a61ccfdfee3afae15f4a3bd67ded2ce555d89d482940a844eeffaede2ee8a',
|
||||||
|
v: 27,
|
||||||
|
};
|
||||||
|
expect(actual).to.deep.eq(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can get an EthSign signature with a private key', () => {
|
||||||
|
const actual = mtx.clone({ signer: keyMaker }).getSignatureWithKey(key);
|
||||||
|
const expected = {
|
||||||
|
signatureType: SignatureType.EthSign,
|
||||||
|
r: '0xbf19b5ef62df8c8315727087e9d8562e3b88d32452ac8193e3ed9f5354a220ef',
|
||||||
|
s: '0x512387e81b2c03e4bc4cf72ee5293c86498c17fde3ae89f18dd0705076a7f472',
|
||||||
|
v: 28,
|
||||||
|
};
|
||||||
|
expect(actual).to.deep.eq(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can get an EIP712 signature with a private key', () => {
|
||||||
|
const actual = mtx.clone({ signer: keyMaker }).getSignatureWithKey(key, SignatureType.EIP712);
|
||||||
|
const expected = {
|
||||||
|
signatureType: SignatureType.EIP712,
|
||||||
|
r: '0x050c6b80a3fafa1b816fdfd646f3e90862a21d3fbf3ed675eaf9c89e092ec405',
|
||||||
|
s: '0x179600bd412820233598628b85b58f1e9f6da4555421f45266ec2ebf94153d1d',
|
||||||
|
v: 27,
|
||||||
|
};
|
||||||
|
expect(actual).to.deep.eq(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
46
yarn.lock
46
yarn.lock
@@ -1337,9 +1337,10 @@
|
|||||||
isomorphic-fetch "^2.2.1"
|
isomorphic-fetch "^2.2.1"
|
||||||
typescript "^3.8.3"
|
typescript "^3.8.3"
|
||||||
|
|
||||||
"@bancor/sdk@^0.2.9":
|
"@bancor/sdk@0.2.9":
|
||||||
version "0.2.9"
|
version "0.2.9"
|
||||||
resolved "https://registry.yarnpkg.com/@bancor/sdk/-/sdk-0.2.9.tgz#2e4c168dc9d667709e3ed85eac3b15362c5676d8"
|
resolved "https://registry.yarnpkg.com/@bancor/sdk/-/sdk-0.2.9.tgz#2e4c168dc9d667709e3ed85eac3b15362c5676d8"
|
||||||
|
integrity sha512-u+ga5+XPCcqYx6XX0It46Sx4hW9iiDRgaVRwpWzKB3QXh6c/V/R2OljLHH18yrh0uDTLJI7R0XmJa2W8v2wvpQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/text-encoding" "0.0.35"
|
"@types/text-encoding" "0.0.35"
|
||||||
decimal.js "^10.2.0"
|
decimal.js "^10.2.0"
|
||||||
@@ -2569,6 +2570,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/isomorphic-fetch@^0.0.35":
|
||||||
|
version "0.0.35"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/isomorphic-fetch/-/isomorphic-fetch-0.0.35.tgz#c1c0d402daac324582b6186b91f8905340ea3361"
|
||||||
|
integrity sha512-DaZNUvLDCAnCTjgwxgiL1eQdxIKEpNLOlTNtAgnZc50bG2copGhRrFN9/PxPBuJe+tZVLCbQ7ls0xveXVRPkvw==
|
||||||
|
|
||||||
"@types/js-combinatorics@^0.5.29":
|
"@types/js-combinatorics@^0.5.29":
|
||||||
version "0.5.32"
|
version "0.5.32"
|
||||||
resolved "https://registry.yarnpkg.com/@types/js-combinatorics/-/js-combinatorics-0.5.32.tgz#befa3c2b6ea10c45fd8d672f7aa477a79a2601ed"
|
resolved "https://registry.yarnpkg.com/@types/js-combinatorics/-/js-combinatorics-0.5.32.tgz#befa3c2b6ea10c45fd8d672f7aa477a79a2601ed"
|
||||||
@@ -2625,6 +2631,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/prompts@^2.0.9":
|
||||||
|
version "2.0.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/prompts/-/prompts-2.0.9.tgz#19f419310eaa224a520476b19d4183f6a2b3bd8f"
|
||||||
|
integrity sha512-TORZP+FSjTYMWwKadftmqEn6bziN5RnfygehByGsjxoK5ydnClddtv6GikGWPvCm24oI+YBwck5WDxIIyNxUrA==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/prop-types@*":
|
"@types/prop-types@*":
|
||||||
version "15.7.3"
|
version "15.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
|
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
|
||||||
@@ -7846,6 +7859,14 @@ isomorphic-fetch@2.2.1, isomorphic-fetch@^2.2.1:
|
|||||||
node-fetch "^1.0.1"
|
node-fetch "^1.0.1"
|
||||||
whatwg-fetch ">=0.10.0"
|
whatwg-fetch ">=0.10.0"
|
||||||
|
|
||||||
|
isomorphic-fetch@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4"
|
||||||
|
integrity sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==
|
||||||
|
dependencies:
|
||||||
|
node-fetch "^2.6.1"
|
||||||
|
whatwg-fetch "^3.4.1"
|
||||||
|
|
||||||
isstream@0.1.x, isstream@~0.1.2:
|
isstream@0.1.x, isstream@~0.1.2:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
||||||
@@ -8138,6 +8159,11 @@ klaw@^1.0.0:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
graceful-fs "^4.1.9"
|
graceful-fs "^4.1.9"
|
||||||
|
|
||||||
|
kleur@^3.0.3:
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
|
||||||
|
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
|
||||||
|
|
||||||
lazy@~1.0.11:
|
lazy@~1.0.11:
|
||||||
version "1.0.11"
|
version "1.0.11"
|
||||||
resolved "https://registry.yarnpkg.com/lazy/-/lazy-1.0.11.tgz#daa068206282542c088288e975c297c1ae77b690"
|
resolved "https://registry.yarnpkg.com/lazy/-/lazy-1.0.11.tgz#daa068206282542c088288e975c297c1ae77b690"
|
||||||
@@ -10300,6 +10326,14 @@ prompt@^1.0.0:
|
|||||||
utile "0.3.x"
|
utile "0.3.x"
|
||||||
winston "2.1.x"
|
winston "2.1.x"
|
||||||
|
|
||||||
|
prompts@^2.4.0:
|
||||||
|
version "2.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.0.tgz#4aa5de0723a231d1ee9121c40fdf663df73f61d7"
|
||||||
|
integrity sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==
|
||||||
|
dependencies:
|
||||||
|
kleur "^3.0.3"
|
||||||
|
sisteransi "^1.0.5"
|
||||||
|
|
||||||
promzard@^0.3.0:
|
promzard@^0.3.0:
|
||||||
version "0.3.0"
|
version "0.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee"
|
resolved "https://registry.yarnpkg.com/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee"
|
||||||
@@ -11333,6 +11367,11 @@ sinon@^4.0.0:
|
|||||||
supports-color "^5.1.0"
|
supports-color "^5.1.0"
|
||||||
type-detect "^4.0.5"
|
type-detect "^4.0.5"
|
||||||
|
|
||||||
|
sisteransi@^1.0.5:
|
||||||
|
version "1.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
|
||||||
|
integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
|
||||||
|
|
||||||
slash@^1.0.0:
|
slash@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
|
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
|
||||||
@@ -13439,6 +13478,11 @@ whatwg-fetch@>=0.10.0:
|
|||||||
version "3.4.1"
|
version "3.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.4.1.tgz#e5f871572d6879663fa5674c8f833f15a8425ab3"
|
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.4.1.tgz#e5f871572d6879663fa5674c8f833f15a8425ab3"
|
||||||
|
|
||||||
|
whatwg-fetch@^3.4.1:
|
||||||
|
version "3.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.5.0.tgz#605a2cd0a7146e5db141e29d1c62ab84c0c4c868"
|
||||||
|
integrity sha512-jXkLtsR42xhXg7akoDKvKWE40eJeI+2KZqcp2h3NsOrRnDvtWX36KcKl30dy+hxECivdk2BVUHVNrPtoMBUx6A==
|
||||||
|
|
||||||
whatwg-url@^7.0.0:
|
whatwg-url@^7.0.0:
|
||||||
version "7.1.0"
|
version "7.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06"
|
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06"
|
||||||
|
Reference in New Issue
Block a user