Commit 8f5982d2 authored by smartcontracts's avatar smartcontracts Committed by GitHub

pkg: Add DTL (#11)

* pkg: Add DTL

* fix: use correct tsconfig when building
Co-authored-by: default avatarGeorgios Konstantopoulos <me@gakonst.com>
parent 46ec5bc3
node_modules
dist
**/dist
results
.nyc_output
*.tsbuildinfo
# General options
DATA_TRANSPORT_LAYER__DB_PATH=./db
DATA_TRANSPORT_LAYER__ADDRESS_MANAGER=
DATA_TRANSPORT_LAYER__POLLING_INTERVAL=5000
DATA_TRANSPORT_LAYER__DANGEROUSLY_CATCH_ALL_ERRORS=true
DATA_TRANSPORT_LAYER__CONFIRMATIONS=12
# Server options
DATA_TRANSPORT_LAYER__SERVER_HOSTNAME=localhost
DATA_TRANSPORT_LAYER__SERVER_PORT=7878
# Set to "true" if you want to sync confirmed transactions from L1 (Ethereum).
# You probably want to set this to "true".
DATA_TRANSPORT_LAYER__SYNC_FROM_L1=true
DATA_TRANSPORT_LAYER__L1_RPC_ENDPOINT=
DATA_TRANSPORT_LAYER__LOGS_PER_POLLING_INTERVAL=2000
# Set to "true" if you want to sync unconfirmed transactions from a sequencer.
# Make sure to fill in the below values if you intend to do so.
DATA_TRANSPORT_LAYER__SYNC_FROM_L2=false
DATA_TRANSPORT_LAYER__L2_RPC_ENDPOINT=
DATA_TRANSPORT_LAYER__TRANSACTIONS_PER_POLLING_INTERVAL=1000
DATA_TRANSPORT_LAYER__L2_CHAIN_ID=69
DATA_TRANSPORT_LAYER__LEGACY_SEQUENCER_COMPATIBILITY=false
/db/
node_modules/
yarn-error.log
.env
test/temp/
build/
\ No newline at end of file
# data transport layer
## v0.1.2
- Fix bug in L2 sync
## v0.1.1
- Prioritize L2 synced API requests
- Stop syncing L2 at a certain height
## v0.1.0
- Sync From L1
- Sync From L2
- Initial Release
(The MIT License)
Copyright 2020-2021 Optimism
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# @eth-optimism/data-transport-layer
## What is this?
The Optimistic Ethereum Data Transport Layer is a long-running software service (written in TypeScript) designed to reliably index Optimistic Ethereum transaction data from Layer 1 (Ethereum). Specifically, this service indexes:
* Transactions that have been enqueued for submission to the CanonicalTransactionChain via [`enqueue`](https://github.com/ethereum-optimism/contracts-v2/blob/13b7deef60f773241723ea874fc6e81b4003b164/contracts/optimistic-ethereum/OVM/chain/OVM_CanonicalTransactionChain.sol#L225-L231).
* Transactions that have been included in the CanonicalTransactionChain via [`appendQueueBatch`](https://github.com/ethereum-optimism/contracts-v2/blob/13b7deef60f773241723ea874fc6e81b4003b164/contracts/optimistic-ethereum/OVM/chain/OVM_CanonicalTransactionChain.sol#L302-L306) or [`appendSequencerBatch`](https://github.com/ethereum-optimism/contracts-v2/blob/13b7deef60f773241723ea874fc6e81b4003b164/contracts/optimistic-ethereum/OVM/chain/OVM_CanonicalTransactionChain.sol#L352-L354).
* State roots (transaction results) that have been published to the StateCommitmentChain via [`appendStateBatch`](https://github.com/ethereum-optimism/contracts-v2/blob/13b7deef60f773241723ea874fc6e81b4003b164/contracts/optimistic-ethereum/OVM/chain/OVM_StateCommitmentChain.sol#L127-L132).
## How does it work?
We run two sub-services, the [`L1IngestionService`](./src/services/l1-ingestion/service.ts) and the [`L1TransportServer`](./src/services/server/service.ts). The `L1IngestionService` is responsible for querying for the various events and transaction data necessary to accurately index information from our Layer 1 (Ethereum) smart contracts. The `L1TransportServer` simply provides an API for accessing this information.
## Getting started
### Configuration
See an example config at [.env.example](.env.example); copy into a `.env` file before running.
`L1_TRANSPORT__L1_RPC_ENDPOINT` can be the JSON RPC endpoint of any L1 Ethereum node. `L1_TRANSPORT__ADDRESS_MANAGER` should be the contract addresss of the Address Manager on the corresponding network; find their values in the [Regenesis repo](https://github.com/ethereum-optimism/regenesis).
### Building and usage
After cloning and switching to the repository, install dependencies:
```bash
$ yarn
```
Use the following commands to build, use, test, and lint:
```bash
$ yarn build
$ yarn start
$ yarn test
$ yarn lint
```
## Configuration
We're using `dotenv` for our configuration.
Copy `.env.example` into `.env`, feel free to modify it.
Here's the list of environment variables you can change:
| Variable | Default | Description |
| ------------------------------------------------------- | --------- | ------------ |
| DATA_TRANSPORT_LAYER__DB_PATH | ./db | Path to the database for this service. |
| DATA_TRANSPORT_LAYER__ADDRESS_MANAGER | - | Address of the AddressManager contract on L1. See [regenesis](https://github.com/ethereum-optimism/regenesis) repo to find this address for mainnet or kovan. |
| DATA_TRANSPORT_LAYER__POLLING_INTERVAL | 5000 | Period of time between execution loops. |
| DATA_TRANSPORT_LAYER__DANGEROUSLY_CATCH_ALL_ERRORS | false | If true, will catch all errors without throwing. |
| DATA_TRANSPORT_LAYER__CONFIRMATIONS | 12 | Number of confirmations to wait before accepting transactions as "canonical". |
| DATA_TRANSPORT_LAYER__SERVER_HOSTNAME | localhost | Host to run the API on. |
| DATA_TRANSPORT_LAYER__SERVER_PORT | 7878 | Port to run the API on. |
| DATA_TRANSPORT_LAYER__SYNC_FROM_L1 | true | Whether or not to sync from L1. |
| DATA_TRANSPORT_LAYER__L1_RPC_ENDPOINT | - | RPC endpoint for an L1 node. |
| DATA_TRANSPORT_LAYER__LOGS_PER_POLLING_INTERVAL | 2000 | Logs to sync per polling interval. |
| DATA_TRANSPORT_LAYER__SYNC_FROM_L2 | false | Whether or not to sync from L2. |
| DATA_TRANSPORT_LAYER__L2_RPC_ENDPOINT | - | RPC endpoint for an L2 node. |
| DATA_TRANSPORT_LAYER__TRANSACTIONS_PER_POLLING_INTERVAL | 1000 | Number of L2 transactions to query per polling interval. |
| DATA_TRANSPORT_LAYER__L2_CHAIN_ID | - | L2 chain ID. |
| DATA_TRANSPORT_LAYER__LEGACY_SEQUENCER_COMPATIBILITY | false | Whether or not to enable "legacy" sequencer sync (without the custom `eth_getBlockRange` endpoint) |
## HTTP API
This section describes the HTTP API for accessing indexed Layer 1 data.
### Latest Ethereum Block Context
#### Request
```
GET /eth/context/latest
```
#### Response
```ts
{
"blockNumber": number,
"timestamp": number
}
```
### Enqueue by Index
#### Request
```
GET /enqueue/index/{index: number}
```
#### Response
```ts
{
"index": number,
"target": string,
"data": string,
"gasLimit": number,
"origin": string,
"blockNumber": number,
"timestamp": number
}
```
### Transaction by Index
#### Request
```
GET /transaction/index/{index: number}
```
#### Response
```ts
{
"transaction": {
"index": number,
"batchIndex": number,
"data": string,
"blockNumber": number,
"timestamp": number,
"gasLimit": number,
"target": string,
"origin": string,
"queueOrigin": string,
"type": string | null,
"decoded": {
"sig": {
"r": string,
"s": string,
"v": string
},
"gasLimit": number,
"gasPrice": number,
"nonce": number,
"target": string,
"data": string
} | null,
"queueIndex": number | null,
},
"batch": {
"index": number,
"blockNumber": number,
"timestamp": number,
"submitter": string,
"size": number,
"root": string,
"prevTotalElements": number,
"extraData": string
}
}
```
### Transaction Batch by Index
#### Request
```
GET /batch/transaction/index/{index: number}
```
#### Response
```ts
{
"batch": {
"index": number,
"blockNumber": number,
"timestamp": number,
"submitter": string,
"size": number,
"root": string,
"prevTotalElements": number,
"extraData": string
},
"transactions": [
{
"index": number,
"batchIndex": number,
"data": string,
"blockNumber": number,
"timestamp": number,
"gasLimit": number,
"target": string,
"origin": string,
"queueOrigin": string,
"type": string | null,
"decoded": {
"sig": {
"r": string,
"s": string,
"v": string
},
"gasLimit": number,
"gasPrice": number,
"nonce": number,
"target": string,
"data": string
} | null,
"queueIndex": number | null,
}
]
}
```
### State Root by Index
#### Request
```
GET /stateroot/index/{index: number}
```
#### Response
```ts
{
"stateRoot": {
"index": number,
"batchIndex": number,
"value": string
},
"batch": {
"index": number,
"blockNumber": number,
"timestamp": number,
"submitter": string,
"size": number,
"root": string,
"prevTotalElements": number,
"extraData": string
},
}
```
### State Root Batch by Index
#### Request
```
GET /batch/stateroot/index/{index: number}
```
#### Response
```ts
{
"batch": {
"index": number,
"blockNumber": number,
"timestamp": number,
"submitter": string,
"size": number,
"root": string,
"prevTotalElements": number,
"extraData": string
},
"stateRoots": [
{
"index": number,
"batchIndex": number,
"value": string
}
]
}
```
openapi: 3.0.0
info:
title: Ethereum Optimism Data Transport Later
description: |
Make sequencer or contract data available behind a common API
version: 0.1.0
components:
schemas:
EthereumContext:
type: object
properties:
blockNumber:
type: uint64
timestamp:
type: uint64
blockHash:
type: string
EnqueueEntry:
type: object
properties:
index:
type: number
target:
type: string
data:
type: string
gasLimit:
type: number
origin:
type: string
blockNumber:
type: number
timestamp:
type: number
ctcIndex:
type: number
BatchEntry:
type: object
properties:
index:
type: number
blockNumber:
type: number
timestamp:
type: number
submitter:
type: string
size:
type: number
root:
type: string
prevTotalElements:
type: number
extraData:
type: string
l1TransactionHash:
type: string
TransactionEntry:
type: object
properties:
index:
type: number
batchIndex:
type: number
data:
type: string
blockNumber:
type: number
timestamp:
type: number
gasLimit:
type: number
target:
type: string
origin:
type: string
queueOrigin:
type: string
queueIndex: number
type: string
decoded:
type: object
$ref: '#/components/schemas/DecodedSequencerBatchTransaction'
TransactionResponse:
type: object
properties:
batch:
type: object
$ref: '#/components/schemas/BatchEntry'
transaction:
type: object
$ref: '#/components/schemas/TransactionEntry'
TransactionBatchResponse:
type: object
properties:
batch:
type: object
$ref: '#/components/schemas/BatchEntry'
transactions:
type: array
items:
type: object
$ref: '#/components/schemas/TransactionEntry'
DecodedSequencerBatchTransaction:
type: object
properties:
sig:
type: object
$ref: '#/components/schemas/ECDSASignature'
gasLimit:
type: number
gasPrice:
type: number
nonce:
type: number
target:
type: string
data:
type: string
ECDSASignature:
type: object
properties:
r:
type: string
s:
type: string
v:
type: number
StateRootEntry:
type: object
properties:
index:
type: number
batchIndex:
type: number
value:
type: string
StateRootResponse:
type: object
properties:
batch:
type: object
$ref: '#/components/schemas/BatchEntry'
stateRoot:
type: object
$ref: '#/components/schemas/StateRootEntry'
StateRootBatchResponse:
type: object
properties:
batch:
type: object
$ref: '#/components/schemas/BatchEntry'
stateRoots:
type: array
items:
type: object
$ref: '#/components/schemas/StateRootEntry'
paths:
/eth/syncing:
get:
summary: Returns the sync status
description: |
The node may still be syncing to the tip and downstream services can learn up to what
point has been indexed.
responses:
'200':
description: An object representing the progression of the service
content:
application/json:
schema:
type: object
properties:
syncing:
type: boolean
currentTransactionIndex:
type: uint64
highestKnownTransactionIndex:
type: uint64
/eth/context/latest:
get:
summary: Returns the latest Ethereum Layer one context
description: |
This returns the L1 blocknumber, block hash and timestamp corresponding to the most
recently ingested block
responses:
'200':
description: An object representing the Ethereum context
content:
application/json:
schema:
$ref: '#/components/schemas/EthereumContext'
/eth/context/blocknumber/{number}:
get:
summary: Returns the Ethereum Layer one context at a specific height
description: |
This returns the L1 blocknumber, block hash and timestamp corresponding to a specific
Ethereum context
responses:
'200':
description: An object representing the Ethereum context
content:
application/json:
schema:
$ref: '#/components/schemas/EthereumContext'
/enqueue/latest:
get:
summary: Returns the latest enqueued transaction
description: |
This returns the latest transaction sent to the Canonical Transaction Chain `enqueue()`
responses:
'200':
description: An object representing the latest enqueued transaction
content:
application/json:
schema:
$ref: '#/components/schemas/EnqueueEntry'
/enqueue/index/{index}:
get:
summary: Returns the enqueued transaction by index
description: |
This returns the Canonical Transaction Chain `enqueue()` by index
responses:
'200':
description: An object representing the enqueued transaction by index
content:
application/json:
schema:
$ref: '#/components/schemas/EnqueueEntry'
/transaction/latest:
get:
summary: Returns the latest Canonical Transaction Chain transaction
description: |
This returns the latest transaction to be appended to the Canonical Transaction
Chain via a `sequencerBatchAppend()` or `queueBatchAppend()`
responses:
'200':
description: An object representing the latest transaction
content:
application/json:
schema:
$ref: '#/components/schemas/EnqueueEntry'
/transaction/index/{index}:
get:
summary: Returns a Canonical Transaction Chain transaction by index
description: |
This returns a transaction that has been appended to the Canonical Transaction
Chain via a `sequencerBatchAppend()` or `queueBatchAppend()` by index
responses:
'200':
description: An object representing a Transaction by index
content:
application/json:
schema:
$ref: '#/components/schemas/TransactionResponse'
/batch/transaction/latest:
get:
summary: Returns the latest Batch to be appended to the Canonical Transaction Chain
description: |
This returns the latest batch that has been appended to the Canonical Transaction
Chain via a `sequencerBatchAppend()`
responses:
'200':
description: An object representing the latest batch
content:
application/json:
schema:
$ref: '#/components/schemas/TransactionBatchResponse'
/batch/transaction/index/{index}:
get:
summary: Returns the Batch to be appended to the Canonical Transaction Chain by index
description: |
This returns a batch that has been appended to the Canonical Transaction
Chain via a `sequencerBatchAppend()` by index
responses:
'200':
description: An object representing the latest batch
content:
application/json:
schema:
$ref: '#/components/schemas/TransactionBatchResponse'
/stateroot/latest:
get:
summary: Returns the latest state root
description: |
This returns the latest state root appended to the State Commitment Chain
responses:
'200':
description: An object representing the latest state root
content:
application/json:
schema:
$ref: '#/components/schemas/StateRootResponse'
/stateroot/index/{index}:
get:
summary: Returns the state root by index
description: |
This returns a state root appended to the State Commitment Chain by index
responses:
'200':
description: An object representing the a state root
content:
application/json:
schema:
$ref: '#/components/schemas/StateRootResponse'
/batch/stateroot/latest:
get:
summary: Returns the latest state root batch
description: |
This returns the latest batch of state roots appended to the State Commitment Chain
responses:
'200':
description: An object representing the latest state root batch
content:
application/json:
schema:
$ref: '#/components/schemas/StateRootBatchResponse'
/batch/stateroot/index/{index}:
get:
summary: Returns the state root batch by index
description: |
This returns a state root batch appended to the State Commitment Chain by index
responses:
'200':
description: An object representing the state root batch by index
content:
application/json:
schema:
$ref: '#/components/schemas/StateRootBatchResponse'
{
"name": "@eth-optimism/data-transport-layer",
"version": "0.1.2",
"main": "build/index.js",
"license": "MIT",
"files": [
"build/**/*.js",
"build/**/*.js.map",
"build/**/*.ts"
],
"types": "build/index.d.ts",
"scripts": {
"clean": "rimraf ./build",
"clean:db": "rimraf ./db",
"lint": "yarn run lint:fix && yarn run lint:check",
"lint:check": "tslint --format stylish --project .",
"lint:fix": "prettier --config prettier-config.json --write \"{src,exec,test}/**/*.ts\"",
"start": "ts-node ./src/services/run.ts",
"start:local": "ts-node ./src/services/run.ts | pino-pretty",
"test": "hardhat --config test/config/hardhat.config.ts test",
"build": "tsc -p tsconfig.build.json"
},
"dependencies": {
"@eth-optimism/contracts": "^0.1.6",
"@eth-optimism/core-utils": "^0.1.10",
"@eth-optimism/service-base": "^1.1.5",
"@ethersproject/providers": "^5.0.21",
"@types/express": "^4.17.11",
"bcfg": "^0.1.6",
"browser-or-node": "^1.3.0",
"colors": "^1.4.0",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"ethers": "^5.0.26",
"express": "^4.17.1",
"level": "^6.0.1",
"levelup": "^4.4.0",
"node-fetch": "^2.6.1"
},
"devDependencies": {
"@eth-optimism/dev": "^1.1.1",
"@nomiclabs/hardhat-ethers": "^2.0.1",
"@types/browser-or-node": "^1.3.0",
"@types/cors": "^2.8.9",
"@types/levelup": "^4.3.0",
"@types/node-fetch": "^2.5.8",
"@types/rimraf": "^3.0.0",
"chai": "^4.3.4",
"chai-as-promised": "^7.1.1",
"hardhat": "^2.0.9",
"mocha": "^8.3.2",
"pino-pretty": "^4.7.1",
"rimraf": "^3.0.2",
"ts-node": "^9.1.1",
"typescript": "^4.2.3"
}
}
../../prettier-config.json
\ No newline at end of file
// Only load if not in browser.
import { isNode } from 'browser-or-node'
declare var window: any
/* tslint:disable-next-line:no-var-requires */
const fetch = isNode ? require('node-fetch') : window.fetch
import {
EnqueueResponse,
StateRootBatchResponse,
StateRootResponse,
SyncingResponse,
TransactionBatchResponse,
TransactionResponse,
} from '../types'
export class L1DataTransportClient {
constructor(private url: string) {}
public async syncing(): Promise<SyncingResponse> {
return this._get(`/eth/syncing`)
}
public async getEnqueueByIndex(index: number): Promise<EnqueueResponse> {
return this._get(`/enqueue/index/${index}`)
}
public async getLatestEnqueue(): Promise<EnqueueResponse> {
return this._get(`/enqueue/latest`)
}
public async getTransactionByIndex(
index: number
): Promise<TransactionResponse> {
return this._get(`/transaction/index/${index}`)
}
public async getLatestTransacton(): Promise<TransactionResponse> {
return this._get(`/transaction/latest`)
}
public async getTransactionBatchByIndex(
index: number
): Promise<TransactionBatchResponse> {
return this._get(`/batch/transaction/index/${index}`)
}
public async getLatestTransactionBatch(): Promise<TransactionBatchResponse> {
return this._get(`/batch/transaction/latest`)
}
public async getStateRootByIndex(index: number): Promise<StateRootResponse> {
return this._get(`/stateroot/index/${index}`)
}
public async getLatestStateRoot(): Promise<StateRootResponse> {
return this._get(`/stateroot/latest`)
}
public async getStateRootBatchByIndex(
index: number
): Promise<StateRootBatchResponse> {
return this._get(`/batch/stateroot/index/${index}`)
}
public async getLatestStateRootBatch(): Promise<StateRootBatchResponse> {
return this._get(`/batch/stateroot/latest`)
}
private async _get<TResponse>(endpoint: string): Promise<TResponse> {
return (await fetch(`${this.url}${endpoint}`)).json()
}
}
/* Imports: External */
import { LevelUp } from 'levelup'
import { BigNumber } from 'ethers'
export class SimpleDB {
constructor(public db: LevelUp) {}
public async get<TEntry>(key: string, index: number): Promise<TEntry | null> {
try {
// TODO: Better checks here.
return JSON.parse(await this.db.get(this._makeKey(key, index)))
} catch (err) {
return null
}
}
public async range<TEntry>(
key: string,
startIndex: number,
endIndex: number
): Promise<TEntry[] | []> {
try {
return new Promise<any[]>((resolve, reject) => {
const entries: any[] = []
this.db
.createValueStream({
gte: this._makeKey(key, startIndex),
lt: this._makeKey(key, endIndex),
})
.on('data', (transaction: string) => {
entries.push(JSON.parse(transaction))
})
.on('error', (err: any) => {
resolve(null)
})
.on('close', () => {
// TODO: Close vs end? Need to double check later.
resolve(entries)
})
.on('end', () => {
resolve(entries)
})
})
} catch (err) {
return []
}
}
public async put<TEntry>(
entries: {
key: string
index: number
value: TEntry
}[]
): Promise<void> {
return this.db.batch(
entries.map((entry) => {
return {
type: 'put',
key: this._makeKey(entry.key, entry.index),
value: JSON.stringify(entry.value),
}
})
)
}
private _makeKey(key: string, index: number): string {
return `${key}:${BigNumber.from(index).toString().padStart(32, '0')}`
}
}
/* Imports: External */
import { LevelUp } from 'levelup'
import { BigNumber } from 'ethers'
/* Imports: Internal */
import {
EnqueueEntry,
StateRootBatchEntry,
StateRootEntry,
TransactionBatchEntry,
TransactionEntry,
} from '../types/database-types'
import { SimpleDB } from './simple-db'
const TRANSPORT_DB_KEYS = {
ENQUEUE: `enqueue`,
ENQUEUE_CTC_INDEX: `ctc:enqueue`,
TRANSACTION: `transaction`,
UNCONFIRMED_TRANSACTION: `unconfirmed:transaction`,
UNCONFIRMED_HIGHEST: `unconfirmed:highest`,
TRANSACTION_BATCH: `batch:transaction`,
STATE_ROOT: `stateroot`,
UNCONFIRMED_STATE_ROOT: `unconfirmed:stateroot`,
STATE_ROOT_BATCH: `batch:stateroot`,
STARTING_L1_BLOCK: `l1:starting`,
HIGHEST_L2_BLOCK: `l2:highest`,
HIGHEST_SYNCED_BLOCK: `synced:highest`,
}
interface Indexed {
index: number
}
export class TransportDB {
public db: SimpleDB
constructor(leveldb: LevelUp) {
this.db = new SimpleDB(leveldb)
}
public async putEnqueueEntries(entries: EnqueueEntry[]): Promise<void> {
await this._putEntries(TRANSPORT_DB_KEYS.ENQUEUE, entries)
}
public async putTransactionEntries(
entries: TransactionEntry[]
): Promise<void> {
await this._putEntries(TRANSPORT_DB_KEYS.TRANSACTION, entries)
}
public async putUnconfirmedTransactionEntries(
entries: TransactionEntry[]
): Promise<void> {
await this._putEntries(TRANSPORT_DB_KEYS.UNCONFIRMED_TRANSACTION, entries)
}
public async putTransactionBatchEntries(
entries: TransactionBatchEntry[]
): Promise<void> {
await this._putEntries(TRANSPORT_DB_KEYS.TRANSACTION_BATCH, entries)
}
public async putStateRootEntries(entries: StateRootEntry[]): Promise<void> {
await this._putEntries(TRANSPORT_DB_KEYS.STATE_ROOT, entries)
}
public async putUnconfirmedStateRootEntries(
entries: StateRootEntry[]
): Promise<void> {
await this._putEntries(TRANSPORT_DB_KEYS.UNCONFIRMED_STATE_ROOT, entries)
}
public async putStateRootBatchEntries(
entries: StateRootBatchEntry[]
): Promise<void> {
await this._putEntries(TRANSPORT_DB_KEYS.STATE_ROOT_BATCH, entries)
}
public async putTransactionIndexByQueueIndex(
queueIndex: number,
index: number
): Promise<void> {
await this.db.put([
{
key: TRANSPORT_DB_KEYS.ENQUEUE_CTC_INDEX,
index: queueIndex,
value: index,
},
])
}
public async getTransactionIndexByQueueIndex(index: number): Promise<number> {
return this.db.get(TRANSPORT_DB_KEYS.ENQUEUE_CTC_INDEX, index)
}
public async getEnqueueByIndex(index: number): Promise<EnqueueEntry> {
return this._getEntryByIndex(TRANSPORT_DB_KEYS.ENQUEUE, index)
}
public async getTransactionByIndex(index: number): Promise<TransactionEntry> {
return this._getEntryByIndex(TRANSPORT_DB_KEYS.TRANSACTION, index)
}
public async getUnconfirmedTransactionByIndex(
index: number
): Promise<TransactionEntry> {
return this._getEntryByIndex(
TRANSPORT_DB_KEYS.UNCONFIRMED_TRANSACTION,
index
)
}
public async getTransactionsByIndexRange(
start: number,
end: number
): Promise<TransactionEntry[]> {
return this._getEntries(TRANSPORT_DB_KEYS.TRANSACTION, start, end)
}
public async getTransactionBatchByIndex(
index: number
): Promise<TransactionBatchEntry> {
return this._getEntryByIndex(TRANSPORT_DB_KEYS.TRANSACTION_BATCH, index)
}
public async getStateRootByIndex(index: number): Promise<StateRootEntry> {
return this._getEntryByIndex(TRANSPORT_DB_KEYS.STATE_ROOT, index)
}
public async getUnconfirmedStateRootByIndex(
index: number
): Promise<StateRootEntry> {
return this._getEntryByIndex(
TRANSPORT_DB_KEYS.UNCONFIRMED_STATE_ROOT,
index
)
}
public async getStateRootsByIndexRange(
start: number,
end: number
): Promise<StateRootEntry[]> {
return this._getEntries(TRANSPORT_DB_KEYS.STATE_ROOT, start, end)
}
public async getStateRootBatchByIndex(
index: number
): Promise<StateRootBatchEntry> {
return this._getEntryByIndex(TRANSPORT_DB_KEYS.STATE_ROOT_BATCH, index)
}
public async getLatestEnqueue(): Promise<EnqueueEntry> {
return this._getLatestEntry(TRANSPORT_DB_KEYS.ENQUEUE)
}
public async getLatestTransaction(): Promise<TransactionEntry> {
return this._getLatestEntry(TRANSPORT_DB_KEYS.TRANSACTION)
}
public async getLatestUnconfirmedTransaction(): Promise<TransactionEntry> {
return this._getLatestEntry(TRANSPORT_DB_KEYS.UNCONFIRMED_TRANSACTION)
}
public async getLatestTransactionBatch(): Promise<TransactionBatchEntry> {
return this._getLatestEntry(TRANSPORT_DB_KEYS.TRANSACTION_BATCH)
}
public async getLatestStateRoot(): Promise<StateRootEntry> {
return this._getLatestEntry(TRANSPORT_DB_KEYS.STATE_ROOT)
}
public async getLatestUnconfirmedStateRoot(): Promise<StateRootEntry> {
return this._getLatestEntry(TRANSPORT_DB_KEYS.UNCONFIRMED_STATE_ROOT)
}
public async getLatestStateRootBatch(): Promise<StateRootBatchEntry> {
return this._getLatestEntry(TRANSPORT_DB_KEYS.STATE_ROOT_BATCH)
}
public async getHighestL2BlockNumber(): Promise<number> {
return this.db.get<number>(TRANSPORT_DB_KEYS.HIGHEST_L2_BLOCK, 0)
}
public async putHighestL2BlockNumber(
block: number | BigNumber
): Promise<void> {
if (block <= (await this.getHighestL2BlockNumber())) {
return
}
return this.db.put<number>([
{
key: TRANSPORT_DB_KEYS.HIGHEST_L2_BLOCK,
index: 0,
value: BigNumber.from(block).toNumber(),
},
])
}
public async getHighestSyncedUnconfirmedBlock(): Promise<number> {
return (
(await this.db.get<number>(TRANSPORT_DB_KEYS.UNCONFIRMED_HIGHEST, 0)) || 0
)
}
public async setHighestSyncedUnconfirmedBlock(block: number): Promise<void> {
return this.db.put<number>([
{
key: TRANSPORT_DB_KEYS.UNCONFIRMED_HIGHEST,
index: 0,
value: block,
},
])
}
public async getHighestSyncedL1Block(): Promise<number> {
return (
(await this.db.get<number>(TRANSPORT_DB_KEYS.HIGHEST_SYNCED_BLOCK, 0)) ||
0
)
}
public async setHighestSyncedL1Block(block: number): Promise<void> {
return this.db.put<number>([
{
key: TRANSPORT_DB_KEYS.HIGHEST_SYNCED_BLOCK,
index: 0,
value: block,
},
])
}
public async getStartingL1Block(): Promise<number> {
return this.db.get<number>(TRANSPORT_DB_KEYS.STARTING_L1_BLOCK, 0)
}
public async setStartingL1Block(block: number): Promise<void> {
return this.db.put<number>([
{
key: TRANSPORT_DB_KEYS.STARTING_L1_BLOCK,
index: 0,
value: block,
},
])
}
// Not sure if this next section belongs in this class.
public async getFullTransactionByIndex(
index: number
): Promise<TransactionEntry> {
const transaction = await this.getTransactionByIndex(index)
if (transaction === null) {
return null
}
if (transaction.queueOrigin === 'l1') {
const enqueue = await this.getEnqueueByIndex(transaction.queueIndex)
if (enqueue === null) {
return null
}
return {
...transaction,
...{
blockNumber: enqueue.blockNumber,
timestamp: enqueue.timestamp,
gasLimit: enqueue.gasLimit,
target: enqueue.target,
origin: enqueue.origin,
data: enqueue.data,
},
}
} else {
return transaction
}
}
public async getLatestFullTransaction(): Promise<TransactionEntry> {
return this.getFullTransactionByIndex(
await this._getLatestEntryIndex(TRANSPORT_DB_KEYS.TRANSACTION)
)
}
public async getFullTransactionsByIndexRange(
start: number,
end: number
): Promise<TransactionEntry[]> {
const transactions = await this.getTransactionsByIndexRange(start, end)
if (transactions === null) {
return null
}
const fullTransactions = []
for (const transaction of transactions) {
if (transaction.queueOrigin === 'l1') {
const enqueue = await this.getEnqueueByIndex(transaction.queueIndex)
if (enqueue === null) {
return null
}
fullTransactions.push({
...transaction,
...{
blockNumber: enqueue.blockNumber,
timestamp: enqueue.timestamp,
gasLimit: enqueue.gasLimit,
target: enqueue.target,
origin: enqueue.origin,
data: enqueue.data,
},
})
} else {
fullTransactions.push(transaction)
}
}
return fullTransactions
}
private async _getLatestEntryIndex(key: string): Promise<number> {
return this.db.get<number>(`${key}:latest`, 0) || 0
}
private async _putLatestEntryIndex(
key: string,
index: number
): Promise<void> {
return this.db.put<number>([
{
key: `${key}:latest`,
index: 0,
value: index,
},
])
}
private async _getLatestEntry<TEntry extends Indexed>(
key: string
): Promise<TEntry | null> {
return this._getEntryByIndex(key, await this._getLatestEntryIndex(key))
}
private async _putLatestEntry<TEntry extends Indexed>(
key: string,
entry: TEntry
): Promise<void> {
const latest = await this._getLatestEntryIndex(key)
if (entry.index >= latest) {
await this._putLatestEntryIndex(key, entry.index)
}
}
private async _putEntries<TEntry extends Indexed>(
key: string,
entries: TEntry[]
): Promise<void> {
if (entries.length === 0) {
return
}
await this.db.put<TEntry>(
entries.map((entry) => {
return {
key: `${key}:index`,
index: entry.index,
value: entry,
}
})
)
await this._putLatestEntry(key, entries[entries.length - 1])
}
private async _getEntryByIndex<TEntry extends Indexed>(
key: string,
index: number
): Promise<TEntry | null> {
if (index === null) {
return null
}
return this.db.get<TEntry>(`${key}:index`, index)
}
private async _getEntries<TEntry extends Indexed>(
key: string,
startIndex: number,
endIndex: number
): Promise<TEntry[] | []> {
return this.db.range<TEntry>(`${key}:index`, startIndex, endIndex)
}
}
export * from './client/client'
export * from './types'
/* Imports: External */
import { BigNumber, ethers, constants } from 'ethers'
import { getContractFactory } from '@eth-optimism/contracts'
import {
ctcCoder,
fromHexString,
toHexString,
TxType,
} from '@eth-optimism/core-utils'
/* Imports: Internal */
import {
DecodedSequencerBatchTransaction,
EventArgsSequencerBatchAppended,
TransactionBatchEntry,
TransactionEntry,
EventHandlerSet,
} from '../../../types'
import {
SEQUENCER_ENTRYPOINT_ADDRESS,
SEQUENCER_GAS_LIMIT,
} from '../../../utils'
export interface SequencerBatchAppendedExtraData {
timestamp: number
blockNumber: number
submitter: string
l1TransactionData: string
l1TransactionHash: string
gasLimit: number
// Stuff from TransactionBatchAppended.
prevTotalElements: BigNumber
batchIndex: BigNumber
batchSize: BigNumber
batchRoot: string
batchExtraData: string
}
export interface SequencerBatchAppendedParsedEvent {
transactionBatchEntry: TransactionBatchEntry
transactionEntries: TransactionEntry[]
}
export const handleEventsSequencerBatchAppended: EventHandlerSet<
EventArgsSequencerBatchAppended,
SequencerBatchAppendedExtraData,
SequencerBatchAppendedParsedEvent
> = {
getExtraData: async (event, l1RpcProvider) => {
const l1Transaction = await event.getTransaction()
const eventBlock = await event.getBlock()
// TODO: We need to update our events so that we actually have enough information to parse this
// batch without having to pull out this extra event. For the meantime, we need to find this
// "TransactonBatchAppended" event to get the rest of the data.
const OVM_CanonicalTransactionChain = getContractFactory(
'OVM_CanonicalTransactionChain'
)
.attach(event.address)
.connect(l1RpcProvider)
const batchSubmissionEvent = (
await OVM_CanonicalTransactionChain.queryFilter(
OVM_CanonicalTransactionChain.filters.TransactionBatchAppended(),
eventBlock.number,
eventBlock.number
)
).find((foundEvent: ethers.Event) => {
// We might have more than one event in this block, so we specifically want to find a
// "TransactonBatchAppended" event emitted immediately before the event in question.
return (
foundEvent.transactionHash === event.transactionHash &&
foundEvent.logIndex === event.logIndex - 1
)
})
if (!batchSubmissionEvent) {
throw new Error(
`Well, this really shouldn't happen. A SequencerBatchAppended event doesn't have a corresponding TransactionBatchAppended event.`
)
}
return {
timestamp: eventBlock.timestamp,
blockNumber: eventBlock.number,
submitter: l1Transaction.from,
l1TransactionHash: l1Transaction.hash,
l1TransactionData: l1Transaction.data,
gasLimit: SEQUENCER_GAS_LIMIT,
prevTotalElements: batchSubmissionEvent.args._prevTotalElements,
batchIndex: batchSubmissionEvent.args._batchIndex,
batchSize: batchSubmissionEvent.args._batchSize,
batchRoot: batchSubmissionEvent.args._batchRoot,
batchExtraData: batchSubmissionEvent.args._extraData,
}
},
parseEvent: (event, extraData) => {
const transactionEntries: TransactionEntry[] = []
// It's easier to deal with this data if it's a Buffer.
const calldata = fromHexString(extraData.l1TransactionData)
if (calldata.length < 12) {
throw new Error(
`Block ${extraData.blockNumber} transaction data is invalid for decoding: ${extraData.l1TransactionData} , ` +
`converted buffer length is < 12.`
)
}
const numContexts = BigNumber.from(calldata.slice(12, 15)).toNumber()
let transactionIndex = 0
let enqueuedCount = 0
let nextTxPointer = 15 + 16 * numContexts
for (let i = 0; i < numContexts; i++) {
const contextPointer = 15 + 16 * i
const context = parseSequencerBatchContext(calldata, contextPointer)
for (let j = 0; j < context.numSequencedTransactions; j++) {
const sequencerTransaction = parseSequencerBatchTransaction(
calldata,
nextTxPointer
)
const { decoded, type } = maybeDecodeSequencerBatchTransaction(
sequencerTransaction
)
transactionEntries.push({
index: extraData.prevTotalElements
.add(BigNumber.from(transactionIndex))
.toNumber(),
batchIndex: extraData.batchIndex.toNumber(),
blockNumber: BigNumber.from(context.blockNumber).toNumber(),
timestamp: BigNumber.from(context.timestamp).toNumber(),
gasLimit: BigNumber.from(extraData.gasLimit).toNumber(),
target: SEQUENCER_ENTRYPOINT_ADDRESS,
origin: null,
data: toHexString(sequencerTransaction),
queueOrigin: 'sequencer',
type,
queueIndex: null,
decoded,
confirmed: true,
})
nextTxPointer += 3 + sequencerTransaction.length
transactionIndex++
}
for (let j = 0; j < context.numSubsequentQueueTransactions; j++) {
const queueIndex = event.args._startingQueueIndex.add(
BigNumber.from(enqueuedCount)
)
// Okay, so. Since events are processed in parallel, we don't know if the Enqueue
// event associated with this queue element has already been processed. So we'll ask
// the api to fetch that data for itself later on and we use fake values for some
// fields. The real TODO here is to make sure we fix this data structure to avoid ugly
// "dummy" fields.
transactionEntries.push({
index: extraData.prevTotalElements
.add(BigNumber.from(transactionIndex))
.toNumber(),
batchIndex: extraData.batchIndex.toNumber(),
blockNumber: BigNumber.from(0).toNumber(),
timestamp: BigNumber.from(0).toNumber(),
gasLimit: BigNumber.from(0).toNumber(),
target: constants.AddressZero,
origin: constants.AddressZero,
data: '0x',
queueOrigin: 'l1',
type: 'EIP155',
queueIndex: queueIndex.toNumber(),
decoded: null,
confirmed: true,
})
enqueuedCount++
transactionIndex++
}
}
const transactionBatchEntry: TransactionBatchEntry = {
index: extraData.batchIndex.toNumber(),
root: extraData.batchRoot,
size: extraData.batchSize.toNumber(),
prevTotalElements: extraData.prevTotalElements.toNumber(),
extraData: extraData.batchExtraData,
blockNumber: BigNumber.from(extraData.blockNumber).toNumber(),
timestamp: BigNumber.from(extraData.timestamp).toNumber(),
submitter: extraData.submitter,
l1TransactionHash: extraData.l1TransactionHash,
}
return {
transactionBatchEntry,
transactionEntries,
}
},
storeEvent: async (entry, db) => {
await db.putTransactionBatchEntries([entry.transactionBatchEntry])
await db.putTransactionEntries(entry.transactionEntries)
// Add an additional field to the enqueued transactions in the database
// if they have already been confirmed
for (const transactionEntry of entry.transactionEntries) {
if (transactionEntry.queueOrigin === 'l1') {
await db.putTransactionIndexByQueueIndex(
transactionEntry.queueIndex,
transactionEntry.index
)
}
}
},
}
interface SequencerBatchContext {
numSequencedTransactions: number
numSubsequentQueueTransactions: number
timestamp: number
blockNumber: number
}
const parseSequencerBatchContext = (
calldata: Buffer,
offset: number
): SequencerBatchContext => {
return {
numSequencedTransactions: BigNumber.from(
calldata.slice(offset, offset + 3)
).toNumber(),
numSubsequentQueueTransactions: BigNumber.from(
calldata.slice(offset + 3, offset + 6)
).toNumber(),
timestamp: BigNumber.from(
calldata.slice(offset + 6, offset + 11)
).toNumber(),
blockNumber: BigNumber.from(
calldata.slice(offset + 11, offset + 16)
).toNumber(),
}
}
const parseSequencerBatchTransaction = (
calldata: Buffer,
offset: number
): Buffer => {
const transactionLength = BigNumber.from(
calldata.slice(offset, offset + 3)
).toNumber()
return calldata.slice(offset + 3, offset + 3 + transactionLength)
}
const maybeDecodeSequencerBatchTransaction = (
transaction: Buffer
): {
decoded: DecodedSequencerBatchTransaction | null
type: 'EIP155' | 'ETH_SIGN' | null
} => {
let decoded = null
let type = null
try {
const txType = transaction.slice(0, 1).readUInt8()
if (txType === TxType.EIP155) {
type = 'EIP155'
decoded = ctcCoder.eip155TxData.decode(transaction.toString('hex'))
} else if (txType === TxType.EthSign) {
type = 'ETH_SIGN'
decoded = ctcCoder.ethSignTxData.decode(transaction.toString('hex'))
} else {
throw new Error(`Unknown sequencer transaction type.`)
}
// Validate the transaction
if (!validateBatchTransaction(type, decoded)) {
decoded = null
}
} catch (err) {
// Do nothing
}
return {
decoded,
type,
}
}
export function validateBatchTransaction(
type: string | null,
decoded: DecodedSequencerBatchTransaction | null
): boolean {
// Unknown types are considered invalid
if (type === null) {
return false
}
if (type === 'EIP155' || type === 'ETH_SIGN') {
if (decoded.sig.v !== 1 && decoded.sig.v !== 0) {
return false
}
return true
}
// Allow soft forks
return false
}
/* Imports: External */
import { getContractFactory } from '@eth-optimism/contracts'
import { BigNumber } from 'ethers'
/* Imports: Internal */
import {
EventArgsStateBatchAppended,
StateRootBatchEntry,
StateRootEntry,
EventHandlerSet,
} from '../../../types'
export const handleEventsStateBatchAppended: EventHandlerSet<
EventArgsStateBatchAppended,
{
timestamp: number
blockNumber: number
submitter: string
l1TransactionHash: string
l1TransactionData: string
},
{
stateRootBatchEntry: StateRootBatchEntry
stateRootEntries: StateRootEntry[]
}
> = {
getExtraData: async (event) => {
const eventBlock = await event.getBlock()
const l1Transaction = await event.getTransaction()
return {
timestamp: eventBlock.timestamp,
blockNumber: eventBlock.number,
submitter: l1Transaction.from,
l1TransactionHash: l1Transaction.hash,
l1TransactionData: l1Transaction.data,
}
},
parseEvent: (event, extraData) => {
const stateRoots = getContractFactory(
'OVM_StateCommitmentChain'
).interface.decodeFunctionData(
'appendStateBatch',
extraData.l1TransactionData
)[0]
const stateRootEntries: StateRootEntry[] = []
for (let i = 0; i < stateRoots.length; i++) {
stateRootEntries.push({
index: event.args._prevTotalElements.add(BigNumber.from(i)).toNumber(),
batchIndex: event.args._batchIndex.toNumber(),
value: stateRoots[i],
confirmed: true,
})
}
// Using .toNumber() here and in other places because I want to move everything to use
// BigNumber + hex, but that'll take a lot of work. This makes it easier in the future.
const stateRootBatchEntry: StateRootBatchEntry = {
index: event.args._batchIndex.toNumber(),
blockNumber: BigNumber.from(extraData.blockNumber).toNumber(),
timestamp: BigNumber.from(extraData.timestamp).toNumber(),
submitter: extraData.submitter,
size: event.args._batchSize.toNumber(),
root: event.args._batchRoot,
prevTotalElements: event.args._prevTotalElements.toNumber(),
extraData: event.args._extraData,
l1TransactionHash: extraData.l1TransactionHash,
}
return {
stateRootBatchEntry,
stateRootEntries,
}
},
storeEvent: async (entry, db) => {
await db.putStateRootBatchEntries([entry.stateRootBatchEntry])
await db.putStateRootEntries(entry.stateRootEntries)
},
}
/* Imports: Internal */
import { BigNumber } from 'ethers'
import {
EnqueueEntry,
EventArgsTransactionEnqueued,
EventHandlerSet,
} from '../../../types'
export const handleEventsTransactionEnqueued: EventHandlerSet<
EventArgsTransactionEnqueued,
null,
EnqueueEntry
> = {
getExtraData: async () => {
return null
},
parseEvent: (event) => {
return {
index: event.args._queueIndex.toNumber(),
target: event.args._target,
data: event.args._data,
gasLimit: event.args._gasLimit.toNumber(),
origin: event.args._l1TxOrigin,
blockNumber: BigNumber.from(event.blockNumber).toNumber(),
timestamp: event.args._timestamp.toNumber(),
ctcIndex: null,
}
},
storeEvent: async (entry, db) => {
await db.putEnqueueEntries([entry])
},
}
/* Imports: External */
import { BaseService } from '@eth-optimism/service-base'
import { fromHexString } from '@eth-optimism/core-utils'
import { JsonRpcProvider } from '@ethersproject/providers'
import colors from 'colors/safe'
import { LevelUp } from 'levelup'
/* Imports: Internal */
import { TransportDB } from '../../db/transport-db'
import {
OptimismContracts,
sleep,
loadOptimismContracts,
loadContract,
validators,
} from '../../utils'
import {
EventArgsAddressSet,
TypedEthersEvent,
EventHandlerSet,
} from '../../types'
import { handleEventsTransactionEnqueued } from './handlers/transaction-enqueued'
import { handleEventsSequencerBatchAppended } from './handlers/sequencer-batch-appended'
import { handleEventsStateBatchAppended } from './handlers/state-batch-appended'
import { L1DataTransportServiceOptions } from '../main/service'
import { constants } from 'ethers'
export interface L1IngestionServiceOptions
extends L1DataTransportServiceOptions {
db: LevelUp
}
export class L1IngestionService extends BaseService<L1IngestionServiceOptions> {
protected name = 'L1 Ingestion Service'
protected optionSettings = {
db: {
validate: validators.isLevelUP,
},
addressManager: {
validate: validators.isAddress,
},
confirmations: {
default: 35,
validate: validators.isInteger,
},
pollingInterval: {
default: 5000,
validate: validators.isInteger,
},
logsPerPollingInterval: {
default: 2000,
validate: validators.isInteger,
},
dangerouslyCatchAllErrors: {
default: false,
validate: validators.isBoolean,
},
l1RpcProvider: {
validate: (val: any) => {
return validators.isUrl(val) || validators.isJsonRpcProvider(val)
},
},
}
private state: {
db: TransportDB
contracts: OptimismContracts
l1RpcProvider: JsonRpcProvider
startingL1BlockNumber: number
} = {} as any
protected async _init(): Promise<void> {
this.state.db = new TransportDB(this.options.db)
this.state.l1RpcProvider =
typeof this.options.l1RpcProvider === 'string'
? new JsonRpcProvider(this.options.l1RpcProvider)
: this.options.l1RpcProvider
this.logger.info('Using AddressManager', {
addressManager: this.options.addressManager,
})
const Lib_AddressManager = loadContract(
'Lib_AddressManager',
this.options.addressManager,
this.state.l1RpcProvider
)
const code = await this.state.l1RpcProvider.getCode(
Lib_AddressManager.address
)
if (fromHexString(code).length === 0) {
throw new Error(
`Provided AddressManager doesn't have any code: ${Lib_AddressManager.address}`
)
}
try {
// Just check to make sure this doesn't throw. If this is a valid AddressManager, then this
// call should succeed. If it throws, then our AddressManager is broken. We don't care about
// the result.
await Lib_AddressManager.getAddress(
`Here's a contract name that definitely doesn't exist.`
)
} catch (err) {
throw new Error(
`Seems like your AddressManager is busted: ${Lib_AddressManager.address}`
)
}
// Would be nice if this weren't necessary, maybe one day.
// TODO: Probably just assert inside here that all of the contracts have code in them.
this.state.contracts = await loadOptimismContracts(
this.state.l1RpcProvider,
this.options.addressManager
)
const startingL1BlockNumber = await this.state.db.getStartingL1Block()
if (startingL1BlockNumber) {
this.state.startingL1BlockNumber = startingL1BlockNumber
} else {
this.logger.info(
'Attempting to find an appropriate L1 block height to begin sync...'
)
this.state.startingL1BlockNumber = await this._findStartingL1BlockNumber()
this.logger.info('Starting sync', {
startingL1BlockNumber: this.state.startingL1BlockNumber,
})
await this.state.db.setStartingL1Block(this.state.startingL1BlockNumber)
}
// Store the total number of submitted transactions so the server can tell clients if we're
// done syncing or not
const totalElements = await this.state.contracts.OVM_CanonicalTransactionChain.getTotalElements()
if (totalElements > 0) {
await this.state.db.putHighestL2BlockNumber(totalElements - 1)
}
}
protected async _start(): Promise<void> {
// This is our main function. It's basically just an infinite loop that attempts to stay in
// sync with events coming from Ethereum. Loops as quickly as it can until it approaches the
// tip of the chain, after which it starts waiting for a few seconds between each loop to avoid
// unnecessary spam.
while (this.running) {
try {
const highestSyncedL1Block =
(await this.state.db.getHighestSyncedL1Block()) ||
this.state.startingL1BlockNumber
const currentL1Block = await this.state.l1RpcProvider.getBlockNumber()
const targetL1Block = Math.min(
highestSyncedL1Block + this.options.logsPerPollingInterval,
currentL1Block - this.options.confirmations
)
// We're already at the head, so no point in attempting to sync.
if (highestSyncedL1Block === targetL1Block) {
await sleep(this.options.pollingInterval)
continue
}
this.logger.info('Synchronizing events from Layer 1 (Ethereum)', {
highestSyncedL1Block,
targetL1Block,
})
// I prefer to do this in serial to avoid non-determinism. We could have a discussion about
// using Promise.all if necessary, but I don't see a good reason to do so unless parsing is
// really, really slow for all event types.
await this._syncEvents(
'OVM_CanonicalTransactionChain',
'TransactionEnqueued',
highestSyncedL1Block,
targetL1Block,
handleEventsTransactionEnqueued
)
await this._syncEvents(
'OVM_CanonicalTransactionChain',
'SequencerBatchAppended',
highestSyncedL1Block,
targetL1Block,
handleEventsSequencerBatchAppended
)
await this._syncEvents(
'OVM_StateCommitmentChain',
'StateBatchAppended',
highestSyncedL1Block,
targetL1Block,
handleEventsStateBatchAppended
)
await this.state.db.setHighestSyncedL1Block(targetL1Block)
if (
currentL1Block - highestSyncedL1Block <
this.options.logsPerPollingInterval
) {
await sleep(this.options.pollingInterval)
}
} catch (err) {
if (!this.running || this.options.dangerouslyCatchAllErrors) {
this.logger.error('Caught an unhandled error', { err })
await sleep(this.options.pollingInterval)
} else {
// TODO: Is this the best thing to do here?
throw err
}
}
}
}
private async _syncEvents(
contractName: string,
eventName: string,
fromL1Block: number,
toL1Block: number,
handlers: EventHandlerSet<any, any, any>
): Promise<void> {
// Basic sanity checks.
if (!this.state.contracts[contractName]) {
throw new Error(`Contract ${contractName} does not exist.`)
}
// Basic sanity checks.
if (!this.state.contracts[contractName].filters[eventName]) {
throw new Error(
`Event ${eventName} does not exist on contract ${contractName}`
)
}
// We need to figure out how to make this work without Infura. Mark and I think that infura is
// doing some indexing of events beyond Geth's native capabilities, meaning some event logic
// will only work on Infura and not on a local geth instance. Not great.
const addressSetEvents = ((await this.state.contracts.Lib_AddressManager.queryFilter(
this.state.contracts.Lib_AddressManager.filters.AddressSet(),
fromL1Block,
toL1Block
)) as TypedEthersEvent<EventArgsAddressSet>[]).filter((event) => {
return event.args._name === contractName
})
// We're going to parse things out in ranges because the address of a given contract may have
// changed in the range provided by the user.
const eventRanges: {
address: string
fromBlock: number
toBlock: number
}[] = []
// Add a range for each address change.
let l1BlockRangeStart = fromL1Block
for (const addressSetEvent of addressSetEvents) {
eventRanges.push({
address: await this._getContractAddressAtBlock(
contractName,
addressSetEvent.blockNumber
),
fromBlock: l1BlockRangeStart,
toBlock: addressSetEvent.blockNumber,
})
l1BlockRangeStart = addressSetEvent.blockNumber
}
// Add one more range to get us to the end of the user-provided block range.
eventRanges.push({
address: await this._getContractAddressAtBlock(contractName, toL1Block),
fromBlock: l1BlockRangeStart,
toBlock: toL1Block,
})
for (const eventRange of eventRanges) {
// Find all relevant events within the range.
const events: TypedEthersEvent<any>[] = await this.state.contracts[
contractName
]
.attach(eventRange.address)
.queryFilter(
this.state.contracts[contractName].filters[eventName](),
eventRange.fromBlock,
eventRange.toBlock
)
// Handle events, if any.
if (events.length > 0) {
const tick = Date.now()
for (const event of events) {
const extraData = await handlers.getExtraData(
event,
this.state.l1RpcProvider
)
const parsedEvent = await handlers.parseEvent(event, extraData)
await handlers.storeEvent(parsedEvent, this.state.db)
}
const tock = Date.now()
this.logger.info('Processed events', {
eventName,
numEvents: events.length,
durationMs: tock - tick,
})
}
}
}
/**
* Gets the address of a contract at a particular block in the past.
* @param contractName Name of the contract to get an address for.
* @param blockNumber Block at which to get an address.
* @return Contract address.
*/
private async _getContractAddressAtBlock(
contractName: string,
blockNumber: number
): Promise<string> {
// TODO: Should be much easier than this. Need to change the params of this event.
const relevantAddressSetEvents = (
await this.state.contracts.Lib_AddressManager.queryFilter(
this.state.contracts.Lib_AddressManager.filters.AddressSet(),
this.state.startingL1BlockNumber
)
).filter((event) => {
return (
event.args._name === contractName && event.blockNumber < blockNumber
)
})
if (relevantAddressSetEvents.length > 0) {
return relevantAddressSetEvents[relevantAddressSetEvents.length - 1].args
._newAddress
} else {
// Address wasn't set before this.
return constants.AddressZero
}
}
private async _findStartingL1BlockNumber(): Promise<number> {
const currentL1Block = await this.state.l1RpcProvider.getBlockNumber()
for (let i = 0; i < currentL1Block; i += 1000000) {
const events = await this.state.contracts.Lib_AddressManager.queryFilter(
this.state.contracts.Lib_AddressManager.filters.AddressSet(),
i,
Math.min(i + 1000000, currentL1Block)
)
if (events.length > 0) {
return events[0].blockNumber
}
}
throw new Error(`Unable to find appropriate L1 starting block number`)
}
}
/* Imports: External */
import { ctcCoder } from '@eth-optimism/core-utils'
import { BigNumber, constants, ethers } from 'ethers'
/* Imports: Internal */
import { TransportDB } from '../../../db/transport-db'
import {
DecodedSequencerBatchTransaction,
StateRootEntry,
TransactionEntry,
} from '../../../types'
import {
padHexString,
SEQUENCER_ENTRYPOINT_ADDRESS,
SEQUENCER_GAS_LIMIT,
} from '../../../utils'
export const handleSequencerBlock = {
parseBlock: async (
block: any,
chainId: number
): Promise<{
transactionEntry: TransactionEntry
stateRootEntry: StateRootEntry
}> => {
const transaction = block.transactions[0]
const transactionIndex =
transaction.index === null || transaction.index === undefined
? BigNumber.from(transaction.blockNumber).toNumber() - 1
: BigNumber.from(transaction.index).toNumber()
let transactionEntry: Partial<TransactionEntry> = {
// Legacy support.
index: transactionIndex,
batchIndex: null,
blockNumber: BigNumber.from(transaction.l1BlockNumber).toNumber(),
timestamp: BigNumber.from(transaction.l1Timestamp).toNumber(),
queueOrigin: transaction.queueOrigin,
type: parseTxType(transaction.txType),
confirmed: false,
}
if (transaction.queueOrigin === 'sequencer') {
const decodedTransaction: DecodedSequencerBatchTransaction = {
sig: {
v: BigNumber.from(transaction.v).toNumber() - 2 * chainId - 35,
r: padHexString(transaction.r, 32),
s: padHexString(transaction.s, 32),
},
gasLimit: BigNumber.from(transaction.gas).toNumber(),
gasPrice: BigNumber.from(transaction.gasPrice).toNumber(), // ?
nonce: BigNumber.from(transaction.nonce).toNumber(),
target: transaction.to || constants.AddressZero, // ?
data: transaction.input,
type: transaction.txType,
}
transactionEntry = {
...transactionEntry,
gasLimit: SEQUENCER_GAS_LIMIT, // ?
target: SEQUENCER_ENTRYPOINT_ADDRESS,
origin: null,
data: maybeEncodeSequencerBatchTransaction(
decodedTransaction,
transaction.txType
),
decoded: decodedTransaction,
queueIndex: null,
}
} else {
transactionEntry = {
...transactionEntry,
gasLimit: BigNumber.from(transaction.gas).toNumber(),
target: ethers.utils.getAddress(transaction.to),
origin: ethers.utils.getAddress(transaction.l1TxOrigin),
data: transaction.input,
decoded: null,
queueIndex:
transaction.queueIndex === null ||
transaction.queueIndex === undefined
? BigNumber.from(transaction.nonce).toNumber()
: BigNumber.from(transaction.queueIndex).toNumber(),
}
}
const stateRootEntry: StateRootEntry = {
index: transactionIndex,
batchIndex: null,
value: block.stateRoot,
confirmed: false,
}
return {
transactionEntry: transactionEntry as TransactionEntry, // Not the cleanest thing in the world. Could be improved.
stateRootEntry,
}
},
storeBlock: async (
entry: {
transactionEntry: TransactionEntry
stateRootEntry: StateRootEntry
},
db: TransportDB
): Promise<void> => {
// Having separate indices for confirmed/unconfirmed means we never have to worry about
// accidentally overwriting a confirmed transaction with an unconfirmed one. Unconfirmed
// transactions are purely extra information.
await db.putUnconfirmedTransactionEntries([entry.transactionEntry])
await db.putUnconfirmedStateRootEntries([entry.stateRootEntry])
},
}
/**
* Attempts to encode a sequencer batch transaction.
* @param transaction Transaction to encode.
* @param type Transaction type.
*/
const maybeEncodeSequencerBatchTransaction = (
transaction: DecodedSequencerBatchTransaction,
type: 'EIP155' | 'EthSign' | null
): string => {
if (type === 'EIP155') {
return ctcCoder.eip155TxData.encode(transaction).toLowerCase()
} else if (type === 'EthSign') {
return ctcCoder.ethSignTxData.encode(transaction).toLowerCase()
} else {
// Throw?
return
}
}
/**
* Handles differences between the sequencer's enum strings and our own.
* Will probably want to move this into core-utils eventually.
* @param type Sequencer transaction type to parse.
*/
const parseTxType = (
type: 'EIP155' | 'EthSign' | null
): 'EIP155' | 'ETH_SIGN' | null => {
if (type === 'EthSign') {
return 'ETH_SIGN'
} else {
return type
}
}
/* Imports: External */
import { BaseService } from '@eth-optimism/service-base'
import { JsonRpcProvider } from '@ethersproject/providers'
import colors from 'colors/safe'
import { BigNumber } from 'ethers'
import { LevelUp } from 'levelup'
/* Imports: Internal */
import { TransportDB } from '../../db/transport-db'
import { sleep, toRpcHexString, validators } from '../../utils'
import { L1DataTransportServiceOptions } from '../main/service'
import { handleSequencerBlock } from './handlers/transaction'
export interface L2IngestionServiceOptions
extends L1DataTransportServiceOptions {
db: LevelUp
}
export class L2IngestionService extends BaseService<L2IngestionServiceOptions> {
protected name = 'L2 Ingestion Service'
protected optionSettings = {
db: {
validate: validators.isLevelUP,
},
l2RpcProvider: {
validate: (val: any) => {
return validators.isUrl(val) || validators.isJsonRpcProvider(val)
},
},
l2ChainId: {
validate: validators.isInteger,
},
pollingInterval: {
default: 5000,
validate: validators.isInteger,
},
transactionsPerPollingInterval: {
default: 1000,
validate: validators.isInteger,
},
dangerouslyCatchAllErrors: {
default: false,
validate: validators.isBoolean,
},
legacySequencerCompatibility: {
default: false,
validate: validators.isBoolean,
},
stopL2SyncAtBlock: {
default: Infinity,
validate: validators.isInteger,
},
}
private state: {
db: TransportDB
l2RpcProvider: JsonRpcProvider
} = {} as any
protected async _init(): Promise<void> {
if (this.options.legacySequencerCompatibility) {
this.logger.info(
'Using legacy sync, this will be quite a bit slower than normal'
)
}
this.state.db = new TransportDB(this.options.db)
this.state.l2RpcProvider =
typeof this.options.l2RpcProvider === 'string'
? new JsonRpcProvider(this.options.l2RpcProvider)
: this.options.l2RpcProvider
}
protected async _start(): Promise<void> {
while (this.running) {
try {
const highestSyncedL2BlockNumber =
(await this.state.db.getHighestSyncedUnconfirmedBlock()) || 1
// Shut down if we're at the stop block.
if (
this.options.stopL2SyncAtBlock !== undefined &&
this.options.stopL2SyncAtBlock !== null &&
highestSyncedL2BlockNumber >= this.options.stopL2SyncAtBlock
) {
this.logger.info(
"L2 sync is shutting down because we've reached your target block. Goodbye!"
)
return
}
let currentL2Block = await this.state.l2RpcProvider.getBlockNumber()
// Make sure we can't exceed the stop block.
if (
this.options.stopL2SyncAtBlock !== undefined &&
this.options.stopL2SyncAtBlock !== null
) {
currentL2Block = Math.min(
currentL2Block,
this.options.stopL2SyncAtBlock
)
}
// Make sure we don't exceed the tip.
const targetL2Block = Math.min(
highestSyncedL2BlockNumber +
this.options.transactionsPerPollingInterval,
currentL2Block
)
// We're already at the head, so no point in attempting to sync.
if (highestSyncedL2BlockNumber === targetL2Block) {
await sleep(this.options.pollingInterval)
continue
}
this.logger.info(
'Synchronizing unconfirmed transactions from Layer 2 (Optimistic Ethereum)',
{
fromBlock: highestSyncedL2BlockNumber,
toBlock: targetL2Block,
}
)
// Synchronize by requesting blocks from the sequencer. Sync from L1 takes precedence.
await this._syncSequencerBlocks(
highestSyncedL2BlockNumber,
targetL2Block
)
await this.state.db.setHighestSyncedUnconfirmedBlock(targetL2Block)
if (
currentL2Block - highestSyncedL2BlockNumber <
this.options.transactionsPerPollingInterval
) {
await sleep(this.options.pollingInterval)
}
} catch (err) {
if (!this.running || this.options.dangerouslyCatchAllErrors) {
this.logger.error('Caught an unhandled error', { err })
await sleep(this.options.pollingInterval)
} else {
// TODO: Is this the best thing to do here?
throw err
}
}
}
}
/**
* Synchronizes unconfirmed transactions from a range of sequencer blocks.
* @param startBlockNumber Block to start querying from.
* @param endBlockNumber Block to query to.
*/
private async _syncSequencerBlocks(
startBlockNumber: number,
endBlockNumber: number
): Promise<void> {
if (startBlockNumber > endBlockNumber) {
this.logger.warn(
'Cannot query with start block number larger than end block number',
{
startBlockNumber,
endBlockNumber,
}
)
return
}
let blocks: any = []
if (this.options.legacySequencerCompatibility) {
const blockPromises = []
for (let i = startBlockNumber; i <= endBlockNumber; i++) {
blockPromises.push(
this.state.l2RpcProvider.send('eth_getBlockByNumber', [
toRpcHexString(i),
true,
])
)
}
// Just making sure that the blocks will come back in increasing order.
blocks = (await Promise.all(blockPromises)).sort((a, b) => {
return (
BigNumber.from(a.number).toNumber() -
BigNumber.from(b.number).toNumber()
)
})
} else {
blocks = await this.state.l2RpcProvider.send('eth_getBlockRange', [
toRpcHexString(startBlockNumber),
toRpcHexString(endBlockNumber),
true,
])
}
for (const block of blocks) {
const entry = await handleSequencerBlock.parseBlock(
block,
this.options.l2ChainId
)
await handleSequencerBlock.storeBlock(entry, this.state.db)
}
}
}
/* Imports: External */
import { BaseService } from '@eth-optimism/service-base'
import { LevelUp } from 'levelup'
import level from 'level'
/* Imports: Internal */
import { L1IngestionService } from '../l1-ingestion/service'
import { L1TransportServer } from '../server/service'
import { validators } from '../../utils'
import { L2IngestionService } from '../l2-ingestion/service'
export interface L1DataTransportServiceOptions {
addressManager: string
confirmations: number
dangerouslyCatchAllErrors?: boolean
hostname: string
l1RpcProvider: string
l2ChainId: number
l2RpcProvider: string
dbPath: string
logsPerPollingInterval: number
pollingInterval: number
port: number
showUnconfirmedTransactions: boolean
syncFromL1?: boolean
syncFromL2?: boolean
transactionsPerPollingInterval: number
legacySequencerCompatibility: boolean
stopL2SyncAtBlock?: number
}
export class L1DataTransportService extends BaseService<L1DataTransportServiceOptions> {
protected name = 'L1 Data Transport Service'
protected optionSettings = {
syncFromL1: {
default: true,
validate: validators.isBoolean,
},
syncFromL2: {
default: false,
validate: validators.isBoolean,
},
}
private state: {
db: LevelUp
l1IngestionService?: L1IngestionService
l2IngestionService?: L2IngestionService
l1TransportServer: L1TransportServer
} = {} as any
protected async _init(): Promise<void> {
this.state.db = level(this.options.dbPath)
await this.state.db.open()
this.state.l1TransportServer = new L1TransportServer({
...this.options,
db: this.state.db,
})
// Optionally enable sync from L1.
if (this.options.syncFromL1) {
this.state.l1IngestionService = new L1IngestionService({
...this.options,
db: this.state.db,
})
}
// Optionally enable sync from L2.
if (this.options.syncFromL2) {
this.state.l2IngestionService = new L2IngestionService({
...(this.options as any), // TODO: Correct thing to do here is to assert this type.
db: this.state.db,
})
}
await this.state.l1TransportServer.init()
if (this.options.syncFromL1) {
await this.state.l1IngestionService.init()
}
if (this.options.syncFromL2) {
await this.state.l2IngestionService.init()
}
}
protected async _start(): Promise<void> {
await Promise.all([
this.state.l1TransportServer.start(),
this.options.syncFromL1 ? this.state.l1IngestionService.start() : null,
this.options.syncFromL2 ? this.state.l2IngestionService.start() : null,
])
}
protected async _stop(): Promise<void> {
await Promise.all([
this.state.l1TransportServer.stop(),
this.options.syncFromL1 ? this.state.l1IngestionService.stop() : null,
this.options.syncFromL2 ? this.state.l2IngestionService.stop() : null,
])
await this.state.db.close()
}
}
/* Imports: External */
import * as dotenv from 'dotenv'
import Config from 'bcfg' // TODO: Add some types for bcfg if we get the chance.
/* Imports: Internal */
import { L1DataTransportService } from './main/service'
interface Bcfg {
load: (options: { env?: boolean; argv?: boolean }) => void
str: (name: string, defaultValue?: string) => string
uint: (name: string, defaultValue?: number) => number
bool: (name: string, defaultValue?: boolean) => boolean
}
;(async () => {
try {
dotenv.config()
const config: Bcfg = new Config('data-transport-layer')
config.load({
env: true,
argv: true,
})
const service = new L1DataTransportService({
dbPath: config.str('dbPath', './db'),
port: config.uint('serverPort', 7878),
hostname: config.str('serverHostname', 'localhost'),
confirmations: config.uint('confirmations', 35),
l1RpcProvider: config.str('l1RpcEndpoint'),
addressManager: config.str('addressManager'),
pollingInterval: config.uint('pollingInterval', 5000),
logsPerPollingInterval: config.uint('logsPerPollingInterval', 2000),
dangerouslyCatchAllErrors: config.bool(
'dangerouslyCatchAllErrors',
false
),
l2RpcProvider: config.str('l2RpcEndpoint'),
l2ChainId: config.uint('l2ChainId'),
syncFromL1: config.bool('syncFromL1', true),
syncFromL2: config.bool('syncFromL2', false),
showUnconfirmedTransactions: config.bool('syncFromL2', false),
transactionsPerPollingInterval: config.uint(
'transactionsPerPollingInterval',
1000
),
legacySequencerCompatibility: config.bool(
'legacySequencerCompatibility',
false
),
stopL2SyncAtBlock: config.uint('stopL2SyncAtBlock'),
})
await service.start()
} catch (err) {
console.error(
`Well, that's that. We ran into a fatal error. Here's the dump. Goodbye!`
)
throw err
}
})()
/* Imports: External */
import { BaseService } from '@eth-optimism/service-base'
import express, { Request, Response } from 'express'
import cors from 'cors'
import { BigNumber } from 'ethers'
import { JsonRpcProvider } from '@ethersproject/providers'
import { LevelUp } from 'levelup'
/* Imports: Internal */
import { TransportDB } from '../../db/transport-db'
import {
ContextResponse,
GasPriceResponse,
EnqueueResponse,
StateRootBatchResponse,
StateRootResponse,
SyncingResponse,
TransactionBatchResponse,
TransactionResponse,
} from '../../types'
import { validators } from '../../utils'
import { L1DataTransportServiceOptions } from '../main/service'
export interface L1TransportServerOptions
extends L1DataTransportServiceOptions {
db: LevelUp
}
export class L1TransportServer extends BaseService<L1TransportServerOptions> {
protected name = 'L1 Transport Server'
protected optionSettings = {
db: {
validate: validators.isLevelUP,
},
port: {
default: 7878,
validate: validators.isInteger,
},
hostname: {
default: 'localhost',
validate: validators.isString,
},
confirmations: {
validate: validators.isInteger,
},
l1RpcProvider: {
validate: (val: any) => {
return validators.isUrl(val) || validators.isJsonRpcProvider(val)
},
},
showUnconfirmedTransactions: {
validate: validators.isBoolean,
},
}
private state: {
app: express.Express
server: any
db: TransportDB
l1RpcProvider: JsonRpcProvider
} = {} as any
protected async _init(): Promise<void> {
// TODO: I don't know if this is strictly necessary, but it's probably a good thing to do.
if (!this.options.db.isOpen()) {
await this.options.db.open()
}
this.state.db = new TransportDB(this.options.db)
this.state.l1RpcProvider =
typeof this.options.l1RpcProvider === 'string'
? new JsonRpcProvider(this.options.l1RpcProvider)
: this.options.l1RpcProvider
this._initializeApp()
}
protected async _start(): Promise<void> {
this.state.server = this.state.app.listen(
this.options.port,
this.options.hostname
)
this.logger.info('Server started and listening', {
host: this.options.hostname,
port: this.options.port,
})
}
protected async _stop(): Promise<void> {
this.state.server.close()
}
/**
* Initializes the server application.
* Do any sort of initialization here that you want. Mostly just important that
* `_registerAllRoutes` is called at the end.
*/
private _initializeApp() {
// TODO: Maybe pass this in as a parameter instead of creating it here?
this.state.app = express()
this.state.app.use(cors())
this._registerAllRoutes()
}
/**
* Registers a route on the server.
* @param method Http method type.
* @param route Route to register.
* @param handler Handler called and is expected to return a JSON response.
*/
private _registerRoute(
method: 'get', // Just handle GET for now, but could extend this with whatever.
route: string,
handler: (req?: Request, res?: Response) => Promise<any>
): void {
// TODO: Better typing on the return value of the handler function.
// TODO: Check for route collisions.
// TODO: Add a different function to allow for removing routes.
this.state.app[method](route, async (req, res) => {
const start = Date.now()
try {
const json = await handler(req, res)
const elapsed = Date.now() - start
this.logger.info('Served HTTP Request', {
method: req.method,
url: req.url,
elapsed,
})
return res.json(json)
} catch (e) {
const elapsed = Date.now() - start
this.logger.info('Failed HTTP Request', {
method: req.method,
url: req.url,
elapsed,
msg: e.toString(),
})
return res.status(400).json({
error: e.toString(),
})
}
})
}
/**
* Registers all of the server routes we want to expose.
* TODO: Link to our API spec.
*/
private _registerAllRoutes(): void {
// TODO: Maybe add doc-like comments to each of these routes?
this._registerRoute(
'get',
'/eth/syncing',
async (): Promise<SyncingResponse> => {
const highestL2BlockNumber = await this.state.db.getHighestL2BlockNumber()
const currentL2Block = await this.state.db.getLatestTransaction()
if (currentL2Block === null) {
if (highestL2BlockNumber === null) {
return {
syncing: false,
currentTransactionIndex: 0,
}
} else {
return {
syncing: true,
highestKnownTransactionIndex: highestL2BlockNumber,
currentTransactionIndex: 0,
}
}
}
if (highestL2BlockNumber > currentL2Block.index) {
return {
syncing: true,
highestKnownTransactionIndex: highestL2BlockNumber,
currentTransactionIndex: currentL2Block.index,
}
} else {
return {
syncing: false,
currentTransactionIndex: currentL2Block.index,
}
}
}
)
this._registerRoute(
'get',
'/eth/gasprice',
async (): Promise<GasPriceResponse> => {
const gasPrice = await this.state.l1RpcProvider.getGasPrice()
return {
gasPrice: gasPrice.toString(),
}
}
)
this._registerRoute(
'get',
'/eth/context/latest',
async (): Promise<ContextResponse> => {
const tip = await this.state.l1RpcProvider.getBlockNumber()
const blockNumber = Math.max(0, tip - this.options.confirmations)
const block = await this.state.l1RpcProvider.getBlock(blockNumber)
return {
blockNumber: block.number,
timestamp: block.timestamp,
blockHash: block.hash,
}
}
)
this._registerRoute(
'get',
'/eth/context/blocknumber/:number',
async (req): Promise<ContextResponse> => {
const number = BigNumber.from(req.params.number).toNumber()
const tip = await this.state.l1RpcProvider.getBlockNumber()
const blockNumber = Math.max(0, tip - this.options.confirmations)
if (number > blockNumber) {
return {
blockNumber: null,
timestamp: null,
blockHash: null,
}
}
const block = await this.state.l1RpcProvider.getBlock(number)
return {
blockNumber: block.number,
timestamp: block.timestamp,
blockHash: block.hash,
}
}
)
this._registerRoute(
'get',
'/enqueue/latest',
async (): Promise<EnqueueResponse> => {
const enqueue = await this.state.db.getLatestEnqueue()
if (enqueue === null) {
return {
index: null,
target: null,
data: null,
gasLimit: null,
origin: null,
blockNumber: null,
timestamp: null,
ctcIndex: null,
}
}
const ctcIndex = await this.state.db.getTransactionIndexByQueueIndex(
enqueue.index
)
return {
...enqueue,
ctcIndex,
}
}
)
this._registerRoute(
'get',
'/enqueue/index/:index',
async (req): Promise<EnqueueResponse> => {
const enqueue = await this.state.db.getEnqueueByIndex(
BigNumber.from(req.params.index).toNumber()
)
if (enqueue === null) {
return {
index: null,
target: null,
data: null,
gasLimit: null,
origin: null,
blockNumber: null,
timestamp: null,
ctcIndex: null,
}
}
const ctcIndex = await this.state.db.getTransactionIndexByQueueIndex(
enqueue.index
)
return {
...enqueue,
ctcIndex,
}
}
)
this._registerRoute(
'get',
'/transaction/latest',
async (): Promise<TransactionResponse> => {
let transaction = await this.state.db.getLatestFullTransaction()
if (this.options.showUnconfirmedTransactions) {
const latestUnconfirmedTx = await this.state.db.getLatestUnconfirmedTransaction()
if (
transaction === null ||
transaction === undefined ||
latestUnconfirmedTx.index >= transaction.index
) {
transaction = latestUnconfirmedTx
}
}
if (transaction === null) {
transaction = await this.state.db.getLatestFullTransaction()
}
if (transaction === null) {
return {
transaction: null,
batch: null,
}
}
const batch = await this.state.db.getTransactionBatchByIndex(
transaction.batchIndex
)
return {
transaction,
batch,
}
}
)
this._registerRoute(
'get',
'/transaction/index/:index',
async (req): Promise<TransactionResponse> => {
let transaction = null
if (this.options.showUnconfirmedTransactions) {
transaction = await this.state.db.getUnconfirmedTransactionByIndex(
BigNumber.from(req.params.index).toNumber()
)
}
if (transaction === null) {
transaction = await this.state.db.getFullTransactionByIndex(
BigNumber.from(req.params.index).toNumber()
)
}
if (transaction === null) {
return {
transaction: null,
batch: null,
}
}
const batch = await this.state.db.getTransactionBatchByIndex(
transaction.batchIndex
)
return {
transaction,
batch,
}
}
)
this._registerRoute(
'get',
'/batch/transaction/latest',
async (): Promise<TransactionBatchResponse> => {
const batch = await this.state.db.getLatestTransactionBatch()
if (batch === null) {
return {
batch: null,
transactions: [],
}
}
const transactions = await this.state.db.getFullTransactionsByIndexRange(
BigNumber.from(batch.prevTotalElements).toNumber(),
BigNumber.from(batch.prevTotalElements).toNumber() +
BigNumber.from(batch.size).toNumber()
)
return {
batch,
transactions,
}
}
)
this._registerRoute(
'get',
'/batch/transaction/index/:index',
async (req): Promise<TransactionBatchResponse> => {
const batch = await this.state.db.getTransactionBatchByIndex(
BigNumber.from(req.params.index).toNumber()
)
if (batch === null) {
return {
batch: null,
transactions: [],
}
}
const transactions = await this.state.db.getFullTransactionsByIndexRange(
BigNumber.from(batch.prevTotalElements).toNumber(),
BigNumber.from(batch.prevTotalElements).toNumber() +
BigNumber.from(batch.size).toNumber()
)
return {
batch,
transactions,
}
}
)
this._registerRoute(
'get',
'/stateroot/latest',
async (): Promise<StateRootResponse> => {
let stateRoot = await this.state.db.getLatestStateRoot()
if (this.options.showUnconfirmedTransactions) {
const latestUnconfirmedStateRoot = await this.state.db.getLatestUnconfirmedStateRoot()
if (
stateRoot === null ||
stateRoot === undefined ||
latestUnconfirmedStateRoot.index >= stateRoot.index
) {
stateRoot = latestUnconfirmedStateRoot
}
}
if (stateRoot === null) {
stateRoot = await this.state.db.getLatestStateRoot()
}
if (stateRoot === null) {
return {
stateRoot: null,
batch: null,
}
}
const batch = await this.state.db.getStateRootBatchByIndex(
stateRoot.batchIndex
)
return {
stateRoot,
batch,
}
}
)
this._registerRoute(
'get',
'/stateroot/index/:index',
async (req): Promise<StateRootResponse> => {
let stateRoot = null
if (this.options.showUnconfirmedTransactions) {
stateRoot = await this.state.db.getUnconfirmedStateRootByIndex(
BigNumber.from(req.params.index).toNumber()
)
}
if (stateRoot === null) {
stateRoot = await this.state.db.getStateRootByIndex(
BigNumber.from(req.params.index).toNumber()
)
}
if (stateRoot === null) {
return {
stateRoot: null,
batch: null,
}
}
const batch = await this.state.db.getStateRootBatchByIndex(
stateRoot.batchIndex
)
return {
stateRoot,
batch,
}
}
)
this._registerRoute(
'get',
'/batch/stateroot/latest',
async (): Promise<StateRootBatchResponse> => {
const batch = await this.state.db.getLatestStateRootBatch()
if (batch === null) {
return {
batch: null,
stateRoots: [],
}
}
const stateRoots = await this.state.db.getStateRootsByIndexRange(
BigNumber.from(batch.prevTotalElements).toNumber(),
BigNumber.from(batch.prevTotalElements).toNumber() +
BigNumber.from(batch.size).toNumber()
)
return {
batch,
stateRoots,
}
}
)
this._registerRoute(
'get',
'/batch/stateroot/index/:index',
async (req): Promise<StateRootBatchResponse> => {
const batch = await this.state.db.getStateRootBatchByIndex(
BigNumber.from(req.params.index).toNumber()
)
if (batch === null) {
return {
batch: null,
stateRoots: [],
}
}
const stateRoots = await this.state.db.getStateRootsByIndexRange(
BigNumber.from(batch.prevTotalElements).toNumber(),
BigNumber.from(batch.prevTotalElements).toNumber() +
BigNumber.from(batch.size).toNumber()
)
return {
batch,
stateRoots,
}
}
)
}
}
import {
EnqueueEntry,
StateRootBatchEntry,
StateRootEntry,
TransactionBatchEntry,
TransactionEntry,
} from './database-types'
export type EnqueueResponse = EnqueueEntry & {
ctcIndex: number | null
}
export interface TransactionResponse {
batch: TransactionBatchEntry
transaction: TransactionEntry
}
export interface TransactionBatchResponse {
batch: TransactionBatchEntry
transactions: TransactionEntry[]
}
export interface StateRootResponse {
batch: StateRootBatchEntry
stateRoot: StateRootEntry
}
export interface StateRootBatchResponse {
batch: StateRootBatchEntry
stateRoots: StateRootEntry[]
}
export interface ContextResponse {
blockNumber: number
timestamp: number
blockHash: string
}
export interface GasPriceResponse {
gasPrice: string
}
export type SyncingResponse =
| {
syncing: true
highestKnownTransactionIndex: number
currentTransactionIndex: number
}
| {
syncing: false
currentTransactionIndex: number
}
export interface DecodedSequencerBatchTransaction {
sig: {
r: string
s: string
v: number
}
gasLimit: number
gasPrice: number
nonce: number
target: string
data: string
type: number
}
export interface EnqueueEntry {
index: number
target: string
data: string
gasLimit: number
origin: string
blockNumber: number
timestamp: number
}
export interface TransactionEntry {
index: number
batchIndex: number
data: string
blockNumber: number
timestamp: number
gasLimit: number
target: string
origin: string
queueOrigin: 'sequencer' | 'l1'
queueIndex: number | null
type: 'EIP155' | 'ETH_SIGN' | null
decoded: DecodedSequencerBatchTransaction | null
confirmed: boolean
}
interface BatchEntry {
index: number
blockNumber: number
timestamp: number
submitter: string
size: number
root: string
prevTotalElements: number
extraData: string
l1TransactionHash: string
}
export type TransactionBatchEntry = BatchEntry
export type StateRootBatchEntry = BatchEntry
export interface StateRootEntry {
index: number
batchIndex: number
value: string
confirmed: boolean
}
import { JsonRpcProvider } from '@ethersproject/providers'
import { TransportDB } from '../db/transport-db'
import { TypedEthersEvent } from './event-types'
export type GetExtraDataHandler<TEventArgs, TExtraData> = (
event?: TypedEthersEvent<TEventArgs>,
l1RpcProvider?: JsonRpcProvider
) => Promise<TExtraData>
export type ParseEventHandler<TEventArgs, TExtraData, TParsedEvent> = (
event: TypedEthersEvent<TEventArgs>,
extraData: TExtraData
) => TParsedEvent
export type StoreEventHandler<TParsedEvent> = (
parsedEvent: TParsedEvent,
db: TransportDB
) => Promise<void>
export interface EventHandlerSet<TEventArgs, TExtraData, TParsedEvent> {
getExtraData: GetExtraDataHandler<TEventArgs, TExtraData>
parseEvent: ParseEventHandler<TEventArgs, TExtraData, TParsedEvent>
storeEvent: StoreEventHandler<TParsedEvent>
}
/* Imports: External */
import { ethers } from 'ethers'
export type TypedEthersEvent<T> = ethers.Event & {
args: T
}
export interface EventArgsAddressSet {
_name: string
_newAddress: string
}
export interface EventArgsTransactionEnqueued {
_l1TxOrigin: string
_target: string
_gasLimit: ethers.BigNumber
_data: string
_queueIndex: ethers.BigNumber
_timestamp: ethers.BigNumber
}
export interface EventArgsTransactionBatchAppended {
_batchIndex: ethers.BigNumber
_batchRoot: string
_batchSize: ethers.BigNumber
_prevTotalElements: ethers.BigNumber
_extraData: string
}
export interface EventArgsStateBatchAppended {
_batchIndex: ethers.BigNumber
_batchRoot: string
_batchSize: ethers.BigNumber
_prevTotalElements: ethers.BigNumber
_extraData: string
}
export interface EventArgsSequencerBatchAppended {
_startingQueueIndex: ethers.BigNumber
_numQueueElements: ethers.BigNumber
_totalElements: ethers.BigNumber
}
export * from './api-types'
export * from './database-types'
export * from './event-handler-types'
export * from './event-types'
import { toHexString } from '@eth-optimism/core-utils'
/**
* Basic timeout-based async sleep function.
* @param ms Number of milliseconds to sleep.
*/
export const sleep = async (ms: number): Promise<void> => {
return new Promise<void>((resolve) => {
setTimeout(resolve, ms)
})
}
export const assert = (condition: () => boolean, reason?: string) => {
try {
if (condition() === false) {
throw new Error(`Assertion failed: ${reason}`)
}
} catch (err) {
throw new Error(`Assertion failed: ${reason}\n${err}`)
}
}
export const toRpcHexString = (n: number): string => {
if (n === 0) {
return '0x0'
} else {
return '0x' + toHexString(n).slice(2).replace(/^0+/, '')
}
}
export const padHexString = (str: string, length: number): string => {
if (str.length === 2 + length * 2) {
return str
} else {
return '0x' + str.slice(2).padStart(length * 2, '0')
}
}
export const SEQUENCER_GAS_LIMIT = 8_000_000 // TODO: Remove and use value from event.
export const SEQUENCER_ENTRYPOINT_ADDRESS =
'0x4200000000000000000000000000000000000005'
/* Imports: External */
import { constants, Contract, Signer } from 'ethers'
import { JsonRpcProvider } from '@ethersproject/providers'
import { getContractInterface } from '@eth-optimism/contracts/build/src/contract-defs'
export const loadContract = (
name: string,
address: string,
provider: JsonRpcProvider
): Contract => {
return new Contract(address, getContractInterface(name) as any, provider)
}
export const loadProxyFromManager = async (
name: string,
proxy: string,
Lib_AddressManager: Contract,
provider: JsonRpcProvider
): Promise<Contract> => {
const address = await Lib_AddressManager.getAddress(proxy)
if (address === constants.AddressZero) {
throw new Error(
`Lib_AddressManager does not have a record for a contract named: ${proxy}`
)
}
return loadContract(name, address, provider)
}
export interface OptimismContracts {
Lib_AddressManager: Contract
OVM_StateCommitmentChain: Contract
OVM_CanonicalTransactionChain: Contract
OVM_ExecutionManager: Contract
}
export const loadOptimismContracts = async (
l1RpcProvider: JsonRpcProvider,
addressManagerAddress: string,
signer?: Signer
): Promise<OptimismContracts> => {
const Lib_AddressManager = loadContract(
'Lib_AddressManager',
addressManagerAddress,
l1RpcProvider
)
const inputs = [
{
name: 'OVM_StateCommitmentChain',
interface: 'iOVM_StateCommitmentChain',
},
{
name: 'OVM_CanonicalTransactionChain',
interface: 'iOVM_CanonicalTransactionChain',
},
{
name: 'OVM_ExecutionManager',
interface: 'iOVM_ExecutionManager',
},
]
const contracts = {}
for (const input of inputs) {
contracts[input.name] = await loadProxyFromManager(
input.interface,
input.name,
Lib_AddressManager,
l1RpcProvider
)
if (signer) {
contracts[input.name] = contracts[input.name].connect(signer)
}
}
contracts['Lib_AddressManager'] = Lib_AddressManager
// TODO: sorry
return contracts as OptimismContracts
}
export * from './common'
export * from './constants'
export * from './contracts'
export * from './validation'
import { fromHexString } from '@eth-optimism/core-utils'
import * as url from 'url'
export const validators = {
isBoolean: (val: any): boolean => {
return typeof val === 'boolean'
},
isString: (val: any): boolean => {
return typeof val === 'string'
},
isHexString: (val: any): boolean => {
return (
validators.isString(val) &&
val.startsWith('0x') &&
fromHexString(val).length === (val.length - 2) / 2
)
},
isAddress: (val: any): boolean => {
return validators.isHexString(val) && val.length === 42
},
isInteger: (val: any): boolean => {
return Number.isInteger(val)
},
isUrl: (val: any): boolean => {
try {
const parsed = new url.URL(val)
return (
parsed.protocol === 'ws:' ||
parsed.protocol === 'http:' ||
parsed.protocol === 'https:'
)
} catch (err) {
return false
}
},
isJsonRpcProvider: (val: any): boolean => {
return val.ready !== undefined
},
isLevelUP: (val: any): boolean => {
// TODO: Fix?
return val && val.db
},
}
import { HardhatUserConfig } from 'hardhat/config'
const config: HardhatUserConfig = {
// All paths relative to ** this file **.
paths: {
tests: '../../test',
cache: '../temp/cache',
artifacts: '../temp/artifacts',
},
}
export default config
/* External Imports */
import chai = require('chai')
import Mocha from 'mocha'
import chaiAsPromised from 'chai-as-promised'
// Chai plugins go here.
chai.use(chaiAsPromised)
const should = chai.should()
const expect = chai.expect
export { should, expect, Mocha }
// Corresponds to tx:
// https://etherscan.io/tx/0x6effe006836b841205ace4d99d7ae1b74ee96aac499a3f358b97fccd32ee9af2
export const l1TransactionData =
'0xd0f893440000011fcd00006500000400001900000000603e47620000b67be400000c00000200603e4aef0000b67c2d00003c00000000603e4c020000b67c4a00000200000000603e4f870000b67c9700006301d8914345750baf3939d57f2502c481154979e99b6049e5db5ae3d164376c2c543a5b75aabd10a41dd6305486b3b0a253b5cf247e0615370d7100c13f550d0ccd0189543f0000000000448700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00014300eabe2f5db7a62275de799b9aee03f55f01743563bbee17e722cd2efb0b1fbc1a246ba5723b499d41bd46f7c20c5eab5216e7b5adac96043c2d671de2b47588200089543f000000005ec2631e93a0fb06b5ec6d52c0a2d89a3f9672d6ba64bfa005ce000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000603e49750000000000000000000000000000000000000000000000000000000000000001534e58000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000013ee5cec085b3800000006301f1fc332c1562c12b6224e441f25a10d661e009adabce0a29e41896a85323bdaf6afac2438882c57aaa9e5f137e0365ecc27d27b3a73a108957c6ed4ee5bff8530189543f00000000000d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063017f8f5cdac49108109ad45484957fb00202937447785218f4674332cf42fc6f3e458acda19fa8bd5f14b3209dd235667096cadc57c2c65330b20c6f6db7806a470189543f0000000000318700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000143008d4e5a4defd67ce253e0232e10a592857b6d04b0be36abe1fe4cefa24987df124b96b5840e08d70c536c1a49e28d767289c388b6cfade572989e74a4f28b36660089543f000000005ec3631e93a0fb06b5ec6d52c0a2d89a3f9672d6ba64bfa005ce000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000603e49d40000000000000000000000000000000000000000000000000000000000000001534e580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000141371c5e0331000000006301bcd1432ea3305683f046d921354633808c8a9175fc9b248146b721f4261749d962c7bd3c3b7412d3f8714faf932cd9b44da135b645e8c1d2bb3c44ddbde358910189543f0000000000798700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630169a529e91ff78552b8289c3e42e42a4b69ed57e87e0d3a8739058b34ad1423eb68545b3c1a89683f0b110c66fe0771e0701d24ccdb31de1fef9307b9b3fb7d3401493e0000000000007a4a16a42407aa491564643e1dfc1fd50af29794efd294f093000063013172648acf5ea4cff6ea4893d30aa73983f67773a9ce7084a40e07dccd61cecd0833133b648af4df5ba168234583bc1214eca2687901e6ee71e23fbc42059ad00089543f00000000029c8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630195447e28a93f2dd4cbb2e00f71011ac7e938d280909be4a029054521bc348aae7c226ab01aaec7c2a00880e57ffb25197d05bf0be7c314a91383844824373c6c0089543f0000000000028700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063008070b45c42792670949cbae7e60278d2cd809c9347f26b976a3fc21b3757331904afa0cda74e27122ee02098e26504173be953a608fa01e02b124020e5639d54007a11ff0000000000bdd85eafa37734e4ad237c3a3443d64dc94ae998e7af086c7e000083019cedf37f33c099a4b81e0dc68c4b8d23a1150624d5cca9b938d4aa3c21aaef8c17ff865e8605486a0ecec67b685f1ae38f3c0158e19ed23a986363ccd2ccdb0f0189543f00000000002a8700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000006f05b59d3b20000000006301165d2c69ba74b1b1ee893303b60c48797f883efbcb2019762feb26a9257bea9e7019c339f05e311e79d4799f2bfde302b7c0f82b53ef5b5c9e837a6441e877e00189543f0000000000188700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301d539deb29e2e26049c9168eca08fff60ecf63daf9cedaf27a01d95f6f8b47624185f18f727d156b6ecb4cc3dc75ad29756a1f0447d5c3ab638fd734dd090c9120089543f0000000000258700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630125e4deda3a4e62f65113248e1b3b8a834e6c2d291cedfb71a21e544c3c3217d06ed5d15f56cace11db5e0120e0d5f67a09ca9084cde7728991827851e753b14d0189543f0000000000108700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000143009d2d26177c80858fdba6683fa5d095dacb9634e9deb842286ec79c17d85f0d7b70c617d7878dded6bf0a7ca7fc1371e8bced146f6e7d0da215f67e9e0f92940c0189543f000000005ec4631e93a0fb06b5ec6d52c0a2d89a3f9672d6ba64bfa005ce000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000603e4ae20000000000000000000000000000000000000000000000000000000000000001534e580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000143687087b5b40000000083018e22501ff1c962656f4bfbdab7479cc4ed8c91486c4b19150d03782d7f374467036d7cd2cdba105a4d916dfea70fb8cc2c60121f3baca85c6f2a48909c34d3780189543f00000000001a8700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000008ac7230489e8000000006301019753a47c457cd6cad98373b2092f11a1aea45e9bdd27ab1105609b4c8f87453121c425f6864b1b85910b4c0b82743287f61bc210a46de870b484c07b17cd310189543f0000000000038700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301d08818d883a43fc9f5f64623dca45128884b782fef41d513efa33d4d40139b467641ed4c17071fb9c2bd045023307ed0b23a3fcbbb2f71e81b37e3a7aba1c6560089543f0000000000058700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301c90fa6273070d9ae5af8eea1f687ae507410a258c7861e726d2cf943e00b456f2b3bf75861fe705039179eda8db6ea2781480afd89a98e133f1fe53e379e1eda0189543f00000000000b8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063018b5913d83c37ddc2564db791692072efd3dc8e5fe10584ed96451778789131a14efe012befc05c4ad479a46ea57c7e48f7f64dd7b7c175a48dd445215b2ffa170189543f0000000000418700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000083019d241891335c51ad2cc632ed85dd94ab8eee00da71cd812ea1c40fbcb9c441f56e2f5a7f83e9aa02ae614ee89a4abd10c4332be95a1335be44d5cff6356a903a0089543f0000000000148700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000013f306a2409fc0000000063018dc9bf7f0c853dc0884f0070f8a0413a83f0d9d4d8e5f6f1325deb65684f3485152071dfdeef0a933691bafc850265ccd3624ed00671f68e9cea9bb5b2575c6f01493e000000000000064a16a42407aa491564643e1dfc1fd50af29794efd294f0930000830148b78f666b6d633554897e96f80bb797908467619895cd83eaef7f10b84415d3060967fb0afd49eec5d6759aa9f92c4a5a356369b518210b95c93a0a89ca81e60189543f0000000000088700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000068155a43676e000000000630122d546e170dee47a7952b7c158aacbc38b035f7e183e90a74e7ab91746dbf2bc5aaee131243eedd4781b970b3dd7cc40f6a2bb5fa15684d91e61bf38002c7e9d0189543f0000000000358700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000083016f747cffb6ad3bfaec6d17404d49502ec4bbace678deceb0a6d288dfe353e22b794bb94612e7016f8abf6d15186a3d737536d84f7b43be2ce552a4316280154a0089543f0000000000258700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000c40eb7864285bedec00000630124b164e81e73327bab1cfa0ec9b73e6dc492b4078dcfafb66c11a01134705a9f0d0da528387e2ad59cb192ac51fa9c7b6ebee791e30b27fbf05b956d1c5bc4f20089543f0000000000158700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063011226070ed0b7b181dce226d2ac772265b9831fb462e54a040bd2644ea5092dda6e468fff3a6701d19b413086fb37919617e6bff18a0d9b0791f4bf373d1fbce10089543f0000000000108700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301db43b348b8b2008a01869382c9a91d50ece8adbc0f3a7b2e20b63d55f5db1088624cbc9eb64bcee9508a2352e8e7b03d226c7f06752bccc322bf3af7bc6b68d40189543f0000000000428700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063011a496ca06b19d3b6673c701ae9633e0a986edab74d9fb5b90c9840d129c0ab0967a424474451adb879fe40f49db3ff3505201a5136f88d93a486dffc95a8bbfb0089543f00000000000c8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00008301fbc6c4f4c92ca11aa750c91cd1ee111d69eee808eae4283931a27860599be53344f9763d439d3054e0dbf90667cb801320e802e74ac409610e25c1516caec4ae0089543f0000000000128700daec35af8ff88c16bdf0418774cb3d7599b48a29001400000000000000000000000000000000000000000000001043561a882930000000006301fdeb20b52896ceb6392d4d2f4514cbc187587990d736539a07ca5f59ee697ba84443d3b91894442e03741b67a76afe25ff709e0d318276b404abfc499071b3810189543f0000000000068700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301ebd8f5fda3ce757e953430c5d8c1ad689f2785c7c32f3b607410bd2a1955c3c1228a6bb7dced4e85680ab1d9e2a228c9b0a75114ff636aacd0dbae447e113eb90189543f00000000003f8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301837692935ae091a54c121aaf663f22e86b5c757c1c769ca2be3120f8d20f035a5d6ea340bf095f6a60c36e1e38fb975ad6d895723999abebe38b429637be73e00089543f0000000000208700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00014300bb50bf8b5c9c56a8b515110b42c4013f4e99cf9fcf6874c32e881554562265fa13a1539b789d0364d1e82e7587aad4adb894f4f3320d3517bd90bb08394d42640089543f000000005ec5631e93a0fb06b5ec6d52c0a2d89a3f9672d6ba64bfa005ce000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000603e4cfa0000000000000000000000000000000000000000000000000000000000000001534e580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000143bdb4672850000000006301f96d41806e43f65d8570be44e855870bd2d9b78b512e61264b17105a22267e6338984c7bee80abb6a93f1c4680ae79bc8306cf8598e86d1d25126ce53866c1a10089543f0000000000ac8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630131b960ebca035ef79df2a1e63acdcd5d6e27789b2eb538f9f8232169165473216c51e7febcefc39ca0ed2ab56a316676bff81e5a051651767e0c1fb23588829a00493e0000000000000f4a16a42407aa491564643e1dfc1fd50af29794efd294f093000063015643222807be2acb62072275ed4652806827ecbe9780cf68af1d79a075135c7308230547dd63b8fee254f0a48fae06c7f74b9b1b7c9efa4b6c6801c8d3e98b940089543f0000000000368700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301ee5b0c65075609b827be37d236859f440e182f6c56b3e10736e04f26fab8bf993d3dee5160eb8e6845cbd7198f2afac45c41e93476f9e5b2b401bd1fd6085ad40089543f0000000000018700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301d13c1423797c1e5541f4e730ba701bb6e46bdaa1985cefca4129cb2928baae6c24634a3d4d58d705e65cb6eba169e82ef765a396cc808c273dd4cfa74e37ea680089543f0000000000108700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063019af7bc274b54c2434832f9714315c03020e4465bd283a4b3b25236fa308b5132447f528bae32614e2526c060df7b1139b67fd461b8c0fb688d0517fdc23826ed0189543f00000000000c8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630166b80cd6e820bd1ed6cb92d5a4c0bf178d4078666c1c1a6576f7bdeca1dd3ff40fdbf763431560cf1490d900aff9a42a7b1b0c53c16f93b56039dbc4059de7980089543f0000000000148700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063019eb6f4205fb5ee8009209763b2ee373fcad19b04e9b4411ec7393403e2a815d43b5453d8ff70c0b60ef45917c4789c6afc45c8955cd59432abc2b32bc2c37b5d01493e000000000000024a16a42407aa491564643e1dfc1fd50af29794efd294f09300008301d22b7b46b935bbe378ac97d58bbe84f8b57d9b240f56e9335ed8134214a480c032b15645d64f624cd707628971ece2c06366fd2bbff50cbb40891fe1cf0de2250189543f0000000000098700daec35af8ff88c16bdf0418774cb3d7599b48a29001400000000000000000000000000000000000000000000001043561a882930000000008301f3ecf58a1113fb3387131db1ea3feeb9e2f46e3e48ee1755a1171d3ebcde8ce12d35af50f607a050a62b614074a70d3101211de5f0e40bccdbb49a7206d049ad0089543f0000000000018700daec35af8ff88c16bdf0418774cb3d7599b4295da87d0000000000000000000000000000000000000000000000008ac7230489e800000000630188270db66b963f84c9707597b0c8b7b353042bf61585be66b653a97ead1bbf6a4c373e6831c72dae73ac1a9a6a4e20133f92f57815f0d5a3673d31ae8e7aa0030089543f0000000000168700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301bf64ddfdac64b87045ee2b616ff27224598cd3f74704abdf729f11a5c196436e43b5e9e7fca7b0fa3c3fe149ebf561f14342cfc7c08781a3c85b6cdfa574e0a40089543f0000000000208700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063010beb607a64dcab788fe6c6ad52674b82d41eb785fab9c3c7de72ad96472e9b5c094b2c6b1fe1ff3a4e03a0ed1f0a1a56c9fd10776c61062aa80352c9bc524a240189543f0000000000218700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630152df9af9c07cec85267c8d281bc35087dec67370356cfb4fdcf2afeb322082ad33376cb37f77411963298ba7d127a04236a3d00fe1feb8270c3a42fb090bbe210089543f00000000001b8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00008301244318f4f681328908180b145ca3467846f5cdc40fe6e9ada54f2e7dba843ee414b22618c62cd57adbbc86c5ac079bc69f7e45e87108c53f22ff619501a773be0089543f0000000000108700daec35af8ff88c16bdf0418774cb3d7599b48a29001400000000000000000000000000000000000000000000000340aad21b3b700000000063016715ac2a06087215c0d619320bbbe1f25e9643ec2b7a4dad9813875832360b33592babf1ed961d1c20fd6c9334fc35214ccd20a168c5d53df0b3cfcd40ce46f40089543f0000000000168700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301e3f78d50a6720d493515f979f3242514d8de70a93d4d00caa62b3991fc48e1811c7000a1cec9a13cb59a3bd639a346d6a5834f8f6bfc0b4896eb9f12f52a67750089543f00000000001d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063010d7a3ee143952287c195738b1a238e88fe1cd7c721339f21a2c9fdab99352d862ad25cb00e9d28afad4de9a1c21d8bf16fbc09d47483c928343bd12d90cf0e940089543f0000000000458700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301675ad48eb9a9cb237be82eca75f0f058a53fc42e5952a1009e51523e3e7403ca2bc2086ee05a853fe839bfe3c8123c59373066885185f052c9d6835a6c20d9770189543f0000000000188700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301d104e95267f9651e269ef7ea3a5f0a99c044a0be82bf883c536102918dffed0e38e0e7635046abf595ad5fb1860dc3a13e858bbe1e053436ebf694b7d478f1c00189543f00000000000f8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630170935022f25655a71178c3439cdb3cdc7c2f9857fb65ee2c3af721997e849c55584d650cb4fb8b15f56b708828470224802f1ec9d7fd1afe2f0ad399c5e38ea40189543f00000000004e8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301c5bbd5a56cf2621d47a4acfe87b993fe625218e9b331ab25ea7b07005a40520d70ef48e060693a69c1dcb23300d407658551bcdc270da2a8cdacc91d5cfe25760089543f00000000001d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00014300ff75b419c212ab48019e879247e2aad3a917dc6000e4c0a788af865637b2d8ac422cd67f43ce27c0d0d46c32cdc80409a74029d00c41ab9579adf760671cfd370089543f000000005ec6631e93a0fb06b5ec6d52c0a2d89a3f9672d6ba64bfa005ce000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000603e4e9c0000000000000000000000000000000000000000000000000000000000000001534e58000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000013f1070b03f0180000000630194b0adafa5ed2c77b5b500b13df63ba79d971f383e6716b58df912738766e33f01c11739fd39045d7f2ca3a6ff582391d4094c8910f683a4525b087f5fc5d49a0189543f0000000000a28700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063013a0f2024f85cd3038d8f565ef3e3a7f1ed920bf2340621f1c961c3cf2551a21d234a8d69ab9127dffc3193d3a6af98e5d5f70e166121094ad51d91c93e9e938d0089543f0000000000288700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006300f322135adf6c6959bc012fe5ffb14bb8da6443a80f4e85799ca645ffc369eb09155f05bd65a64741cf089427597a1c35130428c2ecef60dad2b3f1f6da63e3c0007a11ff0000000000bed85eafa37734e4ad237c3a3443d64dc94ae998e7af086c7e00008301ff3ecc7ecf5a169c2b1c4360c63dcf9a8d786d804f400f1bd4bb0fcb9401644f618ca9780aae23bab8a688cf6bf40258e0c44b8089e6bf0989d5aafe9236d3fa0089543f0000000000158700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000001bc16d674ec800000000630142e1b6d027dd1fb9cf43dbb834aaa03547ec356a65a693502546416bd8596250570285bcce68e72ce1bbd7fd3f1b3f8713d06e2b854eb718cafd1fbe3b2ea8ba0089543f0000000000138700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301f343a551e25a7254de23e818c649c30cf974b4f1e7feef4c30eab697f62e71a809006682008e5c25b1816228ce36d77e503e6ce1bdfe3027c6d29787be3ae1330089543f0000000000138700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301bf45e708b0d48a939480d9a16994e7f9b325b55d3a1faa989abca22ec877c799269be034bb0d4dc36a2f684295057e1f1a7de8577114fc660bbe37edbf16f84c0089543f00000000001a8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00008301f4cc4215e75d412821ef74ff9c20918c421227217f7c135f9b56a5bab8411f7061a8b99a11ea37c5346f62e70521f60ed7e664fb4b0f82e4c6a1d3e62c33607c0089543f0000000000048700daec35af8ff88c16bdf0418774cb3d7599b48a290014000000000000000000000000000000000000000000000001158e460913d00000000063013bd03efc62589e24d9d53547a31e18a18e24cb4c820ddbdf2cb3c20323d19ea01ebc043698a7a80ddc632682461ff9cadb95b19ec520f7528dea5bc57017d6df0089543f00000000001a8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301ab3f7535024aaf1bbadd31d33aeedfaf3325d617e8b3b7814080802dd4fe79d55b40120b9f68ece14e45bb347841bd2bad35c0e93f43da8ccf8c0857e4eb1fee0189543f0000000000208700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630145d68cab27031deb7b55e2e6df0df2629c3f07a60a09f433bbd45cd3594855f8508c5ab1201189091b8197d082a7d62abc6e4a28570ef28cfab8bcaf42679a8b0089543f0000000000058700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063013615d80a47143ea2f0102f196e6a2feda49b3be3352c88bb2dec2a9a567e22632fb5097f949412b398ca857b2f105b0f032a69be0f8bd401174a29ae67a8da5a0189543f0000000000148700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630128b5fef41c92369c84e8e742df1253f6eb624cfe5c807de5cb49869a7eaf573416ba55a9f86034b35a11f0c158af9ef07640e71cbcb093cb03a38b75d427ca360089543f0000000000058700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301fc2fc4d368f9f3ef73d9a4114c4a61116f7fd6259c4f172599511882a874993b437960110af8633fd8727372a880194569d295862c321dd1cfd961394e6c21eb0089543f0000000000b48700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301b2c09b2cda9dca2a6c7441d40e24d2a7b481ff76b7759d2a2aebdd7b1b2b3e9d551638ba91ae1511d03be6c90e13d8a931cb46ba48edf1f3c972c2bc6c4b30a50189543f0000000000428700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301ea34d378265585e18e8bac13f67c9733a4055886ddabbd6e467396d964c3dea6361f61256ec3eb9658c88fb1333ae4e74343622e18573a1545ca38af763a4ec40089543f0000000000348700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301563eed133b104dc9d7430fe5082fa1b722b14236053ba0003bff64fb52f073ae6c0c3985fc59c43bc799868fc2222410131a5f08b01dca6bdfc33e9df01c48900089543f0000000000028700daec35af8ff88c16bdf0418774cb3d7599b49741fb2200006301f8773e89f4266cb5afc584dc3e10b4a52b7fefa672ca7f627f758810ed1577ec56cd9dc44571c53fae24ecccd19907791eee25d5e0dd058e5444b02ad91836300089543f0000000000078700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063019ac5f89805eb37c5089ceb5f22bcba2abe7e5da0085adb520d0c2ee4efdf3cab3533f854c08f23f4071c42270b9a074b14bba44a2027e382b2dc0377621fd31100493e000000000000034a16a42407aa491564643e1dfc1fd50af29794efd294f0930000630135234c82daed5f5bb355d7f3f81edc0f5f9f0d4d11365d4e948bd2c02e7031cc291ca1f5c76d8ebe5c1a930a9375acf73f2343cc860aa0633643aecf7420eeb00089543f00000000001d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0001430051884bdaf68fe6c64943a5f3160b5fcd7ef9140463937fc8d1ecdb3c916d7096354237eb3c63430116601c5b887dc44a4d4bce5639be1fa36b91243bb76e2b350189543f000000005ec7631e93a0fb06b5ec6d52c0a2d89a3f9672d6ba64bfa005ce000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000603e508c0000000000000000000000000000000000000000000000000000000000000001534e58000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000013cf0dfffc45f0000000063016e3845ed8e5ae218069cfa7ca98d3ffd86505d0383a3655f8cd46c557bc399c50c81cdfef32719125e7b84b1fd4e7c7535bf5a464a23a86fd9801b146b700d8f0189543f00000000000d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063014c626ef869f07ea4cee4520d5bd24f2c2772a42ce289dfaedaed255446b03c0d2abca85ccfe8233061169c0fcd12d740750f0b65dd707dfe665d4d9cfaf757230189543f00000000000c8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301a0d59339cea6846e08148f7d9daba52402649546b5875c594f2f81209b36794a3e310fa64c0563d53334ae6fe87762426142dfa75b31f1021dd4092f4be895e80089543f00000000002f8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063014bd68c44229ea3cd8c4ff073a9cedbb5ff4387b257b29ce0e18ace691cd97677406b528411c6cd65df52a670dccc257bf290abd1805ba551f151fbd6355bdb980189543f0000000000068700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063016e7f6fe17cb0d1ed5970c49cc0745e3cae90e8e5a2004217472db6a1a1b9ab1124797d03a271feb38e66e690453904f568e61e6623900299f3b8079027fa15570189543f00000000002e8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063015702ed8c76f782970f0579f74a02931fb3e9dca6f3ba750d97d0df7ae60d3d10221ad66f898704bc9987b54d1109da9735c8d7acf2e4a25668134d641ac2d9120089543f00000000000e8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630129bd1bc30f7bfeb956ae7725bcd7144bc42291caf0628d7c8fb75a103f57fd6d4ce006acba63dd5593603368a0b154197d0826b8f9c3d23b93d72dc830c48a0e0189543f00000000001d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000083019cbadc64fb19e3e912ffc2492bf42130d21fb67e4654c11551f3c3dd28e4ded951eae21b5b7f98a7f1e27fdf30fce409363823c97ba03643cbd7e65b964432a90089543f0000000000008700daec35af8ff88c16bdf0418774cb3d7599b48a29001400000000000000000000000000000000000000000000000b38b3bb4459dc00000000630198e2a81ec1c025dc5bcec824708cc1f264134aa6121d90e7c284c77907ee0d0845b88fd9c030bc20002dcbf5a6709af1c42ba4c8ad6e84a55e3e48a1c79974160089543f00000000005b8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063014cc92f396e2790dd5ab926c2e4c1be43000b1098fe7eb2822e0b49ea5ea076f01fb68a84640524d5c525bf989c4a9aff9a02750a81c535c031a8452aed300f420189543f0000000000218700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063019af9087760f24d93e0fbde3bd0e2f2e64fb8d8c36d024fd6cb4a58c34000b2e810e39efa69c5310be3580a4d94bbf069487d65172dc8d39d9bcb75261fd841560189543f0000000000458700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063015402786088ba83e4a68e5f05a7e9c8089c862bfd9c134b9a9ee38aed4a39a08d12d4bf29039655440937a990e14d699101d883246a29b62977aeb9a0e8252f6d0089543f0000000000178700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301bca73ae8381f33c7ec9e0f6a5b518f12854b0061e63a805b3d5941c1516128ce0775f6819e673108d4654a2018289aa951f9340dbee582b9c6b548ec6450149e0189543f0000000000828700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063011bcab33ce1cd156607d9c5a4cefa0d67b82c89462b0d3d4cfd79fa7b2945ee0c14103722c3702f1e247190a3baf4329fd0867d9ceff2bf212e9ff633a89915010089543f00000000001c8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301b9ca39e85b19a0f5f03b067f2bca5be6dc27294107dd4e9ffa9c1af204b6feb235c7e21a385cc3fad8ddfa3aefe7951e2494d63f0a9f057923b39a73008e2a250189543f0000000000388700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063013656758d6e0c9219bb9de64106bb2dcaaec309bc54c0e53993cd37cd9cd03bb15b5102cbef23c0f2e60fcd16e8729059bc6f01546f8dd2c94ee4c9cdae79a0040089543f00000000003f8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630157b7af6659df3a6f211042201fefbaf98780f3b2945398494147dd0a08add01a37fb4cdbccd4df5949381357110e41daf963859999bde4cee42291dadb4fd31b0089543f00000000000d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063012a0b8faa1536ed92b8c2eef51fd0e8a5a82d4a7ada7f2a46ed8e6b6ec29e9a263bd1866f30953a0ceefd68c4a931975bf487ff35fed2b6b3837cc83f0b5486510189543f0000000000218700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000083012aa213c2eaa5a3f0887ba7ab48498bd289bc54a9e59fbff3a8a2a13e13eb9598399c58f16b07789e8c36268985d107091b01fe0afa3faa6fd71baaeef68afb340089543f00000000001f8700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000056bc75e2d6310000000006301da0581ee658208bd90e7e1cc34873f56a3395523a5bdb7dcc1b3245b3cf2b4ad2fd8f550641f2ec4700865d040583e8ff7297dc21f06821332bd5e1aa01ba2770089543f0000000000468700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630114adc64102322203225ec04c16480aa2d7b73e323f4ca68256753726fb81e4450f4318ca276d297a402371994ced771b50eb7409744c8f9182d01599e9c5c6ba0189543f0000000000158700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e'
This source diff could not be displayed because it is too large. You can view the blob instead.
import { BigNumber, ethers } from 'ethers'
import { expect } from '../../../../setup'
import {
SequencerBatchAppendedExtraData,
validateBatchTransaction,
handleEventsSequencerBatchAppended,
} from '../../../../../src/services/l1-ingestion/handlers/sequencer-batch-appended'
import { l1TransactionData } from '../../../examples/l1-data'
import { blocksOnL2 } from '../../../examples/l2-data'
describe('Event Handlers: OVM_CanonicalTransactionChain.SequencerBatchAppended', () => {
describe('validateBatchTransaction', () => {
it('should mark a transaction as invalid if the type is null', () => {
const input1: [any, any] = [null, null]
const output1 = validateBatchTransaction(...input1)
const expected1 = false
expect(output1).to.equal(expected1)
})
it('should mark a transaction as invalid if the type is not EIP155 or ETH_SIGN', () => {
const input1: [any, any] = ['SOME_RANDOM_TYPE', null]
const output1 = validateBatchTransaction(...input1)
const expected1 = false
expect(output1).to.equal(expected1)
})
describe('when the transaction type is EIP155 or ETH_SIGN', () => {
it('should mark a transaction as valid if the `v` parameter is 0', () => {
// CTC index 23159
const input1: [any, any] = [
'EIP155',
{
sig: {
v: 0,
},
},
]
const output1 = validateBatchTransaction(...input1)
const expected1 = true
expect(output1).to.equal(expected1)
})
it('should mark a transaction as valid if the `v` parameter is 1', () => {
// CTC index 23159
const input1: [any, any] = [
'EIP155',
{
sig: {
v: 1,
},
},
]
const output1 = validateBatchTransaction(...input1)
const expected1 = true
expect(output1).to.equal(expected1)
})
it('should mark a transaction as invalid if the `v` parameter is greater than 1', () => {
// CTC index 23159
const input1: [any, any] = [
'EIP155',
{
sig: {
v: 2,
},
},
]
const output1 = validateBatchTransaction(...input1)
const expected1 = false
expect(output1).to.equal(expected1)
})
})
describe('regressions', () => {
it('should catch the invalid transaction', () => {
// CTC index 23159
const input1: [any, any] = [
'EIP155',
{
sig: {
r:
'0x0fbef2080fadc4198ee0d6027e2eb70799d3418574cc085c34a14dcefe14d5d3',
s:
'0x3bf394a7cb2aca6790e67382f782a406aefce7553212db52b54a4e087c2195ad',
v: 56,
},
gasLimit: 8000000,
gasPrice: 0,
nonce: 0,
target: '0x1111111111111111111111111111111111111111',
data: '0x1234',
},
]
const output1 = validateBatchTransaction(...input1)
const expected1 = false
expect(output1).to.equal(expected1)
})
})
})
describe('handleEventsSequencerBatchAppended.parseEvent', () => {
// This tests the behavior of parsing a real mainnet transaction,
// so it will break if the encoding scheme changes.
// Transaction and extra data from
// https://etherscan.io/tx/0x6effe006836b841205ace4d99d7ae1b74ee96aac499a3f358b97fccd32ee9af2
const exampleExtraData = {
timestamp: 1614862375,
blockNumber: 11969713,
submitter: '0xfd7d4de366850c08ee2cba32d851385a3071ec8d',
l1TransactionHash:
'0x6effe006836b841205ace4d99d7ae1b74ee96aac499a3f358b97fccd32ee9af2',
gasLimit: 548976,
prevTotalElements: BigNumber.from(73677),
batchIndex: BigNumber.from(743),
batchSize: BigNumber.from(101),
batchRoot:
'10B99425FB53AD7D40A939205C0F7B35CBB89AB4D67E7AE64BDAC5F1073943B4',
batchExtraData: '',
}
it('should correctly parse a mainnet transaction', async () => {
const input1: [any, SequencerBatchAppendedExtraData] = [
{
args: {
_startingQueueIndex: ethers.constants.Zero,
_numQueueElements: ethers.constants.Zero,
_totalElements: ethers.constants.Zero,
},
},
{
l1TransactionData,
...exampleExtraData,
},
]
const output1 = await handleEventsSequencerBatchAppended.parseEvent(
...input1
)
const batchEntry = output1.transactionBatchEntry
expect(batchEntry.index).to.eq(exampleExtraData.batchIndex.toNumber())
expect(batchEntry.root).to.eq(exampleExtraData.batchRoot)
expect(batchEntry.size).to.eq(exampleExtraData.batchSize.toNumber())
expect(batchEntry.prevTotalElements).to.eq(
exampleExtraData.prevTotalElements.toNumber()
)
expect(batchEntry.extraData).to.eq(exampleExtraData.batchExtraData)
expect(batchEntry.blockNumber).to.eq(exampleExtraData.blockNumber)
expect(batchEntry.timestamp).to.eq(exampleExtraData.timestamp)
expect(batchEntry.submitter).to.eq(exampleExtraData.submitter)
expect(batchEntry.l1TransactionHash).to.eq(
exampleExtraData.l1TransactionHash
)
// Expected transaction entry results based on mainnet data
// Source: https://ethtx.info/mainnet/0x6effe006836b841205ace4d99d7ae1b74ee96aac499a3f358b97fccd32ee9af2
const txEntries = output1.transactionEntries
expect(txEntries).to.have.length(101)
expect(txEntries.every((t) => t.queueOrigin === 'sequencer' || 'l1')).to
.be.true
// Sequencer transactions are decoded, but l1 transactions are not
txEntries.forEach((tx, i) => {
if (tx.queueOrigin === 'l1') {
expect(tx.decoded).to.be.null
} else {
const l2Tx = blocksOnL2[i].transactions[0]
expect(tx.decoded.data).to.equal(l2Tx.data)
expect(tx.decoded.target).to.equal(l2Tx.to.toLowerCase())
expect(tx.decoded.nonce).to.equal(l2Tx.nonce)
expect(tx.decoded.gasLimit).to.equal(
BigNumber.from(l2Tx.gasLimit.hex).toNumber()
)
expect(tx.decoded.gasPrice).to.equal(
BigNumber.from(l2Tx.gasPrice.hex).toNumber()
)
}
})
})
it('should error on malformed transaction data', async () => {
const input1: [any, SequencerBatchAppendedExtraData] = [
{
args: {
_startingQueueIndex: ethers.constants.Zero,
_numQueueElements: ethers.constants.Zero,
_totalElements: ethers.constants.Zero,
},
},
{
l1TransactionData: '0x00000',
...exampleExtraData,
},
]
expect(() => {
handleEventsSequencerBatchAppended.parseEvent(...input1)
}).to.throw(
`Block ${input1[1].blockNumber} transaction data is invalid for decoding: ${input1[1].l1TransactionData} , ` +
`converted buffer length is < 12.`
)
})
})
})
import { ethers, BigNumber } from 'ethers'
import { expect } from '../../../../setup'
import { handleEventsTransactionEnqueued } from '../../../../../src/services/l1-ingestion/handlers/transaction-enqueued'
const MAX_ITERATIONS = 128
describe('Event Handlers: OVM_CanonicalTransactionChain.TransactionEnqueued', () => {
describe('getExtraData', () => {
it('should return null', async () => {
const output1 = await handleEventsTransactionEnqueued.getExtraData()
const expected1 = null
expect(output1).to.equal(expected1)
})
})
describe('parseEvent', () => {
// TODO: Honestly this is the simplest `parseEvent` function we have and there isn't much logic
// to test. We could add a lot more tests that guarantee the correctness of the provided input,
// but it's probably better to get wider test coverage first.
it('should have a ctcIndex equal to null', () => {
const input1: [any, any] = [
{
blockNumber: 0,
args: {
_queueIndex: ethers.constants.Zero,
_gasLimit: ethers.constants.Zero,
_timestamp: ethers.constants.Zero,
},
},
null,
]
const output1 = handleEventsTransactionEnqueued.parseEvent(...input1)
const expected1 = null
expect(output1).to.have.property('ctcIndex', expected1)
})
it('should have a blockNumber equal to the integer value of the blockNumber parameter', () => {
for (
let i = 0;
i < Number.MAX_SAFE_INTEGER;
i += Math.floor(Number.MAX_SAFE_INTEGER / MAX_ITERATIONS)
) {
const input1: [any, any] = [
{
blockNumber: i,
args: {
_queueIndex: ethers.constants.Zero,
_gasLimit: ethers.constants.Zero,
_timestamp: ethers.constants.Zero,
},
},
null,
]
const output1 = handleEventsTransactionEnqueued.parseEvent(...input1)
const expected1 = BigNumber.from(i).toNumber()
expect(output1).to.have.property('blockNumber', expected1)
}
})
it('should have an index equal to the integer value of the _queueIndex argument', () => {
for (
let i = 0;
i < Number.MAX_SAFE_INTEGER;
i += Math.floor(Number.MAX_SAFE_INTEGER / MAX_ITERATIONS)
) {
const input1: [any, any] = [
{
blockNumber: 0,
args: {
_queueIndex: BigNumber.from(i),
_gasLimit: ethers.constants.Zero,
_timestamp: ethers.constants.Zero,
},
},
null,
]
const output1 = handleEventsTransactionEnqueued.parseEvent(...input1)
const expected1 = BigNumber.from(i).toNumber()
expect(output1).to.have.property('index', expected1)
}
})
it('should have a gasLimit equal to the integer value of the _gasLimit argument', () => {
for (
let i = 0;
i < Number.MAX_SAFE_INTEGER;
i += Math.floor(Number.MAX_SAFE_INTEGER / MAX_ITERATIONS)
) {
const input1: [any, any] = [
{
blockNumber: 0,
args: {
_queueIndex: ethers.constants.Zero,
_gasLimit: BigNumber.from(i),
_timestamp: ethers.constants.Zero,
},
},
null,
]
const output1 = handleEventsTransactionEnqueued.parseEvent(...input1)
const expected1 = BigNumber.from(i).toNumber()
expect(output1).to.have.property('gasLimit', expected1)
}
})
it('should have a timestamp equal to the integer value of the _timestamp argument', () => {
for (
let i = 0;
i < Number.MAX_SAFE_INTEGER;
i += Math.floor(Number.MAX_SAFE_INTEGER / MAX_ITERATIONS)
) {
const input1: [any, any] = [
{
blockNumber: 0,
args: {
_queueIndex: ethers.constants.Zero,
_gasLimit: ethers.constants.Zero,
_timestamp: BigNumber.from(i),
},
},
null,
]
const output1 = handleEventsTransactionEnqueued.parseEvent(...input1)
const expected1 = BigNumber.from(i).toNumber()
expect(output1).to.have.property('timestamp', expected1)
}
})
})
describe.skip('storeEvent', () => {
// TODO: I don't know the best way to test this, plus it's just a single line. Going to ignore
// it for now.
})
})
import { expect } from '../../../../setup'
import { l2Block } from '../../../examples/l2-data'
import { handleSequencerBlock } from '../../../../../src/services/l2-ingestion/handlers/transaction'
describe('Handlers: handleSequencerBlock', () => {
describe('parseBlock', () => {
it('should correctly extract key fields from an L2 mainnet transaction', async () => {
const input1: [any, number] = [l2Block, 420]
const output1 = await handleSequencerBlock.parseBlock(...input1)
expect(output1.stateRootEntry.value).to.equal(l2Block.stateRoot)
expect(output1.transactionEntry.decoded.data).to.equal(
l2Block.transactions[0].input
)
})
})
})
{
"extends": "../../tsconfig.build.json",
"compilerOptions": {
"outDir": "./dist"
},
"include": [
"src/**/*"
],
}
{
"extends": "../../tsconfig.json"
}
{
"extends": "../../tslint.base.json"
}
......@@ -21,6 +21,7 @@
"no-submodule-imports": false,
"no-unused-expression": false,
"object-literal-sort-keys": false,
"prefer-conditional-expression": false,
"ordered-imports": false,
"semicolon": false,
"variable-name": false,
......
......@@ -39,6 +39,61 @@
resolved "https://registry.yarnpkg.com/@ensdomains/resolver/-/resolver-0.2.4.tgz#c10fe28bf5efbf49bff4666d909aed0265efbc89"
integrity sha512-bvaTH34PMCbv6anRa9I/0zjLJgY4EuznbEMgbV77JBCQ9KNC46rzi0avuxpOfu+xDjPEtSFGqVEOr5GlUSGudA==
"@eth-optimism/contracts@^0.1.6":
version "0.1.11"
resolved "https://registry.yarnpkg.com/@eth-optimism/contracts/-/contracts-0.1.11.tgz#d33354b69e1bdaf11eac799a1f4dfb17295a04e7"
integrity sha512-Ak4VoPwn9ZTH7Th5Ioh9HxGt9mtriOuiH1YuOtRihnhmmsJ0DO5k5dqdzbckUKYDW1s3sA7N6Yw3pbtZ9qiVWw==
dependencies:
"@eth-optimism/core-utils" "^0.1.8"
"@eth-optimism/dev" "^1.1.1"
"@ethersproject/abstract-provider" "^5.0.8"
"@ethersproject/contracts" "^5.0.5"
"@ethersproject/hardware-wallets" "^5.0.8"
"@openzeppelin/contracts" "^3.3.0"
ganache-core "^2.12.1"
glob "^7.1.6"
"@eth-optimism/core-utils@0.1.9":
version "0.1.9"
resolved "https://registry.yarnpkg.com/@eth-optimism/core-utils/-/core-utils-0.1.9.tgz#b63d8dc417b1a27977bdb19d4ca435232f88f944"
integrity sha512-esTG0Fi98pWut9EH+7NKPIJvGY+aTtNvcqtffd/p9oXJjj7Z719zrmqwdvhv3oyymxGIqiZSE38HMPTxVDPVrw==
dependencies:
"@ethersproject/abstract-provider" "^5.0.9"
colors "^1.4.0"
debug "^4.3.1"
ethers "^5.0.31"
pino "^6.11.1"
pino-pretty "^4.7.1"
"@eth-optimism/dev@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@eth-optimism/dev/-/dev-1.1.1.tgz#7bae95b975c1d6641b4ae550cb3ec631c667a56b"
integrity sha512-BiKvjL8VoS2OsPHobyTe533XoZkYYBKUKhw9tkHskylD+s+/UwcgkkimrxQ67aJgLE22GW9YCdF2eeB3UcNwgA==
dependencies:
"@types/chai" "^4.2.15"
"@types/chai-as-promised" "^7.1.3"
"@types/mocha" "^8.2.0"
"@types/node" "^14.14.27"
chai "^4.3.0"
chai-as-promised "^7.1.1"
mocha "^8.3.0"
prettier "^2.2.1"
rimraf "^3.0.2"
ts-node "^9.1.1"
tslint "^6.1.3"
tslint-config-prettier "^1.18.0"
tslint-no-focused-test "^0.5.0"
tslint-plugin-prettier "^2.3.0"
typescript "^4.1.5"
"@eth-optimism/service-base@^1.1.5":
version "1.1.5"
resolved "https://registry.yarnpkg.com/@eth-optimism/service-base/-/service-base-1.1.5.tgz#fddb8f51f2d082a106fced0826a8e45eabf0a16c"
integrity sha512-0xNwZv5aZknj1LKLiob4VXcGDOUKDLc3YKbW1Cub6j9t0bwIZ8/Y5t2sVJ3/d15wBW/LrSSmGkZRL+tR56ZoNA==
dependencies:
"@eth-optimism/core-utils" "0.1.9"
colors "^1.4.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"
......@@ -195,7 +250,7 @@
dependencies:
"@ethersproject/bignumber" "^5.0.13"
"@ethersproject/contracts@5.0.12":
"@ethersproject/contracts@5.0.12", "@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==
......@@ -210,6 +265,18 @@
"@ethersproject/logger" "^5.0.8"
"@ethersproject/properties" "^5.0.7"
"@ethersproject/hardware-wallets@^5.0.8":
version "5.0.14"
resolved "https://registry.yarnpkg.com/@ethersproject/hardware-wallets/-/hardware-wallets-5.0.14.tgz#50d5acc4b66b6bf1b7698e0b61d6361e58cc80c0"
integrity sha512-HdlNQFKL7JnTFk5PaCZydu15oVK0j3+fkOjOm2h2pmO1UHfbvYTMukkZjojIrmB4vT3nOqoR5J/1AMbDsAaL2w==
dependencies:
"@ledgerhq/hw-app-eth" "5.27.2"
"@ledgerhq/hw-transport" "5.26.0"
"@ledgerhq/hw-transport-u2f" "5.26.0"
ethers "^5.0.25"
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":
version "5.0.12"
resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.0.12.tgz#1074599f7509e2ca2bb7a3d4f4e39ab3a796da42"
......@@ -296,7 +363,7 @@
dependencies:
"@ethersproject/logger" "^5.0.8"
"@ethersproject/providers@5.0.24":
"@ethersproject/providers@5.0.24", "@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==
......@@ -443,6 +510,102 @@
"@ethersproject/properties" "^5.0.7"
"@ethersproject/strings" "^5.0.8"
"@hapi/bourne@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-2.0.0.tgz#5bb2193eb685c0007540ca61d166d4e1edaf918d"
integrity sha512-WEezM1FWztfbzqIUbsDzFRVMxSoLy3HugVcux6KDDtTqzPsLE8NDRHfXvev66aH1i2oOKKar3/XDjbvh/OUBdg==
"@ledgerhq/cryptoassets@^5.27.2":
version "5.47.3"
resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-5.47.3.tgz#d5fb5303bd820c2277be20641a9274e870bbac9b"
integrity sha512-0FTz82nliTTxiZRnie8eoEGZjUJyZvFbqQnyf4E6sKyGKY13caEtj2HN5epfCA7tXWVMub6CGwvTN9rOaHcoqw==
dependencies:
invariant "2"
"@ledgerhq/devices@^5.26.0", "@ledgerhq/devices@^5.46.0":
version "5.46.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-5.46.0.tgz#0b9a76e02ebd0fbbf5cee253ccae3ee89a0dba1a"
integrity sha512-bQ9YVR0xocVv8sXDiKMabXauLpn870Su71RwumuG9z4o7f/8s710/5NEh7K11mmNJ7ztJsgDeqXq7ai2ZqilUw==
dependencies:
"@ledgerhq/errors" "^5.46.0"
"@ledgerhq/logs" "^5.46.0"
rxjs "^6.6.6"
semver "^7.3.4"
"@ledgerhq/errors@^5.26.0", "@ledgerhq/errors@^5.46.0":
version "5.46.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-5.46.0.tgz#573e1c758dce36acf5f414278d8b5970f45fe2b8"
integrity sha512-1/q/Tqv+aznX/rO3kdpg3Hv7O3xv68dWHkcfai8BZGTdTIwh6vdguFsdUNJ7eiNxEMKNA9gU+p78ZS/PDzoitQ==
"@ledgerhq/hw-app-eth@5.27.2":
version "5.27.2"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-5.27.2.tgz#65a2ed613a69340e0cd69c942147455ec513d006"
integrity sha512-llNdrE894cCN8j6yxJEUniciyLVcLmu5N0UmIJLOObztG+5rOF4bX54h4SreTWK+E10Z0CzHSeyE5Lz/tVcqqQ==
dependencies:
"@ledgerhq/cryptoassets" "^5.27.2"
"@ledgerhq/errors" "^5.26.0"
"@ledgerhq/hw-transport" "^5.26.0"
bignumber.js "^9.0.1"
rlp "^2.2.6"
"@ledgerhq/hw-transport-node-hid-noevents@^5.26.0":
version "5.46.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-5.46.0.tgz#432a220de614efa5af026bcc7715545fe217e7aa"
integrity sha512-v9OkEQ6kUtDGESeM5DLWvjCQlkJwarH6Z1fWyWofoR9LLuas25/56R5uMMpOqXwmyk0kJVwg6PJ2Cg8rJBSG0w==
dependencies:
"@ledgerhq/devices" "^5.46.0"
"@ledgerhq/errors" "^5.46.0"
"@ledgerhq/hw-transport" "^5.46.0"
"@ledgerhq/logs" "^5.46.0"
node-hid "2.1.1"
"@ledgerhq/hw-transport-node-hid@5.26.0":
version "5.26.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-5.26.0.tgz#69bc4f8067cdd9c09ef4aed0e0b3c58328936e4b"
integrity sha512-qhaefZVZatJ6UuK8Wb6WSFNOLWc2mxcv/xgsfKi5HJCIr4bPF/ecIeN+7fRcEaycxj4XykY6Z4A7zDVulfFH4w==
dependencies:
"@ledgerhq/devices" "^5.26.0"
"@ledgerhq/errors" "^5.26.0"
"@ledgerhq/hw-transport" "^5.26.0"
"@ledgerhq/hw-transport-node-hid-noevents" "^5.26.0"
"@ledgerhq/logs" "^5.26.0"
lodash "^4.17.20"
node-hid "1.3.0"
usb "^1.6.3"
"@ledgerhq/hw-transport-u2f@5.26.0":
version "5.26.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-u2f/-/hw-transport-u2f-5.26.0.tgz#b7d9d13193eb82b051fd7a838cd652372f907ec5"
integrity sha512-QTxP1Rsh+WZ184LUOelYVLeaQl3++V3I2jFik+l9JZtakwEHjD0XqOT750xpYNL/vfHsy31Wlz+oicdxGzFk+w==
dependencies:
"@ledgerhq/errors" "^5.26.0"
"@ledgerhq/hw-transport" "^5.26.0"
"@ledgerhq/logs" "^5.26.0"
u2f-api "0.2.7"
"@ledgerhq/hw-transport@5.26.0":
version "5.26.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-5.26.0.tgz#bfedc3d48400ad2fe48278d9444344b72aa9d0fe"
integrity sha512-NFeJOJmyEfAX8uuIBTpocWHcz630sqPcXbu864Q+OCBm4EK5UOKV1h/pX7e0xgNIKY8zhJ/O4p4cIZp9tnXLHQ==
dependencies:
"@ledgerhq/devices" "^5.26.0"
"@ledgerhq/errors" "^5.26.0"
events "^3.2.0"
"@ledgerhq/hw-transport@^5.26.0", "@ledgerhq/hw-transport@^5.46.0":
version "5.46.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-5.46.0.tgz#b4ad6432b8aef29069554c8d2d5d095f26719bbc"
integrity sha512-iROB4eowxR7Bg67MEH+OlIsfN+UIjNLULr+A74gEFtMpHB3jvVc/aeq2gezv4fIueQQWV9X5IgNsxkPBI0vgtw==
dependencies:
"@ledgerhq/devices" "^5.46.0"
"@ledgerhq/errors" "^5.46.0"
events "^3.3.0"
"@ledgerhq/logs@^5.26.0", "@ledgerhq/logs@^5.46.0":
version "5.46.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-5.46.0.tgz#fc850416716d77090c1c5a8c186118561fbd8d8b"
integrity sha512-WiFy1uwRhcqkj6aTSha532Nl6Gdsv5GN+Nsbp7pY66Zg+6WUE/SBmpwHEJfEXzEfARI5+A718a3trGinY6Dftg==
"@lerna/add@4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@lerna/add/-/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f"
......@@ -1156,7 +1319,7 @@
safe-buffer "^5.1.1"
util.promisify "^1.0.0"
"@nomiclabs/hardhat-ethers@^2.0.2":
"@nomiclabs/hardhat-ethers@^2.0.1", "@nomiclabs/hardhat-ethers@^2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.2.tgz#c472abcba0c5185aaa4ad4070146e95213c68511"
integrity sha512-6quxWe8wwS4X5v3Au8q1jOvXYEPkS1Fh+cME5u6AwNdnI4uERvPlVjlgRWzpnb+Rrt1l/cEqiNRH9GlsBMSDQg==
......@@ -1336,6 +1499,11 @@
dependencies:
"@octokit/openapi-types" "^6.0.0"
"@openzeppelin/contracts@^3.3.0":
version "3.4.1"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.1.tgz#03c891fec7f93be0ae44ed74e57a122a38732ce7"
integrity sha512-cUriqMauq1ylzP2TxePNdPqkwI7Le3Annh4K9rrpvKfSBB/bdW+Iu1ihBaTIABTAAJ85LmKL5SSPPL9ry8d1gQ==
"@resolver-engine/core@^0.3.3":
version "0.3.3"
resolved "https://registry.yarnpkg.com/@resolver-engine/core/-/core-0.3.3.tgz#590f77d85d45bc7ecc4e06c654f41345db6ca967"
......@@ -1470,6 +1638,11 @@
dependencies:
ethers "^5.0.2"
"@types/abstract-leveldown@*":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@types/abstract-leveldown/-/abstract-leveldown-5.0.1.tgz#3c7750d0186b954c7f2d2f6acc8c3c7ba0c3412e"
integrity sha512-wYxU3kp5zItbxKmeRYCEplS2MW7DzyBnxPGj+GJVHZEUZiK/nn5Ei1sUFgURDh+X051+zsGe28iud3oHjrYWQQ==
"@types/bn.js@*", "@types/bn.js@^5.1.0":
version "5.1.0"
resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68"
......@@ -1484,16 +1657,83 @@
dependencies:
"@types/node" "*"
"@types/chai@*":
"@types/body-parser@*":
version "1.19.0"
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f"
integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==
dependencies:
"@types/connect" "*"
"@types/node" "*"
"@types/browser-or-node@^1.3.0":
version "1.3.0"
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":
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":
version "4.2.15"
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.15.tgz#b7a6d263c2cecf44b6de9a051cf496249b154553"
integrity sha512-rYff6FI+ZTKAPkJUoyz7Udq3GaoDZnxYDEvdEdFZASiA7PoErltHezDishqQiSDWrGxvxmplH304jyzQmjp0AQ==
"@types/connect@*":
version "3.4.34"
resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.34.tgz#170a40223a6d666006d93ca128af2beb1d9b1901"
integrity sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==
dependencies:
"@types/node" "*"
"@types/cors@^2.8.9":
version "2.8.10"
resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.10.tgz#61cc8469849e5bcdd0c7044122265c39cec10cf4"
integrity sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==
"@types/express-serve-static-core@^4.17.18":
version "4.17.19"
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz#00acfc1632e729acac4f1530e9e16f6dd1508a1d"
integrity sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA==
dependencies:
"@types/node" "*"
"@types/qs" "*"
"@types/range-parser" "*"
"@types/express@^4.17.11":
version "4.17.11"
resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.11.tgz#debe3caa6f8e5fcda96b47bd54e2f40c4ee59545"
integrity sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==
dependencies:
"@types/body-parser" "*"
"@types/express-serve-static-core" "^4.17.18"
"@types/qs" "*"
"@types/serve-static" "*"
"@types/glob@*":
version "7.1.3"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183"
integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==
dependencies:
"@types/minimatch" "*"
"@types/node" "*"
"@types/json5@^0.0.29":
version "0.0.29"
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
"@types/levelup@^4.3.0":
version "4.3.0"
resolved "https://registry.yarnpkg.com/@types/levelup/-/levelup-4.3.0.tgz#4f55585e05a33caa08c1439c344bbba93e947327"
integrity sha512-h82BoajhjU/zwLoM4BUBX/SCodCFi1ae/ZlFOYh5Z4GbHeaXj9H709fF1LYl/StrK8KSwnJOeMRPo9lnC6sz4w==
dependencies:
"@types/abstract-leveldown" "*"
"@types/node" "*"
"@types/lodash@^4.14.161":
version "4.14.168"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008"
......@@ -1504,7 +1744,12 @@
resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.0.tgz#57f228f2b80c046b4a1bd5cac031f81f207f4f03"
integrity sha512-RaE0B+14ToE4l6UqdarKPnXwVDuigfFv+5j9Dze/Nqr23yyuqdNvzcZi3xB+3Agvi5R4EOgAksfv3lXX4vBt9w==
"@types/minimatch@^3.0.3":
"@types/mime@^1":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==
"@types/minimatch@*", "@types/minimatch@^3.0.3":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21"
integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==
......@@ -1521,12 +1766,12 @@
dependencies:
"@types/node" "*"
"@types/mocha@^8.2.2":
"@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"
integrity sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==
"@types/node-fetch@^2.5.5":
"@types/node-fetch@^2.5.5", "@types/node-fetch@^2.5.8":
version "2.5.8"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.8.tgz#e199c835d234c7eb0846f6618012e558544ee2fb"
integrity sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw==
......@@ -1544,6 +1789,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.6.tgz#7b73cce37352936e628c5ba40326193443cfba25"
integrity sha512-sRVq8d+ApGslmkE9e3i+D3gFGk7aZHAT+G4cIpIEdLJYPsWiSPwcAnJEjddLQQDqV3Ra2jOclX/Sv6YrvGYiWA==
"@types/node@^14.14.27":
version "14.14.36"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.36.tgz#5637905dbb15c30a33a3c65b9ef7c20e3c85ebad"
integrity sha512-kjivUwDJfIjngzbhooRnOLhGYz6oRFi+L+EpMjxroDYXwDw9lHrJJ43E+dJ6KAd3V3WxWAJ/qZE9XKYHhjPOFQ==
"@types/normalize-package-data@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
......@@ -1582,6 +1832,16 @@
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.2.3.tgz#ef65165aea2924c9359205bf748865b8881753c0"
integrity sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==
"@types/qs@*":
version "6.9.6"
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.6.tgz#df9c3c8b31a247ec315e6996566be3171df4b3b1"
integrity sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==
"@types/range-parser@*":
version "1.2.3"
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c"
integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==
"@types/resolve@^0.0.8":
version "0.0.8"
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194"
......@@ -1589,6 +1849,14 @@
dependencies:
"@types/node" "*"
"@types/rimraf@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-3.0.0.tgz#b9d03f090ece263671898d57bb7bb007023ac19f"
integrity sha512-7WhJ0MdpFgYQPXlF4Dx+DhgvlPCfz/x5mHaeDQAKhcenvQP1KCpLQ18JklAqeGMYSAT2PxLpzd0g2/HE7fj7hQ==
dependencies:
"@types/glob" "*"
"@types/node" "*"
"@types/secp256k1@^4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.1.tgz#fb3aa61a1848ad97d7425ff9dcba784549fca5a4"
......@@ -1596,6 +1864,14 @@
dependencies:
"@types/node" "*"
"@types/serve-static@*":
version "1.13.9"
resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.9.tgz#aacf28a85a05ee29a11fb7c3ead935ac56f33e4e"
integrity sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA==
dependencies:
"@types/mime" "^1"
"@types/node" "*"
"@types/sinon-chai@^3.2.3":
version "3.2.5"
resolved "https://registry.yarnpkg.com/@types/sinon-chai/-/sinon-chai-3.2.5.tgz#df21ae57b10757da0b26f512145c065f2ad45c48"
......@@ -1687,6 +1963,17 @@ abstract-leveldown@^5.0.0, abstract-leveldown@~5.0.0:
dependencies:
xtend "~4.0.0"
abstract-leveldown@^6.2.1:
version "6.3.0"
resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz#d25221d1e6612f820c35963ba4bd739928f6026a"
integrity sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ==
dependencies:
buffer "^5.5.0"
immediate "^3.2.3"
level-concat-iterator "~2.0.0"
level-supports "~1.0.0"
xtend "~4.0.0"
abstract-leveldown@~2.6.0:
version "2.6.3"
resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz#1c5e8c6a5ef965ae8c35dfb3a8770c476b82c4b8"
......@@ -1694,6 +1981,17 @@ abstract-leveldown@~2.6.0:
dependencies:
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"
integrity sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==
dependencies:
buffer "^5.5.0"
immediate "^3.2.3"
level-concat-iterator "~2.0.0"
level-supports "~1.0.0"
xtend "~4.0.0"
accepts@~1.3.7:
version "1.3.7"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
......@@ -1838,6 +2136,11 @@ are-we-there-yet@~1.1.2:
delegates "^1.0.0"
readable-stream "^2.0.6"
arg@^4.1.0:
version "4.1.3"
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
argparse@^1.0.7:
version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
......@@ -1850,6 +2153,16 @@ argparse@^2.0.1:
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
args@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/args/-/args-5.0.1.tgz#4bf298df90a4799a09521362c579278cc2fdd761"
integrity sha512-1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ==
dependencies:
camelcase "5.0.0"
chalk "2.4.2"
leven "2.1.0"
mri "1.1.4"
arr-diff@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
......@@ -2573,6 +2886,13 @@ base@^0.11.1:
mixin-deep "^1.2.0"
pascalcase "^0.1.1"
bcfg@^0.1.6:
version "0.1.6"
resolved "https://registry.yarnpkg.com/bcfg/-/bcfg-0.1.6.tgz#f77a6323bddef14f3886222e7ef8ccc0bc2143ec"
integrity sha512-BR2vwQZwu24aRm588XHOnPVjjQtbK8sF0RopRFgMuke63/REJMWnePTa2YHKDBefuBYiVdgkowuB1/e4K7Ue3g==
dependencies:
bsert "~0.0.10"
bcrypt-pbkdf@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
......@@ -2590,7 +2910,7 @@ before-after-hook@^2.2.0:
resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.0.tgz#09c40d92e936c64777aa385c4e9b904f8147eaf0"
integrity sha512-jH6rKQIfroBbhEXVmI7XmXe3ix5S/PgJqpzdDPnR8JGLHWNYLsYZ6tK5iWOF/Ra3oqEX0NobXGlzbiylIzVphQ==
bignumber.js@^9.0.0:
bignumber.js@^9.0.0, bignumber.js@^9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5"
integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==
......@@ -2600,6 +2920,13 @@ binary-extensions@^2.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
bindings@^1.4.0, bindings@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
dependencies:
file-uri-to-path "1.0.0"
bip39@2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/bip39/-/bip39-2.5.0.tgz#51cbd5179460504a63ea3c000db3f787ca051235"
......@@ -2611,6 +2938,15 @@ bip39@2.5.0:
safe-buffer "^5.0.1"
unorm "^1.3.3"
bl@^4.0.3:
version "4.1.0"
resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
dependencies:
buffer "^5.5.0"
inherits "^2.0.4"
readable-stream "^3.4.0"
blakejs@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.0.tgz#69df92ef953aa88ca51a32df6ab1c54a155fc7a5"
......@@ -2688,6 +3024,11 @@ brorand@^1.0.1, brorand@^1.1.0:
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
browser-or-node@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/browser-or-node/-/browser-or-node-1.3.0.tgz#f2a4e8568f60263050a6714b2cc236bb976647a7"
integrity sha512-0F2z/VSnLbmEeBcUrSuDH5l0HxTXdQQzLjkmBR4cYfvg1zJrKSlmIZFqyFR8oX0NrwPhy3c3HQ6i3OxMbew4Tg==
browser-stdout@1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
......@@ -2771,6 +3112,11 @@ bs58check@^2.1.2:
create-hash "^1.1.0"
safe-buffer "^5.1.2"
bsert@~0.0.10:
version "0.0.10"
resolved "https://registry.yarnpkg.com/bsert/-/bsert-0.0.10.tgz#231ac82873a1418c6ade301ab5cd9ae385895597"
integrity sha512-NHNwlac+WPy4t2LoNh8pXk8uaIGH3NSaIUbTTRXGpE2WEbq0te/tDykYHkFK57YKLPjv/aGHmbqvnGeVWDz57Q==
buffer-from@^1.0.0, buffer-from@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
......@@ -2937,6 +3283,11 @@ camelcase-keys@^6.2.2:
map-obj "^4.0.0"
quick-lru "^4.0.1"
camelcase@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42"
integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==
camelcase@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
......@@ -2967,7 +3318,14 @@ caseless@~0.12.0:
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
chai@^4.3.0:
chai-as-promised@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-7.1.1.tgz#08645d825deb8696ee61725dbf590c012eb00ca0"
integrity sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==
dependencies:
check-error "^1.0.2"
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==
......@@ -2979,6 +3337,15 @@ chai@^4.3.0:
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:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
......@@ -2990,15 +3357,6 @@ chalk@^1.1.3:
strip-ansi "^3.0.0"
supports-color "^2.0.0"
chalk@^2.0.0, 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==
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
chalk@^4.0.0, chalk@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a"
......@@ -3217,6 +3575,11 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
colors@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
columnify@^1.5.4:
version "1.5.4"
resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb"
......@@ -3460,7 +3823,7 @@ core-util-is@1.0.2, core-util-is@~1.0.0:
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
cors@^2.8.1:
cors@^2.8.1, cors@^2.8.5:
version "2.8.5"
resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
......@@ -3510,6 +3873,11 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
create-require@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
cross-fetch@^2.1.0, cross-fetch@^2.1.1:
version "2.2.3"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-2.2.3.tgz#e8a0b3c54598136e037f8650f8e823ccdfac198e"
......@@ -3587,6 +3955,11 @@ dateformat@^3.0.0:
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
dateformat@^4.5.1:
version "4.5.1"
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.5.1.tgz#c20e7a9ca77d147906b6dc2261a8be0a5bd2173c"
integrity sha512-OD0TZ+B7yP7ZgpJf5K2DIbj3FZvFvxgFUuaqA/V5zTjAtAAXZ1E8bktHxmAGs4x5b7PflqA9LeQ84Og7wYtF7Q==
debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
......@@ -3601,7 +3974,7 @@ debug@3.2.6:
dependencies:
ms "^2.1.1"
debug@4, debug@4.3.1, debug@^4.1.0, debug@^4.1.1:
debug@4, debug@4.3.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
......@@ -3650,6 +4023,13 @@ decompress-response@^3.2.0, decompress-response@^3.3.0:
dependencies:
mimic-response "^1.0.0"
decompress-response@^4.2.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986"
integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==
dependencies:
mimic-response "^2.0.0"
dedent@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
......@@ -3674,6 +4054,11 @@ deep-equal@~1.1.1:
object-keys "^1.1.1"
regexp.prototype.flags "^1.2.0"
deep-extend@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
defaults@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d"
......@@ -3701,6 +4086,14 @@ deferred-leveldown@~4.0.0:
abstract-leveldown "~5.0.0"
inherits "^2.0.3"
deferred-leveldown@~5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz#27a997ad95408b61161aa69bd489b86c71b78058"
integrity sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw==
dependencies:
abstract-leveldown "~6.2.1"
inherits "^2.0.3"
define-properties@^1.1.2, define-properties@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
......@@ -3785,6 +4178,11 @@ detect-indent@^6.0.0:
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.0.0.tgz#0abd0f549f69fc6659a254fe96786186b6f528fd"
integrity sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA==
detect-libc@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
dezalgo@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456"
......@@ -3843,6 +4241,11 @@ dot-prop@^6.0.1:
dependencies:
is-obj "^2.0.0"
dotenv@^8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
dotignore@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/dotignore/-/dotignore-0.1.2.tgz#f942f2200d28c3a76fbdd6f0ee9f3257c8a2e905"
......@@ -3917,6 +4320,16 @@ encoding-down@5.0.4, encoding-down@~5.0.0:
level-errors "^2.0.0"
xtend "^4.0.1"
encoding-down@^6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-6.3.0.tgz#b1c4eb0e1728c146ecaef8e32963c549e76d082b"
integrity sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw==
dependencies:
abstract-leveldown "^6.2.1"
inherits "^2.0.3"
level-codec "^9.0.0"
level-errors "^2.0.0"
encoding@^0.1.11, encoding@^0.1.12:
version "0.1.13"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
......@@ -3924,7 +4337,7 @@ encoding@^0.1.11, encoding@^0.1.12:
dependencies:
iconv-lite "^0.6.2"
end-of-stream@^1.1.0:
end-of-stream@^1.1.0, end-of-stream@^1.4.1:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
......@@ -4458,7 +4871,7 @@ 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.31, ethers@^5.0.32:
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:
version "5.0.32"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.0.32.tgz#f009970be31d96a589bf0ce597a39c10c7e297a6"
integrity sha512-rORfGWR0HsA4pjKMMcWZorw12DHsXqfIAuPVHJsXt+vI24jvXcVqx+rLsSvgOoLdaCMdxiN5qlIq2+4axKG31g==
......@@ -4525,7 +4938,7 @@ eventemitter3@^4.0.4:
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
events@^3.0.0:
events@^3.0.0, events@^3.2.0, events@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
......@@ -4566,7 +4979,12 @@ expand-brackets@^2.1.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
express@^4.14.0:
expand-template@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
express@^4.14.0, express@^4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
......@@ -4727,6 +5145,11 @@ figures@^3.0.0:
dependencies:
escape-string-regexp "^1.0.5"
file-uri-to-path@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
fill-range@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
......@@ -4912,6 +5335,11 @@ fresh@0.5.2:
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
fs-constants@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
fs-extra@^0.30.0:
version "0.30.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0"
......@@ -4990,7 +5418,7 @@ functional-red-black-tree@^1.0.1, functional-red-black-tree@~1.0.1:
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
ganache-core@^2.13.2:
ganache-core@^2.12.1, ganache-core@^2.13.2:
version "2.13.2"
resolved "https://registry.yarnpkg.com/ganache-core/-/ganache-core-2.13.2.tgz#27e6fc5417c10e6e76e2e646671869d7665814a3"
integrity sha512-tIF5cR+ANQz0+3pHWxHjIwHqFXcVo0Mb+kcsNhglNFALcYo49aQpnS9dqHartqPfMFjiHh/qFoD3mYK0d/qGgw==
......@@ -5171,6 +5599,11 @@ gitconfiglocal@^1.0.0:
dependencies:
ini "^1.3.2"
github-from-package@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=
glob-parent@^5.1.0, glob-parent@^5.1.1, glob-parent@~5.1.0:
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
......@@ -5304,7 +5737,7 @@ hard-rejection@^2.1.0:
resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883"
integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==
hardhat@^2.1.1, hardhat@^2.1.2:
hardhat@^2.0.9, hardhat@^2.1.1, hardhat@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.1.2.tgz#a2128b71b0fb216ffc978c85a2030835b4e306ea"
integrity sha512-42iOheDsDl6Gr7sBfpA0S+bQUIcXSDEUrrqmnFEcBHx9qBoQad3s212y2ODmmkdLt+PqqTM+Mq8N3bZDTdjoLg==
......@@ -5680,7 +6113,7 @@ inherits@2.0.3:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
ini@^1.3.2, ini@^1.3.4:
ini@^1.3.2, ini@^1.3.4, ini@~1.3.0:
version "1.3.8"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
......@@ -5723,7 +6156,7 @@ interpret@^1.0.0:
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==
invariant@^2.2.2:
invariant@2, invariant@^2.2.2:
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
......@@ -6116,6 +6549,16 @@ jest-docblock@^21.0.0:
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414"
integrity sha512-5IZ7sY9dBAYSV+YjQ0Ovb540Ku7AO9Z5o2Cg789xj167iQuZ2cG+z0f3Uct6WeYLbU6aQiM2pCs7sZ+4dotydw==
jmespath@^0.15.0:
version "0.15.0"
resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217"
integrity sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=
joycon@^2.2.5:
version "2.2.5"
resolved "https://registry.yarnpkg.com/joycon/-/joycon-2.2.5.tgz#8d4cf4cbb2544d7b7583c216fcdfec19f6be1615"
integrity sha512-YqvUxoOcVPnCp0VU1/56f+iKSdvIRJYPznH22BdXV3xMk75SFXhWeJkZ8C9XxUWt1b5x2X1SxuFygW1U0FmkEQ==
js-sha3@0.5.7, js-sha3@^0.5.7:
version "0.5.7"
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7"
......@@ -6386,6 +6829,11 @@ level-codec@~7.0.0:
resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-7.0.1.tgz#341f22f907ce0f16763f24bddd681e395a0fb8a7"
integrity sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ==
level-concat-iterator@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz#1d1009cf108340252cb38c51f9727311193e6263"
integrity sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw==
level-errors@^1.0.3:
version "1.1.2"
resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.1.2.tgz#4399c2f3d3ab87d0625f7e3676e2d807deff404d"
......@@ -6435,6 +6883,25 @@ level-iterator-stream@~3.0.0:
readable-stream "^2.3.6"
xtend "^4.0.0"
level-iterator-stream@~4.0.0:
version "4.0.2"
resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz#7ceba69b713b0d7e22fcc0d1f128ccdc8a24f79c"
integrity sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q==
dependencies:
inherits "^2.0.4"
readable-stream "^3.4.0"
xtend "^4.0.2"
level-js@^5.0.0:
version "5.0.2"
resolved "https://registry.yarnpkg.com/level-js/-/level-js-5.0.2.tgz#5e280b8f93abd9ef3a305b13faf0b5397c969b55"
integrity sha512-SnBIDo2pdO5VXh02ZmtAyPP6/+6YTJg2ibLtl9C34pWvmtMEmRTWpra+qO/hifkUtBTOtfx6S9vLDjBsBK4gRg==
dependencies:
abstract-leveldown "~6.2.3"
buffer "^5.5.0"
inherits "^2.0.3"
ltgt "^2.1.2"
level-mem@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/level-mem/-/level-mem-3.0.1.tgz#7ce8cf256eac40f716eb6489654726247f5a89e5"
......@@ -6443,6 +6910,14 @@ level-mem@^3.0.1:
level-packager "~4.0.0"
memdown "~3.0.0"
level-packager@^5.1.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-5.1.1.tgz#323ec842d6babe7336f70299c14df2e329c18939"
integrity sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ==
dependencies:
encoding-down "^6.3.0"
levelup "^4.3.2"
level-packager@~4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-4.0.1.tgz#7e7d3016af005be0869bc5fa8de93d2a7f56ffe6"
......@@ -6474,6 +6949,13 @@ level-sublevel@6.6.4:
typewiselite "~1.0.0"
xtend "~4.0.0"
level-supports@~1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-1.0.1.tgz#2f530a596834c7301622521988e2c36bb77d122d"
integrity sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==
dependencies:
xtend "^4.0.2"
level-ws@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-0.0.0.tgz#372e512177924a00424b0b43aef2bb42496d228b"
......@@ -6491,6 +6973,24 @@ level-ws@^1.0.0:
readable-stream "^2.2.8"
xtend "^4.0.1"
level@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/level/-/level-6.0.1.tgz#dc34c5edb81846a6de5079eac15706334b0d7cd6"
integrity sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw==
dependencies:
level-js "^5.0.0"
level-packager "^5.1.0"
leveldown "^5.4.0"
leveldown@^5.4.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-5.6.0.tgz#16ba937bb2991c6094e13ac5a6898ee66d3eee98"
integrity sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ==
dependencies:
abstract-leveldown "~6.2.1"
napi-macros "~2.0.0"
node-gyp-build "~4.1.0"
levelup@3.1.1, levelup@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/levelup/-/levelup-3.1.1.tgz#c2c0b3be2b4dc316647c53b42e2f559e232d2189"
......@@ -6514,6 +7014,22 @@ levelup@^1.2.1:
semver "~5.4.1"
xtend "~4.0.0"
levelup@^4.3.2, levelup@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/levelup/-/levelup-4.4.0.tgz#f89da3a228c38deb49c48f88a70fb71f01cafed6"
integrity sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ==
dependencies:
deferred-leveldown "~5.3.0"
level-errors "~2.0.0"
level-iterator-stream "~4.0.0"
level-supports "~1.0.0"
xtend "~4.0.0"
leven@2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580"
integrity sha1-wuep93IJTe6dNCAq6KzORoeHVYA=
libnpmaccess@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-4.0.1.tgz#17e842e03bef759854adf6eb6c2ede32e782639f"
......@@ -6974,6 +7490,11 @@ mimic-response@^1.0.0, mimic-response@^1.0.1:
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
mimic-response@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43"
integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==
min-document@^2.19.0:
version "2.19.0"
resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685"
......@@ -7012,7 +7533,7 @@ minimist-options@4.1.0:
is-plain-obj "^1.1.0"
kind-of "^6.0.3"
minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.5:
minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@~1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
......@@ -7102,6 +7623,11 @@ mixin-deep@^1.2.0:
for-in "^1.0.2"
is-extendable "^1.0.1"
mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
version "0.5.3"
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
mkdirp-infer-owner@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz#55d3b368e7d89065c38f32fd38e638f0ab61d316"
......@@ -7167,7 +7693,7 @@ mocha@^7.1.2:
yargs-parser "13.1.2"
yargs-unparser "1.6.0"
mocha@^8.3.0:
mocha@^8.3.0, mocha@^8.3.2:
version "8.3.2"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.3.2.tgz#53406f195fa86fbdebe71f8b1c6fb23221d69fcc"
integrity sha512-UdmISwr/5w+uXLPKspgoV7/RXZwKRTiTjJ2/AC5ZiEztIoOYdfKb19+9jNmEInzx5pBsCyJQzarAxqIGBNYJhg==
......@@ -7208,6 +7734,11 @@ modify-values@^1.0.0:
resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==
mri@1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.4.tgz#7cb1dd1b9b40905f1fac053abe25b6720f44744a"
integrity sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
......@@ -7284,6 +7815,16 @@ mute-stream@0.0.8, mute-stream@~0.0.4:
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
nan@2.13.2:
version "2.13.2"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7"
integrity sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==
nan@^2.14.0:
version "2.14.2"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
nano-json-stream-parser@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f"
......@@ -7311,6 +7852,16 @@ nanomatch@^1.2.9:
snapdragon "^0.8.1"
to-regex "^3.0.1"
napi-build-utils@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806"
integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==
napi-macros@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b"
integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==
negotiator@0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
......@@ -7331,11 +7882,23 @@ nice-try@^1.0.4:
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
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"
integrity sha512-smhrivuPqEM3H5LmnY3KU6HfYv0u4QklgAxfFyRNujKUzbUcYZ+Jc2EhukB9SRcD2VpqhxM7n/MIcp1Ua1/JMg==
dependencies:
semver "^5.4.1"
node-addon-api@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32"
integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==
node-addon-api@^3.0.2:
version "3.1.0"
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.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088"
......@@ -7367,6 +7930,11 @@ node-gyp-build@^4.2.0:
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739"
integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==
node-gyp-build@~4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.1.1.tgz#d7270b5d86717068d114cc57fff352f96d745feb"
integrity sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ==
node-gyp@^5.0.2:
version "5.1.1"
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.1.1.tgz#eb915f7b631c937d282e33aed44cb7a025f62a3e"
......@@ -7400,6 +7968,30 @@ node-gyp@^7.1.0:
tar "^6.0.2"
which "^2.0.2"
node-hid@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/node-hid/-/node-hid-1.3.0.tgz#346a468505cee13d69ccd760052cbaf749f66a41"
integrity sha512-BA6G4V84kiNd1uAChub/Z/5s/xS3EHBCxotQ0nyYrUG65mXewUDHE1tWOSqA2dp3N+mV0Ffq9wo2AW9t4p/G7g==
dependencies:
bindings "^1.5.0"
nan "^2.14.0"
node-abi "^2.18.0"
prebuild-install "^5.3.4"
node-hid@2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/node-hid/-/node-hid-2.1.1.tgz#f83c8aa0bb4e6758b5f7383542477da93f67359d"
integrity sha512-Skzhqow7hyLZU93eIPthM9yjot9lszg9xrKxESleEs05V2NcbUptZc5HFqzjOkSmL0sFlZFr3kmvaYebx06wrw==
dependencies:
bindings "^1.5.0"
node-addon-api "^3.0.2"
prebuild-install "^6.0.0"
noop-logger@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2"
integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=
nopt@^4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48"
......@@ -7533,7 +8125,7 @@ npm-run-path@^4.0.1:
dependencies:
path-key "^3.0.0"
npmlog@^4.1.2:
npmlog@^4.0.1, npmlog@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
......@@ -8108,6 +8700,23 @@ pinkie@^2.0.0:
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
pino-pretty@^4.7.1:
version "4.7.1"
resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-4.7.1.tgz#499cf185e110399deae731221c899915c811bd1a"
integrity sha512-ILE5YBpur88FlZ0cr1BNqVjgG9fOoK+md3peqmcs7AC6oq7SNiaJioIcrykMxfNsuygMYjUJtvAcARRE9aRc9w==
dependencies:
"@hapi/bourne" "^2.0.0"
args "^5.0.1"
chalk "^4.0.0"
dateformat "^4.5.1"
fast-safe-stringify "^2.0.7"
jmespath "^0.15.0"
joycon "^2.2.5"
pump "^3.0.0"
readable-stream "^3.6.0"
split2 "^3.1.1"
strip-json-comments "^3.1.1"
pino-std-serializers@^3.1.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz#b56487c402d882eb96cd67c257868016b61ad671"
......@@ -8142,6 +8751,48 @@ postinstall-postinstall@^2.1.0:
resolved "https://registry.yarnpkg.com/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz#4f7f77441ef539d1512c40bd04c71b06a4704ca3"
integrity sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ==
prebuild-install@^5.3.3, prebuild-install@^5.3.4:
version "5.3.6"
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.6.tgz#7c225568d864c71d89d07f8796042733a3f54291"
integrity sha512-s8Aai8++QQGi4sSbs/M1Qku62PFK49Jm1CbgXklGz4nmHveDq0wzJkg7Na5QbnO1uNH8K7iqx2EQ/mV0MZEmOg==
dependencies:
detect-libc "^1.0.3"
expand-template "^2.0.3"
github-from-package "0.0.0"
minimist "^1.2.3"
mkdirp-classic "^0.5.3"
napi-build-utils "^1.0.1"
node-abi "^2.7.0"
noop-logger "^0.1.1"
npmlog "^4.0.1"
pump "^3.0.0"
rc "^1.2.7"
simple-get "^3.0.3"
tar-fs "^2.0.0"
tunnel-agent "^0.6.0"
which-pm-runs "^1.0.0"
prebuild-install@^6.0.0:
version "6.0.1"
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.0.1.tgz#5902172f7a40eb67305b96c2a695db32636ee26d"
integrity sha512-7GOJrLuow8yeiyv75rmvZyeMGzl8mdEX5gY69d6a6bHWmiPevwqFw+tQavhK0EYMaSg3/KD24cWqeQv1EWsqDQ==
dependencies:
detect-libc "^1.0.3"
expand-template "^2.0.3"
github-from-package "0.0.0"
minimist "^1.2.3"
mkdirp-classic "^0.5.3"
napi-build-utils "^1.0.1"
node-abi "^2.7.0"
noop-logger "^0.1.1"
npmlog "^4.0.1"
pump "^3.0.0"
rc "^1.2.7"
simple-get "^3.0.3"
tar-fs "^2.0.0"
tunnel-agent "^0.6.0"
which-pm-runs "^1.0.0"
precond@0.2:
version "0.2.3"
resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac"
......@@ -8422,6 +9073,16 @@ raw-body@^2.4.1:
iconv-lite "0.4.24"
unpipe "1.0.0"
rc@^1.2.7:
version "1.2.8"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
dependencies:
deep-extend "^0.6.0"
ini "~1.3.0"
minimist "^1.2.0"
strip-json-comments "~2.0.1"
read-cmd-shim@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz#4a50a71d6f0965364938e9038476f7eede3928d9"
......@@ -8524,7 +9185,7 @@ read@1, read@~1.0.1:
dependencies:
mute-stream "~0.0.4"
readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.6.0:
readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
......@@ -8833,7 +9494,7 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^3.0.0"
inherits "^2.0.1"
rlp@^2.0.0, rlp@^2.2.1, rlp@^2.2.2, rlp@^2.2.3, rlp@^2.2.4:
rlp@^2.0.0, rlp@^2.2.1, rlp@^2.2.2, rlp@^2.2.3, rlp@^2.2.4, rlp@^2.2.6:
version "2.2.6"
resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.6.tgz#c80ba6266ac7a483ef1e69e8e2f056656de2fb2c"
integrity sha512-HAfAmL6SDYNWPUOJNrM500x4Thn4PZsEy5pijPh40U9WfNk0z15hUYzO9xVIMAdIHdFtD8CBDHd75Td1g36Mjg==
......@@ -8857,7 +9518,7 @@ rustbn.js@~0.2.0:
resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca"
integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==
rxjs@^6.6.0:
rxjs@^6.6.0, rxjs@^6.6.6:
version "6.6.6"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.6.tgz#14d8417aa5a07c5e633995b525e1e3c0dec03b70"
integrity sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg==
......@@ -8924,7 +9585,7 @@ semaphore@>=1.0.1, semaphore@^1.0.3, semaphore@^1.1.0:
resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa"
integrity sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA==
"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1:
"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
......@@ -9099,6 +9760,15 @@ simple-get@^2.7.0:
once "^1.3.1"
simple-concat "^1.0.0"
simple-get@^3.0.3:
version "3.1.0"
resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.0.tgz#b45be062435e50d159540b576202ceec40b9c6b3"
integrity sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==
dependencies:
decompress-response "^4.2.0"
once "^1.3.1"
simple-concat "^1.0.0"
slash@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
......@@ -9259,7 +9929,7 @@ source-map-support@^0.4.15:
dependencies:
source-map "^0.5.6"
source-map-support@^0.5.13, source-map-support@^0.5.6:
source-map-support@^0.5.13, source-map-support@^0.5.17, source-map-support@^0.5.6:
version "0.5.19"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
......@@ -9320,7 +9990,7 @@ split-string@^3.0.1, split-string@^3.0.2:
dependencies:
extend-shallow "^3.0.0"
split2@^3.0.0:
split2@^3.0.0, split2@^3.1.1:
version "3.2.2"
resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f"
integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==
......@@ -9549,12 +10219,12 @@ strip-indent@^3.0.0:
dependencies:
min-indent "^1.0.0"
strip-json-comments@2.0.1:
strip-json-comments@2.0.1, strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
strip-json-comments@3.1.1:
strip-json-comments@3.1.1, strip-json-comments@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
......@@ -9639,6 +10309,27 @@ tape@^4.6.3:
string.prototype.trim "~1.2.1"
through "~2.3.8"
tar-fs@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784"
integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==
dependencies:
chownr "^1.1.1"
mkdirp-classic "^0.5.2"
pump "^3.0.0"
tar-stream "^2.1.4"
tar-stream@^2.1.4:
version "2.2.0"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
dependencies:
bl "^4.0.3"
end-of-stream "^1.4.1"
fs-constants "^1.0.0"
inherits "^2.0.3"
readable-stream "^3.1.1"
tar@^4.0.2, tar@^4.4.12:
version "4.4.13"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525"
......@@ -9872,6 +10563,18 @@ ts-node@7.0.1:
source-map-support "^0.5.6"
yn "^2.0.0"
ts-node@^9.1.1:
version "9.1.1"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d"
integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==
dependencies:
arg "^4.1.0"
create-require "^1.1.0"
diff "^4.0.1"
make-error "^1.1.1"
source-map-support "^0.5.17"
yn "3.1.1"
tsconfig-paths@^3.5.0:
version "3.9.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b"
......@@ -10037,7 +10740,7 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript@^4.2.3:
typescript@^4.1.5, typescript@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3"
integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==
......@@ -10064,6 +10767,11 @@ typical@^2.6.0, typical@^2.6.1:
resolved "https://registry.yarnpkg.com/typical/-/typical-2.6.1.tgz#5c080e5d661cbbe38259d2e70a3c7253e873881d"
integrity sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=
u2f-api@0.2.7:
version "0.2.7"
resolved "https://registry.yarnpkg.com/u2f-api/-/u2f-api-0.2.7.tgz#17bf196b242f6bf72353d9858e6a7566cc192720"
integrity sha512-fqLNg8vpvLOD5J/z4B6wpPg4Lvowz1nJ9xdHcCzdUPKcFE/qNCceV2gNZxSJd5vhAZemHr/K/hbzVA0zxB5mkg==
uglify-js@^3.1.4:
version "3.13.2"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.2.tgz#fe10319861bccc8682bfe2e8151fbdd8aa921c44"
......@@ -10205,6 +10913,15 @@ url@^0.11.0:
punycode "1.3.2"
querystring "0.2.0"
usb@^1.6.3:
version "1.6.5"
resolved "https://registry.yarnpkg.com/usb/-/usb-1.6.5.tgz#482dc18468a6ed7b33d92ef88500630c68da3c7d"
integrity sha512-gLVrerQce+F+TSkWgzXACV07nOw+uBlv0gT3svsqTWWxNDe3ESQBIhss3qonIDArMvWPJp6z3I4hXEDYTmPlHQ==
dependencies:
bindings "^1.4.0"
nan "2.13.2"
prebuild-install "^5.3.3"
use@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
......@@ -10644,6 +11361,11 @@ which-module@^2.0.0:
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
which-pm-runs@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb"
integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=
which@1.3.1, which@^1.2.9, which@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
......@@ -10826,7 +11548,7 @@ xhr@^2.0.4, xhr@^2.2.0, xhr@^2.3.3:
parse-headers "^2.0.0"
xtend "^4.0.0"
xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1:
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"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
......@@ -10967,6 +11689,11 @@ yargs@^4.7.1:
y18n "^3.2.1"
yargs-parser "^2.4.1"
yn@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
yn@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a"
......
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