Add test package (#164)

* Add test package

* Add a separate command to perform an eth-call

* Add snapshot test suite

* Add eth-calls for UniswapV2 Pair, USDC, Compound, Dai contracts

* Add args for Uniswap and USDC contracts

* Add args for Compound and Dai contracts

* Add getStorageAt calls to the test suite

* Refactor code and add documentation

* Loop over test slots for getStorageAt calls

* Add support for individual calls

* Use debug for logging
This commit is contained in:
prathamesh0 2022-08-19 11:06:11 +05:30 committed by GitHub
parent 7238f614c0
commit a5b3c7942d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1189 additions and 0 deletions

View File

@ -0,0 +1,5 @@
# Don't lint node_modules.
node_modules
# Don't lint build output.
dist

View File

@ -0,0 +1,23 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"overrides": [
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/no-explicit-any": "off"
}
}

View File

@ -0,0 +1,4 @@
timeout: '0' # No timeout since RPC calls may take longer
bail: true
exit: true
require: 'ts-node/register'

76
packages/test/README.md Normal file
View File

@ -0,0 +1,76 @@
# test
## Setup
* Run the following command to install required packages:
```bash
yarn
```
## Test Snapshot
* The snapshot test suite compares results for eth-calls and `getStorageAt` calls to the provided endpoints.
* Contracts considered:
* UniswapV2 Factory
* UniswapV2 Pair
* USDC
* Compound
* Dai / Maker
* Edit the [config file](./environments/local.toml):
Eg:
```toml
blockTag = "0xB5FFFF" # block tag to perform eth-call and getStorageAt call with (eg. block number in hex)
[endpoints]
endpoint1 = "http://127.0.0.1:8545" # endpoint1 URL
endpoint2 = "http://127.0.0.1:8082" # endpoint2 URL
```
* Run the following command to run the snapshot test suite:
```bash
yarn test:snapshot
```
## Individual Calls
* Run the following to make an eth-call:
```bash
yarn eth-call -e <endpoint> -c <contract> -a <abi> -m <method-name> --method-args [method-args] -b [block-tag]
```
* `endpoint` (`e`): Endpoint to perform eth-call against
* `contract` (`c`): Contract address
* `abi` (`a`): Contract ABI path
* `method-name` (`m`): Contract method to call
* `method-args`: Contract method arguments
* `block-tag` (`b`): Block tag to make eth-call with (block number (hex) / block hash)
Eg.
```bash
yarn eth-call -e http://127.0.0.1:8545 -c 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f -a abis/UniswapV2Factory.json -m allPairs --method-args 100 -b 0xB5FFFF
```
* Run the following to make a `getStorageAt` call:
```bash
yarn get-storage-at -e <endpoint> -c <contract> -s <slot> -b [block-tag]
```
* `endpoint` (`e`): Endpoint to perform getStorageAt call against
* `contract` (`c`): Contract address
* `slot` (`s`): Storge slot
* `block-tag` (`b`): Block tag to make getStorageAt call with (block number (hex) / block hash)
Eg.
```bash
yarn get-storage-at -e http://127.0.0.1:8545 -c 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f -s 0x1 -b 0xB5FFFF
```

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
[{"inputs":[{"internalType":"address","name":"_feeToSetter","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token0","type":"address"},{"indexed":true,"internalType":"address","name":"token1","type":"address"},{"indexed":false,"internalType":"address","name":"pair","type":"address"},{"indexed":false,"internalType":"uint256","name":"","type":"uint256"}],"name":"PairCreated","type":"event"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allPairs","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"allPairsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"name":"createPair","outputs":[{"internalType":"address","name":"pair","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"feeTo","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"feeToSetter","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"getPair","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_feeTo","type":"address"}],"name":"setFeeTo","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_feeToSetter","type":"address"}],"name":"setFeeToSetter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
blockTag = "" # block tag to perform eth-call and getStorageAt call with (eg. block number in hex)
[endpoints]
endpoint1 = "" # endpoint1 URL
endpoint2 = "" # endpoint2 URL

