From cb43e71f83a1e660959a5e8a7a24110e75f1463c Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Tue, 6 Apr 2021 18:16:09 +0300 Subject: [PATCH] Integration Tests (#33) * test: add the tests * ci: enable integration test * ci: always run integration tests * ci: build integration tests deps * ci: cache integration test deps * ci: properly cache yarn deps * fix(integration-tests): clone the provider instead of referencing Otherwise it interferes with test isolation * chore: yarn lint --- .github/workflows/integration.yml | 33 ++- .github/workflows/ts-packages.yml | 20 +- integration-tests/contracts/Address.sol | 189 +++++++++++++++ .../contracts/ChainlinkERC20.sol | 207 +++++++++++++++++ integration-tests/contracts/Initializable.sol | 55 +++++ integration-tests/contracts/Proxy.sol | 83 +++++++ .../contracts/UpgradeableProxy.sol | 78 +++++++ integration-tests/hardhat.config.ts | 13 ++ integration-tests/package.json | 26 +++ integration-tests/prettier-config.json | 1 + .../test/queue-ingestion.spec.ts | 130 +++++++++++ .../test/read-proxy-event.spec.ts | 75 ++++++ integration-tests/test/utils.ts | 95 ++++++++ integration-tests/tsconfig.json | 8 + integration-tests/tslint.json | 7 + package.json | 3 +- tsconfig.json | 2 +- yarn.lock | 216 +++++++++++++++++- 18 files changed, 1221 insertions(+), 20 deletions(-) create mode 100644 integration-tests/contracts/Address.sol create mode 100644 integration-tests/contracts/ChainlinkERC20.sol create mode 100644 integration-tests/contracts/Initializable.sol create mode 100644 integration-tests/contracts/Proxy.sol create mode 100644 integration-tests/contracts/UpgradeableProxy.sol create mode 100644 integration-tests/hardhat.config.ts create mode 100644 integration-tests/package.json create mode 120000 integration-tests/prettier-config.json create mode 100644 integration-tests/test/queue-ingestion.spec.ts create mode 100644 integration-tests/test/read-proxy-event.spec.ts create mode 100644 integration-tests/test/utils.ts create mode 100644 integration-tests/tsconfig.json create mode 100644 integration-tests/tslint.json diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index f5ce042801d7..c6b0785a909c 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -5,12 +5,6 @@ on: branches: - master pull_request: - branches: - - master - -defaults: - run: - working-directory: ./ops jobs: integration: @@ -22,13 +16,38 @@ jobs: - uses: actions/checkout@v2 - name: Build the base layer dependencies + working-directory: ./ops run: docker-compose build --parallel -- builder l2geth l1_chain - name: Build the other services + working-directory: ./ops run: docker-compose build --parallel -- deployer dtl batch_submitter - name: Bring the stack up and wait for the sequencer to be ready + working-directory: ./ops run: docker-compose up -d && ./scripts/wait-for-sequencer.sh + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v2 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Install integration test dependencies + working-directory: ./integration-tests + # only install dependencies if the cache was invalidated + if: steps.yarn-cache.outputs.cache-hit != 'true' + run: yarn install + + - name: Build deps for the integration tests + run: yarn build + - name: Run the integration tests - run: echo "Integration tests will be run here" + working-directory: ./integration-tests + run: yarn test:integration diff --git a/.github/workflows/ts-packages.yml b/.github/workflows/ts-packages.yml index dfa1e693268f..c32de003941d 100644 --- a/.github/workflows/ts-packages.yml +++ b/.github/workflows/ts-packages.yml @@ -34,17 +34,21 @@ jobs: with: node-version: ${{ matrix.node }} - # START DEPENDENCY CACHING - - name: Cache root deps - uses: actions/cache@v1 - id: cache_base - with: - path: node_modules - key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('package.json') }} + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" - # END DEPENDENCY CACHING + - uses: actions/cache@v2 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- - name: Install Dependencies + # only install dependencies if there was a change in the deps + if: steps.yarn-cache.outputs.cache-hit != 'true' run: yarn install - name: Build diff --git a/integration-tests/contracts/Address.sol b/integration-tests/contracts/Address.sol new file mode 100644 index 000000000000..42a9dc1ebe9a --- /dev/null +++ b/integration-tests/contracts/Address.sol @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.6.2 <0.8.0; + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + */ + function isContract(address account) internal view returns (bool) { + // This method relies on extcodesize, which returns 0 for contracts in + // construction, since the code is only stored at the end of the + // constructor execution. + + uint256 size; + // solhint-disable-next-line no-inline-assembly + assembly { size := extcodesize(account) } + return size > 0; + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + // solhint-disable-next-line avoid-low-level-calls, avoid-call-value + (bool success, ) = recipient.call{ value: amount }(""); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain`call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCall(target, data, "Address: low-level call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + require(isContract(target), "Address: call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.call{ value: value }(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { + return functionStaticCall(target, data, "Address: low-level static call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { + require(isContract(target), "Address: static call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.staticcall(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { + return functionDelegateCall(target, data, "Address: low-level delegate call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { + require(isContract(target), "Address: delegate call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.delegatecall(data); + return _verifyCallResult(success, returndata, errorMessage); + } + + function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { + if (success) { + return returndata; + } else { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + + // solhint-disable-next-line no-inline-assembly + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } + } +} diff --git a/integration-tests/contracts/ChainlinkERC20.sol b/integration-tests/contracts/ChainlinkERC20.sol new file mode 100644 index 000000000000..b2c45df9567e --- /dev/null +++ b/integration-tests/contracts/ChainlinkERC20.sol @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: MIT +pragma solidity >0.6.0 <0.8.0; + +/** + * @title ERC20 + * @dev A super simple ERC20 implementation! + */ +contract ChainlinkERC20 { + + /********** + * Events * + **********/ + + event Transfer( + address indexed _from, + address indexed _to, + uint256 _value + ); + + event Approval( + address indexed _owner, + address indexed _spender, + uint256 _value + ); + + + /************* + * Variables * + *************/ + + mapping (address => uint256) public balances; + mapping (address => mapping (address => uint256)) public allowances; + + // Some optional extra goodies. + uint256 public totalSupply; + string public name; + + + /*************** + * Constructor * + ***************/ + + /** + * @param _initialSupply Initial maximum token supply. + * @param _name A name for our ERC20 (technically optional, but it's fun ok jeez). + */ + function init( + uint256 _initialSupply, + string memory _name + ) + public + { + balances[msg.sender] = _initialSupply; + totalSupply = _initialSupply; + name = _name; + } + + + /******************** + * Public Functions * + ********************/ + + /** + * Checks the balance of an address. + * @param _owner Address to check a balance for. + * @return Balance of the address. + */ + function balanceOf( + address _owner + ) + external + view + returns ( + uint256 + ) + { + return balances[_owner]; + } + + /** + * Transfers a balance from your account to someone else's account! + * @param _to Address to transfer a balance to. + * @param _amount Amount to transfer to the other account. + * @return true if the transfer was successful. + */ + function transfer( + address _to, + uint256 _amount + ) + external + returns ( + bool + ) + { + require( + balances[msg.sender] >= _amount, + "You don't have enough balance to make this transfer!" + ); + + balances[msg.sender] -= _amount; + balances[_to] += _amount; + + emit Transfer( + msg.sender, + _to, + _amount + ); + + return true; + } + + /** + * Transfers a balance from someone else's account to another account. You need an allowance + * from the sending account for this to work! + * @param _from Account to transfer a balance from. + * @param _to Account to transfer a balance to. + * @param _amount Amount to transfer to the other account. + * @return true if the transfer was successful. + */ + function transferFrom( + address _from, + address _to, + uint256 _amount + ) + external + returns ( + bool + ) + { + require( + balances[_from] >= _amount, + "Can't transfer from the desired account because it doesn't have enough balance." + ); + + require( + allowances[_from][msg.sender] >= _amount, + "Can't transfer from the desired account because you don't have enough of an allowance." + ); + + balances[_to] += _amount; + balances[_from] -= _amount; + + emit Transfer( + _from, + _to, + _amount + ); + + return true; + } + + /** + * Approves an account to spend some amount from your account. + * @param _spender Account to approve a balance for. + * @param _amount Amount to allow the account to spend from your account. + * @return true if the allowance was successful. + */ + function approve( + address _spender, + uint256 _amount + ) + external + returns ( + bool + ) + { + allowances[msg.sender][_spender] = _amount; + + emit Approval( + msg.sender, + _spender, + _amount + ); + + return true; + } + + /** + * Checks how much a given account is allowed to spend from another given account. + * @param _owner Address of the account to check an allowance from. + * @param _spender Address of the account trying to spend from the owner. + * @return Allowance for the spender from the owner. + */ + function allowance( + address _owner, + address _spender + ) + external + view + returns ( + uint256 + ) + { + return allowances[_owner][_spender]; + } + + function testRequire() + external + view + returns ( + uint256 + ) + { + require(false, "This is an revert string"); + return balances[msg.sender]; + } +} diff --git a/integration-tests/contracts/Initializable.sol b/integration-tests/contracts/Initializable.sol new file mode 100644 index 000000000000..d8d620126b4d --- /dev/null +++ b/integration-tests/contracts/Initializable.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT + +// solhint-disable-next-line compiler-version +pragma solidity >=0.4.24 <0.8.0; + +import "./Address.sol"; + +/** + * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed + * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an + * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer + * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. + * + * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as + * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}. + * + * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure + * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. + */ +abstract contract Initializable { + + /** + * @dev Indicates that the contract has been initialized. + */ + bool private _initialized; + + /** + * @dev Indicates that the contract is in the process of being initialized. + */ + bool private _initializing; + + /** + * @dev Modifier to protect an initializer function from being invoked twice. + */ + modifier initializer() { + require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized"); + + bool isTopLevelCall = !_initializing; + if (isTopLevelCall) { + _initializing = true; + _initialized = true; + } + + _; + + if (isTopLevelCall) { + _initializing = false; + } + } + + /// @dev Returns true if and only if the function is running in the constructor + function _isConstructor() private view returns (bool) { + return !Address.isContract(address(this)); + } +} diff --git a/integration-tests/contracts/Proxy.sol b/integration-tests/contracts/Proxy.sol new file mode 100644 index 000000000000..7f51c61d3e46 --- /dev/null +++ b/integration-tests/contracts/Proxy.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.6.0 <0.8.0; + +/** + * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM + * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to + * be specified by overriding the virtual {_implementation} function. + * + * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a + * different contract through the {_delegate} function. + * + * The success and return data of the delegated call will be returned back to the caller of the proxy. + */ +abstract contract Proxy { + /** + * @dev Delegates the current call to `implementation`. + * + * This function does not return to its internall call site, it will return directly to the external caller. + */ + function _delegate(address implementation) internal virtual { + // solhint-disable-next-line no-inline-assembly + assembly { + // Copy msg.data. We take full control of memory in this inline assembly + // block because it will not return to Solidity code. We overwrite the + // Solidity scratch pad at memory position 0. + calldatacopy(0, 0, calldatasize()) + + // Call the implementation. + // out and outsize are 0 because we don't know the size yet. + let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) + + // Copy the returned data. + returndatacopy(0, 0, returndatasize()) + + switch result + // delegatecall returns 0 on error. + case 0 { revert(0, returndatasize()) } + default { return(0, returndatasize()) } + } + } + + /** + * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function + * and {_fallback} should delegate. + */ + function _implementation() internal view virtual returns (address); + + /** + * @dev Delegates the current call to the address returned by `_implementation()`. + * + * This function does not return to its internall call site, it will return directly to the external caller. + */ + function _fallback() internal virtual { + _beforeFallback(); + _delegate(_implementation()); + } + + /** + * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other + * function in the contract matches the call data. + */ + fallback () external payable virtual { + _fallback(); + } + + /** + * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data + * is empty. + */ + receive () external payable virtual { + _fallback(); + } + + /** + * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback` + * call, or as part of the Solidity `fallback` or `receive` functions. + * + * If overriden should call `super._beforeFallback()`. + */ + function _beforeFallback() internal virtual { + } +} diff --git a/integration-tests/contracts/UpgradeableProxy.sol b/integration-tests/contracts/UpgradeableProxy.sol new file mode 100644 index 000000000000..40f24c879645 --- /dev/null +++ b/integration-tests/contracts/UpgradeableProxy.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.6.0 <0.8.0; + +import "./Proxy.sol"; +import "./Address.sol"; + +/** + * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an + * implementation address that can be changed. This address is stored in storage in the location specified by + * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the + * implementation behind the proxy. + * + * Upgradeability is only provided internally through {_upgradeTo}. For an externally upgradeable proxy see + * {TransparentUpgradeableProxy}. + */ +contract UpgradeableProxy is Proxy { + /** + * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`. + * + * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded + * function call, and allows initializating the storage of the proxy like a Solidity constructor. + */ + constructor(address _logic, bytes memory _data) public payable { + assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)); + _setImplementation(_logic); + if(_data.length > 0) { + Address.functionDelegateCall(_logic, _data); + } + } + + /** + * @dev Emitted when the implementation is upgraded. + */ + event Upgraded(address indexed implementation); + + /** + * @dev Storage slot with the address of the current implementation. + * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is + * validated in the constructor. + */ + bytes32 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + + /** + * @dev Returns the current implementation address. + */ + function _implementation() internal view virtual override returns (address impl) { + bytes32 slot = _IMPLEMENTATION_SLOT; + // solhint-disable-next-line no-inline-assembly + assembly { + impl := sload(slot) + } + } + + /** + * @dev Upgrades the proxy to a new implementation. + * + * Emits an {Upgraded} event. + */ + function _upgradeTo(address newImplementation) internal virtual { + _setImplementation(newImplementation); + emit Upgraded(newImplementation); + } + + /** + * @dev Stores a new address in the EIP1967 implementation slot. + */ + function _setImplementation(address newImplementation) private { + require(Address.isContract(newImplementation), "UpgradeableProxy: new implementation is not a contract"); + + bytes32 slot = _IMPLEMENTATION_SLOT; + + // solhint-disable-next-line no-inline-assembly + assembly { + sstore(slot, newImplementation) + } + } +} diff --git a/integration-tests/hardhat.config.ts b/integration-tests/hardhat.config.ts new file mode 100644 index 000000000000..14a4bc649a39 --- /dev/null +++ b/integration-tests/hardhat.config.ts @@ -0,0 +1,13 @@ +import { HardhatUserConfig } from 'hardhat/types' + +// Hardhat plugins +import '@nomiclabs/hardhat-ethers' +import '@eth-optimism/hardhat-ovm' + +const config: HardhatUserConfig = { + mocha: { + timeout: 100000, + }, +} + +export default config diff --git a/integration-tests/package.json b/integration-tests/package.json new file mode 100644 index 000000000000..a85a9b5ed77f --- /dev/null +++ b/integration-tests/package.json @@ -0,0 +1,26 @@ +{ + "name": "@eth-optimism/sequencer-interactions", + "version": "0.0.1", + "description": "[Optimism] Integration Tests: Sequencer Interactions", + "author": "Optimism PBC", + "license": "MIT", + "scripts": { + "lint": "yarn lint:fix && yarn lint:check", + "lint:check": "tslint --format stylish --project .", + "lint:fix": "prettier --config ./prettier-config.json --write 'test/**/*.ts'", + "test:integration": "TARGET=ovm hardhat test" + }, + "devDependencies": { + "@eth-optimism/contracts": "^0.1.11", + "@eth-optimism/core-utils": "^0.1.10", + "@eth-optimism/hardhat-ovm": "^0.0.1", + "@ethersproject/providers": "^5.0.24", + "@nomiclabs/hardhat-ethers": "^2.0.2", + "chai": "^4.3.3", + "ethereum-waffle": "^3.3.0", + "ethers": "^5.0.32", + "hardhat": "^2.1.2", + "lodash": "^4.17.21", + "mocha": "^8.3.1" + } +} diff --git a/integration-tests/prettier-config.json b/integration-tests/prettier-config.json new file mode 120000 index 000000000000..543d2f30491a --- /dev/null +++ b/integration-tests/prettier-config.json @@ -0,0 +1 @@ +../prettier-config.json \ No newline at end of file diff --git a/integration-tests/test/queue-ingestion.spec.ts b/integration-tests/test/queue-ingestion.spec.ts new file mode 100644 index 000000000000..1278cafaad0c --- /dev/null +++ b/integration-tests/test/queue-ingestion.spec.ts @@ -0,0 +1,130 @@ +/* Imports: Internal */ +import { getContractFactory } from '@eth-optimism/contracts' +import { injectL2Context } from './utils' + +/* Imports: External */ +import { Contract, Signer, Wallet, providers } from 'ethers' +import { expect } from 'chai' + +function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)) +} + +// This test ensures that the transactions which get `enqueue`d get +// added to the L2 blocks by the Sync Service (which queries the DTL) +describe('Queue Ingestion', () => { + const RETRIES = 20 + const numTxs = 5 + let startBlock: number + let endBlock: number + + let l1Signer: Signer + let l2Provider: providers.JsonRpcProvider + + let addressResolver: Contract + let canonicalTransactionChain: Contract + + const receipts = [] + before(async () => { + const httpPort = 8545 + const l1HttpPort = 9545 + l2Provider = injectL2Context( + new providers.JsonRpcProvider(`http://localhost:${httpPort}`) + ) + l1Signer = new Wallet( + '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80', + new providers.JsonRpcProvider(`http://localhost:${l1HttpPort}`) + ) + + const addressResolverAddress = '0x5FbDB2315678afecb367f032d93F642f64180aa3' + addressResolver = getContractFactory('Lib_AddressManager') + .connect(l1Signer) + .attach(addressResolverAddress) + + const ctcAddress = await addressResolver.getAddress( + 'OVM_CanonicalTransactionChain' + ) + canonicalTransactionChain = getContractFactory( + 'OVM_CanonicalTransactionChain' + ).attach(ctcAddress) + }) + + // The transactions are enqueue'd with a `to` address of i.repeat(40) + // meaning that the `to` value is different each iteration in a deterministic + // way. They need to be inserted into the L2 chain in an ascending order. + // Keep track of the receipts so that the blockNumber can be compared + // against the `L1BlockNumber` on the tx objects. + before(async () => { + // Keep track of the L2 tip before submitting any transactions so that + // the subsequent transactions can be queried for in the next test + startBlock = (await l2Provider.getBlockNumber()) + 1 + endBlock = startBlock + numTxs - 1 + + // Enqueue some transactions by building the calldata and then sending + // the transaction to Layer 1 + for (let i = 0; i < numTxs; i++) { + const input = ['0x' + `${i}`.repeat(40), 500_000, `0x0${i}`] + const calldata = canonicalTransactionChain.interface.encodeFunctionData( + 'enqueue', + input + ) + + const txResponse = await l1Signer.sendTransaction({ + data: calldata, + to: canonicalTransactionChain.address, + }) + + const receipt = await txResponse.wait() + receipts.push(receipt) + } + }) + + // The batch submitter will notice that there are transactions + // that are in the queue and submit them. L2 will pick up the + // sequencer batch appended event and play the transactions. + it('should order transactions correctly', async () => { + // Wait until each tx from the previous test has + // been executed + let i: number + for (i = 0; i < RETRIES; i++) { + const tip = await l2Provider.getBlockNumber() + if (tip >= endBlock) { + break + } + await sleep(1000) + } + + if (i === RETRIES) { + throw new Error( + 'timed out waiting for queued transactions to be inserted' + ) + } + + const from = await l1Signer.getAddress() + // Keep track of an index into the receipts list and + // increment it for each block fetched. + let receiptIndex = 0 + // Fetch blocks + for (i = 0; i < numTxs; i++) { + const block = await l2Provider.getBlock(startBlock + i) + const hash = block.transactions[0] + // Use as any hack because additional properties are + // added to the transaction response + const tx = await (l2Provider.getTransaction(hash) as any) + + // The `to` addresses are defined in the previous test and + // increment sequentially. + expect(tx.to).to.be.equal('0x' + `${i}`.repeat(40)) + // The transaction type is EIP155 + expect(tx.txType).to.be.equal('EIP155') + // The queue origin is Layer 1 + expect(tx.queueOrigin).to.be.equal('l1') + // the L1TxOrigin is equal to the Layer one from + expect(tx.l1TxOrigin).to.be.equal(from.toLowerCase()) + expect(typeof tx.l1BlockNumber).to.be.equal('number') + // Get the receipt and increment the recept index + const receipt = receipts[receiptIndex++] + expect(tx.l1BlockNumber).to.be.equal(receipt.blockNumber) + } + }) +}) diff --git a/integration-tests/test/read-proxy-event.spec.ts b/integration-tests/test/read-proxy-event.spec.ts new file mode 100644 index 000000000000..105a95006f39 --- /dev/null +++ b/integration-tests/test/read-proxy-event.spec.ts @@ -0,0 +1,75 @@ +import { expect } from 'chai' +import { ethers } from 'hardhat' + +/* Imports: External */ +import { Contract, Wallet, providers } from 'ethers' + +describe('Reading events from proxy contracts', () => { + let l2Provider: providers.JsonRpcProvider + let l2Wallet: Wallet + before(async () => { + const httpPort = 8545 + l2Provider = new providers.JsonRpcProvider(`http://localhost:${httpPort}`) + l2Wallet = Wallet.createRandom().connect(l2Provider) + }) + + // helper to query the transfers + const _queryFilterTransfer = async ( + queryContract: Contract, + filterContract: Contract + ) => { + // Get the filter + const filter = filterContract.filters.Transfer(null, null, null) + // Query the filter + return queryContract.queryFilter(filter, 0, 'latest') + } + + let ProxyERC20: Contract + let ERC20: Contract + + beforeEach(async () => { + // Set up our contract factories in advance. + const Factory__ERC20 = await ethers.getContractFactory( + 'ChainlinkERC20', + l2Wallet + ) + const Factory__UpgradeableProxy = await ethers.getContractFactory( + 'UpgradeableProxy', + l2Wallet + ) + + // Deploy the underlying ERC20 implementation. + ERC20 = await Factory__ERC20.deploy() + await ERC20.deployTransaction.wait() + + // Deploy the upgradeable proxy and execute the init function. + ProxyERC20 = await Factory__UpgradeableProxy.deploy( + ERC20.address, + ERC20.interface.encodeFunctionData('init', [ + 1000, // initial supply + 'Cool Token Name Goes Here', // token name + ]) + ) + await ProxyERC20.deployTransaction.wait() + ProxyERC20 = new ethers.Contract( + ProxyERC20.address, + ERC20.interface, + l2Wallet + ) + }) + + it('should read transfer events from a proxy ERC20', async () => { + // Make two transfers. + const recipient = '0x0000000000000000000000000000000000000000' + const transfer1 = await ProxyERC20.transfer(recipient, 1) + await transfer1.wait() + const transfer2 = await ProxyERC20.transfer(recipient, 1) + await transfer2.wait() + + // Make sure events are being emitted in the right places. + expect((await _queryFilterTransfer(ERC20, ERC20)).length).to.eq(0) + expect((await _queryFilterTransfer(ERC20, ProxyERC20)).length).to.eq(0) + expect((await _queryFilterTransfer(ProxyERC20, ERC20)).length).to.eq(2) + expect((await _queryFilterTransfer(ProxyERC20, ProxyERC20)).length).to.eq(2) + }) +}) diff --git a/integration-tests/test/utils.ts b/integration-tests/test/utils.ts new file mode 100644 index 000000000000..09dab6883b7a --- /dev/null +++ b/integration-tests/test/utils.ts @@ -0,0 +1,95 @@ +import { remove0x } from '@eth-optimism/core-utils' +import { JsonRpcProvider } from '@ethersproject/providers' +import cloneDeep from 'lodash/cloneDeep' + +import { utils, providers, Transaction } from 'ethers' + +/** + * Helper for adding additional L2 context to transactions + */ +export const injectL2Context = (l1Provider: providers.JsonRpcProvider) => { + const provider = cloneDeep(l1Provider) + const format = provider.formatter.transaction.bind(provider.formatter) + provider.formatter.transaction = (transaction) => { + const tx = format(transaction) + const sig = utils.joinSignature(tx) + const hash = sighashEthSign(tx) + tx.from = utils.verifyMessage(hash, sig) + return tx + } + + // Pass through the state root + const blockFormat = provider.formatter.block.bind(provider.formatter) + provider.formatter.block = (block) => { + const b = blockFormat(block) + b.stateRoot = block.stateRoot + return b + } + + // Pass through the state root and additional tx data + const blockWithTransactions = provider.formatter.blockWithTransactions.bind( + provider.formatter + ) + provider.formatter.blockWithTransactions = (block) => { + const b = blockWithTransactions(block) + b.stateRoot = block.stateRoot + for (let i = 0; i < b.transactions.length; i++) { + b.transactions[i].l1BlockNumber = block.transactions[i].l1BlockNumber + if (b.transactions[i].l1BlockNumber != null) { + b.transactions[i].l1BlockNumber = parseInt( + b.transactions[i].l1BlockNumber, + 16 + ) + } + b.transactions[i].l1TxOrigin = block.transactions[i].l1TxOrigin + b.transactions[i].txType = block.transactions[i].txType + b.transactions[i].queueOrigin = block.transactions[i].queueOrigin + } + return b + } + + // Handle additional tx data + const formatTxResponse = provider.formatter.transactionResponse.bind( + provider.formatter + ) + provider.formatter.transactionResponse = (transaction) => { + const tx = formatTxResponse(transaction) as any + tx.txType = transaction.txType + tx.queueOrigin = transaction.queueOrigin + tx.rawTransaction = transaction.rawTransaction + tx.l1BlockNumber = transaction.l1BlockNumber + if (tx.l1BlockNumber != null) { + tx.l1BlockNumber = parseInt(tx.l1BlockNumber, 16) + } + tx.l1TxOrigin = transaction.l1TxOrigin + return tx + } + + return provider +} + +function serializeEthSignTransaction(transaction: Transaction): any { + const encoded = utils.defaultAbiCoder.encode( + ['uint256', 'uint256', 'uint256', 'uint256', 'address', 'bytes'], + [ + transaction.nonce, + transaction.gasLimit, + transaction.gasPrice, + transaction.chainId, + transaction.to, + transaction.data, + ] + ) + + return Buffer.from(encoded.slice(2), 'hex') +} + +// Use this function as input to `eth_sign`. It does not +// add the prefix because `eth_sign` does that. It does +// serialize the transaction and hash the serialized +// transaction. +function sighashEthSign(transaction: any): Buffer { + const serialized = serializeEthSignTransaction(transaction) + const hash = remove0x(utils.keccak256(serialized)) + return Buffer.from(hash, 'hex') +} diff --git a/integration-tests/tsconfig.json b/integration-tests/tsconfig.json new file mode 100644 index 000000000000..6f1b3deb5717 --- /dev/null +++ b/integration-tests/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "resolveJsonModule": true + }, + "include": ["./test"], + "files": ["./hardhat.config.ts"] +} diff --git a/integration-tests/tslint.json b/integration-tests/tslint.json new file mode 100644 index 000000000000..d6d9b0e0ca6b --- /dev/null +++ b/integration-tests/tslint.json @@ -0,0 +1,7 @@ +{ + "extends": "../tslint.base.json", + "rules": { + "array-type": false, + "class-name": false + } +} diff --git a/package.json b/package.json index 591c9e9b2de2..c3d45bbf557c 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,8 @@ "version": "1.0.0", "license": "MIT", "workspaces": [ - "packages/*" + "packages/*", + "integration-tests" ], "private": true, "devDependencies": { diff --git a/tsconfig.json b/tsconfig.json index 2aed41a43747..e66a11201db5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "baseUrl": ".", "paths": { - "@eth-optimism/*": ["packages/*/src"] + "@eth-optimism/*": ["packages/*/src", "integration-tests/*/test"] }, "skipLibCheck": true } diff --git a/yarn.lock b/yarn.lock index 9fc52a89dfb5..00eb2d095f86 100644 --- a/yarn.lock +++ b/yarn.lock @@ -199,6 +199,19 @@ "@ethersproject/transactions" "^5.0.9" "@ethersproject/web" "^5.0.12" +"@ethersproject/abstract-provider@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.1.0.tgz#1f24c56cda5524ef4ed3cfc562a01d6b6f8eeb0b" + integrity sha512-8dJUnT8VNvPwWhYIau4dwp7qe1g+KgdRm4XTWvjkI9gAT2zZa90WF5ApdZ3vl1r6NDmnn6vUVvyphClRZRteTQ== + dependencies: + "@ethersproject/bignumber" "^5.1.0" + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/logger" "^5.1.0" + "@ethersproject/networks" "^5.1.0" + "@ethersproject/properties" "^5.1.0" + "@ethersproject/transactions" "^5.1.0" + "@ethersproject/web" "^5.1.0" + "@ethersproject/abstract-signer@5.0.14", "@ethersproject/abstract-signer@^5.0.0", "@ethersproject/abstract-signer@^5.0.10": version "5.0.14" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.0.14.tgz#30ef912b0f86599d90fdffc65c110452e7b55cf1" @@ -210,6 +223,17 @@ "@ethersproject/logger" "^5.0.8" "@ethersproject/properties" "^5.0.7" +"@ethersproject/abstract-signer@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.1.0.tgz#744c7a2d0ebe3cc0bc38294d0f53d5ca3f4e49e3" + integrity sha512-qQDMkjGZSSJSKl6AnfTgmz9FSnzq3iEoEbHTYwjDlEAv+LNP7zd4ixCcVWlWyk+2siud856M5CRhAmPdupeN9w== + dependencies: + "@ethersproject/abstract-provider" "^5.1.0" + "@ethersproject/bignumber" "^5.1.0" + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/logger" "^5.1.0" + "@ethersproject/properties" "^5.1.0" + "@ethersproject/address@5.0.11", "@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.0", "@ethersproject/address@^5.0.9": version "5.0.11" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.0.11.tgz#12022e8c590c33939beb5ab18b401ecf585eac59" @@ -221,6 +245,17 @@ "@ethersproject/logger" "^5.0.8" "@ethersproject/rlp" "^5.0.7" +"@ethersproject/address@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.1.0.tgz#3854fd7ebcb6af7597de66f847c3345dae735b58" + integrity sha512-rfWQR12eHn2cpstCFS4RF7oGjfbkZb0oqep+BfrT+gWEGWG2IowJvIsacPOvzyS1jhNF4MQ4BS59B04Mbovteg== + dependencies: + "@ethersproject/bignumber" "^5.1.0" + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/keccak256" "^5.1.0" + "@ethersproject/logger" "^5.1.0" + "@ethersproject/rlp" "^5.1.0" + "@ethersproject/base64@5.0.9", "@ethersproject/base64@^5.0.0", "@ethersproject/base64@^5.0.7": version "5.0.9" resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.0.9.tgz#bb1f35d3dba92082a574d5e2418f9202a0a1a7e6" @@ -228,6 +263,13 @@ dependencies: "@ethersproject/bytes" "^5.0.9" +"@ethersproject/base64@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.1.0.tgz#27240c174d0a4e13f6eae87416fd876caf7f42b6" + integrity sha512-npD1bLvK4Bcxz+m4EMkx+F8Rd7CnqS9DYnhNu0/GlQBXhWjvfoAZzk5HJ0f1qeyp8d+A86PTuzLOGOXf4/CN8g== + dependencies: + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/basex@5.0.9", "@ethersproject/basex@^5.0.7": version "5.0.9" resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.0.9.tgz#00d727a031bac563cb8bb900955206f1bf3cf1fc" @@ -236,6 +278,14 @@ "@ethersproject/bytes" "^5.0.9" "@ethersproject/properties" "^5.0.7" +"@ethersproject/basex@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.1.0.tgz#80da2e86f9da0cb5ccd446b337364d791f6a131c" + integrity sha512-vBKr39bum7DDbOvkr1Sj19bRMEPA4FnST6Utt6xhDzI7o7L6QNkDn2yrCfP+hnvJGhZFKtLygWwqlTBZoBXYLg== + dependencies: + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/properties" "^5.1.0" + "@ethersproject/bignumber@5.0.15", "@ethersproject/bignumber@>=5.0.0-beta.130", "@ethersproject/bignumber@^5.0.0", "@ethersproject/bignumber@^5.0.13": version "5.0.15" resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.0.15.tgz#b089b3f1e0381338d764ac1c10512f0c93b184ed" @@ -245,6 +295,15 @@ "@ethersproject/logger" "^5.0.8" bn.js "^4.4.0" +"@ethersproject/bignumber@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.1.0.tgz#966a013a5d871fc03fc67bf33cd8aadae627f0fd" + integrity sha512-wUvQlhTjPjFXIdLPOuTrFeQmSa6Wvls1bGXQNQWvB/SEn1NsTCE8PmumIEZxmOPjSHl1eV2uyHP5jBm5Cgj92Q== + dependencies: + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/logger" "^5.1.0" + bn.js "^4.4.0" + "@ethersproject/bytes@5.0.11", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.0", "@ethersproject/bytes@^5.0.9": version "5.0.11" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.0.11.tgz#21118e75b1d00db068984c15530e316021101276" @@ -252,6 +311,13 @@ dependencies: "@ethersproject/logger" "^5.0.8" +"@ethersproject/bytes@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.1.0.tgz#55dfa9c4c21df1b1b538be3accb50fb76d5facfd" + integrity sha512-sGTxb+LVjFxJcJeUswAIK6ncgOrh3D8c192iEJd7mLr95V6du119rRfYT/b87WPkZ5I3gRBUYIYXtdgCWACe8g== + dependencies: + "@ethersproject/logger" "^5.1.0" + "@ethersproject/constants@5.0.10", "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.0.0", "@ethersproject/constants@^5.0.8": version "5.0.10" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.0.10.tgz#eb0c604fbc44c53ba9641eed31a1d0c9e1ebcadc" @@ -259,6 +325,13 @@ dependencies: "@ethersproject/bignumber" "^5.0.13" +"@ethersproject/constants@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.1.0.tgz#4e7da6367ea0e9be87585d8b09f3fccf384b1452" + integrity sha512-0/SuHrxc8R8k+JiLmJymxHJbojUDWBQqO+b+XFdwaP0jGzqC09YDy/CAlSZB6qHsBifY8X3I89HcK/oMqxRdBw== + dependencies: + "@ethersproject/bignumber" "^5.1.0" + "@ethersproject/contracts@5.0.12", "@ethersproject/contracts@^5.0.0", "@ethersproject/contracts@^5.0.5": version "5.0.12" resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.0.12.tgz#6d488db46221258399dfe80b89bf849b3afd7897" @@ -300,6 +373,20 @@ "@ethersproject/properties" "^5.0.7" "@ethersproject/strings" "^5.0.8" +"@ethersproject/hash@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.1.0.tgz#40961d64837d57f580b7b055e0d74174876d891e" + integrity sha512-fNwry20yLLPpnRRwm3fBL+2ksgO+KMadxM44WJmRIoTKzy4269+rbq9KFoe2LTqq2CXJM2CE70beGaNrpuqflQ== + dependencies: + "@ethersproject/abstract-signer" "^5.1.0" + "@ethersproject/address" "^5.1.0" + "@ethersproject/bignumber" "^5.1.0" + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/keccak256" "^5.1.0" + "@ethersproject/logger" "^5.1.0" + "@ethersproject/properties" "^5.1.0" + "@ethersproject/strings" "^5.1.0" + "@ethersproject/hdnode@5.0.10", "@ethersproject/hdnode@^5.0.0", "@ethersproject/hdnode@^5.0.8": version "5.0.10" resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.0.10.tgz#f7cdf154bf5d104c76dce2940745fc71d9e7eb1b" @@ -345,11 +432,24 @@ "@ethersproject/bytes" "^5.0.9" js-sha3 "0.5.7" +"@ethersproject/keccak256@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.1.0.tgz#fdcd88fb13bfef4271b225cdd8dec4d315c8e60e" + integrity sha512-vrTB1W6AEYoadww5c9UyVJ2YcSiyIUTNDRccZIgwTmFFoSHwBtcvG1hqy9RzJ1T0bMdATbM9Hfx2mJ6H0i7Hig== + dependencies: + "@ethersproject/bytes" "^5.1.0" + js-sha3 "0.5.7" + "@ethersproject/logger@5.0.10", "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.0.0", "@ethersproject/logger@^5.0.8": version "5.0.10" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.0.10.tgz#fd884688b3143253e0356ef92d5f22d109d2e026" integrity sha512-0y2T2NqykDrbPM3Zw9RSbPkDOxwChAL8detXaom76CfYoGxsOnRP/zTX8OUAV+x9LdwzgbWvWmeXrc0M7SuDZw== +"@ethersproject/logger@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.1.0.tgz#4cdeeefac029373349d5818f39c31b82cc6d9bbf" + integrity sha512-wtUaD1lBX10HBXjjKV9VHCBnTdUaKQnQ2XSET1ezglqLdPdllNOIlLfhyCRqXm5xwcjExVI5ETokOYfjPtaAlw== + "@ethersproject/networks@5.0.9", "@ethersproject/networks@^5.0.0", "@ethersproject/networks@^5.0.7": version "5.0.9" resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.0.9.tgz#ec5da11e4d4bfd69bec4eaebc9ace33eb9569279" @@ -357,6 +457,13 @@ dependencies: "@ethersproject/logger" "^5.0.8" +"@ethersproject/networks@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.1.0.tgz#f537290cb05aa6dc5e81e910926c04cfd5814bca" + integrity sha512-A/NIrIED/G/IgU1XUukOA3WcFRxn2I4O5GxsYGA5nFlIi+UZWdGojs85I1VXkR1gX9eFnDXzjE6OtbgZHjFhIA== + dependencies: + "@ethersproject/logger" "^5.1.0" + "@ethersproject/pbkdf2@5.0.9", "@ethersproject/pbkdf2@^5.0.0", "@ethersproject/pbkdf2@^5.0.7": version "5.0.9" resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.0.9.tgz#be39c7f0a66c0d3cb1ad1dbb12a78e9bcdf9b5ae" @@ -372,6 +479,13 @@ dependencies: "@ethersproject/logger" "^5.0.8" +"@ethersproject/properties@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.1.0.tgz#9484bd6def16595fc6e4bdc26f29dff4d3f6ac42" + integrity sha512-519KKTwgmH42AQL3+GFV3SX6khYEfHsvI6v8HYejlkigSDuqttdgVygFTDsGlofNFchhDwuclrxQnD5B0YLNMg== + dependencies: + "@ethersproject/logger" "^5.1.0" + "@ethersproject/providers@5.0.24", "@ethersproject/providers@^5.0.0", "@ethersproject/providers@^5.0.14", "@ethersproject/providers@^5.0.21": version "5.0.24" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.0.24.tgz#4c638a029482d052faa18364b5e0e2d3ddd9c0cb" @@ -397,6 +511,31 @@ bech32 "1.1.4" ws "7.2.3" +"@ethersproject/providers@^5.0.24": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.1.0.tgz#27695a02cfafa370428cde1c7a4abab13afb6a35" + integrity sha512-FjpZL2lSXrYpQDg2fMjugZ0HjQD9a+2fOOoRhhihh+Z+qi/xZ8vIlPoumrEP1DzIG4DBV6liUqLNqnX2C6FIAA== + dependencies: + "@ethersproject/abstract-provider" "^5.1.0" + "@ethersproject/abstract-signer" "^5.1.0" + "@ethersproject/address" "^5.1.0" + "@ethersproject/basex" "^5.1.0" + "@ethersproject/bignumber" "^5.1.0" + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/constants" "^5.1.0" + "@ethersproject/hash" "^5.1.0" + "@ethersproject/logger" "^5.1.0" + "@ethersproject/networks" "^5.1.0" + "@ethersproject/properties" "^5.1.0" + "@ethersproject/random" "^5.1.0" + "@ethersproject/rlp" "^5.1.0" + "@ethersproject/sha2" "^5.1.0" + "@ethersproject/strings" "^5.1.0" + "@ethersproject/transactions" "^5.1.0" + "@ethersproject/web" "^5.1.0" + bech32 "1.1.4" + ws "7.2.3" + "@ethersproject/random@5.0.9", "@ethersproject/random@^5.0.0", "@ethersproject/random@^5.0.7": version "5.0.9" resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.0.9.tgz#1903d4436ba66e4c8ac77968b16f756abea3a0d0" @@ -405,6 +544,14 @@ "@ethersproject/bytes" "^5.0.9" "@ethersproject/logger" "^5.0.8" +"@ethersproject/random@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.1.0.tgz#0bdff2554df03ebc5f75689614f2d58ea0d9a71f" + integrity sha512-+uuczLQZ4+no9cP6TCoCktXx0u2YbNaRT7lRkSt12d8263e702f0u+4JnnRO8Qmv5nylWJebnqCHzyxP+6mLqw== + dependencies: + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/logger" "^5.1.0" + "@ethersproject/rlp@5.0.9", "@ethersproject/rlp@^5.0.0", "@ethersproject/rlp@^5.0.7": version "5.0.9" resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.0.9.tgz#da205bf8a34d3c3409eb73ddd237130a4b376aff" @@ -413,6 +560,14 @@ "@ethersproject/bytes" "^5.0.9" "@ethersproject/logger" "^5.0.8" +"@ethersproject/rlp@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.1.0.tgz#700f4f071c27fa298d3c1d637485fefe919dd084" + integrity sha512-vDTyHIwNPrecy55gKGZ47eJZhBm8LLBxihzi5ou+zrSvYTpkSTWRcKUlXFDFQVwfWB+P5PGyERAdiDEI76clxw== + dependencies: + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/logger" "^5.1.0" + "@ethersproject/sha2@5.0.9", "@ethersproject/sha2@^5.0.0", "@ethersproject/sha2@^5.0.7": version "5.0.9" resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.0.9.tgz#41275ee03e6e1660b3c997754005e089e936adc6" @@ -422,6 +577,15 @@ "@ethersproject/logger" "^5.0.8" hash.js "1.1.3" +"@ethersproject/sha2@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.1.0.tgz#6ca42d1a26884b3e32ffa943fe6494af7211506c" + integrity sha512-+fNSeZRstOpdRJpdGUkRONFCaiAqWkc91zXgg76Nlp5ndBQE25Kk5yK8gCPG1aGnCrbariiPr5j9DmrYH78JCA== + dependencies: + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/logger" "^5.1.0" + hash.js "1.1.3" + "@ethersproject/signing-key@5.0.11", "@ethersproject/signing-key@^5.0.0", "@ethersproject/signing-key@^5.0.8": version "5.0.11" resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.0.11.tgz#19fc5c4597e18ad0a5efc6417ba5b74069fdd2af" @@ -432,6 +596,17 @@ "@ethersproject/properties" "^5.0.7" elliptic "6.5.4" +"@ethersproject/signing-key@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.1.0.tgz#6eddfbddb6826b597b9650e01acf817bf8991b9c" + integrity sha512-tE5LFlbmdObG8bY04NpuwPWSRPgEswfxweAI1sH7TbP0ml1elNfqcq7ii/3AvIN05i5U0Pkm3Tf8bramt8MmLw== + dependencies: + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/logger" "^5.1.0" + "@ethersproject/properties" "^5.1.0" + bn.js "^4.4.0" + elliptic "6.5.4" + "@ethersproject/solidity@5.0.10", "@ethersproject/solidity@^5.0.0": version "5.0.10" resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.0.10.tgz#128c9289761cf83d81ff62a1195d6079a924a86c" @@ -452,6 +627,15 @@ "@ethersproject/constants" "^5.0.8" "@ethersproject/logger" "^5.0.8" +"@ethersproject/strings@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.1.0.tgz#0f95a56c3c8c9d5510a06c241d818779750e2da5" + integrity sha512-perBZy0RrmmL0ejiFGUOlBVjMsUceqLut3OBP3zP96LhiJWWbS8u1NqQVgN4/Gyrbziuda66DxiQocXhsvx+Sw== + dependencies: + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/constants" "^5.1.0" + "@ethersproject/logger" "^5.1.0" + "@ethersproject/transactions@5.0.11", "@ethersproject/transactions@^5.0.0", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.0.9": version "5.0.11" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.0.11.tgz#b31df5292f47937136a45885d6ee6112477c13df" @@ -467,6 +651,21 @@ "@ethersproject/rlp" "^5.0.7" "@ethersproject/signing-key" "^5.0.8" +"@ethersproject/transactions@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.1.0.tgz#da7fcd7e77e23dcfcca317a945f60bc228c61b36" + integrity sha512-s10crRLZEA0Bgv6FGEl/AKkTw9f+RVUrlWDX1rHnD4ZncPFeiV2AJr4nT7QSUhxJdFPvjyKRDb3nEH27dIqcPQ== + dependencies: + "@ethersproject/address" "^5.1.0" + "@ethersproject/bignumber" "^5.1.0" + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/constants" "^5.1.0" + "@ethersproject/keccak256" "^5.1.0" + "@ethersproject/logger" "^5.1.0" + "@ethersproject/properties" "^5.1.0" + "@ethersproject/rlp" "^5.1.0" + "@ethersproject/signing-key" "^5.1.0" + "@ethersproject/units@5.0.11", "@ethersproject/units@^5.0.0": version "5.0.11" resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.0.11.tgz#f82f6e353ac0d6fa43b17337790f1f9aa72cb4c8" @@ -508,6 +707,17 @@ "@ethersproject/properties" "^5.0.7" "@ethersproject/strings" "^5.0.8" +"@ethersproject/web@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.1.0.tgz#ed56bbe4e3d9a8ffe3b2ed882da5c62d3551381b" + integrity sha512-LTeluWgTq04+RNqAkVhpydPcRZK/kKxD2Vy7PYGrAD27ABO9kTqTBKwiOuzTyAHKUQHfnvZbXmxBXJAGViSDcA== + dependencies: + "@ethersproject/base64" "^5.1.0" + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/logger" "^5.1.0" + "@ethersproject/properties" "^5.1.0" + "@ethersproject/strings" "^5.1.0" + "@ethersproject/wordlists@5.0.10", "@ethersproject/wordlists@^5.0.0", "@ethersproject/wordlists@^5.0.8": version "5.0.10" resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.0.10.tgz#177b9a0b4d72b9c4f304d08b36612d6c60e9b896" @@ -3409,7 +3619,7 @@ chai-as-promised@^7.1.1: dependencies: check-error "^1.0.2" -chai@^4.2.0, chai@^4.3.0, chai@^4.3.1, chai@^4.3.4: +chai@^4.2.0, chai@^4.3.0, chai@^4.3.1, chai@^4.3.3, chai@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49" integrity sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA== @@ -7364,7 +7574,7 @@ lodash@4.17.20: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== -lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.7.0: +lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -7994,7 +8204,7 @@ mocha@^7.1.2: yargs-parser "13.1.2" yargs-unparser "1.6.0" -mocha@^8.3.0, mocha@^8.3.2: +mocha@^8.3.0, mocha@^8.3.1, mocha@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.3.2.tgz#53406f195fa86fbdebe71f8b1c6fb23221d69fcc" integrity sha512-UdmISwr/5w+uXLPKspgoV7/RXZwKRTiTjJ2/AC5ZiEztIoOYdfKb19+9jNmEInzx5pBsCyJQzarAxqIGBNYJhg==