Exchange signature validation fuzz tests (#2425)
* `@0x/contracts-integrations`: Add Exchange signature validation fuzz tests. * `@0x/contracts-integrations`: Switch from actor pattern to just pure function generators. Co-authored-by: Lawrence Forman <me@merklejerk.com>
This commit is contained in:
parent
8d10736934
commit
de12da18da
@ -9,6 +9,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Add aggregator mainnet tests.",
|
"note": "Add aggregator mainnet tests.",
|
||||||
"pr": 2407
|
"pr": 2407
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add fuzz tests for Exchange signature validation.",
|
||||||
|
"pr": 2425
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"timestamp": 1578272714
|
"timestamp": 1578272714
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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.5;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibEIP1271.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract TestSignatureValidationWallet is
|
||||||
|
LibEIP1271
|
||||||
|
{
|
||||||
|
bytes4 private constant LEGACY_WALLET_MAGIC_VALUE = 0xb0671381;
|
||||||
|
|
||||||
|
// Callback used by `EIP1271Wallet` and `Validator` signature types.
|
||||||
|
function isValidSignature(
|
||||||
|
bytes memory,
|
||||||
|
bytes memory
|
||||||
|
)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (bytes4 magicValue)
|
||||||
|
{
|
||||||
|
return EIP1271_MAGIC_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback used by `Wallet` signature type.
|
||||||
|
function isValidSignature(
|
||||||
|
bytes32,
|
||||||
|
bytes memory
|
||||||
|
)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (bytes4 magicValue)
|
||||||
|
{
|
||||||
|
return LEGACY_WALLET_MAGIC_VALUE;
|
||||||
|
}
|
||||||
|
}
|
@ -38,7 +38,7 @@
|
|||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"publicInterfaceContracts": "TestFramework",
|
"publicInterfaceContracts": "TestFramework",
|
||||||
"abis": "./test/generated-artifacts/@(TestDydxUser|TestEth2Dai|TestEth2DaiBridge|TestFramework|TestMainnetAggregatorFills|TestUniswapBridge|TestUniswapExchange|TestUniswapExchangeFactory).json",
|
"abis": "./test/generated-artifacts/@(TestDydxUser|TestEth2Dai|TestEth2DaiBridge|TestFramework|TestMainnetAggregatorFills|TestSignatureValidationWallet|TestUniswapBridge|TestUniswapExchange|TestUniswapExchangeFactory).json",
|
||||||
"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."
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -10,6 +10,7 @@ import * as TestEth2Dai from '../test/generated-artifacts/TestEth2Dai.json';
|
|||||||
import * as TestEth2DaiBridge from '../test/generated-artifacts/TestEth2DaiBridge.json';
|
import * as TestEth2DaiBridge from '../test/generated-artifacts/TestEth2DaiBridge.json';
|
||||||
import * as TestFramework from '../test/generated-artifacts/TestFramework.json';
|
import * as TestFramework from '../test/generated-artifacts/TestFramework.json';
|
||||||
import * as TestMainnetAggregatorFills from '../test/generated-artifacts/TestMainnetAggregatorFills.json';
|
import * as TestMainnetAggregatorFills from '../test/generated-artifacts/TestMainnetAggregatorFills.json';
|
||||||
|
import * as TestSignatureValidationWallet from '../test/generated-artifacts/TestSignatureValidationWallet.json';
|
||||||
import * as TestUniswapBridge from '../test/generated-artifacts/TestUniswapBridge.json';
|
import * as TestUniswapBridge from '../test/generated-artifacts/TestUniswapBridge.json';
|
||||||
import * as TestUniswapExchange from '../test/generated-artifacts/TestUniswapExchange.json';
|
import * as TestUniswapExchange from '../test/generated-artifacts/TestUniswapExchange.json';
|
||||||
import * as TestUniswapExchangeFactory from '../test/generated-artifacts/TestUniswapExchangeFactory.json';
|
import * as TestUniswapExchangeFactory from '../test/generated-artifacts/TestUniswapExchangeFactory.json';
|
||||||
@ -19,6 +20,7 @@ export const artifacts = {
|
|||||||
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
|
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
|
||||||
TestFramework: TestFramework as ContractArtifact,
|
TestFramework: TestFramework as ContractArtifact,
|
||||||
TestMainnetAggregatorFills: TestMainnetAggregatorFills as ContractArtifact,
|
TestMainnetAggregatorFills: TestMainnetAggregatorFills as ContractArtifact,
|
||||||
|
TestSignatureValidationWallet: TestSignatureValidationWallet as ContractArtifact,
|
||||||
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
|
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
|
||||||
TestUniswapExchange: TestUniswapExchange as ContractArtifact,
|
TestUniswapExchange: TestUniswapExchange as ContractArtifact,
|
||||||
TestUniswapExchangeFactory: TestUniswapExchangeFactory as ContractArtifact,
|
TestUniswapExchangeFactory: TestUniswapExchangeFactory as ContractArtifact,
|
||||||
|
@ -22,6 +22,7 @@ import {
|
|||||||
} from '@0x/contracts-staking';
|
} from '@0x/contracts-staking';
|
||||||
import { BlockchainTestsEnvironment, constants } from '@0x/contracts-test-utils';
|
import { BlockchainTestsEnvironment, constants } from '@0x/contracts-test-utils';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||||
import { TxData } from 'ethereum-types';
|
import { TxData } from 'ethereum-types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
@ -207,6 +208,7 @@ export class DeploymentManager {
|
|||||||
|
|
||||||
// Construct the new instance and return it.
|
// Construct the new instance and return it.
|
||||||
return new DeploymentManager(
|
return new DeploymentManager(
|
||||||
|
environment.web3Wrapper,
|
||||||
assetProxies,
|
assetProxies,
|
||||||
governor,
|
governor,
|
||||||
exchange,
|
exchange,
|
||||||
@ -522,6 +524,7 @@ export class DeploymentManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected constructor(
|
protected constructor(
|
||||||
|
public web3Wrapper: Web3Wrapper,
|
||||||
public assetProxies: AssetProxyContracts,
|
public assetProxies: AssetProxyContracts,
|
||||||
public governor: ZeroExGovernorContract,
|
public governor: ZeroExGovernorContract,
|
||||||
public exchange: ExchangeContract,
|
public exchange: ExchangeContract,
|
||||||
|
@ -48,6 +48,7 @@ export class SimulationEnvironment {
|
|||||||
|
|
||||||
export abstract class Simulation {
|
export abstract class Simulation {
|
||||||
public readonly generator = this._assertionGenerator();
|
public readonly generator = this._assertionGenerator();
|
||||||
|
public resets = false;
|
||||||
|
|
||||||
constructor(public environment: SimulationEnvironment) {}
|
constructor(public environment: SimulationEnvironment) {}
|
||||||
|
|
||||||
@ -66,11 +67,15 @@ export abstract class Simulation {
|
|||||||
protected abstract _assertionGenerator(): AsyncIterableIterator<AssertionResult | void>;
|
protected abstract _assertionGenerator(): AsyncIterableIterator<AssertionResult | void>;
|
||||||
|
|
||||||
private async _stepAsync(): Promise<void> {
|
private async _stepAsync(): Promise<void> {
|
||||||
|
const snapshotId = this.resets ? await this.environment.deployment.web3Wrapper.takeSnapshotAsync() : undefined;
|
||||||
try {
|
try {
|
||||||
await this.generator.next();
|
await this.generator.next();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.logFailure(error, this.environment.state());
|
logger.logFailure(error, this.environment.state());
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
if (snapshotId !== undefined) {
|
||||||
|
await this.environment.deployment.web3Wrapper.revertSnapshotAsync(snapshotId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,17 @@ class PRNGWrapper {
|
|||||||
return ONE.minus(ONE.minus(u).exponentiatedBy(ONE.dividedBy(beta))).exponentiatedBy(ONE.dividedBy(alpha));
|
return ONE.minus(ONE.minus(u).exponentiatedBy(ONE.dividedBy(beta))).exponentiatedBy(ONE.dividedBy(alpha));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pseudorandom version of `hexRandom()`. If no distribution is provided,
|
||||||
|
* samples all byte values uniformly.
|
||||||
|
*/
|
||||||
|
public hex(bytesLength: number = 32, distribution: () => Numberish = this.rng): string {
|
||||||
|
const buf = Buffer.from(_.times(bytesLength, () => this.integer(0, 255, distribution).toNumber())).toString(
|
||||||
|
'hex',
|
||||||
|
);
|
||||||
|
return `0x${buf}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Pseudorandom = new PRNGWrapper();
|
export const Pseudorandom = new PRNGWrapper();
|
||||||
|
@ -0,0 +1,725 @@
|
|||||||
|
import { ExchangeContract } from '@0x/contracts-exchange';
|
||||||
|
import { blockchainTests, constants, expect, signingUtils, transactionHashUtils } from '@0x/contracts-test-utils';
|
||||||
|
import { orderHashUtils } from '@0x/order-utils';
|
||||||
|
import { Order, SignatureType, ZeroExTransaction } from '@0x/types';
|
||||||
|
import { hexUtils, logUtils } from '@0x/utils';
|
||||||
|
import * as ethUtil from 'ethereumjs-util';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { artifacts } from '../artifacts';
|
||||||
|
import { AssertionResult } from '../framework/assertions/function_assertion';
|
||||||
|
import { BlockchainBalanceStore } from '../framework/balances/blockchain_balance_store';
|
||||||
|
import { DeploymentManager } from '../framework/deployment_manager';
|
||||||
|
import { Simulation, SimulationEnvironment } from '../framework/simulation';
|
||||||
|
import { Pseudorandom } from '../framework/utils/pseudorandom';
|
||||||
|
import { TestSignatureValidationWalletContract } from '../wrappers';
|
||||||
|
|
||||||
|
// tslint:disable: max-classes-per-file no-non-null-assertion no-unnecessary-type-assertion
|
||||||
|
const tests = process.env.FUZZ_TEST === 'exchange/signature_validation' ? blockchainTests : blockchainTests.skip;
|
||||||
|
|
||||||
|
tests('Exchange signature validation fuzz tests', env => {
|
||||||
|
const ALL_SIGNATURE_TYPES = [
|
||||||
|
SignatureType.Illegal,
|
||||||
|
SignatureType.Invalid,
|
||||||
|
SignatureType.EthSign,
|
||||||
|
SignatureType.EIP712,
|
||||||
|
SignatureType.Wallet,
|
||||||
|
SignatureType.Validator,
|
||||||
|
SignatureType.PreSigned,
|
||||||
|
SignatureType.EIP1271Wallet,
|
||||||
|
];
|
||||||
|
const ALL_WORKING_SIGNATURE_TYPES = [
|
||||||
|
SignatureType.EthSign,
|
||||||
|
SignatureType.EIP712,
|
||||||
|
SignatureType.Wallet,
|
||||||
|
SignatureType.Validator,
|
||||||
|
SignatureType.PreSigned,
|
||||||
|
SignatureType.EIP1271Wallet,
|
||||||
|
];
|
||||||
|
const HASH_COMPATIBLE_SIGNATURE_TYPES = [
|
||||||
|
SignatureType.EthSign,
|
||||||
|
SignatureType.EIP712,
|
||||||
|
SignatureType.Wallet,
|
||||||
|
SignatureType.PreSigned,
|
||||||
|
];
|
||||||
|
const STATIC_SIGNATURE_TYPES = [SignatureType.EthSign, SignatureType.EIP712, SignatureType.PreSigned];
|
||||||
|
const ALWAYS_FAILING_SIGNATURE_TYPES = [SignatureType.Illegal, SignatureType.Invalid];
|
||||||
|
const WALLET_SIGNATURE_TYPES = [SignatureType.Wallet, SignatureType.EIP1271Wallet];
|
||||||
|
const STRICT_LENGTH_SIGNATURE_TYPES = [SignatureType.EthSign, SignatureType.EIP712];
|
||||||
|
const CALLBACK_SIGNATURE_TYPES = [SignatureType.Wallet, SignatureType.EIP1271Wallet, SignatureType.Validator];
|
||||||
|
|
||||||
|
let walletContractAddress: string;
|
||||||
|
let notWalletContractAddress: string;
|
||||||
|
let deployment: DeploymentManager;
|
||||||
|
let exchange: ExchangeContract;
|
||||||
|
let accounts: string[];
|
||||||
|
let privateKeys: { [address: string]: Buffer };
|
||||||
|
let chainId: number;
|
||||||
|
|
||||||
|
interface SignatureTestParams {
|
||||||
|
signatureType: SignatureType;
|
||||||
|
signer: string;
|
||||||
|
signature: string;
|
||||||
|
hash: string;
|
||||||
|
signerKey?: Buffer;
|
||||||
|
validator?: string;
|
||||||
|
payload?: string;
|
||||||
|
order?: Order;
|
||||||
|
transaction?: ZeroExTransaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
chainId = await env.web3Wrapper.getChainIdAsync();
|
||||||
|
accounts = await env.getAccountAddressesAsync();
|
||||||
|
privateKeys = _.zipObject(accounts, accounts.map((a, i) => constants.TESTRPC_PRIVATE_KEYS[i]));
|
||||||
|
deployment = await DeploymentManager.deployAsync(env, {
|
||||||
|
numErc20TokensToDeploy: 0,
|
||||||
|
numErc721TokensToDeploy: 0,
|
||||||
|
numErc1155TokensToDeploy: 0,
|
||||||
|
});
|
||||||
|
exchange = deployment.exchange;
|
||||||
|
walletContractAddress = (await TestSignatureValidationWalletContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.TestSignatureValidationWallet,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
{},
|
||||||
|
)).address;
|
||||||
|
// This just has to be a contract address that doesn't implement the
|
||||||
|
// wallet spec.
|
||||||
|
notWalletContractAddress = exchange.address;
|
||||||
|
});
|
||||||
|
|
||||||
|
function randomPayload(): string {
|
||||||
|
return Pseudorandom.hex(Pseudorandom.integer(0, 66).toNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
async function presignHashAsync(signer: string, hash: string): Promise<void> {
|
||||||
|
await exchange.preSign(hash).awaitTransactionSuccessAsync({ from: signer });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function approveValidatorAsync(signer: string, validator: string, approved: boolean = true): Promise<void> {
|
||||||
|
await exchange
|
||||||
|
.setSignatureValidatorApproval(validator, approved)
|
||||||
|
.awaitTransactionSuccessAsync({ from: signer });
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSignature(params: {
|
||||||
|
signatureType: SignatureType;
|
||||||
|
hash?: string;
|
||||||
|
signerKey?: Buffer;
|
||||||
|
validator?: string;
|
||||||
|
payload?: string;
|
||||||
|
}): string {
|
||||||
|
const payload = params.payload || constants.NULL_BYTES;
|
||||||
|
const signatureByte = hexUtils.leftPad(params.signatureType, 1);
|
||||||
|
switch (params.signatureType) {
|
||||||
|
default:
|
||||||
|
case SignatureType.Illegal:
|
||||||
|
case SignatureType.Invalid:
|
||||||
|
case SignatureType.PreSigned:
|
||||||
|
return hexUtils.concat(payload, signatureByte);
|
||||||
|
case SignatureType.EIP712:
|
||||||
|
case SignatureType.EthSign:
|
||||||
|
return hexUtils.concat(
|
||||||
|
payload,
|
||||||
|
ethUtil.bufferToHex(
|
||||||
|
signingUtils.signMessage(
|
||||||
|
ethUtil.toBuffer(params.hash),
|
||||||
|
params.signerKey!,
|
||||||
|
params.signatureType,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case SignatureType.Wallet:
|
||||||
|
case SignatureType.EIP1271Wallet:
|
||||||
|
return hexUtils.concat(payload, params.signatureType);
|
||||||
|
case SignatureType.Validator:
|
||||||
|
return hexUtils.concat(payload, params.validator!, params.signatureType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function mangleSignatureParamsAsync(params: SignatureTestParams): Promise<SignatureTestParams> {
|
||||||
|
const mangled = { ...params };
|
||||||
|
const MANGLE_MODES = [
|
||||||
|
'TRUNCATE_SIGNATURE',
|
||||||
|
'RETYPE_SIGNATURE',
|
||||||
|
'RANDOM_HASH',
|
||||||
|
'RANDOM_ORDER',
|
||||||
|
'RANDOM_TRANSACTION',
|
||||||
|
'RANDOM_SIGNER',
|
||||||
|
];
|
||||||
|
const invalidModes = [];
|
||||||
|
if (!STRICT_LENGTH_SIGNATURE_TYPES.includes(mangled.signatureType)) {
|
||||||
|
invalidModes.push('TRUNCATE_SIGNATURE');
|
||||||
|
}
|
||||||
|
if (CALLBACK_SIGNATURE_TYPES.includes(mangled.signatureType)) {
|
||||||
|
invalidModes.push('RANDOM_HASH');
|
||||||
|
}
|
||||||
|
if (params.transaction === undefined) {
|
||||||
|
invalidModes.push('RANDOM_TRANSACTION');
|
||||||
|
}
|
||||||
|
if (params.order === undefined) {
|
||||||
|
invalidModes.push('RANDOM_ORDER');
|
||||||
|
}
|
||||||
|
if (params.order !== undefined || params.hash !== undefined) {
|
||||||
|
invalidModes.push('RANDOM_HASH');
|
||||||
|
}
|
||||||
|
const mode = Pseudorandom.sample(_.without(MANGLE_MODES, ...invalidModes))!;
|
||||||
|
switch (mode) {
|
||||||
|
case 'TRUNCATE_SIGNATURE':
|
||||||
|
while (hexUtils.slice(mangled.signature, -1) === hexUtils.leftPad(mangled.signatureType, 1)) {
|
||||||
|
mangled.signature = hexUtils.slice(mangled.signature, 0, -1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'RETYPE_SIGNATURE':
|
||||||
|
mangled.signatureType = WALLET_SIGNATURE_TYPES.includes(mangled.signatureType)
|
||||||
|
? Pseudorandom.sample(_.without(ALL_SIGNATURE_TYPES, ...WALLET_SIGNATURE_TYPES))!
|
||||||
|
: Pseudorandom.sample(_.without(ALL_SIGNATURE_TYPES, mangled.signatureType))!;
|
||||||
|
mangled.signature = hexUtils.concat(hexUtils.slice(mangled.signature, 0, -1), mangled.signatureType);
|
||||||
|
break;
|
||||||
|
case 'RANDOM_SIGNER':
|
||||||
|
mangled.signer = Pseudorandom.hex(constants.ADDRESS_LENGTH);
|
||||||
|
if (mangled.order) {
|
||||||
|
mangled.order.makerAddress = mangled.signer;
|
||||||
|
}
|
||||||
|
if (mangled.transaction) {
|
||||||
|
mangled.transaction.signerAddress = mangled.signer;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'RANDOM_HASH':
|
||||||
|
mangled.hash = Pseudorandom.hex();
|
||||||
|
break;
|
||||||
|
case 'RANDOM_ORDER':
|
||||||
|
mangled.order = randomOrder({
|
||||||
|
exchangeAddress: mangled.order!.exchangeAddress,
|
||||||
|
chainId: mangled.order!.chainId,
|
||||||
|
});
|
||||||
|
mangled.hash = await orderHashUtils.getOrderHashAsync(mangled.order);
|
||||||
|
break;
|
||||||
|
case 'RANDOM_TRANSACTION':
|
||||||
|
mangled.transaction = randomTransaction({
|
||||||
|
domain: mangled.transaction!.domain,
|
||||||
|
});
|
||||||
|
mangled.hash = await transactionHashUtils.getTransactionHashHex(mangled.transaction);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unhandled mangle mode: ${mode}`);
|
||||||
|
}
|
||||||
|
return mangled;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createHashTestParams(fields: Partial<SignatureTestParams> = {}): SignatureTestParams {
|
||||||
|
const signatureType =
|
||||||
|
fields.signatureType === undefined
|
||||||
|
? Pseudorandom.sample(HASH_COMPATIBLE_SIGNATURE_TYPES)!
|
||||||
|
: fields.signatureType;
|
||||||
|
const signer =
|
||||||
|
fields.signer ||
|
||||||
|
(WALLET_SIGNATURE_TYPES.includes(signatureType) ? walletContractAddress : Pseudorandom.sample(accounts)!);
|
||||||
|
const validator =
|
||||||
|
fields.validator || (signatureType === SignatureType.Validator ? walletContractAddress : undefined);
|
||||||
|
const signerKey = fields.signerKey || privateKeys[signer];
|
||||||
|
const hash = fields.hash || Pseudorandom.hex();
|
||||||
|
const payload =
|
||||||
|
fields.payload ||
|
||||||
|
(STRICT_LENGTH_SIGNATURE_TYPES.includes(signatureType) ? constants.NULL_BYTES : randomPayload());
|
||||||
|
const signature = createSignature({ signatureType, hash, signerKey, payload, validator });
|
||||||
|
return {
|
||||||
|
hash,
|
||||||
|
payload,
|
||||||
|
signature,
|
||||||
|
signatureType,
|
||||||
|
signer,
|
||||||
|
signerKey,
|
||||||
|
validator,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function assertValidHashSignatureAsync(params: {
|
||||||
|
hash: string;
|
||||||
|
signer: string;
|
||||||
|
signature: string;
|
||||||
|
isValid: boolean;
|
||||||
|
}): Promise<void> {
|
||||||
|
try {
|
||||||
|
let result;
|
||||||
|
try {
|
||||||
|
result = await exchange.isValidHashSignature(params.hash, params.signer, params.signature).callAsync();
|
||||||
|
} catch (err) {
|
||||||
|
if (params.isValid) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
expect(result).to.eq(!!params.isValid);
|
||||||
|
} catch (err) {
|
||||||
|
logUtils.warn(params);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* validTestHashSignature(): AsyncIterableIterator<void> {
|
||||||
|
while (true) {
|
||||||
|
const { hash, signature, signatureType, signer } = createHashTestParams();
|
||||||
|
yield (async () => {
|
||||||
|
if (signatureType === SignatureType.PreSigned) {
|
||||||
|
await presignHashAsync(signer, hash);
|
||||||
|
}
|
||||||
|
await assertValidHashSignatureAsync({
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature,
|
||||||
|
isValid: true,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* invalidTestHashStaticSignature(): AsyncIterableIterator<void> {
|
||||||
|
while (true) {
|
||||||
|
const randomSignerKey = ethUtil.toBuffer(Pseudorandom.hex());
|
||||||
|
const signer = Pseudorandom.sample([notWalletContractAddress, walletContractAddress, ...accounts])!;
|
||||||
|
const { hash, signature } = createHashTestParams({
|
||||||
|
signatureType: Pseudorandom.sample([...STATIC_SIGNATURE_TYPES, ...ALWAYS_FAILING_SIGNATURE_TYPES])!,
|
||||||
|
signer,
|
||||||
|
// Always sign with a random key.
|
||||||
|
signerKey: randomSignerKey,
|
||||||
|
});
|
||||||
|
yield assertValidHashSignatureAsync({
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature,
|
||||||
|
isValid: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* invalidTestHashWalletSignature(): AsyncIterableIterator<void> {
|
||||||
|
while (true) {
|
||||||
|
const signer = Pseudorandom.sample([notWalletContractAddress, ...accounts])!;
|
||||||
|
const { hash, signature } = createHashTestParams({
|
||||||
|
signatureType: SignatureType.Wallet,
|
||||||
|
signer,
|
||||||
|
});
|
||||||
|
yield assertValidHashSignatureAsync({
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature,
|
||||||
|
isValid: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* invalidTestHashValidatorSignature(): AsyncIterableIterator<void> {
|
||||||
|
while (true) {
|
||||||
|
const isNotApproved = Pseudorandom.sample([true, false])!;
|
||||||
|
const signer = Pseudorandom.sample([...accounts])!;
|
||||||
|
const validator = isNotApproved
|
||||||
|
? walletContractAddress
|
||||||
|
: Pseudorandom.sample([
|
||||||
|
// All validator signatures are invalid for the hash test, so passing a valid
|
||||||
|
// wallet contract should still fail.
|
||||||
|
walletContractAddress,
|
||||||
|
notWalletContractAddress,
|
||||||
|
...accounts,
|
||||||
|
])!;
|
||||||
|
const { hash, signature } = createHashTestParams({
|
||||||
|
signatureType: SignatureType.Validator,
|
||||||
|
validator,
|
||||||
|
});
|
||||||
|
yield (async () => {
|
||||||
|
if (!isNotApproved) {
|
||||||
|
await approveValidatorAsync(signer, validator);
|
||||||
|
}
|
||||||
|
await assertValidHashSignatureAsync({
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature,
|
||||||
|
isValid: false,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* invalidTestHashMangledSignature(): AsyncIterableIterator<void> {
|
||||||
|
while (true) {
|
||||||
|
const params = createHashTestParams({ signatureType: Pseudorandom.sample(ALL_SIGNATURE_TYPES)! });
|
||||||
|
const mangled = await mangleSignatureParamsAsync(params);
|
||||||
|
yield (async () => {
|
||||||
|
await assertValidHashSignatureAsync({
|
||||||
|
hash: mangled.hash,
|
||||||
|
signer: mangled.signer,
|
||||||
|
signature: mangled.signature,
|
||||||
|
isValid: false,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomOrder(fields: Partial<Order> = {}): Order {
|
||||||
|
return {
|
||||||
|
chainId,
|
||||||
|
exchangeAddress: exchange.address,
|
||||||
|
expirationTimeSeconds: Pseudorandom.integer(1, 2 ** 32),
|
||||||
|
salt: Pseudorandom.integer(0, constants.MAX_UINT256),
|
||||||
|
makerAssetData: Pseudorandom.hex(36),
|
||||||
|
takerAssetData: Pseudorandom.hex(36),
|
||||||
|
makerFeeAssetData: Pseudorandom.hex(36),
|
||||||
|
takerFeeAssetData: Pseudorandom.hex(36),
|
||||||
|
makerAssetAmount: Pseudorandom.integer(0, 100e18),
|
||||||
|
takerAssetAmount: Pseudorandom.integer(0, 100e18),
|
||||||
|
makerFee: Pseudorandom.integer(0, 100e18),
|
||||||
|
takerFee: Pseudorandom.integer(0, 100e18),
|
||||||
|
feeRecipientAddress: Pseudorandom.hex(constants.ADDRESS_LENGTH),
|
||||||
|
makerAddress: Pseudorandom.hex(constants.ADDRESS_LENGTH),
|
||||||
|
takerAddress: Pseudorandom.hex(constants.ADDRESS_LENGTH),
|
||||||
|
senderAddress: Pseudorandom.hex(constants.ADDRESS_LENGTH),
|
||||||
|
...fields,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createOrderTestParamsAsync(
|
||||||
|
fields: Partial<SignatureTestParams> = {},
|
||||||
|
): Promise<SignatureTestParams & { order: Order }> {
|
||||||
|
const signatureType =
|
||||||
|
fields.signatureType === undefined
|
||||||
|
? Pseudorandom.sample(ALL_WORKING_SIGNATURE_TYPES)!
|
||||||
|
: fields.signatureType;
|
||||||
|
const signer =
|
||||||
|
fields.signer ||
|
||||||
|
(WALLET_SIGNATURE_TYPES.includes(signatureType) ? walletContractAddress : Pseudorandom.sample(accounts)!);
|
||||||
|
const validator =
|
||||||
|
fields.validator || (signatureType === SignatureType.Validator ? walletContractAddress : undefined);
|
||||||
|
const signerKey = fields.signerKey || privateKeys[signer];
|
||||||
|
const order = fields.order || randomOrder({ makerAddress: signer });
|
||||||
|
const hash = fields.hash || (await orderHashUtils.getOrderHashAsync(order));
|
||||||
|
const payload =
|
||||||
|
fields.payload ||
|
||||||
|
(STRICT_LENGTH_SIGNATURE_TYPES.includes(signatureType) ? constants.NULL_BYTES : randomPayload());
|
||||||
|
const signature = createSignature({ signatureType, hash, signerKey, payload, validator });
|
||||||
|
return {
|
||||||
|
hash,
|
||||||
|
order,
|
||||||
|
payload,
|
||||||
|
signature,
|
||||||
|
signatureType,
|
||||||
|
signer,
|
||||||
|
signerKey,
|
||||||
|
validator,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function assertValidOrderSignatureAsync(params: {
|
||||||
|
order: Order;
|
||||||
|
signature: string;
|
||||||
|
isValid: boolean;
|
||||||
|
}): Promise<void> {
|
||||||
|
try {
|
||||||
|
let result;
|
||||||
|
try {
|
||||||
|
result = await exchange.isValidOrderSignature(params.order, params.signature).callAsync();
|
||||||
|
} catch (err) {
|
||||||
|
if (params.isValid) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
expect(result).to.eq(!!params.isValid);
|
||||||
|
} catch (err) {
|
||||||
|
logUtils.warn(params);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* validTestOrderSignature(): AsyncIterableIterator<void> {
|
||||||
|
while (true) {
|
||||||
|
const { hash, order, signature, signatureType, signer, validator } = await createOrderTestParamsAsync();
|
||||||
|
yield (async () => {
|
||||||
|
if (signatureType === SignatureType.PreSigned) {
|
||||||
|
await presignHashAsync(signer, hash);
|
||||||
|
} else if (signatureType === SignatureType.Validator) {
|
||||||
|
await approveValidatorAsync(signer, validator!);
|
||||||
|
}
|
||||||
|
await assertValidOrderSignatureAsync({
|
||||||
|
order,
|
||||||
|
signature,
|
||||||
|
isValid: true,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* invalidTestOrderStaticSignature(): AsyncIterableIterator<void> {
|
||||||
|
while (true) {
|
||||||
|
const randomSignerKey = ethUtil.toBuffer(Pseudorandom.hex());
|
||||||
|
const signer = Pseudorandom.sample([notWalletContractAddress, walletContractAddress, ...accounts])!;
|
||||||
|
const { order, signature } = await createOrderTestParamsAsync({
|
||||||
|
signatureType: Pseudorandom.sample([...STATIC_SIGNATURE_TYPES, ...ALWAYS_FAILING_SIGNATURE_TYPES])!,
|
||||||
|
signer,
|
||||||
|
// Always sign with a random key.
|
||||||
|
signerKey: randomSignerKey,
|
||||||
|
});
|
||||||
|
yield assertValidOrderSignatureAsync({
|
||||||
|
order,
|
||||||
|
signature,
|
||||||
|
isValid: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* invalidTestOrderWalletSignature(): AsyncIterableIterator<void> {
|
||||||
|
while (true) {
|
||||||
|
const signer = Pseudorandom.sample([notWalletContractAddress, ...accounts])!;
|
||||||
|
const { order, signature } = await createOrderTestParamsAsync({
|
||||||
|
signatureType: Pseudorandom.sample(WALLET_SIGNATURE_TYPES)!,
|
||||||
|
signer,
|
||||||
|
});
|
||||||
|
yield assertValidOrderSignatureAsync({
|
||||||
|
order,
|
||||||
|
signature,
|
||||||
|
isValid: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* invalidTestOrderValidatorSignature(): AsyncIterableIterator<void> {
|
||||||
|
while (true) {
|
||||||
|
const isNotApproved = Pseudorandom.sample([true, false])!;
|
||||||
|
const signer = Pseudorandom.sample([...accounts])!;
|
||||||
|
const validator = isNotApproved
|
||||||
|
? walletContractAddress
|
||||||
|
: Pseudorandom.sample([notWalletContractAddress, ...accounts])!;
|
||||||
|
const { order, signature } = await createOrderTestParamsAsync({
|
||||||
|
signatureType: SignatureType.Validator,
|
||||||
|
validator,
|
||||||
|
});
|
||||||
|
yield (async () => {
|
||||||
|
if (!isNotApproved) {
|
||||||
|
await approveValidatorAsync(signer, validator);
|
||||||
|
}
|
||||||
|
await assertValidOrderSignatureAsync({
|
||||||
|
order,
|
||||||
|
signature,
|
||||||
|
isValid: false,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* invalidTestOrderMangledSignature(): AsyncIterableIterator<void> {
|
||||||
|
while (true) {
|
||||||
|
const params = await createOrderTestParamsAsync({
|
||||||
|
signatureType: Pseudorandom.sample(ALL_SIGNATURE_TYPES)!,
|
||||||
|
});
|
||||||
|
const mangled = await mangleSignatureParamsAsync(params);
|
||||||
|
yield (async () => {
|
||||||
|
await assertValidOrderSignatureAsync({
|
||||||
|
order: mangled.order!,
|
||||||
|
signature: mangled.signature,
|
||||||
|
isValid: false,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomTransaction(fields: Partial<ZeroExTransaction> = {}): ZeroExTransaction {
|
||||||
|
return {
|
||||||
|
domain: {
|
||||||
|
chainId,
|
||||||
|
verifyingContract: exchange.address,
|
||||||
|
name: '0x Protocol',
|
||||||
|
version: '3.0.0',
|
||||||
|
},
|
||||||
|
gasPrice: Pseudorandom.integer(1e9, 100e9),
|
||||||
|
expirationTimeSeconds: Pseudorandom.integer(1, 2 ** 32),
|
||||||
|
salt: Pseudorandom.integer(0, constants.MAX_UINT256),
|
||||||
|
signerAddress: Pseudorandom.hex(constants.ADDRESS_LENGTH),
|
||||||
|
data: Pseudorandom.hex(Pseudorandom.integer(4, 128).toNumber()),
|
||||||
|
...fields,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createTransactionTestParamsAsync(
|
||||||
|
fields: Partial<SignatureTestParams> = {},
|
||||||
|
): Promise<SignatureTestParams & { transaction: ZeroExTransaction }> {
|
||||||
|
const signatureType =
|
||||||
|
fields.signatureType === undefined
|
||||||
|
? Pseudorandom.sample(ALL_WORKING_SIGNATURE_TYPES)!
|
||||||
|
: fields.signatureType;
|
||||||
|
const signer =
|
||||||
|
fields.signer ||
|
||||||
|
(WALLET_SIGNATURE_TYPES.includes(signatureType) ? walletContractAddress : Pseudorandom.sample(accounts)!);
|
||||||
|
const validator =
|
||||||
|
fields.validator || (signatureType === SignatureType.Validator ? walletContractAddress : undefined);
|
||||||
|
const signerKey = fields.signerKey || privateKeys[signer];
|
||||||
|
const transaction = fields.transaction || randomTransaction({ signerAddress: signer });
|
||||||
|
const hash = fields.hash || transactionHashUtils.getTransactionHashHex(transaction);
|
||||||
|
const payload =
|
||||||
|
fields.payload ||
|
||||||
|
(STRICT_LENGTH_SIGNATURE_TYPES.includes(signatureType) ? constants.NULL_BYTES : randomPayload());
|
||||||
|
const signature = createSignature({ signatureType, hash, signerKey, payload, validator });
|
||||||
|
return {
|
||||||
|
hash,
|
||||||
|
transaction,
|
||||||
|
payload,
|
||||||
|
signature,
|
||||||
|
signatureType,
|
||||||
|
signer,
|
||||||
|
signerKey,
|
||||||
|
validator,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function assertValidTransactionSignatureAsync(params: {
|
||||||
|
transaction: ZeroExTransaction;
|
||||||
|
signature: string;
|
||||||
|
isValid: boolean;
|
||||||
|
}): Promise<void> {
|
||||||
|
try {
|
||||||
|
let result;
|
||||||
|
try {
|
||||||
|
result = await exchange.isValidTransactionSignature(params.transaction, params.signature).callAsync();
|
||||||
|
} catch (err) {
|
||||||
|
if (params.isValid) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
expect(result).to.eq(!!params.isValid);
|
||||||
|
} catch (err) {
|
||||||
|
logUtils.warn(params);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* validTestTransactionSignature(): AsyncIterableIterator<void> {
|
||||||
|
while (true) {
|
||||||
|
const {
|
||||||
|
hash,
|
||||||
|
transaction,
|
||||||
|
signature,
|
||||||
|
signatureType,
|
||||||
|
signer,
|
||||||
|
validator,
|
||||||
|
} = await createTransactionTestParamsAsync();
|
||||||
|
yield (async () => {
|
||||||
|
if (signatureType === SignatureType.PreSigned) {
|
||||||
|
await presignHashAsync(signer, hash);
|
||||||
|
} else if (signatureType === SignatureType.Validator) {
|
||||||
|
await approveValidatorAsync(signer, validator!);
|
||||||
|
}
|
||||||
|
await assertValidTransactionSignatureAsync({
|
||||||
|
transaction,
|
||||||
|
signature,
|
||||||
|
isValid: true,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* invalidTestTransactionStaticSignature(): AsyncIterableIterator<void> {
|
||||||
|
while (true) {
|
||||||
|
const randomSignerKey = ethUtil.toBuffer(Pseudorandom.hex());
|
||||||
|
const signer = Pseudorandom.sample([notWalletContractAddress, walletContractAddress, ...accounts])!;
|
||||||
|
const { transaction, signature } = await createTransactionTestParamsAsync({
|
||||||
|
signatureType: Pseudorandom.sample([...STATIC_SIGNATURE_TYPES, ...ALWAYS_FAILING_SIGNATURE_TYPES])!,
|
||||||
|
signer,
|
||||||
|
// Always sign with a random key.
|
||||||
|
signerKey: randomSignerKey,
|
||||||
|
});
|
||||||
|
yield assertValidTransactionSignatureAsync({
|
||||||
|
transaction,
|
||||||
|
signature,
|
||||||
|
isValid: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* invalidTestTransactionWalletSignature(): AsyncIterableIterator<void> {
|
||||||
|
while (true) {
|
||||||
|
const signer = Pseudorandom.sample([notWalletContractAddress, ...accounts])!;
|
||||||
|
const { transaction, signature } = await createTransactionTestParamsAsync({
|
||||||
|
signatureType: Pseudorandom.sample(WALLET_SIGNATURE_TYPES)!,
|
||||||
|
signer,
|
||||||
|
});
|
||||||
|
yield assertValidTransactionSignatureAsync({
|
||||||
|
transaction,
|
||||||
|
signature,
|
||||||
|
isValid: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* invalidTestTransactionValidatorSignature(): AsyncIterableIterator<void> {
|
||||||
|
while (true) {
|
||||||
|
const isNotApproved = Pseudorandom.sample([true, false])!;
|
||||||
|
const signer = Pseudorandom.sample([...accounts])!;
|
||||||
|
const validator = isNotApproved
|
||||||
|
? walletContractAddress
|
||||||
|
: Pseudorandom.sample([notWalletContractAddress, ...accounts])!;
|
||||||
|
const { transaction, signature } = await createTransactionTestParamsAsync({
|
||||||
|
signatureType: SignatureType.Validator,
|
||||||
|
validator,
|
||||||
|
});
|
||||||
|
yield (async () => {
|
||||||
|
if (!isNotApproved) {
|
||||||
|
await approveValidatorAsync(signer, validator);
|
||||||
|
}
|
||||||
|
await assertValidTransactionSignatureAsync({
|
||||||
|
transaction,
|
||||||
|
signature,
|
||||||
|
isValid: false,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* invalidTestTransactionMangledSignature(): AsyncIterableIterator<void> {
|
||||||
|
while (true) {
|
||||||
|
const params = await createTransactionTestParamsAsync({
|
||||||
|
signatureType: Pseudorandom.sample(ALL_SIGNATURE_TYPES)!,
|
||||||
|
});
|
||||||
|
const mangled = await mangleSignatureParamsAsync(params);
|
||||||
|
yield (async () => {
|
||||||
|
await assertValidTransactionSignatureAsync({
|
||||||
|
transaction: mangled.transaction!,
|
||||||
|
signature: mangled.signature,
|
||||||
|
isValid: false,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it('fuzz', async () => {
|
||||||
|
const FUZZ_ACTIONS = [
|
||||||
|
validTestHashSignature(),
|
||||||
|
invalidTestHashStaticSignature(),
|
||||||
|
invalidTestHashWalletSignature(),
|
||||||
|
invalidTestHashValidatorSignature(),
|
||||||
|
invalidTestHashMangledSignature(),
|
||||||
|
validTestOrderSignature(),
|
||||||
|
invalidTestOrderStaticSignature(),
|
||||||
|
invalidTestOrderWalletSignature(),
|
||||||
|
invalidTestOrderValidatorSignature(),
|
||||||
|
invalidTestOrderMangledSignature(),
|
||||||
|
validTestTransactionSignature(),
|
||||||
|
invalidTestTransactionStaticSignature(),
|
||||||
|
invalidTestTransactionWalletSignature(),
|
||||||
|
invalidTestTransactionValidatorSignature(),
|
||||||
|
invalidTestTransactionMangledSignature(),
|
||||||
|
];
|
||||||
|
const simulationEnvironment = new SimulationEnvironment(deployment, new BlockchainBalanceStore({}, {}), []);
|
||||||
|
const simulation = new class extends Simulation {
|
||||||
|
// tslint:disable-next-line: prefer-function-over-method
|
||||||
|
protected async *_assertionGenerator(): AsyncIterableIterator<AssertionResult | void> {
|
||||||
|
while (true) {
|
||||||
|
const action = Pseudorandom.sample(FUZZ_ACTIONS)!;
|
||||||
|
yield (await action!.next()).value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(simulationEnvironment);
|
||||||
|
simulation.resets = true;
|
||||||
|
return simulation.fuzzAsync();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// tslint:disable-next-line max-file-line-count
|
@ -8,6 +8,7 @@ export * from '../test/generated-wrappers/test_eth2_dai';
|
|||||||
export * from '../test/generated-wrappers/test_eth2_dai_bridge';
|
export * from '../test/generated-wrappers/test_eth2_dai_bridge';
|
||||||
export * from '../test/generated-wrappers/test_framework';
|
export * from '../test/generated-wrappers/test_framework';
|
||||||
export * from '../test/generated-wrappers/test_mainnet_aggregator_fills';
|
export * from '../test/generated-wrappers/test_mainnet_aggregator_fills';
|
||||||
|
export * from '../test/generated-wrappers/test_signature_validation_wallet';
|
||||||
export * from '../test/generated-wrappers/test_uniswap_bridge';
|
export * from '../test/generated-wrappers/test_uniswap_bridge';
|
||||||
export * from '../test/generated-wrappers/test_uniswap_exchange';
|
export * from '../test/generated-wrappers/test_uniswap_exchange';
|
||||||
export * from '../test/generated-wrappers/test_uniswap_exchange_factory';
|
export * from '../test/generated-wrappers/test_uniswap_exchange_factory';
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"test/generated-artifacts/TestEth2DaiBridge.json",
|
"test/generated-artifacts/TestEth2DaiBridge.json",
|
||||||
"test/generated-artifacts/TestFramework.json",
|
"test/generated-artifacts/TestFramework.json",
|
||||||
"test/generated-artifacts/TestMainnetAggregatorFills.json",
|
"test/generated-artifacts/TestMainnetAggregatorFills.json",
|
||||||
|
"test/generated-artifacts/TestSignatureValidationWallet.json",
|
||||||
"test/generated-artifacts/TestUniswapBridge.json",
|
"test/generated-artifacts/TestUniswapBridge.json",
|
||||||
"test/generated-artifacts/TestUniswapExchange.json",
|
"test/generated-artifacts/TestUniswapExchange.json",
|
||||||
"test/generated-artifacts/TestUniswapExchangeFactory.json"
|
"test/generated-artifacts/TestUniswapExchangeFactory.json"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user