Skip to content
This repository has been archived by the owner on Jul 9, 2021. It is now read-only.

@0x/contracts-extensions: Maximum gas price contract #2511

Merged
merged 5 commits into from
Mar 5, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions contracts/extensions/CHANGELOG.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
52 changes: 52 additions & 0 deletions contracts/extensions/contracts/src/MaximumGasPrice.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👀

pragma experimental ABIEncoderV2;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we actually need to use ABIv2 here. I'd just run tests with/without to see if there are any gas differences.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like without is slightly cheaper for the parametrized version



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 data Encodes the maximum gas price.
function checkGasPrice(bytes calldata data)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just take a uint256 here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

didn't realize that would work 🙈

external
view
{
(uint256 maxGasPrice) = abi.decode(data, (uint256));
require(
tx.gasprice <= maxGasPrice,
"MaximumGasPrice/GAS_PRICE_EXCEEDS_MAXIMUM"
);
}
}
2 changes: 1 addition & 1 deletion contracts/extensions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
2 changes: 2 additions & 0 deletions contracts/extensions/src/artifacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions contracts/extensions/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ export {
TupleDataItem,
StateMutability,
} from 'ethereum-types';
export * from './max_gas_price_utils';
44 changes: 44 additions & 0 deletions contracts/extensions/src/max_gas_price_utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
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 maxGasPriceEncoder = AbiEncoder.create([{ name: 'maxGasPrice', type: 'uint256' }]);
const customGasPriceEncoder = AbiEncoder.createMethod('checkGasPrice', [{ name: 'data', type: 'bytes' }]);
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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for writing this helper 🙌

const staticCallData =
maxGasPrice === undefined
? defaultGasPriceEncoder.encode({})
: customGasPriceEncoder.encode({
data: maxGasPriceEncoder.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 {
const { maxGasPrice } = maxGasPriceEncoder.decode(customGasPriceEncoder.strictDecode<string>(staticCallData));
return maxGasPrice;
} catch (e) {
defaultGasPriceEncoder.strictDecode(staticCallData);
return TWENTY_GWEI;
}
}
1 change: 1 addition & 0 deletions contracts/extensions/src/wrappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
2 changes: 2 additions & 0 deletions contracts/extensions/test/artifacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
71 changes: 71 additions & 0 deletions contracts/extensions/test/max_gas_price_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { blockchainTests, constants, expect, getRandomInteger } from '@0x/contracts-test-utils';
import { AbiEncoder } from '@0x/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;
const maxGasPriceEncoder = AbiEncoder.create([{ name: 'maxGasPrice', type: 'uint256' }]);

before(async () => {
maxGasPriceContract = await MaximumGasPriceContract.deployFrom0xArtifactAsync(
artifacts.MaximumGasPrice,
env.provider,
env.txDefaults,
artifacts,
);
});

describe('Contract functionality', () => {
it('does not revert if tx.gasprice < default maximum', async () => {
await maxGasPriceContract.checkGasPrice1().callAsync({ gasPrice: TWENTY_GWEI.minus(1) });
});
it('does not revert if tx.gasprice = default maximum', async () => {
await maxGasPriceContract.checkGasPrice1().callAsync({ gasPrice: TWENTY_GWEI });
});
it('reverts if tx.gasPrice > default maximum', async () => {
const tx = maxGasPriceContract.checkGasPrice1().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));
await maxGasPriceContract
.checkGasPrice2(maxGasPriceEncoder.encode({ maxGasPrice }))
.callAsync({ gasPrice: maxGasPrice.minus(1) });
});
it('does not revert if tx.gasprice = default maximum', async () => {
const maxGasPrice = getRandomInteger(0, TWENTY_GWEI.times(2));
await maxGasPriceContract
.checkGasPrice2(maxGasPriceEncoder.encode({ maxGasPrice }))
.callAsync({ gasPrice: maxGasPrice });
});
it('reverts if tx.gasPrice > default maximum', async () => {
const maxGasPrice = getRandomInteger(0, TWENTY_GWEI.times(2));
const tx = maxGasPriceContract
.checkGasPrice2(maxGasPriceEncoder.encode({ maxGasPrice }))
.callAsync({ gasPrice: maxGasPrice.plus(1) });
return expect(tx).to.revertWith('MaximumGasPrice/GAS_PRICE_EXCEEDS_MAXIMUM');
});
});

describe('Data encoding/decoding tools', () => {
it('correctly decodes default maximum gas price', async () => {
const encoded = encodeMaxGasPriceStaticCallData(maxGasPriceContract.address);
const decoded = decodeMaxGasPriceStaticCallData(encoded);
expect(decoded).to.bignumber.equal(TWENTY_GWEI);
});
it('correctly decodes custom maximum gas price', async () => {
const maxGasPrice = getRandomInteger(0, constants.MAX_UINT256);
const encoded = encodeMaxGasPriceStaticCallData(maxGasPriceContract.address, maxGasPrice);
const decoded = decodeMaxGasPriceStaticCallData(encoded);
expect(decoded).to.bignumber.equal(maxGasPrice);
});
});
});
1 change: 1 addition & 0 deletions contracts/extensions/test/wrappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
2 changes: 2 additions & 0 deletions contracts/extensions/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down