Merge branch 'development' of github.com:0xProject/0x-monorepo into feature/plp-integration

# Conflicts:
#	packages/contract-addresses/addresses.json
#	packages/contract-wrappers/package.json
This commit is contained in:
Daniel Pyrathon 2020-03-06 09:59:16 -08:00
commit d8498134ad
34 changed files with 1010 additions and 51 deletions

View File

@ -1,4 +1,17 @@
[
{
"version": "3.3.0",
"changes": [
{
"note": "Use `LibERC20Token.approveIfBelow()` in DEX bridges for for approvals.",
"pr": 2512
},
{
"note": "Emit `ERC20BridgeTransfer` events in bridges.",
"pr": 2512
}
]
},
{
"timestamp": 1583220306,
"version": "3.2.5",

View File

@ -35,18 +35,26 @@ contract CurveBridge is
IWallet,
DeploymentConstants
{
struct CurveBridgeData {
address curveAddress;
int128 fromCoinIdx;
int128 toCoinIdx;
int128 version;
}
/// @dev Callback for `ICurve`. Tries to buy `amount` of
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
/// (DAI, USDC) to the Curve contract, then transfers the bought
/// tokens to `to`.
/// @param toTokenAddress The token to give to `to` (i.e DAI, USDC, USDT).
/// @param from The maker (this contract).
/// @param to The recipient of the bought tokens.
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
/// @param bridgeData The abi-encoeded "from" token address.
/// @return success The magic bytes if successful.
function bridgeTransferFrom(
address toTokenAddress,
address /* from */,
address from,
address to,
uint256 amount,
bytes calldata bridgeData
@ -55,32 +63,32 @@ contract CurveBridge is
returns (bytes4 success)
{
// Decode the bridge data to get the Curve metadata.
(address curveAddress, int128 fromCoinIdx, int128 toCoinIdx, int128 version) = abi.decode(bridgeData, (address, int128, int128, int128));
ICurve exchange = ICurve(curveAddress);
CurveBridgeData memory data = abi.decode(bridgeData, (CurveBridgeData));
address fromTokenAddress = exchange.underlying_coins(fromCoinIdx);
address fromTokenAddress = ICurve(data.curveAddress).underlying_coins(data.fromCoinIdx);
require(toTokenAddress != fromTokenAddress, "CurveBridge/INVALID_PAIR");
uint256 fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
LibERC20Token.approve(fromTokenAddress, address(exchange), uint256(-1));
LibERC20Token.approveIfBelow(fromTokenAddress, data.curveAddress, fromTokenBalance);
// Try to sell all of this contract's `fromTokenAddress` token balance.
if (version == 0) {
exchange.exchange_underlying(
fromCoinIdx,
toCoinIdx,
if (data.version == 0) {
ICurve(data.curveAddress).exchange_underlying(
data.fromCoinIdx,
data.toCoinIdx,
// dx
IERC20Token(fromTokenAddress).balanceOf(address(this)),
fromTokenBalance,
// min dy
amount,
// expires
block.timestamp + 1
);
} else {
exchange.exchange_underlying(
fromCoinIdx,
toCoinIdx,
ICurve(data.curveAddress).exchange_underlying(
data.fromCoinIdx,
data.toCoinIdx,
// dx
IERC20Token(fromTokenAddress).balanceOf(address(this)),
fromTokenBalance,
// min dy
amount
);
@ -89,6 +97,15 @@ contract CurveBridge is
uint256 toTokenBalance = IERC20Token(toTokenAddress).balanceOf(address(this));
// Transfer the converted `toToken`s to `to`.
LibERC20Token.transfer(toTokenAddress, to, toTokenBalance);
emit ERC20BridgeTransfer(
fromTokenAddress,
toTokenAddress,
fromTokenBalance,
toTokenBalance,
from,
to
);
return BRIDGE_SUCCESS;
}

View File

@ -50,7 +50,7 @@ contract DydxBridge is
/// @param encodedBridgeData An abi-encoded `BridgeData` struct.
/// @return success The magic bytes if successful.
function bridgeTransferFrom(
address,
address, /* toTokenAddress */
address from,
address to,
uint256 amount,
@ -81,6 +81,7 @@ contract DydxBridge is
// Run operation. This will revert on failure.
IDydx(_getDydxAddress()).operate(accounts, actions);
return BRIDGE_SUCCESS;
}

View File

@ -38,13 +38,14 @@ contract Eth2DaiBridge is
/// (DAI or WETH) to the Eth2Dai contract, then transfers the bought
/// tokens to `to`.
/// @param toTokenAddress The token to give to `to` (either DAI or WETH).
/// @param from The maker (this contract).
/// @param to The recipient of the bought tokens.
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
/// @param bridgeData The abi-encoeded "from" token address.
/// @return success The magic bytes if successful.
function bridgeTransferFrom(
address toTokenAddress,
address /* from */,
address from,
address to,
uint256 amount,
bytes calldata bridgeData
@ -56,18 +57,28 @@ contract Eth2DaiBridge is
(address fromTokenAddress) = abi.decode(bridgeData, (address));
IEth2Dai exchange = IEth2Dai(_getEth2DaiAddress());
uint256 fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
LibERC20Token.approve(fromTokenAddress, address(exchange), uint256(-1));
LibERC20Token.approveIfBelow(fromTokenAddress, address(exchange), fromTokenBalance);
// Try to sell all of this contract's `fromTokenAddress` token balance.
uint256 boughtAmount = exchange.sellAllAmount(
fromTokenAddress,
IERC20Token(fromTokenAddress).balanceOf(address(this)),
fromTokenBalance,
toTokenAddress,
amount
);
// Transfer the converted `toToken`s to `to`.
LibERC20Token.transfer(toTokenAddress, to, boughtAmount);
emit ERC20BridgeTransfer(
fromTokenAddress,
toTokenAddress,
fromTokenBalance,
boughtAmount,
from,
to
);
return BRIDGE_SUCCESS;
}

View File

@ -66,13 +66,14 @@ contract KyberBridge is
/// to the `KyberNetworkProxy` contract, then transfers the bought
/// tokens to `to`.
/// @param toTokenAddress The token to give to `to`.
/// @param from The maker (this contract).
/// @param to The recipient of the bought tokens.
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
/// @param bridgeData The abi-encoeded "from" token address.
/// @return success The magic bytes if successful.
function bridgeTransferFrom(
address toTokenAddress,
address /* from */,
address from,
address to,
uint256 amount,
bytes calldata bridgeData
@ -109,7 +110,11 @@ contract KyberBridge is
} else if (state.fromTokenAddress != address(state.weth)) {
// If the input token is not WETH, grant an allowance to the exchange
// to spend them.
LibERC20Token.approve(state.fromTokenAddress, address(state.kyber), uint256(-1));
LibERC20Token.approveIfBelow(
state.fromTokenAddress,
address(state.kyber),
state.fromTokenBalance
);
} else {
// If the input token is WETH, unwrap it and attach it to the call.
state.fromTokenAddress = KYBER_ETH_ADDRESS;
@ -143,6 +148,15 @@ contract KyberBridge is
state.weth.deposit.value(boughtAmount)();
state.weth.transfer(to, boughtAmount);
}
emit ERC20BridgeTransfer(
state.fromTokenAddress == KYBER_ETH_ADDRESS ? address(state.weth) : state.fromTokenAddress,
toTokenAddress,
state.fromTokenBalance,
boughtAmount,
from,
to
);
return BRIDGE_SUCCESS;
}

View File

@ -38,10 +38,11 @@ contract UniswapBridge is
{
// Struct to hold `bridgeTransferFrom()` local variables in memory and to avoid
// stack overflows.
struct WithdrawToState {
struct TransferState {
IUniswapExchange exchange;
uint256 fromTokenBalance;
IEtherToken weth;
uint256 boughtAmount;
}
// solhint-disable no-empty-blocks
@ -55,13 +56,14 @@ contract UniswapBridge is
/// `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress`
/// token encoded in the bridge data.
/// @param toTokenAddress The token to buy and transfer to `to`.
/// @param from The maker (this contract).
/// @param to The recipient of the bought tokens.
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
/// @param bridgeData The abi-encoded "from" token address.
/// @return success The magic bytes if successful.
function bridgeTransferFrom(
address toTokenAddress,
address /* from */,
address from,
address to,
uint256 amount,
bytes calldata bridgeData
@ -70,7 +72,7 @@ contract UniswapBridge is
returns (bytes4 success)
{
// State memory object to avoid stack overflows.
WithdrawToState memory state;
TransferState memory state;
// Decode the bridge data to get the `fromTokenAddress`.
(address fromTokenAddress) = abi.decode(bridgeData, (address));
@ -96,7 +98,7 @@ contract UniswapBridge is
state.weth.withdraw(state.fromTokenBalance);
// Buy as much of `toTokenAddress` token with ETH as possible and
// transfer it to `to`.
state.exchange.ethToTokenTransferInput.value(state.fromTokenBalance)(
state.boughtAmount = state.exchange.ethToTokenTransferInput.value(state.fromTokenBalance)(
// Minimum buy amount.
amount,
// Expires after this block.
@ -108,9 +110,9 @@ contract UniswapBridge is
// Convert from a token to WETH.
} else if (toTokenAddress == address(state.weth)) {
// Grant the exchange an allowance.
_grantExchangeAllowance(state.exchange, fromTokenAddress);
_grantExchangeAllowance(state.exchange, fromTokenAddress, state.fromTokenBalance);
// Buy as much ETH with `fromTokenAddress` token as possible.
uint256 ethBought = state.exchange.tokenToEthSwapInput(
state.boughtAmount = state.exchange.tokenToEthSwapInput(
// Sell all tokens we hold.
state.fromTokenBalance,
// Minimum buy amount.
@ -119,17 +121,17 @@ contract UniswapBridge is
block.timestamp
);
// Wrap the ETH.
state.weth.deposit.value(ethBought)();
state.weth.deposit.value(state.boughtAmount)();
// Transfer the WETH to `to`.
IEtherToken(toTokenAddress).transfer(to, ethBought);
IEtherToken(toTokenAddress).transfer(to, state.boughtAmount);
// Convert from one token to another.
} else {
// Grant the exchange an allowance.
_grantExchangeAllowance(state.exchange, fromTokenAddress);
_grantExchangeAllowance(state.exchange, fromTokenAddress, state.fromTokenBalance);
// Buy as much `toTokenAddress` token with `fromTokenAddress` token
// and transfer it to `to`.
state.exchange.tokenToTokenTransferInput(
state.boughtAmount = state.exchange.tokenToTokenTransferInput(
// Sell all tokens we hold.
state.fromTokenBalance,
// Minimum buy amount.
@ -144,6 +146,15 @@ contract UniswapBridge is
toTokenAddress
);
}
emit ERC20BridgeTransfer(
fromTokenAddress,
toTokenAddress,
state.fromTokenBalance,
state.boughtAmount,
from,
to
);
return BRIDGE_SUCCESS;
}
@ -165,10 +176,19 @@ contract UniswapBridge is
/// on behalf of this contract.
/// @param exchange The Uniswap token exchange.
/// @param tokenAddress The token address for the exchange.
function _grantExchangeAllowance(IUniswapExchange exchange, address tokenAddress)
/// @param minimumAllowance The minimum necessary allowance.
function _grantExchangeAllowance(
IUniswapExchange exchange,
address tokenAddress,
uint256 minimumAllowance
)
private
{
LibERC20Token.approve(tokenAddress, address(exchange), uint256(-1));
LibERC20Token.approveIfBelow(
tokenAddress,
address(exchange),
minimumAllowance
);
}
/// @dev Retrieves the uniswap exchange for a given token pair.

View File

@ -21,7 +21,23 @@ pragma solidity ^0.5.9;
contract IERC20Bridge {
// @dev Result of a successful bridge call.
/// @dev Emitted when a bridge transfer is completed.
/// @param fromToken The address of the "from" token.
/// @param toToken The address of the "to" token.
/// @param fromTokenAmount The "from" token amount consumed.
/// @param toTokenAmount The "to" token amount transferred.
/// @param from Supplier of "fromToken".
/// @param to Receiver of "toToken".
event ERC20BridgeTransfer(
address fromToken,
address toToken,
uint256 fromTokenAmount,
uint256 toTokenAmount,
address from,
address to
);
/// @dev Result of a successful bridge call.
bytes4 constant internal BRIDGE_SUCCESS = 0xdc1600f3;
/// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`.

View File

@ -110,6 +110,10 @@ contract TestToken {
return true;
}
function allowance(address, address) external view returns (uint256) {
return 0;
}
/// @dev Retrieve the balance for `owner`.
function balanceOf(address owner)
external

View File

@ -110,6 +110,10 @@ contract TestToken {
return _testContract.wethDeposit.value(msg.value)(msg.sender);
}
function allowance(address, address) external view returns (uint256) {
return 0;
}
function balanceOf(address owner)
external
view

View File

@ -224,6 +224,10 @@ contract TestToken {
TestEventsRaiser(msg.sender).raiseWethWithdraw(amount);
}
function allowance(address, address) external view returns (uint256) {
return 0;
}
/// @dev Retrieve the balance for `owner`.
function balanceOf(address owner)
external

View File

@ -1,4 +1,13 @@
[
{
"version": "3.2.0",
"changes": [
{
"note": "Add `LibERC20Token.approveIfBelow()`",
"pr": 2512
}
]
},
{
"timestamp": 1583220306,
"version": "3.1.5",

View File

@ -47,6 +47,25 @@ library LibERC20Token {
_callWithOptionalBooleanResult(token, callData);
}
/// @dev Calls `IERC20Token(token).approve()` and sets the allowance to the
/// maximum if the current approval is not already >= an amount.
/// Reverts if `false` is returned or if the return
/// data length is nonzero and not 32 bytes.
/// @param token The address of the token contract.
/// @param spender The address that receives an allowance.
/// @param amount The minimum allowance needed.
function approveIfBelow(
address token,
address spender,
uint256 amount
)
internal
{
if (IERC20Token(token).allowance(address(this), spender) < amount) {
approve(token, spender, uint256(-1));
}
}
/// @dev Calls `IERC20Token(token).transfer()`.
/// Reverts if `false` is returned or if the return
/// data length is nonzero and not 32 bytes.

View File

@ -1,4 +1,13 @@
[
{
"version": "6.2.0",
"changes": [
{
"note": "Add MaximumGasPrice contract, tooling, and unit tests",
"pr": 2511
}
]
},
{
"timestamp": 1583220306,
"version": "6.1.5",

View File

@ -0,0 +1,50 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.16;
contract MaximumGasPrice {
// 20 Gwei
uint256 constant private DEFAULT_MAX_GAS_PRICE = 20 * (10 ** 9);
/// @dev Checks that the current transaction's gas price is less than
/// the default maximum value of 20 Gwei.
function checkGasPrice()
external
view
{
require(
tx.gasprice <= DEFAULT_MAX_GAS_PRICE,
"MaximumGasPrice/GAS_PRICE_EXCEEDS_20_GWEI"
);
}
/// @dev Checks that the current transaction's gas price is less than
/// the specified maximum value.
/// @param maxGasPrice The maximum gas price allowed for the current transaction.
function checkGasPrice(uint256 maxGasPrice)
external
view
{
require(
tx.gasprice <= maxGasPrice,
"MaximumGasPrice/GAS_PRICE_EXCEEDS_MAXIMUM"
);
}
}

View File

@ -38,7 +38,7 @@
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
},
"config": {
"abis": "./test/generated-artifacts/@(LibAssetDataTransfer|LibAssetDataTransferRichErrors|LibWethUtilsRichErrors|MixinWethUtils).json",
"abis": "./test/generated-artifacts/@(LibAssetDataTransfer|LibAssetDataTransferRichErrors|LibWethUtilsRichErrors|MaximumGasPrice|MixinWethUtils).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {

View File

@ -8,9 +8,11 @@ import { ContractArtifact } from 'ethereum-types';
import * as LibAssetDataTransfer from '../generated-artifacts/LibAssetDataTransfer.json';
import * as LibAssetDataTransferRichErrors from '../generated-artifacts/LibAssetDataTransferRichErrors.json';
import * as LibWethUtilsRichErrors from '../generated-artifacts/LibWethUtilsRichErrors.json';
import * as MaximumGasPrice from '../generated-artifacts/MaximumGasPrice.json';
import * as MixinWethUtils from '../generated-artifacts/MixinWethUtils.json';
export const artifacts = {
LibAssetDataTransfer: LibAssetDataTransfer as ContractArtifact,
MaximumGasPrice: MaximumGasPrice as ContractArtifact,
MixinWethUtils: MixinWethUtils as ContractArtifact,
LibAssetDataTransferRichErrors: LibAssetDataTransferRichErrors as ContractArtifact,
LibWethUtilsRichErrors: LibWethUtilsRichErrors as ContractArtifact,

View File

@ -29,3 +29,4 @@ export {
TupleDataItem,
StateMutability,
} from 'ethereum-types';
export * from './max_gas_price_utils';

View File

@ -0,0 +1,38 @@
import { constants } from '@0x/contracts-test-utils';
import { assetDataUtils } from '@0x/order-utils';
import { StaticCallAssetData } from '@0x/types';
import { AbiEncoder, BigNumber } from '@0x/utils';
const customGasPriceEncoder = AbiEncoder.createMethod('checkGasPrice', [{ name: 'maxGasPrice', type: 'uint256' }]);
const defaultGasPriceEncoder = AbiEncoder.createMethod('checkGasPrice', []);
const ONE_GWEI = new BigNumber(10 ** 9);
export const TWENTY_GWEI = ONE_GWEI.times(20);
/**
* Encodes the given stop limit data parameters into StaticCall asset data so that it can be used
* in a 0x order.
*/
export function encodeMaxGasPriceStaticCallData(maxGasPriceContractAddress: string, maxGasPrice?: BigNumber): string {
const staticCallData =
maxGasPrice === undefined ? defaultGasPriceEncoder.encode({}) : customGasPriceEncoder.encode({ maxGasPrice });
return assetDataUtils.encodeStaticCallAssetData(
maxGasPriceContractAddress,
staticCallData,
constants.KECCAK256_NULL,
);
}
/**
* Decodes the maxGasPrice StaticCall asset data.
*/
export function decodeMaxGasPriceStaticCallData(assetData: string): BigNumber {
// tslint:disable-next-line:no-unnecessary-type-assertion
const { staticCallData } = assetDataUtils.decodeAssetDataOrThrow(assetData) as StaticCallAssetData;
try {
return customGasPriceEncoder.strictDecode<BigNumber>(staticCallData);
} catch (e) {
defaultGasPriceEncoder.strictDecode(staticCallData);
return TWENTY_GWEI;
}
}

View File

@ -6,4 +6,5 @@
export * from '../generated-wrappers/lib_asset_data_transfer';
export * from '../generated-wrappers/lib_asset_data_transfer_rich_errors';
export * from '../generated-wrappers/lib_weth_utils_rich_errors';
export * from '../generated-wrappers/maximum_gas_price';
export * from '../generated-wrappers/mixin_weth_utils';

View File

@ -8,9 +8,11 @@ import { ContractArtifact } from 'ethereum-types';
import * as LibAssetDataTransfer from '../test/generated-artifacts/LibAssetDataTransfer.json';
import * as LibAssetDataTransferRichErrors from '../test/generated-artifacts/LibAssetDataTransferRichErrors.json';
import * as LibWethUtilsRichErrors from '../test/generated-artifacts/LibWethUtilsRichErrors.json';
import * as MaximumGasPrice from '../test/generated-artifacts/MaximumGasPrice.json';
import * as MixinWethUtils from '../test/generated-artifacts/MixinWethUtils.json';
export const artifacts = {
LibAssetDataTransfer: LibAssetDataTransfer as ContractArtifact,
MaximumGasPrice: MaximumGasPrice as ContractArtifact,
MixinWethUtils: MixinWethUtils as ContractArtifact,
LibAssetDataTransferRichErrors: LibAssetDataTransferRichErrors as ContractArtifact,
LibWethUtilsRichErrors: LibWethUtilsRichErrors as ContractArtifact,

View File

@ -0,0 +1,89 @@
import { artifacts as assetProxyArtifacts, StaticCallProxyContract } from '@0x/contracts-asset-proxy';
import { blockchainTests, constants, expect, getRandomInteger, randomAddress } from '@0x/contracts-test-utils';
import {
decodeMaxGasPriceStaticCallData,
encodeMaxGasPriceStaticCallData,
TWENTY_GWEI,
} from '../src/max_gas_price_utils';
import { artifacts } from './artifacts';
import { MaximumGasPriceContract } from './wrappers';
blockchainTests.resets('MaximumGasPrice unit tests', env => {
let maxGasPriceContract: MaximumGasPriceContract;
let staticCallProxy: StaticCallProxyContract;
let defaultMaxAssetData: string;
before(async () => {
maxGasPriceContract = await MaximumGasPriceContract.deployFrom0xArtifactAsync(
artifacts.MaximumGasPrice,
env.provider,
env.txDefaults,
artifacts,
);
staticCallProxy = await StaticCallProxyContract.deployFrom0xArtifactAsync(
assetProxyArtifacts.StaticCallProxy,
env.provider,
env.txDefaults,
assetProxyArtifacts,
);
defaultMaxAssetData = encodeMaxGasPriceStaticCallData(maxGasPriceContract.address);
});
describe('Data encoding/decoding tools', () => {
it('correctly decodes default maximum gas price', async () => {
const decoded = decodeMaxGasPriceStaticCallData(defaultMaxAssetData);
expect(decoded).to.bignumber.equal(TWENTY_GWEI);
});
it('correctly decodes custom maximum gas price', async () => {
const customMaxGasPrice = getRandomInteger(0, constants.MAX_UINT256);
const customMaxAssetData = encodeMaxGasPriceStaticCallData(maxGasPriceContract.address, customMaxGasPrice);
const decoded = decodeMaxGasPriceStaticCallData(customMaxAssetData);
expect(decoded).to.bignumber.equal(customMaxGasPrice);
});
});
describe('Contract functionality', () => {
it('does not revert if tx.gasprice < default maximum', async () => {
await staticCallProxy
.transferFrom(defaultMaxAssetData, randomAddress(), randomAddress(), constants.ZERO_AMOUNT)
.callAsync({ gasPrice: TWENTY_GWEI.minus(1) });
});
it('does not revert if tx.gasprice = default maximum', async () => {
await staticCallProxy
.transferFrom(defaultMaxAssetData, randomAddress(), randomAddress(), constants.ZERO_AMOUNT)
.callAsync({ gasPrice: TWENTY_GWEI });
});
it('reverts if tx.gasPrice > default maximum', async () => {
const tx = staticCallProxy
.transferFrom(defaultMaxAssetData, randomAddress(), randomAddress(), constants.ZERO_AMOUNT)
.callAsync({ gasPrice: TWENTY_GWEI.plus(1) });
return expect(tx).to.revertWith('MaximumGasPrice/GAS_PRICE_EXCEEDS_20_GWEI');
});
it('does not revert if tx.gasprice < custom maximum', async () => {
const maxGasPrice = getRandomInteger(0, TWENTY_GWEI.times(2));
const customMaxAssetData = encodeMaxGasPriceStaticCallData(maxGasPriceContract.address, maxGasPrice);
await staticCallProxy
.transferFrom(customMaxAssetData, randomAddress(), randomAddress(), constants.ZERO_AMOUNT)
.callAsync({ gasPrice: maxGasPrice.minus(1) });
});
it('does not revert if tx.gasprice = custom maximum', async () => {
const maxGasPrice = getRandomInteger(0, TWENTY_GWEI.times(2));
const customMaxAssetData = encodeMaxGasPriceStaticCallData(maxGasPriceContract.address, maxGasPrice);
await staticCallProxy
.transferFrom(customMaxAssetData, randomAddress(), randomAddress(), constants.ZERO_AMOUNT)
.callAsync({ gasPrice: maxGasPrice });
});
it('reverts if tx.gasPrice > custom maximum', async () => {
const maxGasPrice = getRandomInteger(0, TWENTY_GWEI.times(2));
const customMaxAssetData = encodeMaxGasPriceStaticCallData(maxGasPriceContract.address, maxGasPrice);
const tx = staticCallProxy
.transferFrom(customMaxAssetData, randomAddress(), randomAddress(), constants.ZERO_AMOUNT)
.callAsync({ gasPrice: maxGasPrice.plus(1) });
return expect(tx).to.revertWith('MaximumGasPrice/GAS_PRICE_EXCEEDS_MAXIMUM');
});
});
});

View File

@ -6,4 +6,5 @@
export * from '../test/generated-wrappers/lib_asset_data_transfer';
export * from '../test/generated-wrappers/lib_asset_data_transfer_rich_errors';
export * from '../test/generated-wrappers/lib_weth_utils_rich_errors';
export * from '../test/generated-wrappers/maximum_gas_price';
export * from '../test/generated-wrappers/mixin_weth_utils';

View File

@ -6,10 +6,12 @@
"generated-artifacts/LibAssetDataTransfer.json",
"generated-artifacts/LibAssetDataTransferRichErrors.json",
"generated-artifacts/LibWethUtilsRichErrors.json",
"generated-artifacts/MaximumGasPrice.json",
"generated-artifacts/MixinWethUtils.json",
"test/generated-artifacts/LibAssetDataTransfer.json",
"test/generated-artifacts/LibAssetDataTransferRichErrors.json",
"test/generated-artifacts/LibWethUtilsRichErrors.json",
"test/generated-artifacts/MaximumGasPrice.json",
"test/generated-artifacts/MixinWethUtils.json"
],
"exclude": ["./deploy/solc/solc_bin"]

View File

@ -1,4 +1,17 @@
[
{
"version": "4.10.0",
"changes": [
{
"note": "Added MaximumGasPrice addresses",
"pr": 2511
},
{
"note": "Redeploy `KyberBridge`, `UniswapBridge`, `Eth2DaiBridge`, `CurveBridge`, `DydxBridge` on mainnet and kovan",
"pr": 2512
}
]
},
{
"version": "4.9.0",
"changes": [

View File

@ -19,16 +19,17 @@
"stakingProxy": "0xa26e80e7dea86279c6d778d702cc413e6cffa777",
"devUtils": "0x74134cf88b21383713e096a5ecf59e297dc7f547",
"erc20BridgeProxy": "0x8ed95d1746bf1e4dab58d8ed4724f1ef95b20db0",
"uniswapBridge": "0x533344cfdf2a3e911e2cf4c6f5ed08e791f5355f",
"uniswapBridge": "0x36691c4f426eb8f42f150ebde43069a31cb080ad",
"erc20BridgeSampler": "0xb2dee8cf2a06fbf0942fda5521f890b6e9911bfe",
"kyberBridge": "0xf342f3a80fdc9b48713d58fe97e17f5cc764ee62",
"eth2DaiBridge": "0xe3379a1956f4a79f39eb2e87bb441419e167538e",
"kyberBridge": "0x1c29670f7a77f1052d30813a0a4f632c78a02610",
"eth2DaiBridge": "0x991c745401d5b5e469b8c3e2cb02c748f08754f1",
"chaiBridge": "0x77c31eba23043b9a72d13470f3a3a311344d7438",
"dydxBridge": "0x55dc8f21d20d4c6ed3c82916a438a413ca68e335",
"dydxBridge": "0x871299bed3ea54577a3e3a43136f7fbb5e377114",
"godsUnchainedValidator": "0x09A379Ef7218BCFD8913fAa8B281ebc5A2E0bC04",
"broker": "0xd4690a51044db77D91d7Aa8f7a3a5ad5dA331Af0",
"chainlinkStopLimit": "0xeb27220f95f364e1d9531992c48613f231839f53",
"curveBridge": "0xe335bdd1fb0ee30f9a9a434f18f8b118dec32df7"
"curveBridge": "0x6dc7950423ada9f56fb2c93a23edb787f1e29088",
"maximumGasPrice": "0xe2bfd35306495d11e3c9db0d8de390cda24563cf"
},
"3": {
"erc20Proxy": "0xb1408f4c245a23c31b98d2c626777d4c0d766caa",
@ -59,7 +60,8 @@
"godsUnchainedValidator": "0xd4690a51044db77D91d7Aa8f7a3a5ad5dA331Af0",
"broker": "0x4Aa817C6f383C8e8aE77301d18Ce48efb16Fd2BE",
"chainlinkStopLimit": "0x67a094cf028221ffdd93fc658f963151d05e2a74",
"curveBridge": "0x0000000000000000000000000000000000000000"
"curveBridge": "0x0000000000000000000000000000000000000000",
"maximumGasPrice": "0x407b4128e9ecad8769b2332312a9f655cb9f5f3a"
},
"4": {
"exchangeV2": "0xbff9493f92a3df4b0429b6d00743b3cfb4c85831",
@ -90,7 +92,8 @@
"godsUnchainedValidator": "0x0000000000000000000000000000000000000000",
"broker": "0x0000000000000000000000000000000000000000",
"chainlinkStopLimit": "0x407b4128e9ecad8769b2332312a9f655cb9f5f3a",
"curveBridge": "0x0000000000000000000000000000000000000000"
"curveBridge": "0x0000000000000000000000000000000000000000",
"maximumGasPrice": "0x47697b44bd89051e93b4d5857ba8e024800a74ac"
},
"42": {
"erc20Proxy": "0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e",
@ -112,16 +115,17 @@
"staking": "0x32b06d5611a03737a5f1834a24ccd641033fd89c",
"stakingProxy": "0xbab9145f1d57cd4bb0c9aa2d1ece0a5b6e734d34",
"erc20BridgeProxy": "0xfb2dd2a1366de37f7241c83d47da58fd503e2c64",
"uniswapBridge": "0x8224aa8fe5c9f07d5a59c735386ff6cc6aaeb568",
"eth2DaiBridge": "0x9485d65c6a2fae0d519cced5bd830e57c41998a9",
"erc20BridgeSampler": "0xf6aeb7bc10709b06244a2c5026b8799672d40312",
"kyberBridge": "0xde7b2747624a647600fdb349184d0448ab954929",
"uniswapBridge": "0x0e85f89f29998df65402391478e5924700c0079d",
"eth2DaiBridge": "0x2d47147429b474d2e4f83e658015858a1312ed5b",
"erc20BridgeSampler": "0x4f1556e5fe03a0da39091260f78d2cf765baa091",
"kyberBridge": "0xaecfa25920f892b6eb496e1f6e84037f59da7f44",
"chaiBridge": "0x0000000000000000000000000000000000000000",
"dydxBridge": "0x0000000000000000000000000000000000000000",
"dydxBridge": "0x080e183c0193b4765d504e402db2f5621d4567e4",
"godsUnchainedValidator": "0x0000000000000000000000000000000000000000",
"broker": "0x0000000000000000000000000000000000000000",
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000",
"curveBridge": "0x0000000000000000000000000000000000000000"
"curveBridge": "0x90c62c91a9f655f4f739e6cee85c84f9ccf47323",
"maximumGasPrice": "0x67a094cf028221ffdd93fc658f963151d05e2a74"
},
"1337": {
"erc20Proxy": "0x1dc4c1cefef38a777b15aa20260a54e584b16c48",
@ -152,6 +156,7 @@
"godsUnchainedValidator": "0x0000000000000000000000000000000000000000",
"broker": "0x0000000000000000000000000000000000000000",
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000",
"curveBridge": "0x0000000000000000000000000000000000000000"
"curveBridge": "0x0000000000000000000000000000000000000000",
"maximumGasPrice": "0x0000000000000000000000000000000000000000"
}
}

View File

@ -30,6 +30,7 @@ export interface ContractAddresses {
godsUnchainedValidator: string;
broker: string;
chainlinkStopLimit: string;
maximumGasPrice: string;
}
export enum ChainId {

View File

@ -1,4 +1,13 @@
[
{
"version": "3.7.0",
"changes": [
{
"note": "Added `MaximumGasPrice` artifact",
"pr": 2511
}
]
},
{
"timestamp": 1582623685,
"version": "3.6.1",

View File

@ -0,0 +1,70 @@
{
"schemaVersion": "2.0.0",
"contractName": "MaximumGasPrice",
"compilerOutput": {
"abi": [
{
"constant": true,
"inputs": [],
"name": "checkGasPrice",
"outputs": [],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [{ "internalType": "uint256", "name": "maxGasPrice", "type": "uint256" }],
"name": "checkGasPrice",
"outputs": [],
"payable": false,
"stateMutability": "view",
"type": "function"
}
],
"devdoc": {
"methods": {
"checkGasPrice()": {
"details": "Checks that the current transaction's gas price is less than the default maximum value of 20 Gwei."
},
"checkGasPrice(uint256)": {
"details": "Checks that the current transaction's gas price is less than the specified maximum value.",
"params": { "maxGasPrice": "The maximum gas price allowed for the current transaction." }
}
}
},
"evm": {
"bytecode": {
"object": "0x608060405234801561001057600080fd5b506101a5806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063d728f5b71461003b578063da5b166a14610045575b600080fd5b610043610062565b005b6100436004803603602081101561005b57600080fd5b50356100c2565b6404a817c8003a11156100c0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806101486029913960400191505060405180910390fd5b565b803a111561011b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602981526020018061011f6029913960400191505060405180910390fd5b5056fe4d6178696d756d47617350726963652f4741535f50524943455f455843454544535f4d4158494d554d4d6178696d756d47617350726963652f4741535f50524943455f455843454544535f32305f47574549a265627a7a72315820b735b9a3a2024167c985358b7c43d479b1e6d937ae2375ccb506b2092c0c20e564736f6c63430005100032"
},
"deployedBytecode": {
"object": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c8063d728f5b71461003b578063da5b166a14610045575b600080fd5b610043610062565b005b6100436004803603602081101561005b57600080fd5b50356100c2565b6404a817c8003a11156100c0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806101486029913960400191505060405180910390fd5b565b803a111561011b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602981526020018061011f6029913960400191505060405180910390fd5b5056fe4d6178696d756d47617350726963652f4741535f50524943455f455843454544535f4d4158494d554d4d6178696d756d47617350726963652f4741535f50524943455f455843454544535f32305f47574549a265627a7a72315820b735b9a3a2024167c985358b7c43d479b1e6d937ae2375ccb506b2092c0c20e564736f6c63430005100032"
}
}
},
"compiler": {
"name": "solc",
"version": "soljson-v0.5.16+commit.9c3226ce.js",
"settings": {
"optimizer": {
"enabled": true,
"runs": 1000000,
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
},
"outputSelection": {
"*": {
"*": [
"abi",
"devdoc",
"evm.bytecode.object",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.object",
"evm.deployedBytecode.sourceMap"
]
}
},
"evmVersion": "istanbul"
}
},
"chains": {}
}

View File

@ -1,4 +1,17 @@
[
{
"version": "13.7.0",
"changes": [
{
"note": "Regenerated wrappers for Broker and GodsUnchainedValidator",
"pr": 2511
},
{
"note": "Added wrapper for MaximumGasPrice",
"pr": 2511
}
]
},
{
"timestamp": 1583220306,
"version": "13.6.3",

View File

@ -31,7 +31,7 @@
"wrappers:generate": "abi-gen --abis ${npm_package_config_abis} --output src/generated-wrappers --backend ethers"
},
"config": {
"abis": "../contract-artifacts/artifacts/@(DevUtils|ERC20Token|ERC721Token|Exchange|Forwarder|IAssetData|LibTransactionDecoder|WETH9|Coordinator|Staking|StakingProxy|IERC20BridgeSampler|ERC20BridgeSampler|GodsUnchainedValidator|Broker|ILiquidityProvider|ILiquidityProviderRegistry|DummyLiquidityProvider|DummyLiquidityProviderRegistry).json"
"abis": "../contract-artifacts/artifacts/@(DevUtils|ERC20Token|ERC721Token|Exchange|Forwarder|IAssetData|LibTransactionDecoder|WETH9|Coordinator|Staking|StakingProxy|IERC20BridgeSampler|ERC20BridgeSampler|GodsUnchainedValidator|Broker|ILiquidityProvider|ILiquidityProviderRegistry|DummyLiquidityProvider|DummyLiquidityProviderRegistry|MaximumGasPrice).json"
},
"repository": {
"type": "git",

View File

@ -9,6 +9,7 @@ import {
BaseContract,
PromiseWithTransactionHash,
methodAbiToFunctionSignature,
linkLibrariesInBytecode,
} from '@0x/base-contract';
import { schemas } from '@0x/json-schemas';
import {
@ -25,7 +26,7 @@ import {
TxDataPayable,
SupportedProvider,
} from 'ethereum-types';
import { BigNumber, classUtils, logUtils, providerUtils } from '@0x/utils';
import { BigNumber, classUtils, hexUtils, logUtils, providerUtils } from '@0x/utils';
import { EventCallback, IndexedFilterValues, SimpleContractArtifact } from '@0x/types';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { assert } from '@0x/assert';
@ -33,6 +34,7 @@ import * as ethers from 'ethers';
// tslint:enable:no-unused-variable
/* istanbul ignore next */
// tslint:disable:array-type
// tslint:disable:no-parameter-reassignment
// tslint:disable-next-line:class-name
export class BrokerContract extends BaseContract {
@ -77,6 +79,50 @@ export class BrokerContract extends BaseContract {
weth,
);
}
public static async deployWithLibrariesFrom0xArtifactAsync(
artifact: ContractArtifact,
libraryArtifacts: { [libraryName: string]: ContractArtifact },
supportedProvider: SupportedProvider,
txDefaults: Partial<TxData>,
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
exchange: string,
weth: string,
): Promise<BrokerContract> {
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
if (artifact.compilerOutput === undefined) {
throw new Error('Compiler output not found in the artifact file');
}
const provider = providerUtils.standardizeOrThrow(supportedProvider);
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
const libraryAddresses = await BrokerContract._deployLibrariesAsync(
artifact,
libraryArtifacts,
new Web3Wrapper(provider),
txDefaults,
);
const bytecode = linkLibrariesInBytecode(artifact, libraryAddresses);
return BrokerContract.deployAsync(
bytecode,
abi,
provider,
txDefaults,
logDecodeDependenciesAbiOnly,
exchange,
weth,
);
}
public static async deployAsync(
bytecode: string,
abi: ContractAbi,
@ -432,12 +478,58 @@ export class BrokerContract extends BaseContract {
return abi;
}
protected static async _deployLibrariesAsync(
artifact: ContractArtifact,
libraryArtifacts: { [libraryName: string]: ContractArtifact },
web3Wrapper: Web3Wrapper,
txDefaults: Partial<TxData>,
libraryAddresses: { [libraryName: string]: string } = {},
): Promise<{ [libraryName: string]: string }> {
const links = artifact.compilerOutput.evm.bytecode.linkReferences;
// Go through all linked libraries, recursively deploying them if necessary.
for (const link of Object.values(links)) {
for (const libraryName of Object.keys(link)) {
if (!libraryAddresses[libraryName]) {
// Library not yet deployed.
const libraryArtifact = libraryArtifacts[libraryName];
if (!libraryArtifact) {
throw new Error(`Missing artifact for linked library "${libraryName}"`);
}
// Deploy any dependent libraries used by this library.
await BrokerContract._deployLibrariesAsync(
libraryArtifact,
libraryArtifacts,
web3Wrapper,
txDefaults,
libraryAddresses,
);
// Deploy this library.
const linkedLibraryBytecode = linkLibrariesInBytecode(libraryArtifact, libraryAddresses);
const txDataWithDefaults = await BaseContract._applyDefaultsToContractTxDataAsync(
{
data: linkedLibraryBytecode,
...txDefaults,
},
web3Wrapper.estimateGasAsync.bind(web3Wrapper),
);
const txHash = await web3Wrapper.sendTransactionAsync(txDataWithDefaults);
logUtils.log(`transactionHash: ${txHash}`);
const { contractAddress } = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
logUtils.log(`${libraryArtifact.contractName} successfully deployed at ${contractAddress}`);
libraryAddresses[libraryArtifact.contractName] = contractAddress as string;
}
}
}
return libraryAddresses;
}
public getFunctionSignature(methodName: string): string {
const index = this._methodABIIndex[methodName];
const methodAbi = BrokerContract.ABI()[index] as MethodAbi; // tslint:disable-line:no-unnecessary-type-assertion
const functionSignature = methodAbiToFunctionSignature(methodAbi);
return functionSignature;
}
public getABIDecodedTransactionData<T>(methodName: string, callData: string): T {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as BrokerContract;
@ -445,6 +537,7 @@ export class BrokerContract extends BaseContract {
const abiDecodedCallData = abiEncoder.strictDecode<T>(callData);
return abiDecodedCallData;
}
public getABIDecodedReturnData<T>(methodName: string, callData: string): T {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as BrokerContract;
@ -452,6 +545,7 @@ export class BrokerContract extends BaseContract {
const abiDecodedCallData = abiEncoder.strictDecodeReturnValue<T>(callData);
return abiDecodedCallData;
}
public getSelector(methodName: string): string {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as BrokerContract;

View File

@ -9,6 +9,7 @@ import {
BaseContract,
PromiseWithTransactionHash,
methodAbiToFunctionSignature,
linkLibrariesInBytecode,
} from '@0x/base-contract';
import { schemas } from '@0x/json-schemas';
import {
@ -25,7 +26,7 @@ import {
TxDataPayable,
SupportedProvider,
} from 'ethereum-types';
import { BigNumber, classUtils, logUtils, providerUtils } from '@0x/utils';
import { BigNumber, classUtils, hexUtils, logUtils, providerUtils } from '@0x/utils';
import { EventCallback, IndexedFilterValues, SimpleContractArtifact } from '@0x/types';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { assert } from '@0x/assert';
@ -33,6 +34,7 @@ import * as ethers from 'ethers';
// tslint:enable:no-unused-variable
/* istanbul ignore next */
// tslint:disable:array-type
// tslint:disable:no-parameter-reassignment
// tslint:disable-next-line:class-name
export class GodsUnchainedValidatorContract extends BaseContract {
@ -75,6 +77,48 @@ export class GodsUnchainedValidatorContract extends BaseContract {
_godsUnchained,
);
}
public static async deployWithLibrariesFrom0xArtifactAsync(
artifact: ContractArtifact,
libraryArtifacts: { [libraryName: string]: ContractArtifact },
supportedProvider: SupportedProvider,
txDefaults: Partial<TxData>,
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
_godsUnchained: string,
): Promise<GodsUnchainedValidatorContract> {
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
if (artifact.compilerOutput === undefined) {
throw new Error('Compiler output not found in the artifact file');
}
const provider = providerUtils.standardizeOrThrow(supportedProvider);
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
const libraryAddresses = await GodsUnchainedValidatorContract._deployLibrariesAsync(
artifact,
libraryArtifacts,
new Web3Wrapper(provider),
txDefaults,
);
const bytecode = linkLibrariesInBytecode(artifact, libraryAddresses);
return GodsUnchainedValidatorContract.deployAsync(
bytecode,
abi,
provider,
txDefaults,
logDecodeDependenciesAbiOnly,
_godsUnchained,
);
}
public static async deployAsync(
bytecode: string,
abi: ContractAbi,
@ -160,12 +204,58 @@ export class GodsUnchainedValidatorContract extends BaseContract {
return abi;
}
protected static async _deployLibrariesAsync(
artifact: ContractArtifact,
libraryArtifacts: { [libraryName: string]: ContractArtifact },
web3Wrapper: Web3Wrapper,
txDefaults: Partial<TxData>,
libraryAddresses: { [libraryName: string]: string } = {},
): Promise<{ [libraryName: string]: string }> {
const links = artifact.compilerOutput.evm.bytecode.linkReferences;
// Go through all linked libraries, recursively deploying them if necessary.
for (const link of Object.values(links)) {
for (const libraryName of Object.keys(link)) {
if (!libraryAddresses[libraryName]) {
// Library not yet deployed.
const libraryArtifact = libraryArtifacts[libraryName];
if (!libraryArtifact) {
throw new Error(`Missing artifact for linked library "${libraryName}"`);
}
// Deploy any dependent libraries used by this library.
await GodsUnchainedValidatorContract._deployLibrariesAsync(
libraryArtifact,
libraryArtifacts,
web3Wrapper,
txDefaults,
libraryAddresses,
);
// Deploy this library.
const linkedLibraryBytecode = linkLibrariesInBytecode(libraryArtifact, libraryAddresses);
const txDataWithDefaults = await BaseContract._applyDefaultsToContractTxDataAsync(
{
data: linkedLibraryBytecode,
...txDefaults,
},
web3Wrapper.estimateGasAsync.bind(web3Wrapper),
);
const txHash = await web3Wrapper.sendTransactionAsync(txDataWithDefaults);
logUtils.log(`transactionHash: ${txHash}`);
const { contractAddress } = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
logUtils.log(`${libraryArtifact.contractName} successfully deployed at ${contractAddress}`);
libraryAddresses[libraryArtifact.contractName] = contractAddress as string;
}
}
}
return libraryAddresses;
}
public getFunctionSignature(methodName: string): string {
const index = this._methodABIIndex[methodName];
const methodAbi = GodsUnchainedValidatorContract.ABI()[index] as MethodAbi; // tslint:disable-line:no-unnecessary-type-assertion
const functionSignature = methodAbiToFunctionSignature(methodAbi);
return functionSignature;
}
public getABIDecodedTransactionData<T>(methodName: string, callData: string): T {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as GodsUnchainedValidatorContract;
@ -173,6 +263,7 @@ export class GodsUnchainedValidatorContract extends BaseContract {
const abiDecodedCallData = abiEncoder.strictDecode<T>(callData);
return abiDecodedCallData;
}
public getABIDecodedReturnData<T>(methodName: string, callData: string): T {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as GodsUnchainedValidatorContract;
@ -180,6 +271,7 @@ export class GodsUnchainedValidatorContract extends BaseContract {
const abiDecodedCallData = abiEncoder.strictDecodeReturnValue<T>(callData);
return abiDecodedCallData;
}
public getSelector(methodName: string): string {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as GodsUnchainedValidatorContract;

View File

@ -0,0 +1,334 @@
// tslint:disable:no-consecutive-blank-lines ordered-imports align trailing-comma enum-naming
// tslint:disable:whitespace no-unbound-method no-trailing-whitespace
// tslint:disable:no-unused-variable
import {
AwaitTransactionSuccessOpts,
ContractFunctionObj,
ContractTxFunctionObj,
SendTransactionOpts,
BaseContract,
PromiseWithTransactionHash,
methodAbiToFunctionSignature,
linkLibrariesInBytecode,
} from '@0x/base-contract';
import { schemas } from '@0x/json-schemas';
import {
BlockParam,
BlockParamLiteral,
BlockRange,
CallData,
ContractAbi,
ContractArtifact,
DecodedLogArgs,
MethodAbi,
TransactionReceiptWithDecodedLogs,
TxData,
TxDataPayable,
SupportedProvider,
} from 'ethereum-types';
import { BigNumber, classUtils, hexUtils, logUtils, providerUtils } from '@0x/utils';
import { EventCallback, IndexedFilterValues, SimpleContractArtifact } from '@0x/types';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { assert } from '@0x/assert';
import * as ethers from 'ethers';
// tslint:enable:no-unused-variable
/* istanbul ignore next */
// tslint:disable:array-type
// tslint:disable:no-parameter-reassignment
// tslint:disable-next-line:class-name
export class MaximumGasPriceContract extends BaseContract {
/**
* @ignore
*/
public static deployedBytecode: string | undefined;
public static contractName = 'MaximumGasPrice';
private readonly _methodABIIndex: { [name: string]: number } = {};
public static async deployFrom0xArtifactAsync(
artifact: ContractArtifact | SimpleContractArtifact,
supportedProvider: SupportedProvider,
txDefaults: Partial<TxData>,
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
): Promise<MaximumGasPriceContract> {
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
if (artifact.compilerOutput === undefined) {
throw new Error('Compiler output not found in the artifact file');
}
const provider = providerUtils.standardizeOrThrow(supportedProvider);
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return MaximumGasPriceContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}
public static async deployWithLibrariesFrom0xArtifactAsync(
artifact: ContractArtifact,
libraryArtifacts: { [libraryName: string]: ContractArtifact },
supportedProvider: SupportedProvider,
txDefaults: Partial<TxData>,
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
): Promise<MaximumGasPriceContract> {
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
if (artifact.compilerOutput === undefined) {
throw new Error('Compiler output not found in the artifact file');
}
const provider = providerUtils.standardizeOrThrow(supportedProvider);
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
const libraryAddresses = await MaximumGasPriceContract._deployLibrariesAsync(
artifact,
libraryArtifacts,
new Web3Wrapper(provider),
txDefaults,
);
const bytecode = linkLibrariesInBytecode(artifact, libraryAddresses);
return MaximumGasPriceContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}
public static async deployAsync(
bytecode: string,
abi: ContractAbi,
supportedProvider: SupportedProvider,
txDefaults: Partial<TxData>,
logDecodeDependencies: { [contractName: string]: ContractAbi },
): Promise<MaximumGasPriceContract> {
assert.isHexString('bytecode', bytecode);
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
const provider = providerUtils.standardizeOrThrow(supportedProvider);
const constructorAbi = BaseContract._lookupConstructorAbi(abi);
[] = BaseContract._formatABIDataItemList(constructorAbi.inputs, [], BaseContract._bigNumberToString);
const iface = new ethers.utils.Interface(abi);
const deployInfo = iface.deployFunction;
const txData = deployInfo.encode(bytecode, []);
const web3Wrapper = new Web3Wrapper(provider);
const txDataWithDefaults = await BaseContract._applyDefaultsToContractTxDataAsync(
{
data: txData,
...txDefaults,
},
web3Wrapper.estimateGasAsync.bind(web3Wrapper),
);
const txHash = await web3Wrapper.sendTransactionAsync(txDataWithDefaults);
logUtils.log(`transactionHash: ${txHash}`);
const txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
logUtils.log(`MaximumGasPrice successfully deployed at ${txReceipt.contractAddress}`);
const contractInstance = new MaximumGasPriceContract(
txReceipt.contractAddress as string,
provider,
txDefaults,
logDecodeDependencies,
);
contractInstance.constructorArgs = [];
return contractInstance;
}
/**
* @returns The contract ABI
*/
public static ABI(): ContractAbi {
const abi = [
{
constant: true,
inputs: [],
name: 'checkGasPrice',
outputs: [],
payable: false,
stateMutability: 'view',
type: 'function',
},
{
constant: true,
inputs: [
{
name: 'maxGasPrice',
type: 'uint256',
},
],
name: 'checkGasPrice',
outputs: [],
payable: false,
stateMutability: 'view',
type: 'function',
},
] as ContractAbi;
return abi;
}
protected static async _deployLibrariesAsync(
artifact: ContractArtifact,
libraryArtifacts: { [libraryName: string]: ContractArtifact },
web3Wrapper: Web3Wrapper,
txDefaults: Partial<TxData>,
libraryAddresses: { [libraryName: string]: string } = {},
): Promise<{ [libraryName: string]: string }> {
const links = artifact.compilerOutput.evm.bytecode.linkReferences;
// Go through all linked libraries, recursively deploying them if necessary.
for (const link of Object.values(links)) {
for (const libraryName of Object.keys(link)) {
if (!libraryAddresses[libraryName]) {
// Library not yet deployed.
const libraryArtifact = libraryArtifacts[libraryName];
if (!libraryArtifact) {
throw new Error(`Missing artifact for linked library "${libraryName}"`);
}
// Deploy any dependent libraries used by this library.
await MaximumGasPriceContract._deployLibrariesAsync(
libraryArtifact,
libraryArtifacts,
web3Wrapper,
txDefaults,
libraryAddresses,
);
// Deploy this library.
const linkedLibraryBytecode = linkLibrariesInBytecode(libraryArtifact, libraryAddresses);
const txDataWithDefaults = await BaseContract._applyDefaultsToContractTxDataAsync(
{
data: linkedLibraryBytecode,
...txDefaults,
},
web3Wrapper.estimateGasAsync.bind(web3Wrapper),
);
const txHash = await web3Wrapper.sendTransactionAsync(txDataWithDefaults);
logUtils.log(`transactionHash: ${txHash}`);
const { contractAddress } = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
logUtils.log(`${libraryArtifact.contractName} successfully deployed at ${contractAddress}`);
libraryAddresses[libraryArtifact.contractName] = contractAddress as string;
}
}
}
return libraryAddresses;
}
public getFunctionSignature(methodName: string): string {
const index = this._methodABIIndex[methodName];
const methodAbi = MaximumGasPriceContract.ABI()[index] as MethodAbi; // tslint:disable-line:no-unnecessary-type-assertion
const functionSignature = methodAbiToFunctionSignature(methodAbi);
return functionSignature;
}
public getABIDecodedTransactionData<T>(methodName: string, callData: string): T {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as MaximumGasPriceContract;
const abiEncoder = self._lookupAbiEncoder(functionSignature);
const abiDecodedCallData = abiEncoder.strictDecode<T>(callData);
return abiDecodedCallData;
}
public getABIDecodedReturnData<T>(methodName: string, callData: string): T {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as MaximumGasPriceContract;
const abiEncoder = self._lookupAbiEncoder(functionSignature);
const abiDecodedCallData = abiEncoder.strictDecodeReturnValue<T>(callData);
return abiDecodedCallData;
}
public getSelector(methodName: string): string {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as MaximumGasPriceContract;
const abiEncoder = self._lookupAbiEncoder(functionSignature);
return abiEncoder.getSelector();
}
/**
* Checks that the current transaction's gas price is less than
* the default maximum value of 20 Gwei.
*/
public checkGasPrice1(): ContractFunctionObj<void> {
const self = (this as any) as MaximumGasPriceContract;
const functionSignature = 'checkGasPrice()';
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<void> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<void>(rawCallResult);
},
getABIEncodedTransactionData(): string {
return self._strictEncodeArguments(functionSignature, []);
},
};
}
/**
* Checks that the current transaction's gas price is less than
* the specified maximum value.
* @param maxGasPrice The maximum gas price allowed for the current
* transaction.
*/
public checkGasPrice2(maxGasPrice: BigNumber): ContractFunctionObj<void> {
const self = (this as any) as MaximumGasPriceContract;
assert.isBigNumber('maxGasPrice', maxGasPrice);
const functionSignature = 'checkGasPrice(uint256)';
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<void> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._performCallAsync(
{ ...callData, data: this.getABIEncodedTransactionData() },
defaultBlock,
);
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<void>(rawCallResult);
},
getABIEncodedTransactionData(): string {
return self._strictEncodeArguments(functionSignature, [maxGasPrice]);
},
};
}
constructor(
address: string,
supportedProvider: SupportedProvider,
txDefaults?: Partial<TxData>,
logDecodeDependencies?: { [contractName: string]: ContractAbi },
deployedBytecode: string | undefined = MaximumGasPriceContract.deployedBytecode,
) {
super(
'MaximumGasPrice',
MaximumGasPriceContract.ABI(),
address,
supportedProvider,
txDefaults,
logDecodeDependencies,
deployedBytecode,
);
classUtils.bindAll(this, ['_abiEncoderByFunctionSignature', 'address', '_web3Wrapper']);
MaximumGasPriceContract.ABI().forEach((item, index) => {
if (item.type === 'function') {
const methodAbi = item as MethodAbi;
this._methodABIIndex[methodAbi.name] = index;
}
});
}
}
// tslint:disable:max-file-line-count
// tslint:enable:no-unbound-method no-parameter-reassignment no-consecutive-blank-lines ordered-imports align
// tslint:enable:trailing-comma whitespace no-trailing-whitespace

View File

@ -307,6 +307,7 @@ export async function runMigrationsAsync(
godsUnchainedValidator: constants.NULL_ADDRESS,
broker: constants.NULL_ADDRESS,
chainlinkStopLimit: constants.NULL_ADDRESS,
maximumGasPrice: constants.NULL_ADDRESS,
};
return contractAddresses;
}