diff --git a/contracts/extensions/contracts/src/MaximumGasPrice.sol b/contracts/extensions/contracts/src/MaximumGasPrice.sol index 37cde8593a..027d9e5d2a 100644 --- a/contracts/extensions/contracts/src/MaximumGasPrice.sol +++ b/contracts/extensions/contracts/src/MaximumGasPrice.sol @@ -17,7 +17,6 @@ */ pragma solidity ^0.5.16; -pragma experimental ABIEncoderV2; contract MaximumGasPrice { @@ -38,12 +37,11 @@ contract MaximumGasPrice { /// @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) + /// @param maxGasPrice The maximum gas price allowed for the current transaction. + function checkGasPrice(uint256 maxGasPrice) external view { - (uint256 maxGasPrice) = abi.decode(data, (uint256)); require( tx.gasprice <= maxGasPrice, "MaximumGasPrice/GAS_PRICE_EXCEEDS_MAXIMUM" diff --git a/contracts/extensions/src/max_gas_price_utils.ts b/contracts/extensions/src/max_gas_price_utils.ts index 754c8d4069..7816423933 100644 --- a/contracts/extensions/src/max_gas_price_utils.ts +++ b/contracts/extensions/src/max_gas_price_utils.ts @@ -3,8 +3,7 @@ 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 customGasPriceEncoder = AbiEncoder.createMethod('checkGasPrice', [{ name: 'maxGasPrice', type: 'uint256' }]); const defaultGasPriceEncoder = AbiEncoder.createMethod('checkGasPrice', []); const ONE_GWEI = new BigNumber(10 ** 9); @@ -16,11 +15,7 @@ export const TWENTY_GWEI = ONE_GWEI.times(20); */ export function encodeMaxGasPriceStaticCallData(maxGasPriceContractAddress: string, maxGasPrice?: BigNumber): string { const staticCallData = - maxGasPrice === undefined - ? defaultGasPriceEncoder.encode({}) - : customGasPriceEncoder.encode({ - data: maxGasPriceEncoder.encode({ maxGasPrice }), - }); + maxGasPrice === undefined ? defaultGasPriceEncoder.encode({}) : customGasPriceEncoder.encode({ maxGasPrice }); return assetDataUtils.encodeStaticCallAssetData( maxGasPriceContractAddress, staticCallData, @@ -35,8 +30,7 @@ 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(staticCallData)); - return maxGasPrice; + return customGasPriceEncoder.strictDecode(staticCallData); } catch (e) { defaultGasPriceEncoder.strictDecode(staticCallData); return TWENTY_GWEI; diff --git a/contracts/extensions/test/max_gas_price_test.ts b/contracts/extensions/test/max_gas_price_test.ts index 3264532d84..76ac63b7cf 100644 --- a/contracts/extensions/test/max_gas_price_test.ts +++ b/contracts/extensions/test/max_gas_price_test.ts @@ -1,5 +1,5 @@ -import { blockchainTests, constants, expect, getRandomInteger } from '@0x/contracts-test-utils'; -import { AbiEncoder } from '@0x/utils'; +import { artifacts as assetProxyArtifacts, StaticCallProxyContract } from '@0x/contracts-asset-proxy'; +import { blockchainTests, constants, expect, getRandomInteger, randomAddress } from '@0x/contracts-test-utils'; import { decodeMaxGasPriceStaticCallData, @@ -12,7 +12,9 @@ import { MaximumGasPriceContract } from './wrappers'; blockchainTests.resets('MaximumGasPrice unit tests', env => { let maxGasPriceContract: MaximumGasPriceContract; - const maxGasPriceEncoder = AbiEncoder.create([{ name: 'maxGasPrice', type: 'uint256' }]); + let staticCallProxy: StaticCallProxyContract; + + let defaultMaxAssetData: string; before(async () => { maxGasPriceContract = await MaximumGasPriceContract.deployFrom0xArtifactAsync( @@ -21,51 +23,67 @@ blockchainTests.resets('MaximumGasPrice unit tests', env => { env.txDefaults, artifacts, ); - }); + staticCallProxy = await StaticCallProxyContract.deployFrom0xArtifactAsync( + assetProxyArtifacts.StaticCallProxy, + env.provider, + env.txDefaults, + assetProxyArtifacts, + ); - 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'); - }); + defaultMaxAssetData = encodeMaxGasPriceStaticCallData(maxGasPriceContract.address); }); describe('Data encoding/decoding tools', () => { it('correctly decodes default maximum gas price', async () => { - const encoded = encodeMaxGasPriceStaticCallData(maxGasPriceContract.address); - const decoded = decodeMaxGasPriceStaticCallData(encoded); + const decoded = decodeMaxGasPriceStaticCallData(defaultMaxAssetData); 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); + 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'); }); }); });