Commit 550a2f69 authored by smartcontracts's avatar smartcontracts Committed by GitHub

pkg: Add batch submitter (#12)

* pkg: Add batch submitter

* chore(batch-submitter): fix configs

* chore(batch-submitter): use latest smock

* fix: use latest typescript

* chore: disable prettier in incompat lines

Long term fixed by switching to eslint

* perf: only test packages that have changed since master

https://github.com/lerna/lerna/tree/main/core/filter-options\#--since-refCo-authored-by: default avatarGeorgios Konstantopoulos <me@gakonst.com>
parent aa8baefd
......@@ -20,6 +20,9 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Fetch history
run: git fetch
- name: Setup node ${{ matrix.node }}
uses: actions/setup-node@v1
with:
......
......@@ -10,10 +10,10 @@
"lerna": "^4.0.0"
},
"scripts": {
"clean": "yarn workspaces run clean",
"build": "yarn workspaces run build",
"test": "yarn lerna run test --parallel",
"lint": "yarn lerna run lint --parallel",
"lint:fix": "yarn workspaces run lint:fix"
"clean": "yarn lerna run clean",
"build": "yarn lerna run build",
"test": "yarn lerna run test --parallel --since origin/master",
"lint": "yarn lerna run lint --parallel --since origin/master",
"lint:fix": "yarn lerna run lint:fix"
}
}
# Logging
DEBUG=info*,error*,warn*,debug*
L1_NODE_WEB3_URL=http://localhost:9545
L2_NODE_WEB3_URL=http://localhost:8545
MAX_TX_SIZE=90000
MIN_TX_SIZE=0
MAX_BATCH_SIZE=50
MAX_BATCH_SUBMISSION_TIME=0
POLL_INTERVAL=15000
NUM_CONFIRMATIONS=0
RESUBMISSION_TIMEOUT=300 # in seconds
FINALITY_CONFIRMATIONS=0
RUN_TX_BATCH_SUBMITTER=true
RUN_STATE_BATCH_SUBMITTER=true
SAFE_MINIMUM_ETHER_BALANCE=0
CLEAR_PENDING_TXS=false
ADDRESS_MANAGER_ADDRESS=
# Optional gas settings
MAX_GAS_PRICE_IN_GWEI=200
GAS_RETRY_INCREMENT=5
GAS_THRESHOLD_IN_GWEI=100
SEQUENCER_PRIVATE_KEY=0xd2ab07f7c10ac88d5f86f1b4c1035d5195e81f27dbe62ad65e59cbf88205629b
build
dist
node_modules
.env
cache/*
# vim
*.swp
# Changelog
## v0.1.3
- Add tx resubmission logic
- Log when the batch submitter runs low on ETH
## v0.1.2
Adds mnemonic config parsing
## v0.1.1
Final fixes before minnet release.
- Add batch submission timeout
- Log sequencer address
- remove ssh
## v0.1.0
The inital release
# Batch Submitter
Contains an executable batch submitter service which watches L1 and a local L2 node and submits batches to the
`CanonicalTransactionChain` & `StateCommitmentChain` based on its local information.
## Configuration
All configuration is done via environment variables. See all variables at [.env.example](.env.example); copy into a `.env` file before running.
## Building & Running
1. Make sure dependencies are installed just run `yarn` in the base directory
2. Build `yarn build`
3. Run `yarn start`
## Controlling log output verbosity
Before running, set the `DEBUG` environment variable to specify the verbosity level. It must be made up of comma-separated values of patterns to match in debug logs. Here's a few common options:
* `debug*` - Will match all debug statements -- very verbose
* `info*` - Will match all info statements -- less verbose, useful in most cases
* `warn*` - Will match all warnings -- recommended at a minimum
* `error*` - Will match all errors -- would not omit this
Examples:
* Everything but debug: `export DEBUG=info*,error*,warn*`
* Most verbose: `export DEBUG=info*,error*,warn*,debug*`
## Testing & linting
### Local
- Run unit tests with `yarn test`
- See lint errors with `yarn lint`; auto-fix with `yarn lint --fix`
### Submission
You may test a submission locally against a local Hardhat fork.
1. Follow the instructions [here](https://github.com/ethereum-optimism/hardhat) to run a Hardhat node.
2. Change the Batch Submitter `.env` field `L1_NODE_WEB3_URL` to the local Hardhat url. Depending on which network you are using, update `ADDRESS_MANAGER_ADDRESS` according to the [Regenesis repo](https://github.com/ethereum-optimism/regenesis).
3. Also check `L2_NODE_WEB3_URL` is correctly set and has transactions to submit.
3. Run `yarn build` to build your changes.
4. Start Batch Submitter with `yarn start`. It will automatically start submitting pending transactions from L2.
#!/usr/bin/env node
const batchSubmitter = require("../build/src/exec/run-batch-submitter")
batchSubmitter.run()
\ No newline at end of file
import '@nomiclabs/hardhat-waffle'
import { HardhatUserConfig } from 'hardhat/config'
import {
DEFAULT_ACCOUNTS_HARDHAT,
RUN_OVM_TEST_GAS,
} from './test/helpers/constants'
const config: HardhatUserConfig = {
networks: {
hardhat: {
accounts: DEFAULT_ACCOUNTS_HARDHAT,
blockGasLimit: RUN_OVM_TEST_GAS * 2,
},
},
mocha: {
timeout: 50000,
},
solidity: {
version: '0.7.0',
settings: {
optimizer: {
enabled: true,
runs: 200,
},
outputSelection: {
'*': {
'*': ['storageLayout'],
},
},
},
},
}
export default config
const rootPath = __dirname
export { rootPath }
{
"name": "@eth-optimism/batch-submitter",
"version": "0.1.5",
"description": "[Optimism] Batch submission for sequencer & aggregators",
"main": "dist/index",
"types": "dist/index",
"files": [
"dist/index"
],
"scripts": {
"start": "node ./exec/run-batch-submitter.js",
"build": "tsc -p ./tsconfig.build.json",
"clean": "rimraf dist/ ./tsconfig.build.tsbuildinfo",
"lint": "yarn lint:fix && yarn lint:check",
"lint:check": "tslint --format stylish --project .",
"lint:fix": "prettier --config prettier-config.json --write \"hardhat.config.ts\" \"{src,exec,test}/**/*.ts\"",
"test": "hardhat test --show-stack-traces"
},
"keywords": [
"optimism",
"ethereum",
"sequencer",
"aggregator"
],
"homepage": "https://github.com/ethereum-optimism/optimism-monorepo/tree/master/packages/batch-submitter#readme",
"license": "MIT",
"author": "Optimism",
"repository": {
"type": "git",
"url": "https://github.com/ethereum-optimism/optimism-monorepo.git"
},
"dependencies": {
"@eth-optimism/contracts": "^0.0.2-alpha.7",
"@eth-optimism/core-utils": "^0.1.10",
"@eth-optimism/provider": "^0.0.1-alpha.13",
"@eth-optimism/ynatm": "^0.2.2",
"@ethersproject/abstract-provider": "^5.0.5",
"@ethersproject/providers": "^5.0.14",
"bluebird": "^3.7.2",
"dotenv": "^8.2.0",
"ethers": "5.0.0",
"new-contracts": "npm:@eth-optimism/contracts@0.1.4"
},
"devDependencies": {
"@eth-optimism/smock": "1.0.0-alpha.3",
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@types/chai": "^4.1.7",
"@types/chai-as-promised": "^7.1.0",
"@types/mocha": "^5.2.6",
"@types/node": "^11.11.3",
"@types/sinon": "^9.0.10",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"ethereum-waffle": "3.0.0",
"ganache-core": "^2.13.2",
"hardhat": "^2.1.1",
"mocha": "^6.1.4",
"prettier": "^1.16.4",
"rimraf": "^2.6.3",
"sinon": "^9.2.4",
"sinon-chai": "^3.5.0",
"ts-node": "^8.2.0",
"typescript": "^4.2.3"
},
"resolutions": {
"ganache-core": "^2.13.2"
},
"publishConfig": {
"access": "public"
}
}
../../prettier-config.json
\ No newline at end of file
/* External Imports */
import { Contract, Signer, utils } from 'ethers'
import { TransactionReceipt } from '@ethersproject/abstract-provider'
import * as ynatm from '@eth-optimism/ynatm'
import { Address, Bytes32, Logger } from '@eth-optimism/core-utils'
import { OptimismProvider } from '@eth-optimism/provider'
import { getContractFactory } from '@eth-optimism/contracts'
export interface RollupInfo {
mode: 'sequencer' | 'verifier'
syncing: boolean
ethContext: {
blockNumber: number
timestamp: number
}
rollupContext: {
index: number
queueIndex: number
}
}
export interface Range {
start: number
end: number
}
export interface ResubmissionConfig {
resubmissionTimeout: number
minGasPriceInGwei: number
maxGasPriceInGwei: number
gasRetryIncrement: number
}
export abstract class BatchSubmitter {
protected rollupInfo: RollupInfo
protected chainContract: Contract
protected l2ChainId: number
protected syncing: boolean
protected lastBatchSubmissionTimestamp: number = 0
constructor(
readonly signer: Signer,
readonly l2Provider: OptimismProvider,
readonly minTxSize: number,
readonly maxTxSize: number,
readonly maxBatchSize: number,
readonly maxBatchSubmissionTime: number,
readonly numConfirmations: number,
readonly resubmissionTimeout: number,
readonly finalityConfirmations: number,
readonly addressManagerAddress: string,
readonly minBalanceEther: number,
readonly minGasPriceInGwei: number,
readonly maxGasPriceInGwei: number,
readonly gasRetryIncrement: number,
readonly gasThresholdInGwei: number,
readonly log: Logger
) {}
public abstract _submitBatch(
startBlock: number,
endBlock: number
): Promise<TransactionReceipt>
public abstract _onSync(): Promise<TransactionReceipt>
public abstract _getBatchStartAndEnd(): Promise<Range>
public abstract _updateChainInfo(): Promise<void>
public async submitNextBatch(): Promise<TransactionReceipt> {
if (typeof this.l2ChainId === 'undefined') {
this.l2ChainId = await this._getL2ChainId()
}
await this._updateChainInfo()
await this._checkBalance()
if (this.syncing === true) {
this.log.info(
'Syncing mode enabled! Skipping batch submission and clearing queue...'
)
return this._onSync()
}
const range = await this._getBatchStartAndEnd()
if (!range) {
return
}
return this._submitBatch(range.start, range.end)
}
protected async _checkBalance(): Promise<void> {
const address = await this.signer.getAddress()
const balance = await this.signer.getBalance()
const ether = utils.formatEther(balance)
const num = parseFloat(ether)
this.log.info('Checked balance', {
address,
ether,
})
if (num < this.minBalanceEther) {
this.log.warn('Current balance lower than min safe balance', {
current: num,
safeBalance: this.minBalanceEther,
})
}
}
protected async _getRollupInfo(): Promise<RollupInfo> {
return this.l2Provider.send('rollup_getInfo', [])
}
protected async _getL2ChainId(): Promise<number> {
return this.l2Provider.send('eth_chainId', [])
}
protected async _getChainAddresses(): Promise<{
ctcAddress: string
sccAddress: string
}> {
const addressManager = (
await getContractFactory('Lib_AddressManager', this.signer)
).attach(this.addressManagerAddress)
const sccAddress = await addressManager.getAddress(
'OVM_StateCommitmentChain'
)
const ctcAddress = await addressManager.getAddress(
'OVM_CanonicalTransactionChain'
)
return {
ctcAddress,
sccAddress,
}
}
protected _shouldSubmitBatch(batchSizeInBytes: number): boolean {
const isTimeoutReached =
this.lastBatchSubmissionTimestamp + this.maxBatchSubmissionTime <=
Date.now()
if (batchSizeInBytes < this.minTxSize) {
if (!isTimeoutReached) {
this.log.info(
'Skipping batch submission. Batch too small & max submission timeout not reached.',
{
batchSizeInBytes,
minTxSize: this.minTxSize,
}
)
return false
}
this.log.info('Timeout reached.')
}
return true
}
public static async getReceiptWithResubmission(
txFunc: (gasPrice) => Promise<TransactionReceipt>,
resubmissionConfig: ResubmissionConfig,
log: Logger
): Promise<TransactionReceipt> {
const {
resubmissionTimeout,
minGasPriceInGwei,
maxGasPriceInGwei,
gasRetryIncrement,
} = resubmissionConfig
const receipt = await ynatm.send({
sendTransactionFunction: txFunc,
minGasPrice: ynatm.toGwei(minGasPriceInGwei),
maxGasPrice: ynatm.toGwei(maxGasPriceInGwei),
gasPriceScalingFunction: ynatm.LINEAR(gasRetryIncrement),
delay: resubmissionTimeout,
})
log.debug('Resubmission tx receipt', { receipt })
return receipt
}
private async _getMinGasPriceInGwei(): Promise<number> {
if (this.minGasPriceInGwei !== 0) {
return this.minGasPriceInGwei
}
let minGasPriceInGwei = parseInt(
utils.formatUnits(await this.signer.getGasPrice(), 'gwei'),
10
)
if (minGasPriceInGwei > this.maxGasPriceInGwei) {
this.log.warn(
'Minimum gas price is higher than max! Ethereum must be congested...'
)
minGasPriceInGwei = this.maxGasPriceInGwei
}
return minGasPriceInGwei
}
protected async _submitAndLogTx(
txFunc: (gasPrice) => Promise<TransactionReceipt>,
successMessage: string
): Promise<TransactionReceipt> {
this.lastBatchSubmissionTimestamp = Date.now()
this.log.debug('Waiting for receipt...')
const resubmissionConfig: ResubmissionConfig = {
resubmissionTimeout: this.resubmissionTimeout,
minGasPriceInGwei: await this._getMinGasPriceInGwei(),
maxGasPriceInGwei: this.maxGasPriceInGwei,
gasRetryIncrement: this.gasRetryIncrement,
}
const receipt = await BatchSubmitter.getReceiptWithResubmission(
txFunc,
resubmissionConfig,
this.log
)
this.log.debug('Transaction receipt:', { receipt })
this.log.info(successMessage)
return receipt
}
}
export * from './batch-submitter'
export * from './tx-batch-submitter'
export * from './state-batch-submitter'
export const TX_BATCH_SUBMITTER_LOG_TAG = 'oe:batch-submitter:tx-chain'
export const STATE_BATCH_SUBMITTER_LOG_TAG = 'oe:batch-submitter:state-chain'
// BLOCK_OFFSET is the number of L2 blocks we need to skip for the
// batch submitter.
export const BLOCK_OFFSET = 1 // TODO: Update testnet / mainnet to make this zero.
/* External Imports */
import { Contract, Signer } from 'ethers'
import { TransactionReceipt } from '@ethersproject/abstract-provider'
import { getContractFactory } from '@eth-optimism/contracts'
import { Logger, Bytes32 } from '@eth-optimism/core-utils'
import { OptimismProvider } from '@eth-optimism/provider'
/* Internal Imports */
import { L2Block } from '..'
import { RollupInfo, Range, BatchSubmitter, BLOCK_OFFSET } from '.'
export class StateBatchSubmitter extends BatchSubmitter {
// TODO: Change this so that we calculate start = scc.totalElements() and end = ctc.totalElements()!
// Not based on the length of the L2 chain -- that is only used in the batch submitter
// Note this means we've got to change the state / end calc logic
protected l2ChainId: number
protected syncing: boolean
protected ctcContract: Contract
private fraudSubmissionAddress: string
constructor(
signer: Signer,
l2Provider: OptimismProvider,
minTxSize: number,
maxTxSize: number,
maxBatchSize: number,
maxBatchSubmissionTime: number,
numConfirmations: number,
resubmissionTimeout: number,
finalityConfirmations: number,
addressManagerAddress: string,
minBalanceEther: number,
minGasPriceInGwei: number,
maxGasPriceInGwei: number,
gasRetryIncrement: number,
gasThresholdInGwei: number,
log: Logger,
fraudSubmissionAddress: string
) {
super(
signer,
l2Provider,
minTxSize,
maxTxSize,
maxBatchSize,
maxBatchSubmissionTime,
numConfirmations,
resubmissionTimeout,
finalityConfirmations,
addressManagerAddress,
minBalanceEther,
minGasPriceInGwei,
maxGasPriceInGwei,
gasRetryIncrement,
gasThresholdInGwei,
log
)
this.fraudSubmissionAddress = fraudSubmissionAddress
}
/*****************************
* Batch Submitter Overrides *
****************************/
public async _updateChainInfo(): Promise<void> {
const info: RollupInfo = await this._getRollupInfo()
if (info.mode === 'verifier') {
this.log.error(
'Verifier mode enabled! Batch submitter only compatible with sequencer mode'
)
process.exit(1)
}
this.syncing = info.syncing
const addrs = await this._getChainAddresses()
const sccAddress = addrs.sccAddress
const ctcAddress = addrs.ctcAddress
if (
typeof this.chainContract !== 'undefined' &&
sccAddress === this.chainContract.address &&
ctcAddress === this.ctcContract.address
) {
return
}
this.chainContract = (
await getContractFactory('OVM_StateCommitmentChain', this.signer)
).attach(sccAddress)
this.ctcContract = (
await getContractFactory('OVM_CanonicalTransactionChain', this.signer)
).attach(ctcAddress)
this.log.info('Connected Optimism contracts', {
stateCommitmentChain: this.chainContract.address,
canonicalTransactionChain: this.ctcContract.address,
})
return
}
public async _onSync(): Promise<TransactionReceipt> {
this.log.info('Syncing mode enabled! Skipping state batch submission...')
return
}
public async _getBatchStartAndEnd(): Promise<Range> {
// TODO: Remove BLOCK_OFFSET by adding a tx to Geth's genesis
const startBlock: number =
(await this.chainContract.getTotalElements()).toNumber() + BLOCK_OFFSET
// We will submit state roots for txs which have been in the tx chain for a while.
const callBlockNumber: number =
(await this.signer.provider.getBlockNumber()) - this.finalityConfirmations
const totalElements: number =
(await this.ctcContract.getTotalElements()).toNumber() + BLOCK_OFFSET
const endBlock: number = Math.min(
startBlock + this.maxBatchSize,
totalElements
)
if (startBlock >= endBlock) {
if (startBlock > endBlock) {
this.log.error(
'State commitment chain is larger than transaction chain. This should never happen!'
)
}
this.log.info(
'No state commitments to submit. Skipping batch submission...'
)
return
}
return {
start: startBlock,
end: endBlock,
}
}
public async _submitBatch(
startBlock: number,
endBlock: number
): Promise<TransactionReceipt> {
const batch = await this._generateStateCommitmentBatch(startBlock, endBlock)
const tx = this.chainContract.interface.encodeFunctionData(
'appendStateBatch',
[batch, startBlock]
)
if (!this._shouldSubmitBatch(tx.length * 2)) {
return
}
const offsetStartsAtIndex = startBlock - BLOCK_OFFSET // TODO: Remove BLOCK_OFFSET by adding a tx to Geth's genesis
this.log.debug('Submitting batch.', { tx })
const nonce = await this.signer.getTransactionCount()
const contractFunction = async (gasPrice): Promise<TransactionReceipt> => {
const contractTx = await this.chainContract.appendStateBatch(
batch,
offsetStartsAtIndex,
{ nonce, gasPrice }
)
return this.signer.provider.waitForTransaction(
contractTx.hash,
this.numConfirmations
)
}
return this._submitAndLogTx(contractFunction, 'Submitted state root batch!')
}
/*********************
* Private Functions *
********************/
private async _generateStateCommitmentBatch(
startBlock: number,
endBlock: number
): Promise<Bytes32[]> {
const batch: Bytes32[] = []
for (let i = startBlock; i < endBlock; i++) {
const block = (await this.l2Provider.getBlockWithTransactions(
i
)) as L2Block
if (block.transactions[0].from === this.fraudSubmissionAddress) {
batch.push(
'0xbad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1'
)
this.fraudSubmissionAddress = 'no fraud'
} else {
batch.push(block.stateRoot)
}
}
let tx = this.chainContract.interface.encodeFunctionData(
'appendStateBatch',
[batch, startBlock]
)
while (tx.length > this.maxTxSize) {
batch.splice(Math.ceil((batch.length * 2) / 3)) // Delete 1/3rd of all of the batch elements
tx = this.chainContract.interface.encodeFunctionData('appendStateBatch', [
batch,
startBlock,
])
}
return batch
}
}
/* External Imports */
import { Promise as bPromise } from 'bluebird'
import { BigNumber, Signer, ethers, Wallet, Contract } from 'ethers'
import {
TransactionResponse,
TransactionReceipt,
} from '@ethersproject/abstract-provider'
import {
getContractInterface,
getContractFactory,
} from '@eth-optimism/contracts'
import { getContractInterface as getNewContractInterface } from 'new-contracts'
import { OptimismProvider } from '@eth-optimism/provider'
import {
Logger,
EIP155TxData,
TxType,
ctcCoder,
EthSignTxData,
txTypePlainText,
} from '@eth-optimism/core-utils'
/* Internal Imports */
import {
CanonicalTransactionChainContract,
encodeAppendSequencerBatch,
BatchContext,
AppendSequencerBatchParams,
} from '../transaction-chain-contract'
import {
L2Block,
BatchElement,
Batch,
QueueOrigin,
queueOriginPlainText,
} from '..'
import { RollupInfo, Range, BatchSubmitter, BLOCK_OFFSET } from '.'
export interface AutoFixBatchOptions {
fixDoublePlayedDeposits: boolean
fixMonotonicity: boolean
}
export class TransactionBatchSubmitter extends BatchSubmitter {
protected chainContract: CanonicalTransactionChainContract
protected l2ChainId: number
protected syncing: boolean
protected lastL1BlockNumber: number
private disableQueueBatchAppend: boolean
private autoFixBatchOptions: AutoFixBatchOptions
constructor(
signer: Signer,
l2Provider: OptimismProvider,
minTxSize: number,
maxTxSize: number,
maxBatchSize: number,
maxBatchSubmissionTime: number,
numConfirmations: number,
resubmissionTimeout: number,
addressManagerAddress: string,
minBalanceEther: number,
minGasPriceInGwei: number,
maxGasPriceInGwei: number,
gasRetryIncrement: number,
gasThresholdInGwei: number,
log: Logger,
disableQueueBatchAppend: boolean,
autoFixBatchOptions: AutoFixBatchOptions = {
fixDoublePlayedDeposits: false,
fixMonotonicity: false,
} // TODO: Remove this
) {
super(
signer,
l2Provider,
minTxSize,
maxTxSize,
maxBatchSize,
maxBatchSubmissionTime,
numConfirmations,
resubmissionTimeout,
0, // Supply dummy value because it is not used.
addressManagerAddress,
minBalanceEther,
minGasPriceInGwei,
maxGasPriceInGwei,
gasRetryIncrement,
gasThresholdInGwei,
log
)
this.disableQueueBatchAppend = disableQueueBatchAppend
this.autoFixBatchOptions = autoFixBatchOptions
}
/*****************************
* Batch Submitter Overrides *
****************************/
public async _updateChainInfo(): Promise<void> {
const info: RollupInfo = await this._getRollupInfo()
if (info.mode === 'verifier') {
this.log.error(
'Verifier mode enabled! Batch submitter only compatible with sequencer mode'
)
process.exit(1)
}
this.syncing = info.syncing
const addrs = await this._getChainAddresses()
const ctcAddress = addrs.ctcAddress
if (
typeof this.chainContract !== 'undefined' &&
ctcAddress === this.chainContract.address
) {
return
}
const unwrapped_OVM_CanonicalTransactionChain = (
await getContractFactory('OVM_CanonicalTransactionChain', this.signer)
).attach(ctcAddress)
this.chainContract = new CanonicalTransactionChainContract(
unwrapped_OVM_CanonicalTransactionChain.address,
getContractInterface('OVM_CanonicalTransactionChain'),
this.signer
)
this.log.info('Initialized new CTC', {
address: this.chainContract.address,
})
return
}
public async _onSync(): Promise<TransactionReceipt> {
const pendingQueueElements = await this.chainContract.getNumPendingQueueElements()
if (pendingQueueElements !== 0) {
this.log.info(
'Syncing mode enabled! Skipping batch submission and clearing queue elements',
{ pendingQueueElements }
)
if (!this.disableQueueBatchAppend) {
const nonce = await this.signer.getTransactionCount()
const contractFunction = async (
gasPrice
): Promise<TransactionReceipt> => {
const tx = await this.chainContract.appendQueueBatch(99999999, {
nonce,
gasPrice,
})
return this.signer.provider.waitForTransaction(
tx.hash,
this.numConfirmations
)
}
// Empty the queue with a huge `appendQueueBatch(..)` call
return this._submitAndLogTx(contractFunction, 'Cleared queue!')
}
}
this.log.info('Syncing mode enabled but queue is empty. Skipping...')
return
}
// TODO: Remove this function and use geth for lastL1BlockNumber!
private async _updateLastL1BlockNumber() {
const pendingQueueElements = await this.chainContract.getNumPendingQueueElements()
if (pendingQueueElements !== 0) {
const nextQueueIndex = await this.chainContract.getNextQueueIndex()
const queueElement = await this.chainContract.getQueueElement(
nextQueueIndex
)
this.lastL1BlockNumber = queueElement[2] // The block number is the 3rd element returned in the array....
} else {
const curBlockNum = await this.chainContract.provider.getBlockNumber()
if (!this.lastL1BlockNumber) {
// Set the block number to the current l1BlockNumber
this.lastL1BlockNumber = curBlockNum
} else {
if (curBlockNum - this.lastL1BlockNumber > 30) {
// If the lastL1BlockNumber is too old, then set it to a recent
// block number. (10 blocks ago to prevent reorgs)
this.lastL1BlockNumber = curBlockNum - 10
}
}
}
}
public async _getBatchStartAndEnd(): Promise<Range> {
// TODO: Remove BLOCK_OFFSET by adding a tx to Geth's genesis
const startBlock =
(await this.chainContract.getTotalElements()).toNumber() + BLOCK_OFFSET
const endBlock =
Math.min(
startBlock + this.maxBatchSize,
await this.l2Provider.getBlockNumber()
) + 1 // +1 because the `endBlock` is *exclusive*
if (startBlock >= endBlock) {
if (startBlock > endBlock) {
this.log
.error(`More chain elements in L1 (${startBlock}) than in the L2 node (${endBlock}).
This shouldn't happen because we don't submit batches if the sequencer is syncing.`)
}
this.log.info('No txs to submit. Skipping batch submission...')
return
}
return {
start: startBlock,
end: endBlock,
}
}
public async _submitBatch(
startBlock: number,
endBlock: number
): Promise<TransactionReceipt> {
// Do not submit batch if gas price above threshold
const gasPriceInGwei = parseInt(
ethers.utils.formatUnits(await this.signer.getGasPrice(), 'gwei'),
10
)
if (gasPriceInGwei > this.gasThresholdInGwei) {
this.log.warn(
'Gas price is higher than gras price threshold; aborting batch submission',
{
gasPriceInGwei,
gasThresholdInGwei: this.gasThresholdInGwei,
}
)
return
}
const [
batchParams,
wasBatchTruncated,
] = await this._generateSequencerBatchParams(startBlock, endBlock)
const batchSizeInBytes = encodeAppendSequencerBatch(batchParams).length * 2
if (!wasBatchTruncated && !this._shouldSubmitBatch(batchSizeInBytes)) {
return
}
this.log.debug('Submitting batch.', {
calldata: batchParams,
})
const nonce = await this.signer.getTransactionCount()
const contractFunction = async (gasPrice): Promise<TransactionReceipt> => {
const tx = await this.chainContract.appendSequencerBatch(batchParams, {
nonce,
gasPrice,
})
return this.signer.provider.waitForTransaction(
tx.hash,
this.numConfirmations
)
}
return this._submitAndLogTx(contractFunction, 'Submitted batch!')
}
/*********************
* Private Functions *
********************/
private async _generateSequencerBatchParams(
startBlock: number,
endBlock: number
): Promise<[AppendSequencerBatchParams, boolean]> {
// Get all L2 BatchElements for the given range
// For now we need to update our internal `lastL1BlockNumber` value
// which is used when submitting batches.
this._updateLastL1BlockNumber() // TODO: Remove this
const blockRange = endBlock - startBlock
let batch: Batch = await bPromise.map(
[...Array(blockRange).keys()],
(i) => {
this.log.debug('Fetching L2BatchElement', { blockNo: startBlock + i })
return this._getL2BatchElement(startBlock + i)
},
{ concurrency: 50 }
)
// Fix our batches if we are configured to. TODO: Remove this.
batch = await this._fixBatch(batch)
if (!(await this._validateBatch(batch))) {
this.log.error('Batch is malformed! Cannot submit next batch!')
throw new Error('Batch is malformed! Cannot submit next batch!')
}
let sequencerBatchParams = await this._getSequencerBatchParams(
startBlock,
batch
)
let wasBatchTruncated = false
let encoded = encodeAppendSequencerBatch(sequencerBatchParams)
while (encoded.length / 2 > this.maxTxSize) {
batch.splice(Math.ceil((batch.length * 2) / 3)) // Delete 1/3rd of all of the batch elements
sequencerBatchParams = await this._getSequencerBatchParams(
startBlock,
batch
)
encoded = encodeAppendSequencerBatch(sequencerBatchParams)
// This is to prevent against the case where a batch is oversized,
// but then gets truncated to the point where it is under the minimum size.
// In this case, we want to submit regardless of the batch's size.
wasBatchTruncated = true
}
return [sequencerBatchParams, wasBatchTruncated]
}
/**
* Returns true if the batch is valid.
*/
protected async _validateBatch(batch: Batch): Promise<boolean> {
// Verify all of the queue elements are what we expect
let nextQueueIndex = await this.chainContract.getNextQueueIndex()
for (const ele of batch) {
this.log.debug('Verifying batch element', { ele })
if (!ele.isSequencerTx) {
this.log.debug('Checking queue equality against L1 queue index', {
nextQueueIndex,
})
if (!(await this._doesQueueElementMatchL1(nextQueueIndex, ele))) {
return false
}
nextQueueIndex++
}
}
// Verify all of the batch elements are monotonic
let lastTimestamp: number
let lastBlockNumber: number
for (const ele of batch) {
if (ele.timestamp < lastTimestamp) {
this.log.error('Timestamp monotonicity violated! Element', { ele })
return false
}
if (ele.blockNumber < lastBlockNumber) {
this.log.error('Block Number monotonicity violated! Element', { ele })
return false
}
lastTimestamp = ele.timestamp
lastBlockNumber = ele.blockNumber
}
return true
}
private async _doesQueueElementMatchL1(
queueIndex: number,
queueElement: BatchElement
): Promise<boolean> {
const logEqualityError = (name, index, expected, got) => {
this.log.error('Observed mismatched values', {
index,
expected,
got,
})
}
let isEqual = true
const [
queueEleHash,
timestamp,
blockNumber,
] = await this.chainContract.getQueueElement(queueIndex)
// TODO: Verify queue element hash equality. The queue element hash can be computed with:
// keccak256( abi.encode( msg.sender, _target, _gasLimit, _data))
// Check timestamp & blockNumber equality
if (timestamp !== queueElement.timestamp) {
isEqual = false
logEqualityError(
'Timestamp',
queueIndex,
timestamp,
queueElement.timestamp
)
}
if (blockNumber !== queueElement.blockNumber) {
isEqual = false
logEqualityError(
'Block Number',
queueIndex,
blockNumber,
queueElement.blockNumber
)
}
return isEqual
}
/**
* Takes in a batch which is potentially malformed & returns corrected version.
* Current fixes that are supported:
* - Double played deposits.
*/
private async _fixBatch(batch: Batch): Promise<Batch> {
const fixDoublePlayedDeposits = async (b: Batch): Promise<Batch> => {
let nextQueueIndex = await this.chainContract.getNextQueueIndex()
const fixedBatch: Batch = []
for (const ele of b) {
if (!ele.isSequencerTx) {
if (!(await this._doesQueueElementMatchL1(nextQueueIndex, ele))) {
this.log.warn('Fixing double played queue element.', {
nextQueueIndex,
})
fixedBatch.push(await this._fixQueueElement(nextQueueIndex, ele))
continue
}
nextQueueIndex++
}
fixedBatch.push(ele)
}
return fixedBatch
}
// TODO: Remove this super complex logic and rely on Geth to actually supply correct block data.
const fixMonotonicity = async (b: Batch): Promise<Batch> => {
// The earliest allowed timestamp/blockNumber is the last timestamp submitted on chain.
const {
lastTimestamp,
lastBlockNumber,
} = await this._getLastTimestampAndBlockNumber()
let earliestTimestamp = lastTimestamp
let earliestBlockNumber = lastBlockNumber
// The latest allowed timestamp/blockNumber is the next queue element!
let nextQueueIndex = await this.chainContract.getNextQueueIndex()
let latestTimestamp: number
let latestBlockNumber: number
// updateLatestTimestampAndBlockNumber is a helper which updates
// the latest timestamp and block number based on the pending queue elements.
const updateLatestTimestampAndBlockNumber = async () => {
const pendingQueueElements = await this.chainContract.getNumPendingQueueElements()
const nextRemoteQueueElements = await this.chainContract.getNextQueueIndex()
const totalQueueElements =
pendingQueueElements + nextRemoteQueueElements
if (nextQueueIndex < totalQueueElements) {
const [
queueEleHash,
queueTimestamp,
queueBlockNumber,
] = await this.chainContract.getQueueElement(nextQueueIndex)
latestTimestamp = queueTimestamp
latestBlockNumber = queueBlockNumber
} else {
// If there are no queue elements left then just allow any timestamp/blocknumber
latestTimestamp = Number.MAX_SAFE_INTEGER
latestBlockNumber = Number.MAX_SAFE_INTEGER
}
}
// Actually update the latest timestamp and block number
await updateLatestTimestampAndBlockNumber()
// Now go through our batch and fix the timestamps and block numbers
// to automatically enforce monotonicity.
const fixedBatch: Batch = []
for (const ele of b) {
if (!ele.isSequencerTx) {
// Set the earliest allowed timestamp to the old latest and set the new latest
// to the next queue element's timestamp / blockNumber
earliestTimestamp = latestTimestamp
earliestBlockNumber = latestBlockNumber
nextQueueIndex++
await updateLatestTimestampAndBlockNumber()
}
// Fix the element if its timestammp/blockNumber is too small
if (
ele.timestamp < earliestTimestamp ||
ele.blockNumber < earliestBlockNumber
) {
this.log.warn('Fixing timestamp/blockNumber too small', {
oldTimestamp: ele.timestamp,
newTimestamp: earliestTimestamp,
oldBlockNumber: ele.blockNumber,
newBlockNumber: earliestBlockNumber,
})
fixedBatch.push({
...ele,
timestamp: earliestTimestamp,
blockNumber: earliestBlockNumber,
})
continue
}
// Fix the element if its timestammp/blockNumber is too large
if (
ele.timestamp > latestTimestamp ||
ele.blockNumber > latestBlockNumber
) {
this.log.warn('Fixing timestamp/blockNumber too large.', {
oldTimestamp: ele.timestamp,
newTimestamp: latestTimestamp,
oldBlockNumber: ele.blockNumber,
newBlockNumber: latestBlockNumber,
})
fixedBatch.push({
...ele,
timestamp: latestTimestamp,
blockNumber: latestBlockNumber,
})
continue
}
// No fixes needed!
fixedBatch.push(ele)
}
return fixedBatch
}
if (this.autoFixBatchOptions.fixDoublePlayedDeposits) {
batch = await fixDoublePlayedDeposits(batch)
}
if (this.autoFixBatchOptions.fixMonotonicity) {
batch = await fixMonotonicity(batch)
}
return batch
}
private async _getLastTimestampAndBlockNumber(): Promise<{
lastTimestamp: number
lastBlockNumber: number
}> {
const manager = new Contract(
this.addressManagerAddress,
getNewContractInterface('Lib_AddressManager'),
this.signer.provider
)
const addr = await manager.getAddress(
'OVM_ChainStorageContainer:CTC:batches'
)
const container = new Contract(
addr,
getNewContractInterface('iOVM_ChainStorageContainer'),
this.signer.provider
)
let meta = await container.getGlobalMetadata()
// remove 0x
meta = meta.slice(2)
// convert to bytes27
meta = meta.slice(10)
const totalElements = meta.slice(-10)
const nextQueueIndex = meta.slice(-20, -10)
const lastTimestamp = parseInt(meta.slice(-30, -20), 16)
const lastBlockNumber = parseInt(meta.slice(-40, -30), 16)
this.log.debug('Retrieved timestamp and block number from CTC', {
lastTimestamp,
lastBlockNumber,
})
return { lastTimestamp, lastBlockNumber }
}
private async _fixQueueElement(
queueIndex: number,
queueElement: BatchElement
): Promise<BatchElement> {
const [
queueEleHash,
timestamp,
blockNumber,
] = await this.chainContract.getQueueElement(queueIndex)
if (
timestamp > queueElement.timestamp &&
blockNumber > queueElement.blockNumber
) {
this.log.warn(
'Double deposit detected!!! Fixing by skipping the deposit & replacing with a dummy tx.'
)
// This implies that we've double played a deposit.
// We can correct this by instead submitting a dummy sequencer tx
const wallet = Wallet.createRandom()
const gasLimit = 8_000_000
const gasPrice = 0
const chainId = 10
const nonce = 0
const rawTx = await wallet.signTransaction({
gasLimit,
gasPrice,
chainId,
nonce,
to: '0x1111111111111111111111111111111111111111',
data: '0x1234',
})
// tx: [0nonce, 1gasprice, 2startgas, 3to, 4value, 5data, 6v, 7r, 8s]
const tx = ethers.utils.RLP.decode(rawTx)
const dummyTx: EIP155TxData = {
sig: {
v: tx[6],
r: tx[7],
s: tx[8],
},
gasLimit,
gasPrice,
nonce,
target: tx[3],
data: tx[5],
type: TxType.EIP155,
}
return {
stateRoot: queueElement.stateRoot,
isSequencerTx: true,
sequencerTxType: TxType.EIP155,
txData: dummyTx,
timestamp: queueElement.timestamp,
blockNumber: queueElement.blockNumber,
}
}
if (
timestamp < queueElement.timestamp &&
blockNumber < queueElement.blockNumber
) {
this.log.error('A deposit seems to have been skipped!')
throw new Error('Skipped deposit?!')
}
throw new Error('Unable to fix queue element!')
}
private async _getSequencerBatchParams(
shouldStartAtIndex: number,
blocks: Batch
): Promise<AppendSequencerBatchParams> {
const totalElementsToAppend = blocks.length
// Generate contexts
const contexts: BatchContext[] = []
let lastBlockIsSequencerTx = false
let lastTimestamp = 0
let lastBlockNumber = 0
const groupedBlocks: Array<{
sequenced: BatchElement[]
queued: BatchElement[]
}> = []
for (const block of blocks) {
if (
(lastBlockIsSequencerTx === false && block.isSequencerTx === true) ||
groupedBlocks.length === 0 ||
(block.timestamp !== lastTimestamp && block.isSequencerTx === true) ||
(block.blockNumber !== lastBlockNumber && block.isSequencerTx === true)
) {
groupedBlocks.push({
sequenced: [],
queued: [],
})
}
const cur = groupedBlocks.length - 1
block.isSequencerTx
? groupedBlocks[cur].sequenced.push(block)
: groupedBlocks[cur].queued.push(block)
lastBlockIsSequencerTx = block.isSequencerTx
lastTimestamp = block.timestamp
lastBlockNumber = block.blockNumber
}
for (const groupedBlock of groupedBlocks) {
if (
groupedBlock.sequenced.length === 0 &&
groupedBlock.queued.length === 0
) {
throw new Error(
'Attempted to generate batch context with 0 queued and 0 sequenced txs!'
)
}
contexts.push({
numSequencedTransactions: groupedBlock.sequenced.length,
numSubsequentQueueTransactions: groupedBlock.queued.length,
timestamp:
groupedBlock.sequenced.length > 0
? groupedBlock.sequenced[0].timestamp
: groupedBlock.queued[0].timestamp,
blockNumber:
groupedBlock.sequenced.length > 0
? groupedBlock.sequenced[0].blockNumber
: groupedBlock.queued[0].blockNumber,
})
}
// Generate sequencer transactions
const transactions: string[] = []
for (const block of blocks) {
if (!block.isSequencerTx) {
continue
}
let encoding: string
if (block.sequencerTxType === TxType.EIP155) {
encoding = ctcCoder.eip155TxData.encode(block.txData as EIP155TxData)
} else if (block.sequencerTxType === TxType.EthSign) {
encoding = ctcCoder.ethSignTxData.encode(block.txData as EthSignTxData)
} else {
throw new Error(
`Trying to build batch with unknown type ${block.sequencerTxType}`
)
}
transactions.push(encoding)
}
return {
// TODO: Remove BLOCK_OFFSET by adding a tx to Geth's genesis
shouldStartAtElement: shouldStartAtIndex - BLOCK_OFFSET,
totalElementsToAppend,
contexts,
transactions,
}
}
private async _getL2BatchElement(blockNumber: number): Promise<BatchElement> {
const block = await this._getBlock(blockNumber)
const txType = block.transactions[0].txType
if (this._isSequencerTx(block)) {
if (txType === TxType.EIP155 || txType === TxType.EthSign) {
return this._getDefaultEcdsaTxBatchElement(block)
} else {
throw new Error('Unsupported Tx Type!')
}
} else {
return {
stateRoot: block.stateRoot,
isSequencerTx: false,
sequencerTxType: undefined,
txData: undefined,
timestamp: block.timestamp,
blockNumber: block.transactions[0].l1BlockNumber,
}
}
}
private async _getBlock(blockNumber: number): Promise<L2Block> {
const block = (await this.l2Provider.getBlockWithTransactions(
blockNumber
)) as L2Block
// Convert the tx type to a number
block.transactions[0].txType = txTypePlainText[block.transactions[0].txType]
block.transactions[0].queueOrigin =
queueOriginPlainText[block.transactions[0].queueOrigin]
// For now just set the l1BlockNumber based on the current l1 block number
if (!block.transactions[0].l1BlockNumber) {
block.transactions[0].l1BlockNumber = this.lastL1BlockNumber
}
return block
}
private _getDefaultEcdsaTxBatchElement(block: L2Block): BatchElement {
const tx: TransactionResponse = block.transactions[0]
const txData: EIP155TxData = {
sig: {
v: tx.v - this.l2ChainId * 2 - 8 - 27,
r: tx.r,
s: tx.s,
},
gasLimit: BigNumber.from(tx.gasLimit).toNumber(),
gasPrice: BigNumber.from(tx.gasPrice).toNumber(),
nonce: tx.nonce,
target: tx.to ? tx.to : '00'.repeat(20),
data: tx.data,
type: block.transactions[0].txType,
}
return {
stateRoot: block.stateRoot,
isSequencerTx: true,
sequencerTxType: block.transactions[0].txType,
txData,
timestamp: block.timestamp,
blockNumber: block.transactions[0].l1BlockNumber,
}
}
private _isSequencerTx(block: L2Block): boolean {
return block.transactions[0].queueOrigin === QueueOrigin.Sequencer
}
}
/* External Imports */
import { Logger } from '@eth-optimism/core-utils'
import { exit } from 'process'
import { Signer, Wallet } from 'ethers'
import {
Provider,
JsonRpcProvider,
TransactionReceipt,
} from '@ethersproject/providers'
import { OptimismProvider } from '@eth-optimism/provider'
import { config } from 'dotenv'
config()
/* Internal Imports */
import {
TransactionBatchSubmitter,
AutoFixBatchOptions,
BatchSubmitter,
StateBatchSubmitter,
STATE_BATCH_SUBMITTER_LOG_TAG,
TX_BATCH_SUBMITTER_LOG_TAG,
} from '..'
/* Logger */
const log = new Logger({ name: 'oe:batch-submitter:init' })
interface RequiredEnvVars {
// The HTTP provider URL for L1.
L1_NODE_WEB3_URL: 'L1_NODE_WEB3_URL'
// The HTTP provider URL for L2.
L2_NODE_WEB3_URL: 'L2_NODE_WEB3_URL'
// The layer one address manager address
ADDRESS_MANAGER_ADDRESS: 'ADDRESS_MANAGER_ADDRESS'
// The minimum size in bytes of any L1 transactions generated by the batch submitter.
MIN_TX_SIZE: 'MIN_TX_SIZE'
// The maximum size in bytes of any L1 transactions generated by the batch submitter.
MAX_TX_SIZE: 'MAX_TX_SIZE'
// The maximum number of L2 transactions that can ever be in a batch.
MAX_BATCH_SIZE: 'MAX_BATCH_SIZE'
// The maximum amount of time (seconds) that we will wait before submitting an under-sized batch.
MAX_BATCH_SUBMISSION_TIME: 'MAX_BATCH_SUBMISSION_TIME'
// The delay in milliseconds between querying L2 for more transactions / to create a new batch.
POLL_INTERVAL: 'POLL_INTERVAL'
// The number of confirmations which we will wait after appending new batches.
NUM_CONFIRMATIONS: 'NUM_CONFIRMATIONS'
// The number of seconds to wait before resubmitting a transaction.
RESUBMISSION_TIMEOUT: 'RESUBMISSION_TIMEOUT'
// The number of confirmations that we should wait before submitting state roots for CTC elements.
FINALITY_CONFIRMATIONS: 'FINALITY_CONFIRMATIONS'
// Whether or not to run the tx batch submitter.
RUN_TX_BATCH_SUBMITTER: 'true' | 'false' | 'RUN_TX_BATCH_SUBMITTER'
// Whether or not to run the state batch submitter.
RUN_STATE_BATCH_SUBMITTER: 'true' | 'false' | 'RUN_STATE_BATCH_SUBMITTER'
// The safe minimum amount of ether the batch submitter key should
// hold before it starts to log errors.
SAFE_MINIMUM_ETHER_BALANCE: 'SAFE_MINIMUM_ETHER_BALANCE'
// A boolean to clear the pending transactions in the mempool
// on start up.
CLEAR_PENDING_TXS: 'true' | 'false' | 'CLEAR_PENDING_TXS'
}
const requiredEnvVars: RequiredEnvVars = {
L1_NODE_WEB3_URL: 'L1_NODE_WEB3_URL',
L2_NODE_WEB3_URL: 'L2_NODE_WEB3_URL',
ADDRESS_MANAGER_ADDRESS: 'ADDRESS_MANAGER_ADDRESS',
MIN_TX_SIZE: 'MIN_TX_SIZE',
MAX_TX_SIZE: 'MAX_TX_SIZE',
MAX_BATCH_SIZE: 'MAX_BATCH_SIZE',
MAX_BATCH_SUBMISSION_TIME: 'MAX_BATCH_SUBMISSION_TIME',
POLL_INTERVAL: 'POLL_INTERVAL',
NUM_CONFIRMATIONS: 'NUM_CONFIRMATIONS',
RESUBMISSION_TIMEOUT: 'RESUBMISSION_TIMEOUT',
FINALITY_CONFIRMATIONS: 'FINALITY_CONFIRMATIONS',
RUN_TX_BATCH_SUBMITTER: 'RUN_TX_BATCH_SUBMITTER',
RUN_STATE_BATCH_SUBMITTER: 'RUN_STATE_BATCH_SUBMITTER',
SAFE_MINIMUM_ETHER_BALANCE: 'SAFE_MINIMUM_ETHER_BALANCE',
CLEAR_PENDING_TXS: 'CLEAR_PENDING_TXS',
}
/* Optional Env Vars
* FRAUD_SUBMISSION_ADDRESS
* DISABLE_QUEUE_BATCH_APPEND
* SEQUENCER_PRIVATE_KEY
* MNEMONIC
*/
const env = process.env
const FRAUD_SUBMISSION_ADDRESS = env.FRAUD_SUBMISSION_ADDRESS || 'no fraud'
const DISABLE_QUEUE_BATCH_APPEND = !!env.DISABLE_QUEUE_BATCH_APPEND
const MIN_GAS_PRICE_IN_GWEI = parseInt(env.MIN_GAS_PRICE_IN_GWEI, 10) || 0
const MAX_GAS_PRICE_IN_GWEI = parseInt(env.MAX_GAS_PRICE_IN_GWEI, 10) || 70
const GAS_RETRY_INCREMENT = parseInt(env.GAS_RETRY_INCREMENT, 10) || 5
const GAS_THRESHOLD_IN_GWEI = parseInt(env.GAS_THRESHOLD_IN_GWEI, 10) || 100
// The private key that will be used to submit tx and state batches.
const SEQUENCER_PRIVATE_KEY = env.SEQUENCER_PRIVATE_KEY
const MNEMONIC = env.MNEMONIC
const HD_PATH = env.HD_PATH
// Auto fix batch options -- TODO: Remove this very hacky config
const AUTO_FIX_BATCH_OPTIONS_CONF = env.AUTO_FIX_BATCH_OPTIONS_CONF
const autoFixBatchOptions: AutoFixBatchOptions = {
fixDoublePlayedDeposits: AUTO_FIX_BATCH_OPTIONS_CONF
? AUTO_FIX_BATCH_OPTIONS_CONF.includes('fixDoublePlayedDeposits')
: false,
fixMonotonicity: AUTO_FIX_BATCH_OPTIONS_CONF
? AUTO_FIX_BATCH_OPTIONS_CONF.includes('fixMonotonicity')
: false,
}
export const run = async () => {
log.info('Starting batch submitter...')
for (const [i, val] of Object.entries(requiredEnvVars)) {
if (!process.env[val]) {
log.warn('Missing environment variable', {
varName: val,
})
exit(1)
}
requiredEnvVars[val] = process.env[val]
}
const clearPendingTxs = requiredEnvVars.CLEAR_PENDING_TXS === 'true'
const l1Provider: Provider = new JsonRpcProvider(
requiredEnvVars.L1_NODE_WEB3_URL
)
const l2Provider: OptimismProvider = new OptimismProvider(
requiredEnvVars.L2_NODE_WEB3_URL
)
let sequencerSigner: Signer
if (SEQUENCER_PRIVATE_KEY) {
sequencerSigner = new Wallet(SEQUENCER_PRIVATE_KEY, l1Provider)
} else if (MNEMONIC) {
sequencerSigner = Wallet.fromMnemonic(MNEMONIC, HD_PATH).connect(l1Provider)
} else {
throw new Error('Must pass one of SEQUENCER_PRIVATE_KEY or MNEMONIC')
}
const address = await sequencerSigner.getAddress()
log.info('Configured batch submitter addresses', {
batchSubmitterAddress: address,
addressManagerAddress: requiredEnvVars.ADDRESS_MANAGER_ADDRESS,
})
const txBatchSubmitter = new TransactionBatchSubmitter(
sequencerSigner,
l2Provider,
parseInt(requiredEnvVars.MIN_TX_SIZE, 10),
parseInt(requiredEnvVars.MAX_TX_SIZE, 10),
parseInt(requiredEnvVars.MAX_BATCH_SIZE, 10),
parseInt(requiredEnvVars.MAX_BATCH_SUBMISSION_TIME, 10) * 1_000,
parseInt(requiredEnvVars.NUM_CONFIRMATIONS, 10),
parseInt(requiredEnvVars.RESUBMISSION_TIMEOUT, 10) * 1_000,
requiredEnvVars.ADDRESS_MANAGER_ADDRESS,
parseFloat(requiredEnvVars.SAFE_MINIMUM_ETHER_BALANCE),
MIN_GAS_PRICE_IN_GWEI,
MAX_GAS_PRICE_IN_GWEI,
GAS_RETRY_INCREMENT,
GAS_THRESHOLD_IN_GWEI,
new Logger({ name: TX_BATCH_SUBMITTER_LOG_TAG }),
DISABLE_QUEUE_BATCH_APPEND,
autoFixBatchOptions
)
const stateBatchSubmitter = new StateBatchSubmitter(
sequencerSigner,
l2Provider,
parseInt(requiredEnvVars.MIN_TX_SIZE, 10),
parseInt(requiredEnvVars.MAX_TX_SIZE, 10),
parseInt(requiredEnvVars.MAX_BATCH_SIZE, 10),
parseInt(requiredEnvVars.MAX_BATCH_SUBMISSION_TIME, 10) * 1_000,
parseInt(requiredEnvVars.NUM_CONFIRMATIONS, 10),
parseInt(requiredEnvVars.RESUBMISSION_TIMEOUT, 10) * 1_000,
parseInt(requiredEnvVars.FINALITY_CONFIRMATIONS, 10),
requiredEnvVars.ADDRESS_MANAGER_ADDRESS,
parseFloat(requiredEnvVars.SAFE_MINIMUM_ETHER_BALANCE),
MIN_GAS_PRICE_IN_GWEI,
MAX_GAS_PRICE_IN_GWEI,
GAS_RETRY_INCREMENT,
GAS_THRESHOLD_IN_GWEI,
new Logger({ name: STATE_BATCH_SUBMITTER_LOG_TAG }),
FRAUD_SUBMISSION_ADDRESS
)
// Loops infinitely!
const loop = async (
func: () => Promise<TransactionReceipt>
): Promise<void> => {
// Clear all pending transactions
if (clearPendingTxs) {
try {
const pendingTxs = await sequencerSigner.getTransactionCount('pending')
const latestTxs = await sequencerSigner.getTransactionCount('latest')
if (pendingTxs > latestTxs) {
log.info('Detected pending transactions. Clearing all transactions!')
for (let i = latestTxs; i < pendingTxs; i++) {
const response = await sequencerSigner.sendTransaction({
to: await sequencerSigner.getAddress(),
value: 0,
nonce: i,
})
log.info('Submitting empty transaction', {
nonce: i,
txHash: response.hash,
})
await sequencerSigner.provider.waitForTransaction(
response.hash,
parseInt(requiredEnvVars.NUM_CONFIRMATIONS, 10)
)
}
}
} catch (err) {
log.error('Cannot clear transactions', err)
process.exit(1)
}
}
while (true) {
try {
await func()
} catch (err) {
log.error('Error submitting batch', err)
log.info('Retrying...')
}
// Sleep
await new Promise((r) =>
setTimeout(r, parseInt(requiredEnvVars.POLL_INTERVAL, 10))
)
}
}
// Run batch submitters in two seperate infinite loops!
if (requiredEnvVars.RUN_TX_BATCH_SUBMITTER === 'true') {
loop(() => txBatchSubmitter.submitNextBatch())
}
if (requiredEnvVars.RUN_STATE_BATCH_SUBMITTER === 'true') {
loop(() => stateBatchSubmitter.submitNextBatch())
}
}
export * from './batch-submitter'
export * from './utils'
export * from './transaction-chain-contract'
export * from './types'
/* External Imports */
import { Contract, BigNumber } from 'ethers'
import {
TransactionResponse,
TransactionRequest,
} from '@ethersproject/abstract-provider'
import { keccak256 } from 'ethers/lib/utils'
import { remove0x, encodeHex } from './utils'
import {
AppendSequencerBatchParams,
BatchContext,
encodeAppendSequencerBatch,
} from '@eth-optimism/core-utils'
export { encodeAppendSequencerBatch, BatchContext, AppendSequencerBatchParams }
/*
* OVM_CanonicalTransactionChainContract is a wrapper around a normal Ethers contract
* where the `appendSequencerBatch(...)` function uses a specialized encoding for improved efficiency.
*/
export class CanonicalTransactionChainContract extends Contract {
public async appendSequencerBatch(
batch: AppendSequencerBatchParams,
options?: TransactionRequest
): Promise<TransactionResponse> {
return appendSequencerBatch(this, batch, options)
}
}
/**********************
* Internal Functions *
*********************/
const APPEND_SEQUENCER_BATCH_METHOD_ID = 'appendSequencerBatch()'
const appendSequencerBatch = async (
OVM_CanonicalTransactionChain: Contract,
batch: AppendSequencerBatchParams,
options?: TransactionRequest
): Promise<TransactionResponse> => {
const methodId = keccak256(
Buffer.from(APPEND_SEQUENCER_BATCH_METHOD_ID)
).slice(2, 10)
const calldata = encodeAppendSequencerBatch(batch)
return OVM_CanonicalTransactionChain.signer.sendTransaction({
to: OVM_CanonicalTransactionChain.address,
data: '0x' + methodId + calldata,
...options,
})
}
const encodeBatchContext = (context: BatchContext): string => {
return (
encodeHex(context.numSequencedTransactions, 6) +
encodeHex(context.numSubsequentQueueTransactions, 6) +
encodeHex(context.timestamp, 10) +
encodeHex(context.blockNumber, 10)
)
}
/* External Imports */
import {
BlockWithTransactions,
Provider,
TransactionResponse,
} from '@ethersproject/abstract-provider'
/* Internal Imports */
import { EIP155TxData, TxType } from '@eth-optimism/core-utils'
export enum QueueOrigin {
Sequencer = 0,
L1ToL2 = 1,
}
export const queueOriginPlainText = {
0: QueueOrigin.Sequencer,
1: QueueOrigin.L1ToL2,
sequencer: QueueOrigin.Sequencer,
l1ToL2: QueueOrigin.L1ToL2,
}
/**
* Transaction & Blocks. These are the true data-types we expect
* from running a batch submitter.
*/
export interface L2Transaction extends TransactionResponse {
l1BlockNumber: number
l1TxOrigin: string
txType: number
queueOrigin: number
}
export interface L2Block extends BlockWithTransactions {
stateRoot: string
transactions: [L2Transaction]
}
/**
* BatchElement & Batch. These are the data-types of the compressed / batched
* block data we submit to L1.
*/
export interface BatchElement {
stateRoot: string
isSequencerTx: boolean
sequencerTxType: undefined | TxType
txData: undefined | EIP155TxData
timestamp: number
blockNumber: number
}
export type Batch = BatchElement[]
/* External Imports */
import { BigNumber } from 'ethers'
export const getLen = (pos: { start; end }) => (pos.end - pos.start) * 2
export const encodeHex = (val: any, len: number) =>
remove0x(BigNumber.from(val).toHexString()).padStart(len, '0')
export const toVerifiedBytes = (val: string, len: number) => {
val = remove0x(val)
if (val.length !== len) {
throw new Error('Invalid length!')
}
return val
}
export const remove0x = (str: string): string => {
if (str.startsWith('0x')) {
return str.slice(2)
} else {
return str
}
}
/* External Imports */
import { OptimismProvider } from '@eth-optimism/provider'
import {
BlockWithTransactions,
TransactionResponse,
} from '@ethersproject/abstract-provider'
/* Internal Imports */
import { L2Transaction, L2Block, RollupInfo } from '../../src'
/**
* Unformatted Transaction & Blocks. This exists because Geth currently
* does not return the correct fields & so this code renames those
* poorly named fields
*/
interface UnformattedL2Transaction extends TransactionResponse {
l1BlockNumber: string
l1MessageSender: string
signatureHashType: string
queueOrigin: string
}
interface UnformattedL2Block extends BlockWithTransactions {
stateRoot: string
transactions: [UnformattedL2Transaction]
}
export class MockchainProvider extends OptimismProvider {
public mockBlockNumber: number = 1
public numBlocksToReturn: number = 2
public mockBlocks: L2Block[] = []
public ctcAddr: string
public sccAddr: string
constructor(ctcAddr: string, sccAddr: string) {
super('https://optimism.io')
for (const block of BLOCKS) {
if (block.number === 0) {
// No need to convert genesis to an L2Block because it has no txs
this.mockBlocks.push(block)
continue
}
this.mockBlocks.push(this._toL2Block(block))
this.ctcAddr = ctcAddr
this.sccAddr = sccAddr
}
}
public async getBlockNumber(): Promise<number> {
// Increment our mock block number every time
if (
this.mockBlockNumber + this.numBlocksToReturn <
this.mockBlocks.length
) {
this.mockBlockNumber += this.numBlocksToReturn
} else {
return this.mockBlocks.length - 1
}
return this.mockBlockNumber
}
public async send(endpoint: string, params: []): Promise<any> {
if (endpoint === 'eth_chainId') {
return this.chainId()
}
if (endpoint === 'rollup_getInfo') {
const info: RollupInfo = {
mode: 'sequencer',
syncing: false,
ethContext: {
timestamp: 0,
blockNumber: 0,
},
rollupContext: {
index: 0,
queueIndex: 0,
},
}
return info
}
throw new Error('Unsupported endpoint!')
}
public setNumBlocksToReturn(numBlocks: number): void {
this.numBlocksToReturn = numBlocks
}
public setL2BlockData(
tx: L2Transaction,
timestamp?: number,
start: number = 1,
end: number = this.mockBlocks.length
) {
for (let i = start; i < end; i++) {
this.mockBlocks[i].timestamp = timestamp
? timestamp
: this.mockBlocks[i].timestamp
this.mockBlocks[i].transactions[0] = {
...this.mockBlocks[i].transactions[0],
...tx,
}
}
}
public async getBlockWithTransactions(blockNumber: number): Promise<L2Block> {
return this.mockBlocks[blockNumber]
}
public chainId(): number {
// We know that mockBlocks will always have at least 1 value
return this.mockBlocks[1].transactions[0].chainId
}
private _toL2Block(block: UnformattedL2Block): L2Block {
const txType: number = parseInt(block.transactions[0].signatureHashType, 10)
const l1BlockNumber: number = parseInt(
block.transactions[0].l1BlockNumber,
10
)
const queueOrigin: number = parseInt(block.transactions[0].queueOrigin, 10)
const l1TxOrigin: string = block.transactions[0].l1MessageSender
const l2Transaction: L2Transaction = {
...block.transactions[0],
// Rename the incorrectly named fields
l1TxOrigin,
txType,
queueOrigin,
l1BlockNumber,
}
// Add an interface here to fix the type casing into L2Block during Object.assign
interface PartialL2Block {
transactions: [L2Transaction]
}
const partialBlock: PartialL2Block = {
transactions: [l2Transaction],
}
const l2Block: L2Block = { ...block, ...partialBlock }
return l2Block
}
}
const BLOCKS = JSON.parse(`
[
{
"hash":"0xbc27fdbd1fee6e001438709ef57210bb7b2b1b8c23b65acb2d79161f4dc3cf05",
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"number":0,
"timestamp":1603651804,
"nonce":"0x0000000000000042",
"difficulty":1,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0x0000000000000000000000000000000000000000",
"extraData":"0x1234",
"transactions":[
]
},
{
"hash":"0x05a7f5c5fce57346f59355184daa58822f97a32e4327fe6ef4a1c37dfd36f2f0",
"parentHash":"0x64e89492b3ea72b9f9f0f4566e5198e19d7bfa583619c54c33872c7112aec9cd",
"number":1,
"timestamp":1603404102,
"nonce":"0x0000000000000042",
"difficulty":131072,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x688ddd1acc3cbccd9112018eac6c78744f43140d408128b8ed0392c7ee28966e",
"blockHash":"0x05a7f5c5fce57346f59355184daa58822f97a32e4327fe6ef4a1c37dfd36f2f0",
"blockNumber":1,
"transactionIndex":0,
"confirmations":16,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0x05bc67"
},
"to":null,
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":0,
"data":"0x608060405234801561001057600080fd5b50600080546001600160a01b03191633178082556040516001600160a01b039190911691907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a361056a806100696000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c8063715018a61461005c5780638da5cb5b146100665780639b2ea4bd1461008a578063bf40fac11461013b578063f2fde38b146101e1575b600080fd5b610064610207565b005b61006e6102b0565b604080516001600160a01b039092168252519081900360200190f35b610064600480360360408110156100a057600080fd5b8101906020810181356401000000008111156100bb57600080fd5b8201836020820111156100cd57600080fd5b803590602001918460018302840111640100000000831117156100ef57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550505090356001600160a01b031691506102bf9050565b61006e6004803603602081101561015157600080fd5b81019060208101813564010000000081111561016c57600080fd5b82018360208201111561017e57600080fd5b803590602001918460018302840111640100000000831117156101a057600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610362945050505050565b610064600480360360208110156101f757600080fd5b50356001600160a01b0316610391565b6000546001600160a01b03163314610266576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6000546001600160a01b031681565b6000546001600160a01b0316331461031e576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b806001600061032c85610490565b815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055505050565b60006001600061037184610490565b81526020810191909152604001600020546001600160a01b031692915050565b6000546001600160a01b031633146103f0576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b0381166104355760405162461bcd60e51b815260040180806020018281038252602d815260200180610508602d913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000816040516020018082805190602001908083835b602083106104c55780518252601f1990920191602091820191016104a6565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405160208183030381529060405280519060200120905091905056fe4f776e61626c653a206e6577206f776e65722063616e6e6f7420626520746865207a65726f2061646472657373a26469706673582212204367ffc2e6671623708150e2d0cff4c12cf566722a26b4748555d789953e2d2264736f6c63430007000033",
"r":"0x2babe370e2e422a38386a5a96cd3bf16772ddbbf8c9dab8aadf4416fff557756",
"s":"0x213ab994b50ed4a38e2de390f851d88cb66dd238a27d89246616b34eb8e859df",
"v":62710,
"creates":"0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA",
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"signatureHashType":"0",
"queueOrigin":"0",
"chainId":31337
}
]
},
{
"hash":"0xb5903854b196abb9b7d1e3c2f5f8f9519b4fedefc21d72f3d92c74f128afcd46",
"parentHash":"0x05a7f5c5fce57346f59355184daa58822f97a32e4327fe6ef4a1c37dfd36f2f0",
"number":2,
"timestamp":1603404103,
"nonce":"0x0000000000000042",
"difficulty":131136,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0xeae52be001bfea0b3d8de2de5edff5c0c39d26f6e6ab0fc6623cd24913dbf150",
"blockHash":"0xb5903854b196abb9b7d1e3c2f5f8f9519b4fedefc21d72f3d92c74f128afcd46",
"blockNumber":2,
"transactionIndex":0,
"confirmations":15,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0xaeab"
},
"to":"0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":1,
"data":"0x9b2ea4bd000000000000000000000000000000000000000000000000000000000000004000000000000000000000000063fc2ad3d021a4d7e64323529a55a9442c444da0000000000000000000000000000000000000000000000000000000000000000d4f564d5f53657175656e63657200000000000000000000000000000000000000",
"r":"0x530be666add21a30fe9a0fadee8072d4d8ccb2b80a9fe07c7e0591c5a1e5f375",
"s":"0x5954b17cd0f6299ee54f20853bcfdfc870f2fc271f0433fd592b8b0fc0329aa4",
"v":62709,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"signatureHashType":"0",
"queueOrigin":"0",
"chainId":31337
}
]
},
{
"hash":"0x7504e7104a2d80e810758c1cc2736028ffe5632a2ec988da740c93b3cfa03945",
"parentHash":"0xb5903854b196abb9b7d1e3c2f5f8f9519b4fedefc21d72f3d92c74f128afcd46",
"number":3,
"timestamp":1603404104,
"nonce":"0x0000000000000042",
"difficulty":131200,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x79f4dc191018a37a8b71b4a5aff02ce501b0341fc72708c8095845e61be15c02",
"blockHash":"0x7504e7104a2d80e810758c1cc2736028ffe5632a2ec988da740c93b3cfa03945",
"blockNumber":3,
"transactionIndex":0,
"confirmations":14,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0xafb0"
},
"to":"0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":2,
"data":"0x9b2ea4bd0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000420000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000224f564d5f4465636f6d7072657373696f6e507265636f6d70696c6541646472657373000000000000000000000000000000000000000000000000000000000000",
"r":"0x388b6b7e4fe10128e92507042971b864edd535d8f3cb70c61247daed1dc734c1",
"s":"0x6ad11366b26fa59ce73bc73fa320560f46ffb6df3142adbd16ebce0bcc3ba9f5",
"v":62710,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"signatureHashType":"0",
"queueOrigin":"0",
"chainId":31337
}
]
},
{
"hash":"0x6587aa5da64944673741bdd3299c0d2471c2fdbfcd94713d511ab7240d127bbb",
"parentHash":"0x7504e7104a2d80e810758c1cc2736028ffe5632a2ec988da740c93b3cfa03945",
"number":4,
"timestamp":1603404105,
"nonce":"0x0000000000000042",
"difficulty":131264,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x0584fbd34096ffc0fc96dbdf1d8ee574c3eda9e07bf85aed9862542416b1a007",
"blockHash":"0x6587aa5da64944673741bdd3299c0d2471c2fdbfcd94713d511ab7240d127bbb",
"blockNumber":4,
"transactionIndex":0,
"confirmations":13,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0x02e294"
},
"to":null,
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":3,
"data":"0x608060405234801561001057600080fd5b50600080546001600160a01b03191633179055610213806100326000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063776d1a0114610077575b60015460408051602036601f8101829004820283018201909352828252610075936001600160a01b0316926000918190840183828082843760009201919091525061009d92505050565b005b6100756004803603602081101561008d57600080fd5b50356001600160a01b031661015d565b60006060836001600160a01b0316836040518082805190602001908083835b602083106100db5780518252601f1990920191602091820191016100bc565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461013d576040519150601f19603f3d011682016040523d82523d6000602084013e610142565b606091505b5091509150811561015557805160208201f35b805160208201fd5b6000546001600160a01b031633141561019057600180546001600160a01b0319166001600160a01b0383161790556101da565b60015460408051602036601f81018290048202830182019093528282526101da936001600160a01b0316926000918190840183828082843760009201919091525061009d92505050565b5056fea2646970667358221220293887d48c4c1c34de868edf3e9a6be82327946c76d71f7c2023e67f556c6ecb64736f6c63430007000033",
"r":"0x2e420851664bb81c0d5d0bd1a805661fc1f83922b92e1d9e0e57c9184eddec0e",
"s":"0x5e00184c9b50ed54e714231af904caed92cf47ee309fc3604793d7d32a9f4988",
"v":62709,
"creates":"0x94BA4d5Ebb0e05A50e977FFbF6e1a1Ee3D89299c",
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"signatureHashType":"0",
"queueOrigin":"0",
"chainId":31337
}
]
},
{
"hash":"0x7a215669ab018e508473ef76b42d19cc4228cc5cc855089dc04cb22555fcf555",
"parentHash":"0x6587aa5da64944673741bdd3299c0d2471c2fdbfcd94713d511ab7240d127bbb",
"number":5,
"timestamp":1603404106,
"nonce":"0x0000000000000042",
"difficulty":131328,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0xc8243d8b9bf79624f8930527a983be3581cb7d969ae76d3e5962f9db2be7b71a",
"blockHash":"0x7a215669ab018e508473ef76b42d19cc4228cc5cc855089dc04cb22555fcf555",
"blockNumber":5,
"transactionIndex":0,
"confirmations":12,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0xa93c"
},
"to":"0x94BA4d5Ebb0e05A50e977FFbF6e1a1Ee3D89299c",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":4,
"data":"0x776d1a01000000000000000000000000e9a9a643588daa154de182f88a5b04e8745909c2",
"r":"0x2de73fc5aec124cc9cf0fa54fc8492692a48b8921a32d31f46f5d431fdeea7a0",
"s":"0x4e813ae50bc7705fb023d79429ed6cc92367cb5209460e6ea3904c014b06cd4d",
"v":62709,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"signatureHashType":"0",
"queueOrigin":"0",
"chainId":31337
}
]
},
{
"hash":"0xb4c01e6e867856dce9b451ba67273f86bdc584ff398ed5b8c46f70ea37b1002f",
"parentHash":"0x7a215669ab018e508473ef76b42d19cc4228cc5cc855089dc04cb22555fcf555",
"number":6,
"timestamp":1603404107,
"nonce":"0x0000000000000042",
"difficulty":131392,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0xa5bf13c2638f1d99a26c4c7d867f1bc64a34ebf141524b35840c879536e69d69",
"blockHash":"0xb4c01e6e867856dce9b451ba67273f86bdc584ff398ed5b8c46f70ea37b1002f",
"blockNumber":6,
"transactionIndex":0,
"confirmations":11,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0xaeff"
},
"to":"0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":5,
"data":"0x9b2ea4bd000000000000000000000000000000000000000000000000000000000000004000000000000000000000000094ba4d5ebb0e05a50e977ffbf6e1a1ee3d89299c00000000000000000000000000000000000000000000000000000000000000144f564d5f457865637574696f6e4d616e61676572000000000000000000000000",
"r":"0x42a7ca8603050e58d948df4573374746fabc8542c3fabc1d3b03391c2e50ae3b",
"s":"0x99b52df93bddbd15393ec9b6649c569ec9f65b1e7dda62b7a81baf32bd951a36",
"v":62709,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"signatureHashType":"0",
"queueOrigin":"0",
"chainId":31337
}
]
},
{
"hash":"0x9f8a3aae03d0f8f769d5af64583c1ee32458bd5b63bf6436147e624ef3297c69",
"parentHash":"0xb4c01e6e867856dce9b451ba67273f86bdc584ff398ed5b8c46f70ea37b1002f",
"number":7,
"timestamp":1603404108,
"nonce":"0x0000000000000042",
"difficulty":131456,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x967c90bfcb19090f0b73b0d0ca4d5f866160d7628d84412fada18abc50e3da69",
"blockHash":"0x9f8a3aae03d0f8f769d5af64583c1ee32458bd5b63bf6436147e624ef3297c69",
"blockNumber":7,
"transactionIndex":0,
"confirmations":10,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0x02e294"
},
"to":null,
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":6,
"data":"0x608060405234801561001057600080fd5b50600080546001600160a01b03191633179055610213806100326000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063776d1a0114610077575b60015460408051602036601f8101829004820283018201909352828252610075936001600160a01b0316926000918190840183828082843760009201919091525061009d92505050565b005b6100756004803603602081101561008d57600080fd5b50356001600160a01b031661015d565b60006060836001600160a01b0316836040518082805190602001908083835b602083106100db5780518252601f1990920191602091820191016100bc565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461013d576040519150601f19603f3d011682016040523d82523d6000602084013e610142565b606091505b5091509150811561015557805160208201f35b805160208201fd5b6000546001600160a01b031633141561019057600180546001600160a01b0319166001600160a01b0383161790556101da565b60015460408051602036601f81018290048202830182019093528282526101da936001600160a01b0316926000918190840183828082843760009201919091525061009d92505050565b5056fea2646970667358221220293887d48c4c1c34de868edf3e9a6be82327946c76d71f7c2023e67f556c6ecb64736f6c63430007000033",
"r":"0x16f49916bda30884d49df7f83c60ca49899fd21311e4cd4b464ac52bfa722b40",
"s":"0x3a2873207cbcba218ff71bb7e3916ea809c393c2407c03a70bbf4e393cbebfcb",
"v":62709,
"creates":"0x956dA338C1518a7FB213042b70c60c021aeBd554",
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"signatureHashType":"0",
"queueOrigin":"0",
"chainId":31337
}
]
},
{
"hash":"0x808315e40a80d00bf171b8ba924b451fb7936d446177c9e3545303b2ef830801",
"parentHash":"0x9f8a3aae03d0f8f769d5af64583c1ee32458bd5b63bf6436147e624ef3297c69",
"number":8,
"timestamp":1603404109,
"nonce":"0x0000000000000042",
"difficulty":131520,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x3dabdb84873e50c3b26df28ead1c053451a340b20d945a8cf8bddf5b5ff11775",
"blockHash":"0x808315e40a80d00bf171b8ba924b451fb7936d446177c9e3545303b2ef830801",
"blockNumber":8,
"transactionIndex":0,
"confirmations":9,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0xa93c"
},
"to":"0x956dA338C1518a7FB213042b70c60c021aeBd554",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":7,
"data":"0x776d1a01000000000000000000000000048b45c16e9631d3f630106c6086ec21a30cdf60",
"r":"0x553525e3656dfb41299a9f2a0d1a0058445e017bb98992de6d9b762203cf975a",
"s":"0x74b9f344513fa2881cfcec5a4c20027bb905de5c52a8e584da6767d946cd465d",
"v":62710,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"signatureHashType":"0",
"queueOrigin":"0",
"chainId":31337
}
]
},
{
"hash":"0xed13ac7f6bf594309a325c1558e4a3032f745e66ee0ffc2f95a701562b53dd13",
"parentHash":"0x808315e40a80d00bf171b8ba924b451fb7936d446177c9e3545303b2ef830801",
"number":9,
"timestamp":1603404110,
"nonce":"0x0000000000000042",
"difficulty":131584,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x2b9e216b390804c731f3ffc0630e522865bff35d0c539f377d204e94a346eddd",
"blockHash":"0xed13ac7f6bf594309a325c1558e4a3032f745e66ee0ffc2f95a701562b53dd13",
"blockNumber":9,
"transactionIndex":0,
"confirmations":8,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0xaf2f"
},
"to":"0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":8,
"data":"0x9b2ea4bd0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000956da338c1518a7fb213042b70c60c021aebd55400000000000000000000000000000000000000000000000000000000000000184f564d5f5374617465436f6d6d69746d656e74436861696e0000000000000000",
"r":"0x6318ce7714d7aefc9add30ebcf9d657a01b50c308c689c6f7d5567d806b6914e",
"s":"0xfe4d90d895f4b18b89309d768a383334bf5d622d367051079ac0e401b23960e0",
"v":62709,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"signatureHashType":"0",
"queueOrigin":"0",
"chainId":31337
}
]
},
{
"hash":"0x65f3831f69d1e746f30d07a3def1eefdcc6650ee0714cbac795c2be3ce9430a2",
"parentHash":"0xed13ac7f6bf594309a325c1558e4a3032f745e66ee0ffc2f95a701562b53dd13",
"number":10,
"timestamp":1603404111,
"nonce":"0x0000000000000042",
"difficulty":131648,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0xa77aca829b5bc91a16c11efb4dee280345851fccbd83b1469f746349894a46ab",
"blockHash":"0x65f3831f69d1e746f30d07a3def1eefdcc6650ee0714cbac795c2be3ce9430a2",
"blockNumber":10,
"transactionIndex":0,
"confirmations":7,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0x24f27e"
},
"to":null,
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":9,
"data":"0x12345678",
"r":"0x06e79a060942823dc5b328a5b059b58cf42372c03617122139deb5b7844c043d",
"s":"0xfabd07fab3f36816397917ae8c048a4675d34d4ca3f7b06ca6595796a159d359",
"v":62710,
"creates":"0x6454C9d69a4721feBA60e26A367bD4D56196Ee7c",
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"signatureHashType":"0",
"queueOrigin":"0",
"chainId":31337
}
]
},
{
"hash":"0x709bb2a24ac6120a12bf56e806cfcf3ceab71a41408b0685892800eebc4e3a45",
"parentHash":"0x65f3831f69d1e746f30d07a3def1eefdcc6650ee0714cbac795c2be3ce9430a2",
"number":11,
"timestamp":1603404112,
"nonce":"0x0000000000000042",
"difficulty":131712,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x2b5cd67036954f3c4cce09951c076adb3d0517750167e712562abba7910bd536",
"blockHash":"0x709bb2a24ac6120a12bf56e806cfcf3ceab71a41408b0685892800eebc4e3a45",
"blockNumber":11,
"transactionIndex":0,
"confirmations":6,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0x0f4240"
},
"to":"0x6454C9d69a4721feBA60e26A367bD4D56196Ee7c",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":10,
"data":"0x6fee07e00000000000000000000000000101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000c350000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000201111111111111111111111111111111111111111111111111111111111111111",
"r":"0x7538f2153c482a762f133f1438b30c8874887f6da52f04adeba5cc65632ee661",
"s":"0x3dc3a219d164b159ad61f874ccb5c75766cb1d73954196eaba142d890b299d0e",
"v":62710,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"signatureHashType":"0",
"queueOrigin":"0",
"chainId":31337
}
]
},
{
"hash":"0x13e683738bac942ae739e45b0a0e451b20d7e986d9463a2dac0acf2015e8d09b",
"parentHash":"0x709bb2a24ac6120a12bf56e806cfcf3ceab71a41408b0685892800eebc4e3a45",
"number":12,
"timestamp":1603404113,
"nonce":"0x0000000000000042",
"difficulty":131776,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x339282191d55c5b9fc53d68f199c53ea9b946c9975ed34f15312acd8a70054f7",
"blockHash":"0x13e683738bac942ae739e45b0a0e451b20d7e986d9463a2dac0acf2015e8d09b",
"blockNumber":12,
"transactionIndex":0,
"confirmations":5,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0x0f4240"
},
"to":"0x6454C9d69a4721feBA60e26A367bD4D56196Ee7c",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":11,
"data":"0x6fee07e00000000000000000000000000101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000c350000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000202222222222222222222222222222222222222222222222222222222222222222",
"r":"0x4ab1b83146fbfa4f0cce0110149c2c52e57971ce7cbe5b97a3fd3086bf9f0935",
"s":"0x11dac6b6c1e1d66a89d833ddd230120e6ecdedf49ae9fb38496f4385cb80057d",
"v":62709,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"signatureHashType":"0",
"queueOrigin":"0",
"chainId":31337
}
]
},
{
"hash":"0x382c468636dafe28d7b3aad0f0521c2e7b9395fc0146b16502d85fa2a49ffe7b",
"parentHash":"0x13e683738bac942ae739e45b0a0e451b20d7e986d9463a2dac0acf2015e8d09b",
"number":13,
"timestamp":1603404114,
"nonce":"0x0000000000000042",
"difficulty":131840,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x8c260f877daaaaeba12b41da6763ce2a641a55ec2e545c4dcbc211b340480e93",
"blockHash":"0x382c468636dafe28d7b3aad0f0521c2e7b9395fc0146b16502d85fa2a49ffe7b",
"blockNumber":13,
"transactionIndex":0,
"confirmations":4,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0x0f4240"
},
"to":"0x6454C9d69a4721feBA60e26A367bD4D56196Ee7c",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":12,
"data":"0x6fee07e00000000000000000000000000101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000c350000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000203333333333333333333333333333333333333333333333333333333333333333",
"r":"0x52f5316fc04aafc95110ac6be222ed656cd0a4ace50fac3e09384408c8b7e32a",
"s":"0x03de7779650526057629e842c45017cfd2dc19137d309cd99f244c0ce9b13186",
"v":62709,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"signatureHashType":"0",
"queueOrigin":"0",
"chainId":31337
}
]
},
{
"hash":"0x5e5f592a3c96a45bc7fceea8681d066af84709dc155d305fe4e3d68f0bb7bd63",
"parentHash":"0x382c468636dafe28d7b3aad0f0521c2e7b9395fc0146b16502d85fa2a49ffe7b",
"number":14,
"timestamp":1603404115,
"nonce":"0x0000000000000042",
"difficulty":131904,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x2fbd6a3a19c6e5778762619ecfd849b4c3beb35bce0c99b1f357077f3380fa9e",
"blockHash":"0x5e5f592a3c96a45bc7fceea8681d066af84709dc155d305fe4e3d68f0bb7bd63",
"blockNumber":14,
"transactionIndex":0,
"confirmations":3,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0x0f4240"
},
"to":"0x6454C9d69a4721feBA60e26A367bD4D56196Ee7c",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":13,
"data":"0x6fee07e00000000000000000000000000101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000c350000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000204444444444444444444444444444444444444444444444444444444444444444",
"r":"0x546c5a59b1753dd9b9f1f7ff0ae10f6f7fe07bfadf9882e140a021b0ca1a8ab4",
"s":"0x8b6c62b492ea8cf8e7dc8e8236ce7283a24e18370ce7950bda5f450fb7993547",
"v":62709,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"signatureHashType":"0",
"queueOrigin":"0",
"chainId":31337
}
]
},
{
"hash":"0x13be1ecbdbaae00332acaa341ea3168781b112e7aff368a8bab060fa102085f4",
"parentHash":"0x5e5f592a3c96a45bc7fceea8681d066af84709dc155d305fe4e3d68f0bb7bd63",
"number":15,
"timestamp":1603404116,
"nonce":"0x0000000000000042",
"difficulty":131968,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x3a60f459be600341f831b6e9b6b75a242cd31d1e4ae6b0bbd763a6b56054ef7b",
"blockHash":"0x13be1ecbdbaae00332acaa341ea3168781b112e7aff368a8bab060fa102085f4",
"blockNumber":15,
"transactionIndex":0,
"confirmations":2,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0x0f4240"
},
"to":"0x6454C9d69a4721feBA60e26A367bD4D56196Ee7c",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":14,
"data":"0x6fee07e00000000000000000000000000101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000c350000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000205555555555555555555555555555555555555555555555555555555555555555",
"r":"0x3b214ae35181aaf5542f970325ef36ca881db2fc1b145524534a4fb6885b05d7",
"s":"0xdf4fd847db5d5a246f4cedd2dee14e15e70ff469a7d88cdc86efa7c6d61d7cad",
"v":62709,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"signatureHashType":"0",
"queueOrigin":"0",
"chainId":31337
}
]
}
]
`)
import { expect, chai } from '../setup'
/* External Imports */
import { ethers } from 'hardhat'
import '@nomiclabs/hardhat-ethers'
import { Signer, ContractFactory, Contract, BigNumber } from 'ethers'
import ganache from 'ganache-core'
import sinon from 'sinon'
import { Web3Provider, JsonRpcProvider } from '@ethersproject/providers'
import { getContractInterface } from '@eth-optimism/contracts'
import { smockit, MockContract } from '@eth-optimism/smock'
/* Internal Imports */
import { MockchainProvider } from './mockchain-provider'
import {
makeAddressManager,
setProxyTarget,
FORCE_INCLUSION_PERIOD_SECONDS,
getContractFactory,
} from '../helpers'
import {
CanonicalTransactionChainContract,
QueueOrigin,
TransactionBatchSubmitter as RealTransactionBatchSubmitter,
TX_BATCH_SUBMITTER_LOG_TAG,
Batch,
BatchSubmitter,
} from '../../src'
import {
Signature,
TxType,
ctcCoder,
remove0x,
Logger,
} from '@eth-optimism/core-utils'
const DECOMPRESSION_ADDRESS = '0x4200000000000000000000000000000000000008'
const MAX_GAS_LIMIT = 8_000_000
const MAX_TX_SIZE = 100_000
const MIN_TX_SIZE = 1_000
const MIN_GAS_PRICE_IN_GWEI = 1
const MAX_GAS_PRICE_IN_GWEI = 70
const GAS_RETRY_INCREMENT = 5
const GAS_THRESHOLD_IN_GWEI = 120
// Helper functions
interface QueueElement {
queueRoot: string
timestamp: number
blockNumber: number
}
const getQueueElement = async (
ctcContract: Contract,
nextQueueIndex?: number
): Promise<QueueElement> => {
if (!nextQueueIndex) {
nextQueueIndex = await ctcContract.getNextQueueIndex()
}
const nextQueueElement = await ctcContract.getQueueElement(nextQueueIndex)
return nextQueueElement
}
const DUMMY_SIG: Signature = {
r: '11'.repeat(32),
s: '22'.repeat(32),
v: 1,
}
// A transaction batch submitter which skips the validate batch check
class TransactionBatchSubmitter extends RealTransactionBatchSubmitter {
protected async _validateBatch(batch: Batch): Promise<boolean> {
return true
}
}
describe('TransactionBatchSubmitter', () => {
let signer: Signer
let sequencer: Signer
before(async () => {
;[signer, sequencer] = await ethers.getSigners()
})
let AddressManager: Contract
let Mock__OVM_ExecutionManager: MockContract
let Mock__OVM_StateCommitmentChain: MockContract
before(async () => {
AddressManager = await makeAddressManager()
await AddressManager.setAddress(
'OVM_Sequencer',
await sequencer.getAddress()
)
await AddressManager.setAddress(
'OVM_DecompressionPrecompileAddress',
DECOMPRESSION_ADDRESS
)
Mock__OVM_ExecutionManager = await smockit(
await getContractFactory('OVM_ExecutionManager')
)
Mock__OVM_StateCommitmentChain = await smockit(
await getContractFactory('OVM_StateCommitmentChain')
)
await setProxyTarget(
AddressManager,
'OVM_ExecutionManager',
Mock__OVM_ExecutionManager
)
await setProxyTarget(
AddressManager,
'OVM_StateCommitmentChain',
Mock__OVM_StateCommitmentChain
)
Mock__OVM_StateCommitmentChain.smocked.canOverwrite.will.return.with(false)
Mock__OVM_ExecutionManager.smocked.getMaxTransactionGasLimit.will.return.with(
MAX_GAS_LIMIT
)
})
let Factory__OVM_CanonicalTransactionChain: ContractFactory
before(async () => {
Factory__OVM_CanonicalTransactionChain = await getContractFactory(
'OVM_CanonicalTransactionChain'
)
})
let OVM_CanonicalTransactionChain: CanonicalTransactionChainContract
let l2Provider: MockchainProvider
beforeEach(async () => {
const unwrapped_OVM_CanonicalTransactionChain = await Factory__OVM_CanonicalTransactionChain.deploy(
AddressManager.address,
FORCE_INCLUSION_PERIOD_SECONDS
)
await unwrapped_OVM_CanonicalTransactionChain.init()
await AddressManager.setAddress(
'OVM_CanonicalTransactionChain',
unwrapped_OVM_CanonicalTransactionChain.address
)
OVM_CanonicalTransactionChain = new CanonicalTransactionChainContract(
unwrapped_OVM_CanonicalTransactionChain.address,
getContractInterface('OVM_CanonicalTransactionChain'),
sequencer
)
l2Provider = new MockchainProvider(
OVM_CanonicalTransactionChain.address,
'0x' + '00'.repeat(20)
)
})
afterEach(() => {
sinon.restore()
})
describe('Submit', () => {
const enqueuedElements: Array<{
blockNumber: number
timestamp: number
}> = []
let batchSubmitter
beforeEach(async () => {
for (let i = 1; i < 15; i++) {
await OVM_CanonicalTransactionChain.enqueue(
'0x' + '01'.repeat(20),
50_000,
'0x' + i.toString().repeat(64),
{
gasLimit: 1_000_000,
}
)
}
batchSubmitter = new TransactionBatchSubmitter(
sequencer,
l2Provider as any,
MIN_TX_SIZE,
MAX_TX_SIZE,
10,
0,
1,
100000,
AddressManager.address,
1,
MIN_GAS_PRICE_IN_GWEI,
MAX_GAS_PRICE_IN_GWEI,
GAS_RETRY_INCREMENT,
GAS_THRESHOLD_IN_GWEI,
new Logger({ name: TX_BATCH_SUBMITTER_LOG_TAG }),
false
)
})
it('should submit a sequencer batch correctly', async () => {
l2Provider.setNumBlocksToReturn(5)
const nextQueueElement = await getQueueElement(
OVM_CanonicalTransactionChain
)
const data = ctcCoder.eip155TxData.encode({
sig: DUMMY_SIG,
gasLimit: 0,
gasPrice: 0,
nonce: 0,
target: '0x0000000000000000000000000000000000000000',
data: '0x',
type: TxType.EIP155,
})
l2Provider.setL2BlockData(
{
data,
l1BlockNumber: nextQueueElement.blockNumber - 1,
txType: TxType.EIP155,
queueOrigin: QueueOrigin.Sequencer,
l1TxOrigin: null,
} as any,
nextQueueElement.timestamp - 1
)
let receipt = await batchSubmitter.submitNextBatch()
let logData = remove0x(receipt.logs[1].data)
expect(parseInt(logData.slice(64 * 0, 64 * 1), 16)).to.equal(0) // _startingQueueIndex
expect(parseInt(logData.slice(64 * 1, 64 * 2), 16)).to.equal(0) // _numQueueElements
expect(parseInt(logData.slice(64 * 2, 64 * 3), 16)).to.equal(6) // _totalElements
receipt = await batchSubmitter.submitNextBatch()
logData = remove0x(receipt.logs[1].data)
expect(parseInt(logData.slice(64 * 0, 64 * 1), 16)).to.equal(0) // _startingQueueIndex
expect(parseInt(logData.slice(64 * 1, 64 * 2), 16)).to.equal(0) // _numQueueElements
expect(parseInt(logData.slice(64 * 2, 64 * 3), 16)).to.equal(11) // _totalElements
})
it('should submit a queue batch correctly', async () => {
l2Provider.setNumBlocksToReturn(5)
l2Provider.setL2BlockData({
queueOrigin: QueueOrigin.L1ToL2,
} as any)
let receipt = await batchSubmitter.submitNextBatch()
let logData = remove0x(receipt.logs[1].data)
expect(parseInt(logData.slice(64 * 0, 64 * 1), 16)).to.equal(0) // _startingQueueIndex
expect(parseInt(logData.slice(64 * 1, 64 * 2), 16)).to.equal(6) // _numQueueElements
expect(parseInt(logData.slice(64 * 2, 64 * 3), 16)).to.equal(6) // _totalElements
receipt = await batchSubmitter.submitNextBatch()
logData = remove0x(receipt.logs[1].data)
expect(parseInt(logData.slice(64 * 0, 64 * 1), 16)).to.equal(6) // _startingQueueIndex
expect(parseInt(logData.slice(64 * 1, 64 * 2), 16)).to.equal(5) // _numQueueElements
expect(parseInt(logData.slice(64 * 2, 64 * 3), 16)).to.equal(11) // _totalElements
})
it('should submit a batch with both queue and sequencer chain elements', async () => {
l2Provider.setNumBlocksToReturn(10) // For this batch we'll return 10 elements!
l2Provider.setL2BlockData({
queueOrigin: QueueOrigin.L1ToL2,
} as any)
// Turn blocks 3-5 into sequencer txs
const nextQueueElement = await getQueueElement(
OVM_CanonicalTransactionChain,
2
)
const data = ctcCoder.ethSignTxData.encode({
sig: DUMMY_SIG,
gasLimit: 0,
gasPrice: 0,
nonce: 0,
target: '0x0000000000000000000000000000000000000000',
data: '0x',
type: TxType.EthSign,
})
l2Provider.setL2BlockData(
{
data,
l1BlockNumber: nextQueueElement.blockNumber - 1,
txType: TxType.EthSign,
queueOrigin: QueueOrigin.Sequencer,
l1TxOrigin: null,
} as any,
nextQueueElement.timestamp - 1,
3,
6
)
const receipt = await batchSubmitter.submitNextBatch()
const logData = remove0x(receipt.logs[1].data)
expect(parseInt(logData.slice(64 * 0, 64 * 1), 16)).to.equal(0) // _startingQueueIndex
expect(parseInt(logData.slice(64 * 1, 64 * 2), 16)).to.equal(8) // _numQueueElements
expect(parseInt(logData.slice(64 * 2, 64 * 3), 16)).to.equal(11) // _totalElements
})
it('should submit a small batch only after the timeout', async () => {
l2Provider.setNumBlocksToReturn(2)
l2Provider.setL2BlockData({
queueOrigin: QueueOrigin.L1ToL2,
} as any)
const createBatchSubmitter = (
timeout: number
): TransactionBatchSubmitter =>
new TransactionBatchSubmitter(
sequencer,
l2Provider as any,
MIN_TX_SIZE,
MAX_TX_SIZE,
10,
timeout,
1,
100000,
AddressManager.address,
1,
MIN_GAS_PRICE_IN_GWEI,
MAX_GAS_PRICE_IN_GWEI,
GAS_RETRY_INCREMENT,
GAS_THRESHOLD_IN_GWEI,
new Logger({ name: TX_BATCH_SUBMITTER_LOG_TAG }),
false
)
// Create a batch submitter with a long timeout & make sure it doesn't submit the batches one after another
const longTimeout = 10_000
batchSubmitter = createBatchSubmitter(longTimeout)
let receipt = await batchSubmitter.submitNextBatch()
expect(receipt).to.not.be.undefined
receipt = await batchSubmitter.submitNextBatch()
// The receipt should be undefined because that means it didn't submit
expect(receipt).to.be.undefined
// This time create a batch submitter with a short timeout & it should submit batches after the timeout is reached
const shortTimeout = 5
batchSubmitter = createBatchSubmitter(shortTimeout)
receipt = await batchSubmitter.submitNextBatch()
expect(receipt).to.not.be.undefined
// Sleep for the short timeout
await new Promise((r) => setTimeout(r, shortTimeout))
receipt = await batchSubmitter.submitNextBatch()
// The receipt should NOT be undefined because that means it successfully submitted!
expect(receipt).to.not.be.undefined
})
it('should not submit if gas price is over threshold', async () => {
l2Provider.setNumBlocksToReturn(2)
l2Provider.setL2BlockData({
queueOrigin: QueueOrigin.L1ToL2,
} as any)
const highGasPriceWei = BigNumber.from(200).mul(1_000_000_000)
sinon
.stub(sequencer, 'getGasPrice')
.callsFake(async () => highGasPriceWei)
const receipt = await batchSubmitter.submitNextBatch()
expect(sequencer.getGasPrice).to.have.been.calledOnce
expect(receipt).to.be.undefined
})
it('should submit if gas price is not over threshold', async () => {
l2Provider.setNumBlocksToReturn(2)
l2Provider.setL2BlockData({
queueOrigin: QueueOrigin.L1ToL2,
} as any)
const lowGasPriceWei = BigNumber.from(2).mul(1_000_000_000)
sinon.stub(sequencer, 'getGasPrice').callsFake(async () => lowGasPriceWei)
const receipt = await batchSubmitter.submitNextBatch()
expect(sequencer.getGasPrice).to.have.been.calledOnce
expect(receipt).to.not.be.undefined
})
})
})
describe('TransactionBatchSubmitter to Ganache', () => {
let signer
const server = ganache.server({
default_balance_ether: 420,
blockTime: 2_000,
})
const provider = new Web3Provider(ganache.provider())
before(async () => {
await server.listen(3001)
signer = await provider.getSigner()
})
after(async () => {
await server.close()
})
// Unit test for getReceiptWithResubmission function,
// tests for increasing gas price on resubmission
it('should resubmit a transaction if it is not confirmed', async () => {
const gasPrices = []
const numConfirmations = 2
const sendTxFunc = async (gasPrice) => {
// push the retried gasPrice
gasPrices.push(gasPrice)
const tx = signer.sendTransaction({
to: DECOMPRESSION_ADDRESS,
value: 88,
nonce: 0,
gasPrice,
})
const response = await tx
return signer.provider.waitForTransaction(response.hash, numConfirmations)
}
const resubmissionConfig = {
numConfirmations,
resubmissionTimeout: 1_000, // retry every second
minGasPriceInGwei: 0,
maxGasPriceInGwei: 100,
gasRetryIncrement: 5,
}
BatchSubmitter.getReceiptWithResubmission(
sendTxFunc,
resubmissionConfig,
new Logger({ name: TX_BATCH_SUBMITTER_LOG_TAG })
)
// Wait 1.5s for at least 1 retry
await new Promise((r) => setTimeout(r, 1500))
// Iterate through gasPrices to ensure each entry increases from
// the last
const isIncreasing = gasPrices.reduce(
(isInc, gasPrice, i, gP) =>
(isInc && gasPrice > gP[i - 1]) || Number.NEGATIVE_INFINITY,
true
)
expect(gasPrices).to.have.lengthOf.above(1) // retried at least once
expect(isIncreasing).to.be.true
})
})
/* External Imports */
import { ethers } from 'ethers'
import { defaultAccounts } from 'ethereum-waffle'
export const FORCE_INCLUSION_PERIOD_SECONDS = 600
export const DEFAULT_ACCOUNTS = defaultAccounts
export const DEFAULT_ACCOUNTS_HARDHAT = defaultAccounts.map((account) => {
return {
balance: ethers.BigNumber.from(account.balance).toHexString(),
privateKey: account.secretKey,
}
})
export const OVM_TX_GAS_LIMIT = 10_000_000
export const RUN_OVM_TEST_GAS = 20_000_000
/* External Imports */
import { keccak256 } from 'ethers/lib/utils'
export const DUMMY_BYTECODE = '0x123412341234'
export const DUMMY_BYTECODE_BYTELEN = 6
export const DUMMY_BYTECODE_HASH = keccak256(DUMMY_BYTECODE)
/* External Imports */
import { ethers } from 'ethers'
export const DUMMY_BYTES32: string[] = Array.from(
{
length: 10,
},
(_, i) => {
return ethers.utils.keccak256(`0x0${i}`)
}
)
export * from './bytes32'
export * from './bytecode'
export * from './dummy'
export * from './constants'
export * from './resolver'
export * from './utils'
/* External Imports */
import { ethers } from 'hardhat'
import { Contract } from 'ethers'
import { getContractFactory as ctFactory } from '@eth-optimism/contracts'
export const getContractFactory = async (contract: string) =>
ctFactory(contract, (await ethers.getSigners())[0])
export const setProxyTarget = async (
AddressManager: Contract,
name: string,
target: Contract
): Promise<void> => {
const SimpleProxy: Contract = await (
await getContractFactory('Helper_SimpleProxy')
).deploy()
await SimpleProxy.setTarget(target.address)
await AddressManager.setAddress(name, SimpleProxy.address)
}
export const makeAddressManager = async (): Promise<Contract> => {
return (await getContractFactory('Lib_AddressManager')).deploy()
}
/* External Imports */
import { BigNumber } from 'ethers'
/**
* Converts a string or buffer to a '0x'-prefixed hex string.
* @param buf String or buffer to convert.
* @returns '0x'-prefixed string.
*/
export const toHexString = (buf: Buffer | string): string => {
return '0x' + fromHexString(buf).toString('hex')
}
/**
* Converts a '0x'-prefixed string to a buffer.
* @param str '0x'-prefixed string to convert.
* @returns Hex buffer.
*/
export const fromHexString = (str: string | Buffer): Buffer => {
if (typeof str === 'string' && str.startsWith('0x')) {
return Buffer.from(str.slice(2), 'hex')
}
return Buffer.from(str)
}
export const toHexString32 = (
input: Buffer | string | number,
padRight = false
): string => {
if (typeof input === 'number') {
input = BigNumber.from(input).toHexString()
}
input = toHexString(input).slice(2)
return '0x' + (padRight ? input.padEnd(64, '0') : input.padStart(64, '0'))
}
export const getHexSlice = (
input: Buffer | string,
start: number,
length: number
): string => {
return toHexString(fromHexString(input).slice(start, start + length))
}
/**
* Generates a hex string of repeated bytes.
* @param byte Byte to repeat.
* @param len Number of times to repeat the byte.
* @return '0x'-prefixed hex string filled with the provided byte.
*/
export const makeHexString = (byte: string, len: number): string => {
return '0x' + byte.repeat(len)
}
/**
* Genereates an address with a repeated byte.
* @param byte Byte to repeat in the address.
* @return Address filled with the repeated byte.
*/
export const makeAddress = (byte: string): string => {
return makeHexString(byte, 20)
}
/**
* Removes '0x' from a hex string.
* @param str Hex string to remove '0x' from.
* @returns String without the '0x' prefix.
*/
export const remove0x = (str: string): string => {
if (str.startsWith('0x')) {
return str.slice(2)
} else {
return str
}
}
export const getEthTime = async (provider: any): Promise<number> => {
return (await provider.getBlock('latest')).timestamp
}
export const setEthTime = async (
provider: any,
time: number
): Promise<void> => {
await provider.send('evm_setNextBlockTimestamp', [time])
}
export const increaseEthTime = async (
provider: any,
amount: number
): Promise<void> => {
await setEthTime(provider, (await getEthTime(provider)) + amount)
await provider.send('evm_mine', [])
}
export const getBlockTime = async (
provider: any,
block: number
): Promise<number> => {
await provider.send('evm_mine', [])
return (await provider.getBlock(block)).timestamp
}
export const getNextBlockNumber = async (provider: any): Promise<number> => {
return (await provider.getBlock('latest')).number + 1
}
export * from './buffer-utils'
export * from './byte-utils'
export * from './eth-time'
/* External Imports */
import chai = require('chai')
import sinonChai from 'sinon-chai'
import Mocha from 'mocha'
const should = chai.should()
const expect = chai.expect
chai.use(sinonChai)
export { should, expect, chai, Mocha }
{
"extends": "../../tsconfig.build.json",
"compilerOptions": {
"outDir": "./dist"
},
"include": [
"src/**/*"
]
}
{
"extends": "../../tsconfig.json"
}
{
"extends": "../../tslint.base.json"
}
......@@ -65,6 +65,7 @@ export class SimpleDB {
}
private _makeKey(key: string, index: number): string {
// prettier-ignore
return `${key}:${BigNumber.from(index).toString().padStart(32, '0')}`
}
}
......@@ -40,6 +40,7 @@ const optionSettings = {
},
}
// prettier-ignore
export class L1DataTransportService extends BaseService<L1DataTransportServiceOptions> {
constructor(options: L1DataTransportServiceOptions) {
super('L1 Data Transport Service', options, optionSettings)
......
......@@ -24,6 +24,7 @@ export const toRpcHexString = (n: number): string => {
if (n === 0) {
return '0x0'
} else {
// prettier-ignore
return '0x' + toHexString(n).slice(2).replace(/^0+/, '')
}
}
......
{
"name": "@eth-optimism/smock",
"version": "1.0.0-alpha.3",
"main": "dist/index",
"types": "dist/index",
"main": "dist/src/index",
"types": "dist/src/index",
"files": [
"dist/index"
"dist/src/index"
],
"license": "MIT",
"scripts": {
......
......@@ -26,6 +26,7 @@
"semicolon": false,
"variable-name": false,
"no-focused-test": true,
"array-type": false,
"prettier": [true, "./prettier-config.json"]
},
"linterOptions": {
......
......@@ -39,6 +39,18 @@
resolved "https://registry.yarnpkg.com/@ensdomains/resolver/-/resolver-0.2.4.tgz#c10fe28bf5efbf49bff4666d909aed0265efbc89"
integrity sha512-bvaTH34PMCbv6anRa9I/0zjLJgY4EuznbEMgbV77JBCQ9KNC46rzi0avuxpOfu+xDjPEtSFGqVEOr5GlUSGudA==
"@eth-optimism/contracts@^0.0.2-alpha.7":
version "0.0.2-alpha.15"
resolved "https://registry.yarnpkg.com/@eth-optimism/contracts/-/contracts-0.0.2-alpha.15.tgz#d602bdb6f1186d111ce9f7c282fc80e023086892"
integrity sha512-upJfYHDWQY7nM0AYT9MKQiuOus2uMUhvqS962qiBq3Ly/9GUq5mS0UALynsrZBGbzT6pflOMKFFEv7jQEORGmA==
dependencies:
"@eth-optimism/solc" "^0.6.12-alpha.1"
"@ethersproject/contracts" "^5.0.5"
"@ethersproject/hardware-wallets" "^5.0.8"
"@openzeppelin/contracts" "^3.3.0"
ethers "5.0.0"
ganache-core "^2.12.1"
"@eth-optimism/contracts@^0.1.6":
version "0.1.11"
resolved "https://registry.yarnpkg.com/@eth-optimism/contracts/-/contracts-0.1.11.tgz#d33354b69e1bdaf11eac799a1f4dfb17295a04e7"
......@@ -53,6 +65,27 @@
ganache-core "^2.12.1"
glob "^7.1.6"
"@eth-optimism/core-utils@0.0.1-alpha.30":
version "0.0.1-alpha.30"
resolved "https://registry.yarnpkg.com/@eth-optimism/core-utils/-/core-utils-0.0.1-alpha.30.tgz#31462596753df182e89cea63e19bf621c07ad898"
integrity sha512-ir8OevZSRu3U7npO+oY9VXADtXIKGHbw/V9CGPMNgXyuoifnuBLuL0OouL0OUhPRAbPHauYwjJX1xr29nJe1Tw==
dependencies:
abstract-leveldown "^6.2.2"
async-lock "^1.2.2"
axios "^0.19.0"
bn.js "^4.11.8"
body-parser "^1.19.0"
chai "^4.2.0"
chai-as-promised "^7.1.1"
debug "^4.1.1"
dotenv "^8.2.0"
ethereumjs-util "^6.2.0"
ethers-v4 "npm:ethers@4"
express "^4.17.1"
memdown "^4.0.0"
ts-md5 "^1.2.4"
uuid "^3.3.3"
"@eth-optimism/dev@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@eth-optimism/dev/-/dev-1.1.1.tgz#7bae95b975c1d6641b4ae550cb3ec631c667a56b"
......@@ -74,7 +107,38 @@
tslint-plugin-prettier "^2.3.0"
typescript "^4.1.5"
"@ethereum-waffle/chai@^3.3.0":
"@eth-optimism/provider@^0.0.1-alpha.13":
version "0.0.1-alpha.14"
resolved "https://registry.yarnpkg.com/@eth-optimism/provider/-/provider-0.0.1-alpha.14.tgz#fec9df0abcdf8281c6a3cf4bc0e0085ccc7691c4"
integrity sha512-fEOSHxWt5nII345c4cQtUUZ7Pb/G3GzqvmJOddx27oUVr3zdjeScOO/VXoIf9B8wXxIqUz93i1MDHks8V95CRg==
dependencies:
"@eth-optimism/core-utils" "0.0.1-alpha.30"
bn.js "^5.1.3"
ethers "^5.0.24"
"@eth-optimism/solc@^0.6.12-alpha.1":
version "0.6.12-alpha.1"
resolved "https://registry.yarnpkg.com/@eth-optimism/solc/-/solc-0.6.12-alpha.1.tgz#041876f83b34c6afe2f19dfe9626568df6ed8590"
integrity sha512-Ky73mo+2iNJs/VTaT751nMeZ7hXns0TBAlffTOxIOsScjAZ/zi/KWsDUo3r89aV2JKXcYAU/bLidxF40MVJeUw==
dependencies:
command-exists "^1.2.8"
commander "3.0.2"
follow-redirects "^1.12.1"
fs-extra "^0.30.0"
js-sha3 "0.8.0"
memorystream "^0.3.1"
require-from-string "^2.0.0"
semver "^5.5.0"
tmp "0.0.33"
"@eth-optimism/ynatm@^0.2.2":
version "0.2.2"
resolved "https://registry.yarnpkg.com/@eth-optimism/ynatm/-/ynatm-0.2.2.tgz#b1f165c3149188f184b66329228746260ae18677"
integrity sha512-R/hIAFWEj2sjf3inNEGCffmGofqMFY/7PS/Hh4A/62Kg0wMM8rsyMyW8pXngMnD/EQAjR8WTtKDutq/L5vSMTQ==
dependencies:
bluebird "^3.7.2"
"@ethereum-waffle/chai@^3.0.0", "@ethereum-waffle/chai@^3.3.0":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@ethereum-waffle/chai/-/chai-3.3.1.tgz#3f20b810d0fa516f19af93c50c3be1091333fa8e"
integrity sha512-+vepCjttfOzCSnmiVEmd1bR8ctA2wYVrtWa8bDLhnTpj91BIIHotNDTwpeq7fyjrOCIBTN3Ai8ACfjNoatc4OA==
......@@ -82,7 +146,7 @@
"@ethereum-waffle/provider" "^3.3.1"
ethers "^5.0.0"
"@ethereum-waffle/compiler@^3.3.0":
"@ethereum-waffle/compiler@^3.0.0", "@ethereum-waffle/compiler@^3.3.0":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@ethereum-waffle/compiler/-/compiler-3.3.1.tgz#946128fd565aa4347075fd716dbd0f3f38189280"
integrity sha512-X/TeQugt94AQwXEdCjIQxcXYGawNulVBYEBE7nloj4wE/RBxNolXwjoVNjcS4kuiMMbKkdO0JkL5sn6ixx8bDg==
......@@ -108,7 +172,7 @@
"@ensdomains/resolver" "^0.2.4"
ethers "^5.0.1"
"@ethereum-waffle/mock-contract@^3.2.2":
"@ethereum-waffle/mock-contract@^3.0.0", "@ethereum-waffle/mock-contract@^3.2.2":
version "3.2.2"
resolved "https://registry.yarnpkg.com/@ethereum-waffle/mock-contract/-/mock-contract-3.2.2.tgz#5749b03cbb4850150f81cf66151c4523eb7436f0"
integrity sha512-H60Cc5C7sYNU4LuPMSKDh8YIaN9/fkwEjznY78CEbOosO+lMlFYdA+5VZjeDGDuYKfsBqsocQdkj1CRyoi1KNw==
......@@ -116,7 +180,7 @@
"@ethersproject/abi" "^5.0.1"
ethers "^5.0.1"
"@ethereum-waffle/provider@^3.3.0", "@ethereum-waffle/provider@^3.3.1":
"@ethereum-waffle/provider@^3.0.0", "@ethereum-waffle/provider@^3.3.0", "@ethereum-waffle/provider@^3.3.1":
version "3.3.2"
resolved "https://registry.yarnpkg.com/@ethereum-waffle/provider/-/provider-3.3.2.tgz#33677baf6af5cbb087c3072d84f38c152968ebb1"
integrity sha512-ilz6cXK0ylSKCmZktTMpY4gjo0CN6rb86JfN7+RZYk6tKtZA6sXoOe95skWEQkGf1fZk7G817fTzLb0CmFDp1g==
......@@ -142,7 +206,7 @@
"@ethersproject/properties" ">=5.0.0-beta.131"
"@ethersproject/strings" ">=5.0.0-beta.130"
"@ethersproject/abi@5.0.13", "@ethersproject/abi@^5.0.1", "@ethersproject/abi@^5.0.10", "@ethersproject/abi@^5.0.13":
"@ethersproject/abi@5.0.13", "@ethersproject/abi@^5.0.0", "@ethersproject/abi@^5.0.1", "@ethersproject/abi@^5.0.10", "@ethersproject/abi@^5.0.13":
version "5.0.13"
resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.13.tgz#600a559c3730467716595658beaa2894b4352bcc"
integrity sha512-2coOH3D7ra1lwamKEH0HVc+Jbcsw5yfeCgmY8ekhCDualEiyyovD2qDcMBBcY3+kjoLHVTmo7ost6MNClxdOrg==
......@@ -157,7 +221,7 @@
"@ethersproject/properties" "^5.0.7"
"@ethersproject/strings" "^5.0.8"
"@ethersproject/abstract-provider@5.0.10", "@ethersproject/abstract-provider@^5.0.10", "@ethersproject/abstract-provider@^5.0.8", "@ethersproject/abstract-provider@^5.0.9":
"@ethersproject/abstract-provider@5.0.10", "@ethersproject/abstract-provider@^5.0.0", "@ethersproject/abstract-provider@^5.0.10", "@ethersproject/abstract-provider@^5.0.5", "@ethersproject/abstract-provider@^5.0.8", "@ethersproject/abstract-provider@^5.0.9":
version "5.0.10"
resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.0.10.tgz#a533aed39a5f27312745c8c4c40fa25fc884831c"
integrity sha512-OSReY5iz94iIaPlRvLiJP8YVIvQLx4aUvMMnHWSaA/vTU8QHZmgNlt4OBdYV1+aFY8Xl+VRYiWBHq72ZDKXXCQ==
......@@ -170,7 +234,7 @@
"@ethersproject/transactions" "^5.0.9"
"@ethersproject/web" "^5.0.12"
"@ethersproject/abstract-signer@5.0.14", "@ethersproject/abstract-signer@^5.0.10":
"@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"
integrity sha512-JztBwVO7o5OHLh2vyjordlS4/1EjRyaECtc8vPdXTF1i4dXN+J0coeRoPN6ZFbBvi/YbaB6br2fvqhst1VQD/g==
......@@ -181,7 +245,7 @@
"@ethersproject/logger" "^5.0.8"
"@ethersproject/properties" "^5.0.7"
"@ethersproject/address@5.0.11", "@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.9":
"@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"
integrity sha512-Et4GBdD8/tsBGjCEOKee9upN29qjL5kbRcmJifb4Penmiuh9GARXL2/xpXvEp5EW+EIW/rfCHFJrkYBgoQFQBw==
......@@ -192,7 +256,7 @@
"@ethersproject/logger" "^5.0.8"
"@ethersproject/rlp" "^5.0.7"
"@ethersproject/base64@5.0.9", "@ethersproject/base64@^5.0.7":
"@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"
integrity sha512-37RBz5LEZ9SlTNGiWCYFttnIN9J7qVs9Xo2EbqGqDH5LfW9EIji66S+YDMpXVo1zWDax1FkEldAoatxHK2gfgA==
......@@ -207,7 +271,7 @@
"@ethersproject/bytes" "^5.0.9"
"@ethersproject/properties" "^5.0.7"
"@ethersproject/bignumber@5.0.15", "@ethersproject/bignumber@>=5.0.0-beta.130", "@ethersproject/bignumber@^5.0.13":
"@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"
integrity sha512-MTADqnyacvdRwtKh7o9ujwNDSM1SDJjYDMYAzjIgjoi9rh6TY4suMbhCa3i2vh3SUXiXSICyTI8ui+NPdrZ9Lw==
......@@ -216,21 +280,21 @@
"@ethersproject/logger" "^5.0.8"
bn.js "^4.4.0"
"@ethersproject/bytes@5.0.11", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.9":
"@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"
integrity sha512-D51plLYY5qF05AsoVQwIZVLqlBkaTPVHVP/1WmmBIWyHB0cRW0C9kh0kx5Exo51rB63Hk8PfHxc7SmpoaQFEyg==
dependencies:
"@ethersproject/logger" "^5.0.8"
"@ethersproject/constants@5.0.10", "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.0.8":
"@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"
integrity sha512-OSo8jxkHLDXieCy8bgOFR7lMfgPxEzKvSDdP+WAWHCDM8+orwch0B6wzkTmiQFgryAtIctrBt5glAdJikZ3hGw==
dependencies:
"@ethersproject/bignumber" "^5.0.13"
"@ethersproject/contracts@5.0.12", "@ethersproject/contracts@^5.0.5":
"@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"
integrity sha512-srijy31idjz8bE+gL1I6IRj2H4I9dUwfQ+QroLrIgNdGArqY8y2iFUKa3QTy+JBX26fJsdYiCQi1kKkaNpnMpQ==
......@@ -257,7 +321,7 @@
optionalDependencies:
"@ledgerhq/hw-transport-node-hid" "5.26.0"
"@ethersproject/hash@5.0.12", "@ethersproject/hash@>=5.0.0-beta.128", "@ethersproject/hash@^5.0.10":
"@ethersproject/hash@5.0.12", "@ethersproject/hash@>=5.0.0-beta.128", "@ethersproject/hash@^5.0.0", "@ethersproject/hash@^5.0.10":
version "5.0.12"
resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.0.12.tgz#1074599f7509e2ca2bb7a3d4f4e39ab3a796da42"
integrity sha512-kn4QN+fhNFbUgX3XZTZUaQixi0oyfIEY+hfW+KtkHu+rq7dV76oAIvaLEEynu1/4npOL38E4X4YI42gGZk+C0Q==
......@@ -271,7 +335,7 @@
"@ethersproject/properties" "^5.0.7"
"@ethersproject/strings" "^5.0.8"
"@ethersproject/hdnode@5.0.10", "@ethersproject/hdnode@^5.0.8":
"@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"
integrity sha512-ZLwMtIcXK7xz2lSITDCl40W04CtRq4K9NwBxhCzdzPdaz6XnoJMwGz2YMVLg+8ksseq+RYtTwIIXtlK6vyvQyg==
......@@ -289,7 +353,7 @@
"@ethersproject/transactions" "^5.0.9"
"@ethersproject/wordlists" "^5.0.8"
"@ethersproject/json-wallets@5.0.12", "@ethersproject/json-wallets@^5.0.10":
"@ethersproject/json-wallets@5.0.12", "@ethersproject/json-wallets@^5.0.0", "@ethersproject/json-wallets@^5.0.10":
version "5.0.12"
resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.0.12.tgz#8946a0fcce1634b636313a50330b7d30a24996e8"
integrity sha512-nac553zGZnOewpjlqbfy7WBl8m3y7qudzRsI2dCxrediYtPIVIs9f6Pbnou8vDmmp8X4/U4W788d+Ma88o+Gbg==
......@@ -308,7 +372,7 @@
aes-js "3.0.0"
scrypt-js "3.0.1"
"@ethersproject/keccak256@5.0.9", "@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.0.7":
"@ethersproject/keccak256@5.0.9", "@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.0.0", "@ethersproject/keccak256@^5.0.7":
version "5.0.9"
resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.0.9.tgz#ca0d86e4af56c13b1ef25e533bde3e96d28f647d"
integrity sha512-zhdUTj6RGtCJSgU+bDrWF6cGbvW453LoIC1DSNWrTlXzC7WuH4a+EiPrgc7/kNoRxerKuA/cxYlI8GwNtVtDlw==
......@@ -316,19 +380,19 @@
"@ethersproject/bytes" "^5.0.9"
js-sha3 "0.5.7"
"@ethersproject/logger@5.0.10", "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.0.8":
"@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/networks@5.0.9", "@ethersproject/networks@^5.0.7":
"@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"
integrity sha512-L8+VCQwArBLGkxZb/5Ns/OH/OxP38AcaveXIxhUTq+VWpXYjrObG3E7RDQIKkUx1S1IcQl/UWTz5w4DK0UitJg==
dependencies:
"@ethersproject/logger" "^5.0.8"
"@ethersproject/pbkdf2@5.0.9", "@ethersproject/pbkdf2@^5.0.7":
"@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"
integrity sha512-ItE/wQ/WVw/ajEHPUVgfu0aEvksPgOQc+278bke8sGKnGO3ppjmqp0MHh17tHc1EBTzJbSms5aLIqc56qZ/oiA==
......@@ -336,14 +400,14 @@
"@ethersproject/bytes" "^5.0.9"
"@ethersproject/sha2" "^5.0.7"
"@ethersproject/properties@5.0.9", "@ethersproject/properties@>=5.0.0-beta.131", "@ethersproject/properties@^5.0.7":
"@ethersproject/properties@5.0.9", "@ethersproject/properties@>=5.0.0-beta.131", "@ethersproject/properties@^5.0.0", "@ethersproject/properties@^5.0.7":
version "5.0.9"
resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.0.9.tgz#d7aae634680760136ea522e25c3ef043ec15b5c2"
integrity sha512-ZCjzbHYTw+rF1Pn8FDCEmx3gQttwIHcm/6Xee8g/M3Ga3SfW4tccNMbs5zqnBH0E4RoOPaeNgyg1O68TaF0tlg==
dependencies:
"@ethersproject/logger" "^5.0.8"
"@ethersproject/providers@5.0.24", "@ethersproject/providers@^5.0.21":
"@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"
integrity sha512-M4Iw1r4gGJkt7ZUa++iREuviKL/DIpmIMsaUlVlXtV+ZrUXeN8xQ3zOTrbz7R4h9W9oljBZM7i4D3Kn1krJ30A==
......@@ -368,7 +432,7 @@
bech32 "1.1.4"
ws "7.2.3"
"@ethersproject/random@5.0.9", "@ethersproject/random@^5.0.7":
"@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"
integrity sha512-DANG8THsKqFbJOantrxumtG6gyETNE54VfbsWa+SQAT8WKpDo9W/X5Zhh73KuhClaey1UI32uVmISZeq/Zxn1A==
......@@ -376,7 +440,7 @@
"@ethersproject/bytes" "^5.0.9"
"@ethersproject/logger" "^5.0.8"
"@ethersproject/rlp@5.0.9", "@ethersproject/rlp@^5.0.7":
"@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"
integrity sha512-ns1U7ZMVeruUW6JXc4om+1w3w4ynHN/0fpwmeNTsAjwGKoF8SAUgue6ylKpHKWSti2idx7jDxbn8hNNFHk67CA==
......@@ -384,7 +448,7 @@
"@ethersproject/bytes" "^5.0.9"
"@ethersproject/logger" "^5.0.8"
"@ethersproject/sha2@5.0.9", "@ethersproject/sha2@^5.0.7":
"@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"
integrity sha512-5FH4s47gM7N1fFAYQ1+m7aX0SbLg0Xr+6tvqndmNqc382/qBIbzXiGlUookrsjlPb6gLNurnTssCXjNM72J6lQ==
......@@ -393,7 +457,7 @@
"@ethersproject/logger" "^5.0.8"
hash.js "1.1.3"
"@ethersproject/signing-key@5.0.11", "@ethersproject/signing-key@^5.0.8":
"@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"
integrity sha512-Jfcru/BGwdkXhLxT+8WCZtFy7LL0TPFZw05FAb5asxB/MyVsEfNdNxGDtjVE9zXfmRSPe/EusXYY4K7wcygOyQ==
......@@ -403,7 +467,7 @@
"@ethersproject/properties" "^5.0.7"
elliptic "6.5.4"
"@ethersproject/solidity@5.0.10":
"@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"
integrity sha512-8OG3HLqynWXDA6mVIHuHfF/ojTTwBahON7hc9GAKCqglzXCkVA3OpyxOJXPzjHClRIAUUiU7r9oy9Z/nsjtT/g==
......@@ -414,7 +478,7 @@
"@ethersproject/sha2" "^5.0.7"
"@ethersproject/strings" "^5.0.8"
"@ethersproject/strings@5.0.10", "@ethersproject/strings@>=5.0.0-beta.130", "@ethersproject/strings@^5.0.8":
"@ethersproject/strings@5.0.10", "@ethersproject/strings@>=5.0.0-beta.130", "@ethersproject/strings@^5.0.0", "@ethersproject/strings@^5.0.8":
version "5.0.10"
resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.0.10.tgz#ddce1e9724f4ac4f3f67e0cac0b48748e964bfdb"
integrity sha512-KAeoS1tZ9/5ECXiIZA6S6hywbD0so2VmuW+Wfyo5EDXeyZ6Na1nxTPhTnW7voQmjbeYJffCrOc0qLFJeylyg7w==
......@@ -423,7 +487,7 @@
"@ethersproject/constants" "^5.0.8"
"@ethersproject/logger" "^5.0.8"
"@ethersproject/transactions@5.0.11", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.0.9":
"@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"
integrity sha512-ftsRvR9+gQp7L63F6+XmstvsZ4w8GtWvQB08e/zB+oB86Fnhq8+i/tkgpJplSHC8I/qgiCisva+M3u2GVhDFPA==
......@@ -438,7 +502,7 @@
"@ethersproject/rlp" "^5.0.7"
"@ethersproject/signing-key" "^5.0.8"
"@ethersproject/units@5.0.11":
"@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"
integrity sha512-nOSPmcCWyB/dwoBRhhTtPGCsTbiXqmc7Q0Adwvafc432AC7hy3Fj3IFZtnSXsbtJ/GdHCIUIoA8gtvxSsFuBJg==
......@@ -447,7 +511,7 @@
"@ethersproject/constants" "^5.0.8"
"@ethersproject/logger" "^5.0.8"
"@ethersproject/wallet@5.0.12":
"@ethersproject/wallet@5.0.12", "@ethersproject/wallet@^5.0.0":
version "5.0.12"
resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.0.12.tgz#bfb96f95e066b4b1b4591c4615207b87afedda8b"
integrity sha512-rboJebGf47/KPZrKZQdYg9BAYuXbc/OwcUyML1K1f2jnJeo1ObWV11U1PAWTjTbhhSy6/Fg+34GO2yMb5Dt1Rw==
......@@ -468,7 +532,7 @@
"@ethersproject/transactions" "^5.0.9"
"@ethersproject/wordlists" "^5.0.8"
"@ethersproject/web@5.0.14", "@ethersproject/web@^5.0.12":
"@ethersproject/web@5.0.14", "@ethersproject/web@^5.0.0", "@ethersproject/web@^5.0.12":
version "5.0.14"
resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.0.14.tgz#6e7bebdd9fb967cb25ee60f44d9218dc0803bac4"
integrity sha512-QpTgplslwZ0Sp9oKNLoRuS6TKxnkwfaEk3gr7zd7XLF8XBsYejsrQO/03fNfnMx/TAT/RR6WEw/mbOwpRSeVRA==
......@@ -479,7 +543,7 @@
"@ethersproject/properties" "^5.0.7"
"@ethersproject/strings" "^5.0.8"
"@ethersproject/wordlists@5.0.10", "@ethersproject/wordlists@^5.0.8":
"@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"
integrity sha512-jWsEm1iJzpg9SCXnNfFz+tcp4Ofzv0TJb6mj+soCNcar9GcT0yGz62ZsHC3pLQWaF4LkCzGwRJHJTXKjHQfG1A==
......@@ -1594,6 +1658,34 @@
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==
"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1":
version "1.8.2"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.2.tgz#858f5c4b48d80778fde4b9d541f27edc0d56488b"
integrity sha512-sruwd86RJHdsVf/AtBoijDmUqJp3B6hF/DGC23C+JaegnDHaZyewCjoVGTdg3J0uz3Zs7NnIT05OBOmML72lQw==
dependencies:
type-detect "4.0.8"
"@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40"
integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==
dependencies:
"@sinonjs/commons" "^1.7.0"
"@sinonjs/samsam@^5.3.1":
version "5.3.1"
resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.3.1.tgz#375a45fe6ed4e92fca2fb920e007c48232a6507f"
integrity sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg==
dependencies:
"@sinonjs/commons" "^1.6.0"
lodash.get "^4.4.2"
type-detect "^4.0.8"
"@sinonjs/text-encoding@^0.7.1":
version "0.7.1"
resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5"
integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==
"@solidity-parser/parser@^0.11.0":
version "0.11.1"
resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.11.1.tgz#fa840af64840c930f24a9c82c08d4a092a068add"
......@@ -1650,14 +1742,14 @@
resolved "https://registry.yarnpkg.com/@types/browser-or-node/-/browser-or-node-1.3.0.tgz#896ec59bcb8109fc858d8e68d3c056c176a19622"
integrity sha512-MVetr65IR7RdJbUxVHsaPFaXAO8fi89zv1g8L/mHygh1Q7xnnK02XZLwfMh57FOpTO6gtnagoPMQ/UOFfctXRQ==
"@types/chai-as-promised@^7.1.3":
"@types/chai-as-promised@^7.1.0", "@types/chai-as-promised@^7.1.3":
version "7.1.3"
resolved "https://registry.yarnpkg.com/@types/chai-as-promised/-/chai-as-promised-7.1.3.tgz#779166b90fda611963a3adbfd00b339d03b747bd"
integrity sha512-FQnh1ohPXJELpKhzjuDkPLR2BZCAqed+a6xV4MI/T3XzHfd2FlarfUGUdZYgqYe8oxkYn0fchHEeHfHqdZ96sg==
dependencies:
"@types/chai" "*"
"@types/chai@*", "@types/chai@^4.2.15":
"@types/chai@*", "@types/chai@^4.1.7", "@types/chai@^4.2.15":
version "4.2.15"
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.15.tgz#b7a6d263c2cecf44b6de9a051cf496249b154553"
integrity sha512-rYff6FI+ZTKAPkJUoyz7Udq3GaoDZnxYDEvdEdFZASiA7PoErltHezDishqQiSDWrGxvxmplH304jyzQmjp0AQ==
......@@ -1746,6 +1838,11 @@
dependencies:
"@types/node" "*"
"@types/mocha@^5.2.6":
version "5.2.7"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea"
integrity sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==
"@types/mocha@^8.2.0", "@types/mocha@^8.2.2":
version "8.2.2"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.2.2.tgz#91daa226eb8c2ff261e6a8cbf8c7304641e095e0"
......@@ -1764,6 +1861,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.35.tgz#42c953a4e2b18ab931f72477e7012172f4ffa313"
integrity sha512-Lt+wj8NVPx0zUmUwumiVXapmaLUcAk3yPuHCFVXras9k5VT9TdhJqKqGVUQCD60OTMCl0qxJ57OiTL0Mic3Iag==
"@types/node@^11.11.3":
version "11.15.50"
resolved "https://registry.yarnpkg.com/@types/node/-/node-11.15.50.tgz#a8c76622a20320d4a04adf2002b04737c510ef11"
integrity sha512-kG/ZmA/uD1L1gVD7vVXQB6v+ICZlJgvakrodHiltT3Zq0YjXq5H9tfgop8MsdMGCwrcLJg9QCQDRP4DZsn9T/g==
"@types/node@^12.12.6":
version "12.20.6"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.6.tgz#7b73cce37352936e628c5ba40326193443cfba25"
......@@ -1860,7 +1962,7 @@
"@types/chai" "*"
"@types/sinon" "*"
"@types/sinon@*":
"@types/sinon@*", "@types/sinon@^9.0.10":
version "9.0.11"
resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.11.tgz#7af202dda5253a847b511c929d8b6dda170562eb"
integrity sha512-PwP4UY33SeeVKodNE37ZlOsR9cReypbMJOhZ7BVE0lB+Hix3efCOxiJWiE5Ia+yL9Cn2Ch72EjFTRze8RZsNtg==
......@@ -1943,7 +2045,7 @@ abstract-leveldown@^5.0.0, abstract-leveldown@~5.0.0:
dependencies:
xtend "~4.0.0"
abstract-leveldown@^6.2.1:
abstract-leveldown@^6.2.1, abstract-leveldown@^6.2.2:
version "6.3.0"
resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz#d25221d1e6612f820c35963ba4bd739928f6026a"
integrity sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ==
......@@ -1961,6 +2063,14 @@ abstract-leveldown@~2.6.0:
dependencies:
xtend "~4.0.0"
abstract-leveldown@~6.0.1:
version "6.0.3"
resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.0.3.tgz#b4b6159343c74b0c5197b2817854782d8f748c4a"
integrity sha512-jzewKKpZbaYUa6HTThnrl+GrJhzjEAeuc7hTVpZdzg7kupXZFoqQDFwyOwLNbmJKJlmzw8yiipMPkDiuKkT06Q==
dependencies:
level-concat-iterator "~2.0.0"
xtend "~4.0.0"
abstract-leveldown@~6.2.1, abstract-leveldown@~6.2.3:
version "6.2.3"
resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz#036543d87e3710f2528e47040bc3261b77a9a8eb"
......@@ -2261,6 +2371,11 @@ async-limiter@~1.0.0:
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
async-lock@^1.2.2:
version "1.2.8"
resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.2.8.tgz#7b02bdfa2de603c0713acecd11184cf97bbc7c4c"
integrity sha512-G+26B2jc0Gw0EG/WN2M6IczuGepBsfR1+DtqLnyFSH4p2C668qkOCtEkGNVEaaNAVlYwEMazy1+/jnLxltBkIQ==
async@2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381"
......@@ -2310,6 +2425,13 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
axios@^0.19.0:
version "0.19.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==
dependencies:
follow-redirects "1.5.10"
babel-code-frame@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
......@@ -2932,7 +3054,7 @@ blakejs@^1.1.0:
resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.0.tgz#69df92ef953aa88ca51a32df6ab1c54a155fc7a5"
integrity sha1-ad+S75U6qIylGjLfarHFShVfx6U=
bluebird@^3.5.0, bluebird@^3.5.2:
bluebird@^3.5.0, bluebird@^3.5.2, bluebird@^3.7.2:
version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
......@@ -2947,12 +3069,12 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.2.0:
bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.1.3, bn.js@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002"
integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==
body-parser@1.19.0, body-parser@^1.16.0:
body-parser@1.19.0, body-parser@^1.16.0, body-parser@^1.19.0:
version "1.19.0"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==
......@@ -3305,7 +3427,7 @@ chai-as-promised@^7.1.1:
dependencies:
check-error "^1.0.2"
chai@^4.3.0, chai@^4.3.4:
chai@^4.2.0, chai@^4.3.0, 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==
......@@ -3317,7 +3439,7 @@ chai@^4.3.0, chai@^4.3.4:
pathval "^1.1.1"
type-detect "^4.0.5"
chalk@2.4.2, chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2:
chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
......@@ -3956,6 +4078,13 @@ debug@4, debug@4.3.1, debug@^4.1.0, debug@^4.1.1:
dependencies:
ms "2.1.2"
debug@=3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
dependencies:
ms "2.0.0"
debug@^3.1.0:
version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
......@@ -4176,7 +4305,7 @@ diff@5.0.0:
resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
diff@^4.0.1:
diff@^4.0.1, diff@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
......@@ -4256,6 +4385,19 @@ electron-to-chromium@^1.3.47:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.700.tgz#a6999a954c698dc7da5e84c369d65432dbe895be"
integrity sha512-wQtaxVZzpOeCjW1CGuC5W3bYjE2jglvk076LcTautBOB9UtHztty7wNzjVsndiMcSsdUsdMy5w76w5J1U7OPTQ==
elliptic@6.5.3:
version "6.5.3"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6"
integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==
dependencies:
bn.js "^4.4.0"
brorand "^1.0.1"
hash.js "^1.0.0"
hmac-drbg "^1.0.0"
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.0"
elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3:
version "6.5.4"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
......@@ -4628,6 +4770,17 @@ ethereum-cryptography@^0.1.2, ethereum-cryptography@^0.1.3:
secp256k1 "^4.0.1"
setimmediate "^1.0.5"
ethereum-waffle@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ethereum-waffle/-/ethereum-waffle-3.0.0.tgz#59e46b8e303eb5decbb5ceebca1ddf378db312d6"
integrity sha512-r7hQHGs6uuUIcZxcwsxXbntk6XQkdC4HST4lFN493H9Ac9JLnYbaQ3Yp2qeGK7P7BqrDUqGZwylSIT+1odtAmA==
dependencies:
"@ethereum-waffle/chai" "^3.0.0"
"@ethereum-waffle/compiler" "^3.0.0"
"@ethereum-waffle/mock-contract" "^3.0.0"
"@ethereum-waffle/provider" "^3.0.0"
ethers "^5.0.1"
ethereum-waffle@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/ethereum-waffle/-/ethereum-waffle-3.3.0.tgz#166a0cc1d3b2925f117b20ef0951b3fe72e38e79"
......@@ -4846,7 +4999,57 @@ ethereumjs-wallet@0.6.5:
utf8 "^3.0.0"
uuid "^3.3.2"
ethers@^5.0.0, ethers@^5.0.1, ethers@^5.0.2, ethers@^5.0.25, ethers@^5.0.26, ethers@^5.0.31, ethers@^5.0.32:
"ethers-v4@npm:ethers@4":
version "4.0.48"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.48.tgz#330c65b8133e112b0613156e57e92d9009d8fbbe"
integrity sha512-sZD5K8H28dOrcidzx9f8KYh8083n5BexIO3+SbE4jK83L85FxtpXZBCQdXb8gkg+7sBqomcLhhkU7UHL+F7I2g==
dependencies:
aes-js "3.0.0"
bn.js "^4.4.0"
elliptic "6.5.3"
hash.js "1.1.3"
js-sha3 "0.5.7"
scrypt-js "2.0.4"
setimmediate "1.0.4"
uuid "2.0.1"
xmlhttprequest "1.8.0"
ethers@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.0.0.tgz#76558a3020766f310a49f4e1a4c6c1e331761abd"
integrity sha512-uOSACd2E8dg8XuiOewpL42uFH7SvrkA5k0oGkHoqSJl2lflrMPV+7ciWzyuPBjyHnOFvAPPJUpsXrwpFKaLFww==
dependencies:
"@ethersproject/abi" "^5.0.0"
"@ethersproject/abstract-provider" "^5.0.0"
"@ethersproject/abstract-signer" "^5.0.0"
"@ethersproject/address" "^5.0.0"
"@ethersproject/base64" "^5.0.0"
"@ethersproject/bignumber" "^5.0.0"
"@ethersproject/bytes" "^5.0.0"
"@ethersproject/constants" "^5.0.0"
"@ethersproject/contracts" "^5.0.0"
"@ethersproject/hash" "^5.0.0"
"@ethersproject/hdnode" "^5.0.0"
"@ethersproject/json-wallets" "^5.0.0"
"@ethersproject/keccak256" "^5.0.0"
"@ethersproject/logger" "^5.0.0"
"@ethersproject/networks" "^5.0.0"
"@ethersproject/pbkdf2" "^5.0.0"
"@ethersproject/properties" "^5.0.0"
"@ethersproject/providers" "^5.0.0"
"@ethersproject/random" "^5.0.0"
"@ethersproject/rlp" "^5.0.0"
"@ethersproject/sha2" "^5.0.0"
"@ethersproject/signing-key" "^5.0.0"
"@ethersproject/solidity" "^5.0.0"
"@ethersproject/strings" "^5.0.0"
"@ethersproject/transactions" "^5.0.0"
"@ethersproject/units" "^5.0.0"
"@ethersproject/wallet" "^5.0.0"
"@ethersproject/web" "^5.0.0"
"@ethersproject/wordlists" "^5.0.0"
ethers@^5.0.0, ethers@^5.0.1, ethers@^5.0.2, ethers@^5.0.24, ethers@^5.0.25, ethers@^5.0.26, ethers@^5.0.31, ethers@^5.0.32:
version "5.0.32"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.0.32.tgz#f009970be31d96a589bf0ce597a39c10c7e297a6"
integrity sha512-rORfGWR0HsA4pjKMMcWZorw12DHsXqfIAuPVHJsXt+vI24jvXcVqx+rLsSvgOoLdaCMdxiN5qlIq2+4axKG31g==
......@@ -5243,6 +5446,13 @@ flow-stoplight@^1.0.0:
resolved "https://registry.yarnpkg.com/flow-stoplight/-/flow-stoplight-1.0.0.tgz#4a292c5bcff8b39fa6cc0cb1a853d86f27eeff7b"
integrity sha1-SiksW8/4s5+mzAyxqFPYbyfu/3s=
follow-redirects@1.5.10:
version "1.5.10"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==
dependencies:
debug "=3.1.0"
follow-redirects@^1.12.1:
version "1.13.3"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267"
......@@ -5880,7 +6090,7 @@ heap@0.2.6:
resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.6.tgz#087e1f10b046932fc8594dd9e6d378afc9d1e5ac"
integrity sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw=
hmac-drbg@^1.0.1:
hmac-drbg@^1.0.0, hmac-drbg@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=
......@@ -6708,6 +6918,11 @@ jsprim@^1.2.2:
json-schema "0.2.3"
verror "1.10.0"
just-extend@^4.0.2:
version "4.1.1"
resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.1.1.tgz#158f1fdb01f128c411dc8b286a7b4837b3545282"
integrity sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA==
keccak@3.0.1, keccak@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.1.tgz#ae30a0e94dbe43414f741375cff6d64c8bea0bff"
......@@ -7102,6 +7317,11 @@ lodash.assign@^4.0.3, lodash.assign@^4.0.6:
resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7"
integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=
lodash.get@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
lodash.ismatch@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37"
......@@ -7132,6 +7352,13 @@ lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
log-symbols@2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==
dependencies:
chalk "^2.0.1"
log-symbols@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4"
......@@ -7306,6 +7533,18 @@ memdown@^1.0.0:
ltgt "~2.2.0"
safe-buffer "~5.1.1"
memdown@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/memdown/-/memdown-4.1.0.tgz#4e667ec75ce524a8aa7391c10a5a357c6d4fde0e"
integrity sha512-RqDUWsJ3IH27+dHwBOg6/ZWJoGul+tesnaQ10Pr/ARx7GpskB01Ky0zNZ8EkM6lvQqXcxyzukmXA0T39XW2sRw==
dependencies:
abstract-leveldown "~6.0.1"
functional-red-black-tree "~1.0.1"
immediate "~3.2.3"
inherits "~2.0.1"
ltgt "~2.2.0"
safe-buffer "~5.1.1"
memdown@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/memdown/-/memdown-3.0.0.tgz#93aca055d743b20efc37492e9e399784f2958309"
......@@ -7487,7 +7726,7 @@ minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
minimalistic-crypto-utils@^1.0.1:
minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
......@@ -7624,6 +7863,13 @@ mkdirp@*, mkdirp@^1.0.3, mkdirp@^1.0.4:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
mkdirp@0.5.4:
version "0.5.4"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.4.tgz#fd01504a6797ec5c9be81ff43d204961ed64a512"
integrity sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==
dependencies:
minimist "^1.2.5"
mkdirp@0.5.5, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
......@@ -7638,6 +7884,35 @@ mnemonist@^0.38.0:
dependencies:
obliterator "^1.6.1"
mocha@^6.1.4:
version "6.2.3"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.3.tgz#e648432181d8b99393410212664450a4c1e31912"
integrity sha512-0R/3FvjIGH3eEuG17ccFPk117XL2rWxatr81a57D+r/x2uTYZRbdZ4oVidEUMh2W2TJDa7MdAb12Lm2/qrKajg==
dependencies:
ansi-colors "3.2.3"
browser-stdout "1.3.1"
debug "3.2.6"
diff "3.5.0"
escape-string-regexp "1.0.5"
find-up "3.0.0"
glob "7.1.3"
growl "1.10.5"
he "1.2.0"
js-yaml "3.13.1"
log-symbols "2.2.0"
minimatch "3.0.4"
mkdirp "0.5.4"
ms "2.1.1"
node-environment-flags "1.0.5"
object.assign "4.1.0"
strip-json-comments "2.0.1"
supports-color "6.0.0"
which "1.3.1"
wide-align "1.1.3"
yargs "13.3.2"
yargs-parser "13.1.2"
yargs-unparser "1.6.0"
mocha@^7.1.2:
version "7.2.0"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.2.0.tgz#01cc227b00d875ab1eed03a75106689cfed5a604"
......@@ -7847,6 +8122,19 @@ neo-async@^2.6.0:
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
"new-contracts@npm:@eth-optimism/contracts@0.1.4":
version "0.1.4"
resolved "https://registry.yarnpkg.com/@eth-optimism/contracts/-/contracts-0.1.4.tgz#a918bffcdfa0ce22c1af3b091a81af732f58a294"
integrity sha512-JlbhPkYDgurQwlar1L90XTAG0QK8Sd2WCU8Zaa6BXn+Bg1mVe3g80samE0YaOFNHmSscjtvjXqy3B/d0tzIH7w==
dependencies:
"@eth-optimism/solc" "^0.6.12-alpha.1"
"@ethersproject/abstract-provider" "^5.0.8"
"@ethersproject/contracts" "^5.0.5"
"@ethersproject/hardware-wallets" "^5.0.8"
"@openzeppelin/contracts" "^3.3.0"
ethers "5.0.0"
ganache-core "^2.12.1"
next-tick@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
......@@ -7857,6 +8145,17 @@ nice-try@^1.0.4:
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
nise@^4.0.4:
version "4.1.0"
resolved "https://registry.yarnpkg.com/nise/-/nise-4.1.0.tgz#8fb75a26e90b99202fa1e63f448f58efbcdedaf6"
integrity sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA==
dependencies:
"@sinonjs/commons" "^1.7.0"
"@sinonjs/fake-timers" "^6.0.0"
"@sinonjs/text-encoding" "^0.7.1"
just-extend "^4.0.2"
path-to-regexp "^1.7.0"
node-abi@^2.18.0, node-abi@^2.7.0:
version "2.21.0"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.21.0.tgz#c2dc9ebad6f4f53d6ea9b531e7b8faad81041d48"
......@@ -7874,6 +8173,14 @@ node-addon-api@^3.0.2:
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.1.0.tgz#98b21931557466c6729e51cb77cd39c965f42239"
integrity sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==
node-environment-flags@1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a"
integrity sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==
dependencies:
object.getownpropertydescriptors "^2.0.3"
semver "^5.7.0"
node-environment-flags@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088"
......@@ -8596,6 +8903,13 @@ path-to-regexp@0.1.7:
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
path-to-regexp@^1.7.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a"
integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
dependencies:
isarray "0.0.1"
path-type@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
......@@ -8783,6 +9097,11 @@ prepend-http@^2.0.0:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
prettier@^1.16.4:
version "1.19.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
prettier@^2.1.2, prettier@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5"
......@@ -9529,6 +9848,11 @@ safe-regex@^1.1.0:
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
scrypt-js@2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16"
integrity sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw==
scrypt-js@3.0.1, scrypt-js@^3.0.0, scrypt-js@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312"
......@@ -9649,6 +9973,11 @@ set-value@^2.0.0, set-value@^2.0.1:
is-plain-object "^2.0.3"
split-string "^3.0.1"
setimmediate@1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.4.tgz#20e81de622d4a02588ce0c8da8973cbcf1d3138f"
integrity sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48=
setimmediate@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
......@@ -9744,6 +10073,23 @@ simple-get@^3.0.3:
once "^1.3.1"
simple-concat "^1.0.0"
sinon-chai@^3.5.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/sinon-chai/-/sinon-chai-3.6.0.tgz#25bd59a37ef8990245e085a40f1f79d6bf023b7a"
integrity sha512-bk2h+0xyKnmvazAnc7HE5esttqmCerSMcBtuB2PS2T4tG6x8woXAxZeJaOJWD+8reXHngnXn0RtIbfEW9OTHFg==
sinon@^9.2.4:
version "9.2.4"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.2.4.tgz#e55af4d3b174a4443a8762fa8421c2976683752b"
integrity sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==
dependencies:
"@sinonjs/commons" "^1.8.1"
"@sinonjs/fake-timers" "^6.0.1"
"@sinonjs/samsam" "^5.3.1"
diff "^4.0.2"
nise "^4.0.4"
supports-color "^7.1.0"
slash@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
......@@ -10515,6 +10861,11 @@ ts-generator@^0.1.1:
resolve "^1.8.1"
ts-essentials "^1.0.0"
ts-md5@^1.2.4:
version "1.2.7"
resolved "https://registry.yarnpkg.com/ts-md5/-/ts-md5-1.2.7.tgz#b76471fc2fd38f0502441f6c3b9494ed04537401"
integrity sha512-emODogvKGWi1KO1l9c6YxLMBn6CEH3VrH5mVPIyOtxBG52BvV4jP3GWz6bOZCz61nLgBc3ffQYE4+EHfCD+V7w==
ts-mocha@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/ts-mocha/-/ts-mocha-8.0.0.tgz#962d0fa12eeb6468aa1a6b594bb3bbc818da3ef0"
......@@ -10538,6 +10889,17 @@ ts-node@7.0.1:
source-map-support "^0.5.6"
yn "^2.0.0"
ts-node@^8.2.0:
version "8.10.2"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d"
integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==
dependencies:
arg "^4.1.0"
diff "^4.0.1"
make-error "^1.1.1"
source-map-support "^0.5.17"
yn "3.1.1"
ts-node@^9.1.1:
version "9.1.1"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d"
......@@ -10637,7 +10999,7 @@ tweetnacl@^1.0.0, tweetnacl@^1.0.3:
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==
type-detect@^4.0.0, type-detect@^4.0.5:
type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
......@@ -10942,12 +11304,17 @@ utils-merge@1.0.1:
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
uuid@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.1.tgz#c2a30dedb3e535d72ccf82e343941a50ba8533ac"
integrity sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=
uuid@3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
uuid@^3.3.2:
uuid@^3.3.2, uuid@^3.3.3:
version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
......@@ -11523,6 +11890,11 @@ xhr@^2.0.4, xhr@^2.2.0, xhr@^2.3.3:
parse-headers "^2.0.0"
xtend "^4.0.0"
xmlhttprequest@1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc"
integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=
xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment