ZeroEx: TransformERC20, TokenSpender (#2545)

* `@0x/contracts-utils`: Convert more 0.6 contracts

* `@0x/contracts-erc20`: Add solidity 0.6 contracts.

* `@0x/utils`: Add new `ZeroExRevertErrors` revert types

* `@0x/contracts-zero-ex`: Introduce the `TransformERC20` feature.

* `@0x/subproviders`: Update ganache-core.
`@0x/web3-wrapper`: Update ganache-core.

* `@0x/contracts-zero-ex`: Make `TokenSpender`'s puppet contract a distinct contract type and rename `getTokenSpenderPuppet()` to `getAllowanceTarget()`

* `@0x/zero-ex`: Rebase and use "slot" instead of "offset" language in storage buckets.

* `@0x/web3-wrapper`: Add `getAccountNonceAsync()` to `Web3Wrapper`

* `@0x/contracts-zero-ex`: Revamp TransformERC20.

* `@0x/contracts-zero-ex`: Remove `payable` from `IERC20Transformer.transform()` and disable hex capitalization linter rule because of prettier conflicts.

* `@0x/contracts-zero-ex`: Use `immutable` owner in `Puppet` instead of `Ownable`.

* `@x/utils`: Address review feedback.

* `@0x/contracts-zero-ex`: Address review feedback.

* `@0x/contracts-utils`: Address review feedback.

* `@0x/contracts-zero-ex`: Return deployment nonce in `transform()`.

* `@0x/contracts-zero-ex`: Finish returning deployment nonce in `transform()`.

* `@0x/contracts-zero-ex`: Fix doc-gen bug.

* `@0x/contracts-zero-ex`: Address review comments.

* `@0x/utils`: Add `NegativeTransformERC20OutputERror`

* `@0x/contracts-zero-ex`: Revert if the taker's output amount decreases.

Co-authored-by: Lawrence Forman <me@merklejerk.com>
This commit is contained in:
Lawrence Forman
2020-05-20 22:47:21 -04:00
committed by GitHub
parent b23f1eb145
commit 2fce332ed7
87 changed files with 5613 additions and 260 deletions

View File

@@ -1,4 +1,13 @@
[
{
"version": "6.1.0",
"changes": [
{
"note": "Update ganache-core",
"pr": 2545
}
]
},
{
"timestamp": 1582623685,
"version": "6.0.8",

View File

@@ -48,7 +48,7 @@
"ethereum-types": "^3.1.0",
"ethereumjs-tx": "^1.3.5",
"ethereumjs-util": "^5.1.1",
"ganache-core": "^2.9.0-istanbul.0",
"ganache-core": "^2.10.2",
"hdkey": "^0.7.1",
"json-rpc-error": "2.0.0",
"lodash": "^4.17.11",

View File

@@ -17,6 +17,10 @@
{
"note": "`instanceof Array` => `Array.isArray`",
"pr": 2567
},
{
"note": "Add more `ZeroExRevertErrors`",
"pr": 2545
}
]
},

View File

@@ -49,4 +49,7 @@ export const ZeroExRevertErrors = {
Proxy: require('./revert_errors/zero-ex/proxy_revert_errors'),
SimpleFunctionRegistry: require('./revert_errors/zero-ex/simple_function_registry_revert_errors'),
Ownable: require('./revert_errors/zero-ex/ownable_revert_errors'),
Spender: require('./revert_errors/zero-ex/spender_revert_errors'),
TransformERC20: require('./revert_errors/zero-ex/transform_erc20_revert_errors'),
Wallet: require('./revert_errors/zero-ex/wallet_revert_errors'),
};

View File

@@ -9,7 +9,16 @@ import { BigNumber } from './configured_bignumber';
// tslint:disable: max-classes-per-file
type ArgTypes = string | BigNumber | number | boolean | BigNumber[] | string[] | number[] | boolean[];
type ArgTypes =
| string
| BigNumber
| number
| boolean
| BigNumber[]
| string[]
| number[]
| boolean[]
| Array<BigNumber | number | string>;
type ValueMap = ObjectMap<ArgTypes | undefined>;
type RevertErrorDecoder = (hex: string) => ValueMap;

View File

@@ -9,6 +9,13 @@ export class OnlyCallableBySelfError extends RevertError {
}
}
// This is identical to the one in utils.
// export class IllegalReentrancyError extends RevertError {
// constructor() {
// super('IllegalReentrancyError', 'IllegalReentrancyError()', {});
// }
// }
const types = [OnlyCallableBySelfError];
// Register the types we've defined.

View File

@@ -16,7 +16,23 @@ export class MigrateCallFailedError extends RevertError {
}
}
const types = [AlreadyMigratingError, MigrateCallFailedError];
export class OnlyOwnerError extends RevertError {
constructor(sender?: string, owner?: string) {
super('OnlyOwnerError', 'OnlyOwnerError(address sender, bytes owner)', {
sender,
owner,
});
}
}
// This is identical to the one in utils.
// export class TransferOwnerToZeroError extends RevertError {
// constructor() {
// super('TransferOwnerToZeroError', 'TransferOwnerToZeroError()', {});
// }
// }
const types = [AlreadyMigratingError, MigrateCallFailedError, OnlyOwnerError];
// Register the types we've defined.
for (const type of types) {

View File

@@ -0,0 +1,26 @@
import { RevertError } from '../../revert_error';
import { Numberish } from '../../types';
// tslint:disable:max-classes-per-file
export class SpenderERC20TransferFromFailedError extends RevertError {
constructor(token?: string, owner?: string, to?: string, amount?: Numberish, errorData?: string) {
super(
'SpenderERC20TransferFromFailedError',
'SpenderERC20TransferFromFailedError(address token, address owner, address to, uint256 amount, bytes errorData)',
{
token,
owner,
to,
amount,
errorData,
},
);
}
}
const types = [SpenderERC20TransferFromFailedError];
// Register the types we've defined.
for (const type of types) {
RevertError.registerType(type);
}

View File

@@ -0,0 +1,153 @@
import { RevertError } from '../../revert_error';
import { Numberish } from '../../types';
// tslint:disable:max-classes-per-file
export class InsufficientEthAttachedError extends RevertError {
constructor(ethAttached?: Numberish, ethNeeded?: Numberish) {
super('InsufficientEthAttachedError', 'InsufficientEthAttachedError(uint256 ethAttached, uint256 ethNeeded)', {
ethAttached,
ethNeeded,
});
}
}
export class IncompleteTransformERC20Error extends RevertError {
constructor(outputToken?: string, outputTokenAmount?: Numberish, minOutputTokenAmount?: Numberish) {
super(
'IncompleteTransformERC20Error',
'IncompleteTransformERC20Error(address outputToken, uint256 outputTokenAmount, uint256 minOutputTokenAmount)',
{
outputToken,
outputTokenAmount,
minOutputTokenAmount,
},
);
}
}
export class NegativeTransformERC20OutputError extends RevertError {
constructor(outputToken?: string, outputTokenLostAmount?: Numberish) {
super(
'NegativeTransformERC20OutputError',
'NegativeTransformERC20OutputError(address outputToken, uint256 outputTokenLostAmount)',
{
outputToken,
outputTokenLostAmount,
},
);
}
}
export class UnauthorizedTransformerError extends RevertError {
constructor(transformer?: string, rlpNonce?: string) {
super('UnauthorizedTransformerError', 'UnauthorizedTransformerError(address transformer, bytes rlpNonce)', {
transformer,
rlpNonce,
});
}
}
export class InvalidRLPNonceError extends RevertError {
constructor(rlpNonce?: string) {
super('InvalidRLPNonceError', 'InvalidRLPNonceError(bytes rlpNonce)', { rlpNonce });
}
}
export class IncompleteFillSellQuoteError extends RevertError {
constructor(sellToken?: string, soldAmount?: Numberish, sellAmount?: Numberish) {
super(
'IncompleteFillSellQuoteError',
'IncompleteFillSellQuoteError(address sellToken, address[] soldAmount, uint256[] sellAmount)',
{
sellToken,
soldAmount,
sellAmount,
},
);
}
}
export class IncompleteFillBuyQuoteError extends RevertError {
constructor(buyToken?: string, boughtAmount?: Numberish, buyAmount?: Numberish) {
super(
'IncompleteFillBuyQuoteError',
'IncompleteFillBuyQuoteError(address buyToken, address[] boughtAmount, uint256[] buyAmount)',
{
buyToken,
boughtAmount,
buyAmount,
},
);
}
}
export class InsufficientTakerTokenError extends RevertError {
constructor(tokenBalance?: Numberish, tokensNeeded?: Numberish) {
super(
'InsufficientTakerTokenError',
'InsufficientTakerTokenError(uint256 tokenBalance, uint256 tokensNeeded)',
{
tokenBalance,
tokensNeeded,
},
);
}
}
export class InsufficientProtocolFeeError extends RevertError {
constructor(ethBalance?: Numberish, ethNeeded?: Numberish) {
super('InsufficientProtocolFeeError', 'InsufficientProtocolFeeError(uint256 ethBalance, uint256 ethNeeded)', {
ethBalance,
ethNeeded,
});
}
}
export class InvalidERC20AssetDataError extends RevertError {
constructor(assetData?: string) {
super('InvalidERC20AssetDataError', 'InvalidERC20AssetDataError(bytes assetData)', {
assetData,
});
}
}
export class WrongNumberOfTokensReceivedError extends RevertError {
constructor(actual?: Numberish, expected?: Numberish) {
super(
'WrongNumberOfTokensReceivedError',
'WrongNumberOfTokensReceivedError(uint256 actual, uint256 expected)',
{
actual,
expected,
},
);
}
}
export class InvalidTokenReceivedError extends RevertError {
constructor(token?: string) {
super('InvalidTokenReceivedError', 'InvalidTokenReceivedError(address token)', {
token,
});
}
}
const types = [
InsufficientEthAttachedError,
IncompleteTransformERC20Error,
NegativeTransformERC20OutputError,
UnauthorizedTransformerError,
InvalidRLPNonceError,
IncompleteFillSellQuoteError,
IncompleteFillBuyQuoteError,
InsufficientTakerTokenError,
InsufficientProtocolFeeError,
InvalidERC20AssetDataError,
WrongNumberOfTokensReceivedError,
InvalidTokenReceivedError,
];
// Register the types we've defined.
for (const type of types) {
RevertError.registerType(type);
}

View File

@@ -0,0 +1,41 @@
import { RevertError } from '../../revert_error';
import { Numberish } from '../../types';
// tslint:disable:max-classes-per-file
export class WalletExecuteCallFailedError extends RevertError {
constructor(wallet?: string, callTarget?: string, callData?: string, callValue?: Numberish, errorData?: string) {
super(
'WalletExecuteCallFailedError',
'WalletExecuteCallFailedError(address wallet, address callTarget, bytes callData, uint256 callValue, bytes errorData)',
{
wallet,
callTarget,
callData,
callValue,
errorData,
},
);
}
}
export class WalletExecuteDelegateCallFailedError extends RevertError {
constructor(wallet?: string, callTarget?: string, callData?: string, errorData?: string) {
super(
'WalletExecuteDelegateCallFailedError',
'WalletExecuteDelegateCallFailedError(address wallet, address callTarget, bytes callData, bytes errorData)',
{
wallet,
callTarget,
callData,
errorData,
},
);
}
}
const types = [WalletExecuteCallFailedError, WalletExecuteDelegateCallFailedError];
// Register the types we've defined.
for (const type of types) {
RevertError.registerType(type);
}

View File

@@ -1,4 +1,17 @@
[
{
"version": "7.1.0",
"changes": [
{
"note": "Add `getAccountNonce()` to `Web3Wrapper`",
"pr": 2545
},
{
"note": "Update ganache-core",
"pr": 2545
}
]
},
{
"timestamp": 1582623685,
"version": "7.0.7",

View File

@@ -47,7 +47,7 @@
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1",
"ganache-core": "^2.9.0-istanbul.0",
"ganache-core": "^2.10.2",
"make-promises-safe": "^1.1.0",
"mocha": "^6.2.0",
"npm-run-all": "^4.1.2",

View File

@@ -360,6 +360,27 @@ export class Web3Wrapper {
const blockNumber = utils.convertHexToNumberOrNull(blockNumberHex);
return blockNumber as number;
}
/**
* Fetches the nonce for an account (transaction count for EOAs).
* @param address Address of account.
* @param defaultBlock Block height at which to make the call. Defaults to `latest`
* @returns Account nonce.
*/
public async getAccountNonceAsync(address: string, defaultBlock?: BlockParam): Promise<number> {
assert.isETHAddressHex('address', address);
if (defaultBlock !== undefined) {
Web3Wrapper._assertBlockParam(defaultBlock);
}
const marshalledDefaultBlock = marshaller.marshalBlockParam(defaultBlock);
const encodedAddress = marshaller.marshalAddress(address);
const nonceHex = await this.sendRawPayloadAsync<string>({
method: 'eth_getTransactionCount',
params: [encodedAddress, marshalledDefaultBlock],
});
assert.isHexString('nonce', nonceHex);
// tslint:disable-next-line:custom-no-magic-numbers
return parseInt(nonceHex.substr(2), 16);
}
/**
* Fetch a specific Ethereum block without transaction data
* @param blockParam The block you wish to fetch (blockHash, blockNumber or blockLiteral)

View File

@@ -35,8 +35,7 @@ describe('Web3Wrapper tests', () => {
describe('#getNodeVersionAsync', () => {
it('gets the node version', async () => {
const nodeVersion = await web3Wrapper.getNodeVersionAsync();
const NODE_VERSION = 'EthereumJS TestRPC/v2.9.0-istanbul.0/ethereum-js';
expect(nodeVersion).to.be.equal(NODE_VERSION);
expect(nodeVersion).to.be.match(/EthereumJS TestRPC\/.+\/ethereum-js$/);
});
});
describe('#getNetworkIdAsync', () => {