View File

@ -0,0 +1,32 @@
{
"name": "@vulcanize/test",
"version": "0.1.0",
"main": "dist/index.js",
"license": "AGPL-3.0",
"scripts": {
"lint": "eslint .",
"build": "tsc",
"eth-call": "DEBUG=vulcanize:* ts-node src/eth-call.ts",
"get-storage-at": "DEBUG=vulcanize:* ts-node src/get-storage-at.ts",
"test:snapshot": "DEBUG=vulcanize:* mocha src/snapshot.test.ts"
},
"dependencies": {
"chai": "^4.3.4",
"ethers": "^5.4.4",
"fs-extra": "^10.0.0",
"lodash": "^4.17.21",
"mocha": "^8.4.0",
"toml": "^3.0.0",
"debug": "^4.3.1",
"yargs": "^17.0.1"
},
"devDependencies": {
"@types/chai": "^4.2.19",
"@types/mocha": "^8.2.2",
"@typescript-eslint/eslint-plugin": "^4.25.0",
"@typescript-eslint/parser": "^4.25.0",
"eslint": "^7.27.0",
"ts-node": "^10.0.0",
"typescript": "^4.3.2"
}
}

View File

@ -0,0 +1,46 @@
//
// Copyright 2022 Vulcanize, Inc.
//
import fs from 'fs-extra';
import path from 'path';
import assert from 'assert';
import toml from 'toml';
export interface Config {
endpoint1URL: string,
endpoint2URL: string,
blockTag: string
}
export const getConfig = async (configFile: string): Promise<Config> => {
const configFilePath = path.resolve(configFile);
const fileExists = await fs.pathExists(configFilePath);
if (!fileExists) {
throw new Error(`Config file not found: ${configFilePath}`);
}
const configString = await fs.readFile(configFilePath, 'utf8');
const config = toml.parse(configString);
const { endpoints: endpointConfig, blockTag } = config;
assert(endpointConfig, 'Missing endpoints config');
const {endpoint1: endpoint1URL, endpoint2: endpoint2URL} = endpointConfig;
assert(endpoint1URL, 'Missing endpoint1 URL');
assert(endpoint2URL, 'Missing endpoint2 URL');
assert(blockTag);
return {
endpoint1URL,
endpoint2URL,
blockTag
};
}
export const readAbi = (abiPath: string): any => {
const fullAbiPath = path.resolve(abiPath);
return JSON.parse(fs.readFileSync(fullAbiPath).toString());
}

View File

@ -0,0 +1,70 @@
//
// Copyright 2022 Vulcanize, Inc.
//
import yargs from 'yargs';
import { ethers, providers } from 'ethers';
import debug from 'debug';
import { readAbi } from './common';
const log = debug('vulcanize:test');
const main = async (): Promise<void> => {
const argv = await yargs.parserConfiguration({
'parse-numbers': false
}).options({
endpoint: {
alias: 'e',
demandOption: true,
describe: 'Endpoint to perform eth-call against',
type: 'string'
},
contract: {
alias: 'c',
demandOption: true,
describe: 'Contract address',
type: 'string'
},
abi: {
alias: 'a',
demandOption: true,
describe: 'Contract ABI path',
type: 'string'
},
methodName: {
alias: 'm',
demandOption: true,
describe: 'Contract method to call',
type: 'string'
},
methodArgs: {
describe: 'Contract method arguments',
type: 'array'
},
blockTag: {
alias: 'b',
describe: 'Block tag to make eth-call with (block number (hex) / block hash)',
type: 'string'
}
}).argv;
const abi = readAbi(argv.abi);
const provider = new providers.JsonRpcProvider(argv.endpoint);
const contract = new ethers.Contract(argv.contract, abi, provider);
let args: (string | number)[] = []
if(argv.methodArgs !== undefined) {
args = argv.methodArgs
}
log(`Making an eth-call (${argv.methodName}) to endpoint ${argv.endpoint}`);
const result = await contract[argv.methodName](...args, {blockTag: argv.blockTag});
log("Result:");
log(result);
}
main().catch(err => {
log(err);
});

View File

@ -0,0 +1,53 @@
//
// Copyright 2022 Vulcanize, Inc.
//
import yargs from 'yargs';
import { ethers, providers } from 'ethers';
import debug from 'debug';
import { readAbi } from './common';
const log = debug('vulcanize:test');
const main = async (): Promise<void> => {
const argv = await yargs.parserConfiguration({
'parse-numbers': false
}).options({
endpoint: {
alias: 'e',
demandOption: true,
describe: 'Endpoint to perform getStorageAt against',
type: 'string'
},
contract: {
alias: 'c',
demandOption: true,
describe: 'Contract address',
type: 'string'
},
slot: {
alias: 's',
demandOption: true,
describe: 'Storge slot',
type: 'string'
},
blockTag: {
alias: 'b',
describe: 'Block tag to make eth-call with (block number (hex) / block hash)',
type: 'string'
},
}).argv;
const provider = new providers.JsonRpcProvider(argv.endpoint);
log(`Making a getStorageAt call for slot ${argv.slot} to endpoint ${argv.endpoint}`);
const result = await provider.getStorageAt(argv.contract, argv.slot, argv.blockTag);
log("Result:");
log(result);
}
main().catch(err => {
log(err);
});

View File

@ -0,0 +1,741 @@
//
// Copyright 2022 Vulcanize, Inc.
//
import { expect } from 'chai';
import { providers, Contract } from 'ethers';
import 'mocha';
import { Config, getConfig } from './common';
import {
uniswapV2FactoryABI,
uniswapV2FactoryAddress,
uniswapV2PairABI,
uniswapV2PairAddress,
usdcABI,
usdcAddress,
compoundABI,
compoundAddress,
daiABI,
daiAddress
} from './test-data';
const DEFAULT_CONFIG_FILE = './environments/local.toml';
describe('snapshot-test', () => {
let config: Config;
let provider1: providers.JsonRpcProvider;
let provider2: providers.JsonRpcProvider;
before("initialize providers", async () => {
config = await getConfig(DEFAULT_CONFIG_FILE);
provider1 = new providers.JsonRpcProvider(config.endpoint1URL);
provider2 = new providers.JsonRpcProvider(config.endpoint2URL);
});
// Compare eth-call results
describe('match results for eth-calls', async () => {
let contract1: Contract;
let contract2: Contract;
describe('match results for eth-calls to UniswapV2 Factory', async () => {
before("initialize contracts", async () => {
contract1 = new Contract(uniswapV2FactoryAddress, uniswapV2FactoryABI, provider1);
contract2 = new Contract(uniswapV2FactoryAddress, uniswapV2FactoryABI, provider2);
});
it('should match results for allPairs', async () => {
const args = [100]
const result1 = await contract1.allPairs(...args, {blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.allPairs(...args, {blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for allPairsLength', async () => {
const result1 = await contract1.allPairsLength({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.allPairsLength({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for feeTo', async () => {
const result1 = await contract1.feeTo({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.feeTo({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for feeToSetter', async () => {
const result1 = await contract1.feeToSetter({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.feeToSetter({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for getPair', async () => {
const args = ["0x8e870d67f660d95d5be530380d0ec0bd388289e1", "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"]
const result1 = await contract1.getPair(...args, {blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.getPair(...args, {blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
});
describe('match results for eth-calls to UniswapV2 Pair', async () => {
before("initialize contracts", async () => {
contract1 = new Contract(uniswapV2PairAddress, uniswapV2PairABI, provider1);
contract2 = new Contract(uniswapV2PairAddress, uniswapV2PairABI, provider2);
});
it('should match results for DOMAIN_SEPARATOR', async () => {
const result1 = await contract1.DOMAIN_SEPARATOR({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.DOMAIN_SEPARATOR({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for MINIMUM_LIQUIDITY', async () => {
const result1 = await contract1.MINIMUM_LIQUIDITY({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.MINIMUM_LIQUIDITY({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for PERMIT_TYPEHASH', async () => {
const result1 = await contract1.PERMIT_TYPEHASH({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.PERMIT_TYPEHASH({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for allowance', async () => {
const args = ["0x9c77233bbd235a3ed219daa051e0a3de5ce03c3e", "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"]
const result1 = await contract1.allowance(...args, {blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.allowance(...args, {blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for balanceOf', async () => {
const args = ["0x052ba6f57c9184a89c34196ee4f3adacaeb58e8d"]
const result1 = await contract1.balanceOf(...args, {blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.balanceOf(...args, {blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for decimals', async () => {
const result1 = await contract1.decimals({blockTag: config.blockTag});
const result2 = await contract2.decimals({blockTag: config.blockTag});
expect(result1).to.deep.equal(result2);
});
it('should match results for factory', async () => {
const result1 = await contract1.factory({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.factory({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for getReserves', async () => {
const result1 = await contract1.getReserves({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.getReserves({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for kLast', async () => {
const result1 = await contract1.kLast({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.kLast({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for name', async () => {
const result1 = await contract1.name({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.name({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for nonces', async () => {
const args = ["0xE0e8C1D735698060477e79a8e4C20276Fc2Ec7A7"]
const result1 = await contract1.nonces(...args, {blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.nonces(...args, {blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for price0CumulativeLast', async () => {
const result1 = await contract1.price0CumulativeLast({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.price0CumulativeLast({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for price1CumulativeLast', async () => {
const result1 = await contract1.price1CumulativeLast({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.price1CumulativeLast({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for symbol', async () => {
const result1 = await contract1.symbol({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.symbol({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for token0', async () => {
const result1 = await contract1.token0({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.token0({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for token1', async () => {
const result1 = await contract1.token1({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.token1({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for totalSupply', async () => {
const result1 = await contract1.totalSupply({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.totalSupply({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
});
describe('match results for eth-calls to USDC', async () => {
before("initialize contracts", async () => {
contract1 = new Contract(usdcAddress, usdcABI, provider1);
contract2 = new Contract(usdcAddress, usdcABI, provider2);
});
it('should match results for CANCEL_AUTHORIZATION_TYPEHASH', async () => {
const result1 = await contract1.CANCEL_AUTHORIZATION_TYPEHASH({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.CANCEL_AUTHORIZATION_TYPEHASH({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for DOMAIN_SEPARATOR', async () => {
const result1 = await contract1.DOMAIN_SEPARATOR({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.DOMAIN_SEPARATOR({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for PERMIT_TYPEHASH', async () => {
const result1 = await contract1.PERMIT_TYPEHASH({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.PERMIT_TYPEHASH({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for TRANSFER_WITH_AUTHORIZATION_TYPEHASH', async () => {
const result1 = await contract1.TRANSFER_WITH_AUTHORIZATION_TYPEHASH({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.TRANSFER_WITH_AUTHORIZATION_TYPEHASH({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for allowance', async () => {
const args = ["0xa064c5d674b0de5ac8d1d1ade3fba7569525ac44", "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"];
const result1 = await contract1.allowance(...args, {blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.allowance(...args, {blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for balanceOf', async () => {
const args = ["0x95ba4cf87d6723ad9c0db21737d862be80e93911"]
const result1 = await contract1.balanceOf(...args, {blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.balanceOf(...args, {blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for blacklister', async () => {
const result1 = await contract1.blacklister({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.blacklister({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for currency', async () => {
const result1 = await contract1.currency({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.currency({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for decimals', async () => {
const result1 = await contract1.decimals({blockTag: config.blockTag});
const result2 = await contract2.decimals({blockTag: config.blockTag});
expect(result1).to.deep.equal(result2);
});
it('should match results for masterMinter', async () => {
const result1 = await contract1.masterMinter({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.masterMinter({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for name', async () => {
const result1 = await contract1.name({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.name({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for owner', async () => {
const result1 = await contract1.owner({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.owner({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for paused', async () => {
const result1 = await contract1.paused({blockTag: config.blockTag});
const result2 = await contract2.paused({blockTag: config.blockTag});
expect(result1).to.deep.equal(result2);
});
it('should match results for pauser', async () => {
const result1 = await contract1.pauser({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.pauser({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for rescuer', async () => {
const result1 = await contract1.rescuer({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.rescuer({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for symbol', async () => {
const result1 = await contract1.symbol({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.symbol({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for totalSupply', async () => {
const result1 = await contract1.totalSupply({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.totalSupply({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
});
describe('match results for eth-calls to Compound', async () => {
before("initialize contracts", async () => {
contract1 = new Contract(compoundAddress, compoundABI, provider1);
contract2 = new Contract(compoundAddress, compoundABI, provider2);
});
it('should match results for DELEGATION_TYPEHASH', async () => {
const result1 = await contract1.DELEGATION_TYPEHASH({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.DELEGATION_TYPEHASH({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for DOMAIN_TYPEHASH', async () => {
const result1 = await contract1.DOMAIN_TYPEHASH({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.DOMAIN_TYPEHASH({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for allowance', async () => {
const args = ["0xa5ffc832b23606f82005688d734c099dfabd5313", "0x70e36f6bf80a52b3b46b3af8e106cc0ed743e8e4"]
const result1 = await contract1.allowance(...args, {blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.allowance(...args, {blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for balanceOf', async () => {
const args = ["0x2FAF487A4414Fe77e2327F0bf4AE2a264a776AD2"]
const result1 = await contract1.balanceOf(...args, {blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.balanceOf(...args, {blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for decimals', async () => {
const result1 = await contract1.decimals({blockTag: config.blockTag});
const result2 = await contract2.decimals({blockTag: config.blockTag});
expect(result1).to.deep.equal(result2);
});
it('should match results for delegates', async () => {
const args = ["0xdaaf40dae51ad67f1eeb8c8bded831f7ab150830"]
const result1 = await contract1.delegates(...args, {blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.delegates(...args, {blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for name', async () => {
const result1 = await contract1.name({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.name({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for numCheckpoints', async () => {
const args = ["0xc00e94Cb662C3520282E6f5717214004A7f26888"]
const result1 = await contract1.numCheckpoints(...args, {blockTag: config.blockTag});
const result2 = await contract2.numCheckpoints(...args, {blockTag: config.blockTag});
expect(result1).to.deep.equal(result2);
});
it('should match results for symbol', async () => {
const result1 = await contract1.symbol({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.symbol({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for totalSupply', async () => {
const result1 = await contract1.totalSupply({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.totalSupply({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
});
describe('match results for eth-calls to Dai', async () => {
before("initialize contracts", async () => {
contract1 = new Contract(daiAddress, daiABI, provider1);
contract2 = new Contract(daiAddress, daiABI, provider2);
});
it('should match results for DOMAIN_SEPARATOR', async () => {
const result1 = await contract1.DOMAIN_SEPARATOR({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.DOMAIN_SEPARATOR({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for PERMIT_TYPEHASH', async () => {
const result1 = await contract1.PERMIT_TYPEHASH({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.PERMIT_TYPEHASH({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for allowance', async () => {
const args = ["0x74ae284dd6044db63dc268f52131571a3b80ad33", "0xa91902085405CE0F648a7Eb82045Aefc1B7BAC01"]
const result1 = await contract1.allowance(...args, {blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.allowance(...args, {blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for balanceOf', async () => {
const args = ["0xab78b4c4a28f6e449e0bf2d6eb4f4f0316343d2a"]
const result1 = await contract1.balanceOf(...args, {blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.balanceOf(...args, {blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for decimals', async () => {
const result1 = await contract1.decimals({blockTag: config.blockTag});
const result2 = await contract2.decimals({blockTag: config.blockTag});
expect(result1).to.deep.equal(result2);
});
it('should match results for name', async () => {
const result1 = await contract1.name({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.name({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for symbol', async () => {
const result1 = await contract1.symbol({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.symbol({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for totalSupply', async () => {
const result1 = await contract1.totalSupply({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.totalSupply({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
it('should match results for version', async () => {
const result1 = await contract1.version({blockTag: config.blockTag});
expect(result1).to.not.be.empty;
const result2 = await contract2.version({blockTag: config.blockTag});
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
});
});
// Compare getStorageAt results
describe('match results for getStorageAt calls', async () => {
const testSlots = ['0x0', '0x1', '0x2', '0x3', '0x4', '0x5', '0x6', '0x7', '0x8', '0x9', '0xa', '0xb', '0xc', '0xd', '0xe', '0xf']
describe('match results for getStorageAt for UniswapV2 Factory', async () => {
testSlots.forEach((testSlot: string) => {
it(`should match results for slot ${testSlot}`, async () => {
const result1 = await provider1.getStorageAt(uniswapV2FactoryAddress, testSlot, config.blockTag)
expect(result1).to.not.be.empty;
const result2 = await provider2.getStorageAt(uniswapV2FactoryAddress, testSlot, config.blockTag)
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
});
});
describe('match results for getStorageAt for UniswapV2 Pair', async () => {
testSlots.forEach((testSlot: string) => {
it(`should match results for slot ${testSlot}`, async () => {
const result1 = await provider1.getStorageAt(uniswapV2PairAddress, testSlot, config.blockTag)
expect(result1).to.not.be.empty;
const result2 = await provider2.getStorageAt(uniswapV2PairAddress, testSlot, config.blockTag)
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
});
});
describe('match results for getStorageAt for USDC', async () => {
testSlots.forEach((testSlot: string) => {
it(`should match results for slot ${testSlot}`, async () => {
const result1 = await provider1.getStorageAt(usdcAddress, testSlot, config.blockTag)
expect(result1).to.not.be.empty;
const result2 = await provider2.getStorageAt(usdcAddress, testSlot, config.blockTag)
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
});
});
describe('match results for getStorageAt for Compound', async () => {
testSlots.forEach((testSlot: string) => {
it(`should match results for slot ${testSlot}`, async () => {
const result1 = await provider1.getStorageAt(compoundAddress, testSlot, config.blockTag)
expect(result1).to.not.be.empty;
const result2 = await provider2.getStorageAt(compoundAddress, testSlot, config.blockTag)
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
});
});
describe('match results for getStorageAt for Dai', async () => {
testSlots.forEach((testSlot: string) => {
it(`should match results for slot ${testSlot}`, async () => {
const result1 = await provider1.getStorageAt(daiAddress, testSlot, config.blockTag)
expect(result1).to.not.be.empty;
const result2 = await provider2.getStorageAt(daiAddress, testSlot, config.blockTag)
expect(result2).to.not.be.empty;
expect(result1).to.deep.equal(result2);
});
});
});
});
});

View File

@ -0,0 +1,25 @@
//
// Copyright 2022 Vulcanize, Inc.
//
import { readAbi } from "./common";
// UniswapV2 Factory contract
export const uniswapV2FactoryAddress = "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f";
export const uniswapV2FactoryABI = readAbi("abis/UniswapV2Factory.json");
// UniswapV2 Pair contract
export const uniswapV2PairAddress = "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc";
export const uniswapV2PairABI = readAbi("abis/UniswapV2Pair.json");
// USDC contract
export const usdcAddress = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
export const usdcABI = readAbi("abis/FiatTokenV2_1.json");
// Compound contract
export const compoundAddress = "0xc00e94Cb662C3520282E6f5717214004A7f26888";
export const compoundABI = readAbi("abis/Comp.json");
// Dai/Maker contract
export const daiAddress = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
export const daiABI = readAbi("abis/Dai.json");

104
packages/test/tsconfig.json Normal file
View File

@ -0,0 +1,104 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es5", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "dist", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
},
"exclude": ["dist", "artifacts"]
}