Commit 6f0ae0a7 authored by Georgios Konstantopoulos's avatar Georgios Konstantopoulos Committed by GitHub

feat: bump dependencies to latest master (#35)

* ci: disable yarn install cache

* chore: use yarn test:ci to only test changes since master

* ci: use separate job for linting contracts

* feat(contracts): update to https://github.com/ethereum-optimism/contracts/commit/06cdfb45223d239b828ca88623717de958c96a69

* feat(hardhat-ovm): replace env var with OVM config network

https://github.com/ethereum-optimism/plugins/pull/35

* feat(batch-submitter): update to https://github.com/ethereum-optimism/batch-submitter/commit/8cd92d49272223ca2aa3d7cd39cb315a2dd1459f

* feat(dtl): update to https://github.com/ethereum-optimism/data-transport-layer/commit/b1e340a32bd6986ba2c381a10229d49c0b274648

More unit tests and slight type refactoring

* chore: regenerate yarn lock

* fix(integration-tests): use `--network optimism` instead of env var

* chore: yarn lint
parent d8a82f47
......@@ -41,8 +41,7 @@ jobs:
- name: Install integration test dependencies
working-directory: ./integration-tests
# only install dependencies if the cache was invalidated
if: steps.yarn-cache.outputs.cache-hit != 'true'
# if: steps.yarn-cache.outputs.cache-hit != 'true'
run: yarn install
- name: Build deps for the integration tests
......
......@@ -15,8 +15,8 @@ on:
- 'packages/**/*.ts'
jobs:
build-test-lint:
name: Run job on ${{matrix.node}}
test:
name: Run tests on ${{matrix.node}}
runs-on: ubuntu-latest
strategy:
......@@ -48,14 +48,42 @@ jobs:
- name: Install Dependencies
# only install dependencies if there was a change in the deps
if: steps.yarn-cache.outputs.cache-hit != 'true'
# if: steps.yarn-cache.outputs.cache-hit != 'true'
run: yarn install
- name: Build
run: yarn build
- name: Lint
run: yarn lint
- name: Test
run: yarn test
run: yarn test:ci
lint:
name: Linting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Fetch history
run: git fetch
- uses: actions/setup-node@v1
with:
node-version: 14
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v2
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install Dependencies
# only install dependencies if there was a change in the deps
# if: steps.yarn-cache.outputs.cache-hit != 'true'
run: yarn install
- name: Lint
run: yarn lint:ci
......@@ -8,6 +8,12 @@ const config: HardhatUserConfig = {
mocha: {
timeout: 100000,
},
networks: {
optimism: {
url: 'http://localhost:8545',
ovm: true,
},
},
}
export default config
......@@ -8,7 +8,7 @@
"lint": "yarn lint:fix && yarn lint:check",
"lint:check": "tslint --format stylish --project .",
"lint:fix": "prettier --config ./prettier-config.json --write 'test/**/*.ts'",
"test:integration": "TARGET=ovm hardhat test"
"test:integration": "hardhat --network optimism test"
},
"devDependencies": {
"@eth-optimism/contracts": "^0.1.11",
......
......@@ -2,9 +2,9 @@ ADDRESS_MANAGER_ADDRESS=
DEBUG=info*,error*,warn*,debug*
MAX_TX_SIZE=90000
MIN_TX_SIZE=0
MAX_BATCH_SIZE=50
MAX_L1_TX_SIZE=90000
MIN_L1_TX_SIZE=0
MAX_TX_BATCH_COUNT=50
POLL_INTERVAL=15000
NUM_CONFIRMATIONS=0
......
......@@ -13,8 +13,10 @@
"scripts": {
"clean": "yarn lerna run clean",
"build": "yarn lerna run build",
"test": "yarn lerna run test --parallel --since origin/master",
"lint": "yarn lerna run lint --parallel --since origin/master",
"test": "yarn lerna run test --parallel",
"test:ci": "yarn lerna run test --parallel --since origin/master",
"lint": "yarn lerna run lint",
"lint:ci": "yarn lerna run lint --parallel --since origin/master",
"lint:fix": "yarn lerna run lint:fix"
}
}
build
dist
node_modules
.env
cache/*
# vim
*.swp
{
"name": "@eth-optimism/batch-submitter",
"version": "0.1.5",
"version": "0.1.7",
"description": "[Optimism] Batch submission for sequencer & aggregators",
"main": "dist/index",
"types": "dist/index",
......@@ -10,7 +10,7 @@
"scripts": {
"start": "node ./exec/run-batch-submitter.js",
"build": "tsc -p ./tsconfig.build.json",
"clean": "rimraf dist/ ./tsconfig.build.tsbuildinfo",
"clean": "rimraf cache/ dist/ ./tsconfig.build.tsbuildinfo",
"lint": "yarn lint:fix && yarn lint:check",
"lint:check": "tslint --format stylish --project .",
"lint:fix": "prettier --config prettier-config.json --write \"hardhat.config.ts\" \"{src,exec,test}/**/*.ts\"",
......
......@@ -131,9 +131,10 @@ export abstract class BatchSubmitter {
}
protected _shouldSubmitBatch(batchSizeInBytes: number): boolean {
const currentTimestamp = Date.now()
const isTimeoutReached =
this.lastBatchSubmissionTimestamp + this.maxBatchSubmissionTime <=
Date.now()
currentTimestamp
if (batchSizeInBytes < this.minTxSize) {
if (!isTimeoutReached) {
this.log.info(
......@@ -141,12 +142,24 @@ export abstract class BatchSubmitter {
{
batchSizeInBytes,
minTxSize: this.minTxSize,
lastBatchSubmissionTimestamp: this.lastBatchSubmissionTimestamp,
currentTimestamp,
}
)
return false
}
this.log.info('Timeout reached.')
this.log.info('Timeout reached, proceeding with batch submission.', {
batchSizeInBytes,
lastBatchSubmissionTimestamp: this.lastBatchSubmissionTimestamp,
currentTimestamp,
})
return true
}
this.log.info('Sufficient batch size, proceeding with batch submission.', {
batchSizeInBytes,
lastBatchSubmissionTimestamp: this.lastBatchSubmissionTimestamp,
currentTimestamp,
})
return true
}
......
/* External Imports */
import { Promise as bPromise } from 'bluebird'
import { Contract, Signer } from 'ethers'
import { TransactionReceipt } from '@ethersproject/abstract-provider'
import { getContractFactory } from '@eth-optimism/contracts'
......@@ -108,14 +109,13 @@ export class StateBatchSubmitter extends BatchSubmitter {
const startBlock: number =
(await this.chainContract.getTotalElements()).toNumber() + BLOCK_OFFSET
// We will submit state roots for txs which have been in the tx chain for a while.
const callBlockNumber: number =
(await this.signer.provider.getBlockNumber()) - this.finalityConfirmations
const totalElements: number =
(await this.ctcContract.getTotalElements()).toNumber() + BLOCK_OFFSET
const endBlock: number = Math.min(
startBlock + this.maxBatchSize,
totalElements
)
if (startBlock >= endBlock) {
if (startBlock > endBlock) {
this.log.error(
......@@ -142,7 +142,7 @@ export class StateBatchSubmitter extends BatchSubmitter {
'appendStateBatch',
[batch, startBlock]
)
if (!this._shouldSubmitBatch(tx.length * 2)) {
if (!this._shouldSubmitBatch(tx.length / 2)) {
return
}
......@@ -172,21 +172,22 @@ export class StateBatchSubmitter extends BatchSubmitter {
startBlock: number,
endBlock: number
): Promise<Bytes32[]> {
const batch: Bytes32[] = []
for (let i = startBlock; i < endBlock; i++) {
const block = (await this.l2Provider.getBlockWithTransactions(
i
)) as L2Block
if (block.transactions[0].from === this.fraudSubmissionAddress) {
batch.push(
'0xbad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1'
)
this.fraudSubmissionAddress = 'no fraud'
} else {
batch.push(block.stateRoot)
}
}
const blockRange = endBlock - startBlock
const batch: Bytes32[] = await bPromise.map(
[...Array(blockRange).keys()],
async (i: number) => {
this.log.debug('Fetching L2BatchElement', { blockNo: startBlock + i })
const block = (await this.l2Provider.getBlockWithTransactions(
startBlock + i
)) as L2Block
if (block.transactions[0].from === this.fraudSubmissionAddress) {
this.fraudSubmissionAddress = 'no fraud'
return '0xbad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1'
}
return block.stateRoot
},
{ concurrency: 100 }
)
let tx = this.chainContract.interface.encodeFunctionData(
'appendStateBatch',
......@@ -199,6 +200,7 @@ export class StateBatchSubmitter extends BatchSubmitter {
startBlock,
])
}
return batch
}
}
......@@ -237,7 +237,12 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
batchParams,
wasBatchTruncated,
] = await this._generateSequencerBatchParams(startBlock, endBlock)
const batchSizeInBytes = encodeAppendSequencerBatch(batchParams).length * 2
const batchSizeInBytes = encodeAppendSequencerBatch(batchParams).length / 2
// Only submit batch if one of the following is true:
// 1. it was truncated
// 2. it is large enough
// 3. enough time has passed since last submission
if (!wasBatchTruncated && !this._shouldSubmitBatch(batchSizeInBytes)) {
return
}
......@@ -278,7 +283,7 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
this.log.debug('Fetching L2BatchElement', { blockNo: startBlock + i })
return this._getL2BatchElement(startBlock + i)
},
{ concurrency: 50 }
{ concurrency: 100 }
)
// Fix our batches if we are configured to. TODO: Remove this.
......
......@@ -32,11 +32,13 @@ interface RequiredEnvVars {
// The layer one address manager address
ADDRESS_MANAGER_ADDRESS: 'ADDRESS_MANAGER_ADDRESS'
// The minimum size in bytes of any L1 transactions generated by the batch submitter.
MIN_TX_SIZE: 'MIN_TX_SIZE'
MIN_L1_TX_SIZE: 'MIN_L1_TX_SIZE'
// The maximum size in bytes of any L1 transactions generated by the batch submitter.
MAX_TX_SIZE: 'MAX_TX_SIZE'
MAX_L1_TX_SIZE: 'MAX_L1_TX_SIZE'
// The maximum number of L2 transactions that can ever be in a batch.
MAX_BATCH_SIZE: 'MAX_BATCH_SIZE'
MAX_TX_BATCH_COUNT: 'MAX_TX_BATCH_COUNT'
// The maximum number of L2 state roots that can ever be in a batch.
MAX_STATE_BATCH_COUNT: 'MAX_STATE_BATCH_COUNT'
// The maximum amount of time (seconds) that we will wait before submitting an under-sized batch.
MAX_BATCH_SUBMISSION_TIME: 'MAX_BATCH_SUBMISSION_TIME'
// The delay in milliseconds between querying L2 for more transactions / to create a new batch.
......@@ -62,9 +64,10 @@ const requiredEnvVars: RequiredEnvVars = {
L1_NODE_WEB3_URL: 'L1_NODE_WEB3_URL',
L2_NODE_WEB3_URL: 'L2_NODE_WEB3_URL',
ADDRESS_MANAGER_ADDRESS: 'ADDRESS_MANAGER_ADDRESS',
MIN_TX_SIZE: 'MIN_TX_SIZE',
MAX_TX_SIZE: 'MAX_TX_SIZE',
MAX_BATCH_SIZE: 'MAX_BATCH_SIZE',
MIN_L1_TX_SIZE: 'MIN_L1_TX_SIZE',
MAX_L1_TX_SIZE: 'MAX_L1_TX_SIZE',
MAX_TX_BATCH_COUNT: 'MAX_TX_BATCH_COUNT',
MAX_STATE_BATCH_COUNT: 'MAX_STATE_BATCH_COUNT',
MAX_BATCH_SUBMISSION_TIME: 'MAX_BATCH_SUBMISSION_TIME',
POLL_INTERVAL: 'POLL_INTERVAL',
NUM_CONFIRMATIONS: 'NUM_CONFIRMATIONS',
......@@ -144,9 +147,9 @@ export const run = async () => {
const txBatchSubmitter = new TransactionBatchSubmitter(
sequencerSigner,
l2Provider,
parseInt(requiredEnvVars.MIN_TX_SIZE, 10),
parseInt(requiredEnvVars.MAX_TX_SIZE, 10),
parseInt(requiredEnvVars.MAX_BATCH_SIZE, 10),
parseInt(requiredEnvVars.MIN_L1_TX_SIZE, 10),
parseInt(requiredEnvVars.MAX_L1_TX_SIZE, 10),
parseInt(requiredEnvVars.MAX_TX_BATCH_COUNT, 10),
parseInt(requiredEnvVars.MAX_BATCH_SUBMISSION_TIME, 10) * 1_000,
parseInt(requiredEnvVars.NUM_CONFIRMATIONS, 10),
parseInt(requiredEnvVars.RESUBMISSION_TIMEOUT, 10) * 1_000,
......@@ -164,9 +167,9 @@ export const run = async () => {
const stateBatchSubmitter = new StateBatchSubmitter(
sequencerSigner,
l2Provider,
parseInt(requiredEnvVars.MIN_TX_SIZE, 10),
parseInt(requiredEnvVars.MAX_TX_SIZE, 10),
parseInt(requiredEnvVars.MAX_BATCH_SIZE, 10),
parseInt(requiredEnvVars.MIN_L1_TX_SIZE, 10),
parseInt(requiredEnvVars.MAX_L1_TX_SIZE, 10),
parseInt(requiredEnvVars.MAX_STATE_BATCH_COUNT, 10),
parseInt(requiredEnvVars.MAX_BATCH_SUBMISSION_TIME, 10) * 1_000,
parseInt(requiredEnvVars.NUM_CONFIRMATIONS, 10),
parseInt(requiredEnvVars.RESUBMISSION_TIMEOUT, 10) * 1_000,
......
import { expect } from '../setup'
/* External Imports */
import { ethers } from 'hardhat'
import '@nomiclabs/hardhat-ethers'
import { Signer, ContractFactory, Contract, BigNumber } from 'ethers'
import ganache from 'ganache-core'
import sinon from 'sinon'
import { Web3Provider } from '@ethersproject/providers'
import { getContractInterface } from '@eth-optimism/contracts'
import * as scc from '@eth-optimism/contracts/dist/artifacts/contracts/optimistic-ethereum/OVM/chain/OVM_StateCommitmentChain.sol/OVM_StateCommitmentChain.json'
import { smockit, MockContract } from '@eth-optimism/smock'
/* Internal Imports */
import { MockchainProvider } from './mockchain-provider'
import {
makeAddressManager,
setProxyTarget,
FORCE_INCLUSION_PERIOD_SECONDS,
getContractFactory,
} from '../helpers'
import {
CanonicalTransactionChainContract,
QueueOrigin,
TransactionBatchSubmitter as RealTransactionBatchSubmitter,
StateBatchSubmitter,
TX_BATCH_SUBMITTER_LOG_TAG,
STATE_BATCH_SUBMITTER_LOG_TAG,
Batch,
BatchSubmitter,
} from '../../src'
import {
Signature,
TxType,
ctcCoder,
remove0x,
Logger,
} from '@eth-optimism/core-utils'
const DECOMPRESSION_ADDRESS = '0x4200000000000000000000000000000000000008'
const DUMMY_ADDRESS = '0x' + '00'.repeat(20)
const EXAMPLE_STATE_ROOT =
'0x16b7f83f409c7195b1f4fde5652f1b54a4477eacb6db7927691becafba5f8801'
const MAX_GAS_LIMIT = 8_000_000
const MAX_TX_SIZE = 100_000
const MIN_TX_SIZE = 1_000
const MIN_GAS_PRICE_IN_GWEI = 1
const MAX_GAS_PRICE_IN_GWEI = 70
const GAS_RETRY_INCREMENT = 5
const GAS_THRESHOLD_IN_GWEI = 120
// Helper functions
interface QueueElement {
queueRoot: string
timestamp: number
blockNumber: number
}
const getQueueElement = async (
ctcContract: Contract,
nextQueueIndex?: number
): Promise<QueueElement> => {
if (!nextQueueIndex) {
nextQueueIndex = await ctcContract.getNextQueueIndex()
}
const nextQueueElement = await ctcContract.getQueueElement(nextQueueIndex)
return nextQueueElement
}
const DUMMY_SIG: Signature = {
r: '11'.repeat(32),
s: '22'.repeat(32),
v: 1,
}
// A transaction batch submitter which skips the validate batch check
class TransactionBatchSubmitter extends RealTransactionBatchSubmitter {
protected async _validateBatch(batch: Batch): Promise<boolean> {
return true
}
}
describe('BatchSubmitter', () => {
let signer: Signer
let sequencer: Signer
before(async () => {
;[signer, sequencer] = await ethers.getSigners()
})
let AddressManager: Contract
let Mock__OVM_ExecutionManager: MockContract
let Mock__OVM_BondManager: MockContract
let Mock__OVM_StateCommitmentChain: MockContract
before(async () => {
AddressManager = await makeAddressManager()
await AddressManager.setAddress(
'OVM_Sequencer',
await sequencer.getAddress()
)
await AddressManager.setAddress(
'OVM_DecompressionPrecompileAddress',
DECOMPRESSION_ADDRESS
)
Mock__OVM_ExecutionManager = await smockit(
await getContractFactory('OVM_ExecutionManager')
)
Mock__OVM_BondManager = await smockit(
await getContractFactory('OVM_BondManager')
)
Mock__OVM_StateCommitmentChain = await smockit(
await getContractFactory('OVM_StateCommitmentChain')
)
await setProxyTarget(
AddressManager,
'OVM_ExecutionManager',
Mock__OVM_ExecutionManager
)
await setProxyTarget(
AddressManager,
'OVM_BondManager',
Mock__OVM_BondManager
)
await setProxyTarget(
AddressManager,
'OVM_StateCommitmentChain',
Mock__OVM_StateCommitmentChain
)
Mock__OVM_StateCommitmentChain.smocked.canOverwrite.will.return.with(false)
Mock__OVM_ExecutionManager.smocked.getMaxTransactionGasLimit.will.return.with(
MAX_GAS_LIMIT
)
Mock__OVM_BondManager.smocked.isCollateralized.will.return.with(true)
})
let Factory__OVM_CanonicalTransactionChain: ContractFactory
let Factory__OVM_StateCommitmentChain: ContractFactory
before(async () => {
Factory__OVM_CanonicalTransactionChain = await getContractFactory(
'OVM_CanonicalTransactionChain'
)
Factory__OVM_StateCommitmentChain = await getContractFactory(
'OVM_StateCommitmentChain'
)
})
let OVM_CanonicalTransactionChain: CanonicalTransactionChainContract
let OVM_StateCommitmentChain: Contract
let l2Provider: MockchainProvider
beforeEach(async () => {
const unwrapped_OVM_CanonicalTransactionChain = await Factory__OVM_CanonicalTransactionChain.deploy(
AddressManager.address,
FORCE_INCLUSION_PERIOD_SECONDS
)
await unwrapped_OVM_CanonicalTransactionChain.init()
await AddressManager.setAddress(
'OVM_CanonicalTransactionChain',
unwrapped_OVM_CanonicalTransactionChain.address
)
OVM_CanonicalTransactionChain = new CanonicalTransactionChainContract(
unwrapped_OVM_CanonicalTransactionChain.address,
getContractInterface('OVM_CanonicalTransactionChain'),
sequencer
)
const unwrapped_OVM_StateCommitmentChain = await Factory__OVM_StateCommitmentChain.deploy(
AddressManager.address,
0, // fraudProofWindowSeconds
0 // sequencerPublishWindowSeconds
)
await unwrapped_OVM_StateCommitmentChain.init()
await AddressManager.setAddress(
'OVM_StateCommitmentChain',
unwrapped_OVM_StateCommitmentChain.address
)
OVM_StateCommitmentChain = new Contract(
unwrapped_OVM_StateCommitmentChain.address,
getContractInterface('OVM_StateCommitmentChain'),
sequencer
)
l2Provider = new MockchainProvider(
OVM_CanonicalTransactionChain.address,
OVM_StateCommitmentChain.address
)
})
afterEach(() => {
sinon.restore()
})
const createBatchSubmitter = (timeout: number): TransactionBatchSubmitter =>
new TransactionBatchSubmitter(
sequencer,
l2Provider as any,
MIN_TX_SIZE,
MAX_TX_SIZE,
10,
timeout,
1,
100000,
AddressManager.address,
1,
MIN_GAS_PRICE_IN_GWEI,
MAX_GAS_PRICE_IN_GWEI,
GAS_RETRY_INCREMENT,
GAS_THRESHOLD_IN_GWEI,
new Logger({ name: TX_BATCH_SUBMITTER_LOG_TAG }),
false
)
describe('TransactionBatchSubmitter', () => {
describe('submitNextBatch', () => {
const enqueuedElements: Array<{
blockNumber: number
timestamp: number
}> = []
let batchSubmitter
beforeEach(async () => {
for (let i = 1; i < 15; i++) {
await OVM_CanonicalTransactionChain.enqueue(
'0x' + '01'.repeat(20),
50_000,
'0x' + i.toString().repeat(64),
{
gasLimit: 1_000_000,
}
)
}
batchSubmitter = createBatchSubmitter(0)
})
it('should submit a sequencer batch correctly', async () => {
l2Provider.setNumBlocksToReturn(5)
const nextQueueElement = await getQueueElement(
OVM_CanonicalTransactionChain
)
const data = ctcCoder.eip155TxData.encode({
sig: DUMMY_SIG,
gasLimit: 0,
gasPrice: 0,
nonce: 0,
target: DUMMY_ADDRESS,
data: '0x',
type: TxType.EIP155,
})
l2Provider.setL2BlockData(
{
data,
l1BlockNumber: nextQueueElement.blockNumber - 1,
txType: TxType.EIP155,
queueOrigin: QueueOrigin.Sequencer,
l1TxOrigin: null,
} as any,
nextQueueElement.timestamp - 1
)
let receipt = await batchSubmitter.submitNextBatch()
let logData = remove0x(receipt.logs[1].data)
expect(parseInt(logData.slice(64 * 0, 64 * 1), 16)).to.equal(0) // _startingQueueIndex
expect(parseInt(logData.slice(64 * 1, 64 * 2), 16)).to.equal(0) // _numQueueElements
expect(parseInt(logData.slice(64 * 2, 64 * 3), 16)).to.equal(6) // _totalElements
receipt = await batchSubmitter.submitNextBatch()
logData = remove0x(receipt.logs[1].data)
expect(parseInt(logData.slice(64 * 0, 64 * 1), 16)).to.equal(0) // _startingQueueIndex
expect(parseInt(logData.slice(64 * 1, 64 * 2), 16)).to.equal(0) // _numQueueElements
expect(parseInt(logData.slice(64 * 2, 64 * 3), 16)).to.equal(11) // _totalElements
})
it('should submit a queue batch correctly', async () => {
l2Provider.setNumBlocksToReturn(5)
l2Provider.setL2BlockData({
queueOrigin: QueueOrigin.L1ToL2,
} as any)
let receipt = await batchSubmitter.submitNextBatch()
let logData = remove0x(receipt.logs[1].data)
expect(parseInt(logData.slice(64 * 0, 64 * 1), 16)).to.equal(0) // _startingQueueIndex
expect(parseInt(logData.slice(64 * 1, 64 * 2), 16)).to.equal(6) // _numQueueElements
expect(parseInt(logData.slice(64 * 2, 64 * 3), 16)).to.equal(6) // _totalElements
receipt = await batchSubmitter.submitNextBatch()
logData = remove0x(receipt.logs[1].data)
expect(parseInt(logData.slice(64 * 0, 64 * 1), 16)).to.equal(6) // _startingQueueIndex
expect(parseInt(logData.slice(64 * 1, 64 * 2), 16)).to.equal(5) // _numQueueElements
expect(parseInt(logData.slice(64 * 2, 64 * 3), 16)).to.equal(11) // _totalElements
})
it('should submit a batch with both queue and sequencer chain elements', async () => {
l2Provider.setNumBlocksToReturn(10) // For this batch we'll return 10 elements!
l2Provider.setL2BlockData({
queueOrigin: QueueOrigin.L1ToL2,
} as any)
// Turn blocks 3-5 into sequencer txs
const nextQueueElement = await getQueueElement(
OVM_CanonicalTransactionChain,
2
)
const data = ctcCoder.ethSignTxData.encode({
sig: DUMMY_SIG,
gasLimit: 0,
gasPrice: 0,
nonce: 0,
target: DUMMY_ADDRESS,
data: '0x',
type: TxType.EthSign,
})
l2Provider.setL2BlockData(
{
data,
l1BlockNumber: nextQueueElement.blockNumber - 1,
txType: TxType.EthSign,
queueOrigin: QueueOrigin.Sequencer,
l1TxOrigin: null,
} as any,
nextQueueElement.timestamp - 1,
'', // blank stateRoot
3,
6
)
const receipt = await batchSubmitter.submitNextBatch()
const logData = remove0x(receipt.logs[1].data)
expect(parseInt(logData.slice(64 * 0, 64 * 1), 16)).to.equal(0) // _startingQueueIndex
expect(parseInt(logData.slice(64 * 1, 64 * 2), 16)).to.equal(8) // _numQueueElements
expect(parseInt(logData.slice(64 * 2, 64 * 3), 16)).to.equal(11) // _totalElements
})
it('should submit a small batch only after the timeout', async () => {
l2Provider.setNumBlocksToReturn(2)
l2Provider.setL2BlockData({
queueOrigin: QueueOrigin.L1ToL2,
} as any)
// Create a batch submitter with a long timeout & make sure it doesn't submit the batches one after another
const longTimeout = 10_000
batchSubmitter = createBatchSubmitter(longTimeout)
let receipt = await batchSubmitter.submitNextBatch()
expect(receipt).to.not.be.undefined
receipt = await batchSubmitter.submitNextBatch()
// The receipt should be undefined because that means it didn't submit
expect(receipt).to.be.undefined
// This time create a batch submitter with a short timeout & it should submit batches after the timeout is reached
const shortTimeout = 5
batchSubmitter = createBatchSubmitter(shortTimeout)
receipt = await batchSubmitter.submitNextBatch()
expect(receipt).to.not.be.undefined
// Sleep for the short timeout
await new Promise((r) => setTimeout(r, shortTimeout))
receipt = await batchSubmitter.submitNextBatch()
// The receipt should NOT be undefined because that means it successfully submitted!
expect(receipt).to.not.be.undefined
})
it('should not submit if gas price is over threshold', async () => {
l2Provider.setNumBlocksToReturn(2)
l2Provider.setL2BlockData({
queueOrigin: QueueOrigin.L1ToL2,
} as any)
const highGasPriceWei = BigNumber.from(200).mul(1_000_000_000)
sinon
.stub(sequencer, 'getGasPrice')
.callsFake(async () => highGasPriceWei)
const receipt = await batchSubmitter.submitNextBatch()
expect(sequencer.getGasPrice).to.have.been.calledOnce
expect(receipt).to.be.undefined
})
it('should submit if gas price is not over threshold', async () => {
l2Provider.setNumBlocksToReturn(2)
l2Provider.setL2BlockData({
queueOrigin: QueueOrigin.L1ToL2,
} as any)
const lowGasPriceWei = BigNumber.from(2).mul(1_000_000_000)
sinon
.stub(sequencer, 'getGasPrice')
.callsFake(async () => lowGasPriceWei)
const receipt = await batchSubmitter.submitNextBatch()
expect(sequencer.getGasPrice).to.have.been.calledOnce
expect(receipt).to.not.be.undefined
})
})
})
describe('StateBatchSubmitter', () => {
let txBatchSubmitter
let stateBatchSubmitter
beforeEach(async () => {
for (let i = 1; i < 15; i++) {
await OVM_CanonicalTransactionChain.enqueue(
'0x' + '01'.repeat(20),
50_000,
'0x' + i.toString().repeat(64),
{
gasLimit: 1_000_000,
}
)
}
txBatchSubmitter = createBatchSubmitter(0)
l2Provider.setNumBlocksToReturn(5)
const nextQueueElement = await getQueueElement(
OVM_CanonicalTransactionChain
)
const data = ctcCoder.eip155TxData.encode({
sig: DUMMY_SIG,
gasLimit: 0,
gasPrice: 0,
nonce: 0,
target: DUMMY_ADDRESS,
data: '0x',
type: TxType.EIP155,
})
l2Provider.setL2BlockData(
{
data,
l1BlockNumber: nextQueueElement.blockNumber - 1,
txType: TxType.EIP155,
queueOrigin: QueueOrigin.Sequencer,
l1TxOrigin: null,
} as any,
nextQueueElement.timestamp - 1,
EXAMPLE_STATE_ROOT // example stateRoot
)
// submit a batch of transactions to enable state batch submission
await txBatchSubmitter.submitNextBatch()
stateBatchSubmitter = new StateBatchSubmitter(
sequencer,
l2Provider as any,
MIN_TX_SIZE,
MAX_TX_SIZE,
10, // maxBatchSize
0,
1,
100000,
0, // finalityConfirmations
AddressManager.address,
1,
MIN_GAS_PRICE_IN_GWEI,
MAX_GAS_PRICE_IN_GWEI,
GAS_RETRY_INCREMENT,
GAS_THRESHOLD_IN_GWEI,
new Logger({ name: STATE_BATCH_SUBMITTER_LOG_TAG }),
'0x' + '01'.repeat(20) // placeholder for fraudSubmissionAddress
)
})
describe('submitNextBatch', () => {
it('should submit a state batch after a transaction batch', async () => {
const receipt = await stateBatchSubmitter.submitNextBatch()
expect(receipt).to.not.be.undefined
const iface = new ethers.utils.Interface(scc.abi)
const parsedLogs = iface.parseLog(receipt.logs[0])
expect(parsedLogs.eventFragment.name).to.eq('StateBatchAppended')
expect(parsedLogs.args._batchIndex.toNumber()).to.eq(0)
expect(parsedLogs.args._batchSize.toNumber()).to.eq(6)
expect(parsedLogs.args._prevTotalElements.toNumber()).to.eq(0)
})
})
})
})
describe('Batch Submitter with Ganache', () => {
let signer
const server = ganache.server({
default_balance_ether: 420,
blockTime: 2_000,
})
const provider = new Web3Provider(ganache.provider())
before(async () => {
await server.listen(3001)
signer = await provider.getSigner()
})
after(async () => {
await server.close()
})
// Unit test for getReceiptWithResubmission function,
// tests for increasing gas price on resubmission
it('should resubmit a transaction if it is not confirmed', async () => {
const gasPrices = []
const numConfirmations = 2
const sendTxFunc = async (gasPrice) => {
// push the retried gasPrice
gasPrices.push(gasPrice)
const tx = signer.sendTransaction({
to: DECOMPRESSION_ADDRESS,
value: 88,
nonce: 0,
gasPrice,
})
const response = await tx
return signer.provider.waitForTransaction(response.hash, numConfirmations)
}
const resubmissionConfig = {
numConfirmations,
resubmissionTimeout: 1_000, // retry every second
minGasPriceInGwei: 0,
maxGasPriceInGwei: 100,
gasRetryIncrement: 5,
}
BatchSubmitter.getReceiptWithResubmission(
sendTxFunc,
resubmissionConfig,
new Logger({ name: TX_BATCH_SUBMITTER_LOG_TAG })
)
// Wait 1.5s for at least 1 retry
await new Promise((r) => setTimeout(r, 1500))
// Iterate through gasPrices to ensure each entry increases from
// the last
const isIncreasing = gasPrices.reduce(
(isInc, gasPrice, i, gP) =>
(isInc && gasPrice > gP[i - 1]) || Number.NEGATIVE_INFINITY,
true
)
expect(gasPrices).to.have.lengthOf.above(1) // retried at least once
expect(isIncreasing).to.be.true
})
})
......@@ -88,6 +88,7 @@ export class MockchainProvider extends OptimismProvider {
public setL2BlockData(
tx: L2Transaction,
timestamp?: number,
stateRoot?: string,
start: number = 1,
end: number = this.mockBlocks.length
) {
......@@ -99,6 +100,7 @@ export class MockchainProvider extends OptimismProvider {
...this.mockBlocks[i].transactions[0],
...tx,
}
this.mockBlocks[i].stateRoot = stateRoot
}
}
......
......@@ -274,6 +274,7 @@ describe('TransactionBatchSubmitter', () => {
l1TxOrigin: null,
} as any,
nextQueueElement.timestamp - 1,
'', // blank state root
3,
6
)
......
{
"extends": "../../tsconfig.json"
"extends": "../../tsconfig.json",
"compilerOptions": {
"resolveJsonModule": true
}
}
module.exports = {
skipFiles: [
'./test-helpers',
'./test-libraries',
'./optimistic-ethereum/mockOVM'
],
mocha: {
grep: "@skip-on-coverage",
invert: true
}
};
......@@ -54,6 +54,16 @@ Run specific tests by giving a path to the file you want to run:
yarn test ./test/path/to/my/test.spec.ts
```
### Measuring test coverage:
```shell
yarn test-coverage
```
The output is most easily viewable by opening the html file in your browser:
```shell
open ./coverage/index.html
```
### Compiling and Building
Easiest way is to run the primary build script:
```shell
......
......@@ -114,7 +114,7 @@ contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount {
// Contract creations are signalled by sending a transaction to the zero address.
if (decodedTx.to == address(0)) {
(address created, bytes memory revertData) = Lib_SafeExecutionManagerWrapper.safeCREATE(
decodedTx.gasLimit,
gasleft(),
decodedTx.data
);
......@@ -131,7 +131,7 @@ contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount {
Lib_SafeExecutionManagerWrapper.safeINCREMENTNONCE();
return Lib_SafeExecutionManagerWrapper.safeCALL(
decodedTx.gasLimit,
gasleft(),
decodedTx.to,
decodedTx.data
);
......
......@@ -19,9 +19,9 @@ import { Abs_BaseCrossDomainMessenger } from "./Abs_BaseCrossDomainMessenger.sol
/**
* @title OVM_L1CrossDomainMessenger
* @dev The L1 Cross Domain Messenger contract sends messages from L1 to L2, and relays messages from L2 onto L1.
* In the event that a message sent from L1 to L2 is rejected for exceeding the L2 epoch gas limit, it can be resubmitted
* via this contract's replay function.
* @dev The L1 Cross Domain Messenger contract sends messages from L1 to L2, and relays messages from L2 onto L1.
* In the event that a message sent from L1 to L2 is rejected for exceeding the L2 epoch gas limit, it can be resubmitted
* via this contract's replay function.
*
* Compiler used: solc
* Runtime target: EVM
......
......@@ -18,7 +18,7 @@ import { Abs_BaseCrossDomainMessenger } from "./Abs_BaseCrossDomainMessenger.sol
* @title OVM_L2CrossDomainMessenger
* @dev The L2 Cross Domain Messenger contract sends messages from L2 to L1, and is the entry point
* for L2 messages sent via the L1 Cross Domain Messenger.
*
*
* Compiler used: optimistic-solc
* Runtime target: OVM
*/
......@@ -75,6 +75,15 @@ contract OVM_L2CrossDomainMessenger is iOVM_L2CrossDomainMessenger, Abs_BaseCros
"Provided message has already been received."
);
// Prevent calls to OVM_L2ToL1MessagePasser, which would enable
// an attacker to maliciously craft the _message to spoof
// a call from any L2 account.
if(_target == resolve("OVM_L2ToL1MessagePasser")){
// Write to the successfulMessages mapping and return immediately.
successfulMessages[xDomainCalldataHash] = true;
return;
}
xDomainMsgSender = _sender;
(bool success, ) = _target.call(_message);
xDomainMsgSender = DEFAULT_XDOMAIN_SENDER;
......
......@@ -10,6 +10,7 @@ import { iOVM_L2DepositedToken } from "../../../iOVM/bridge/tokens/iOVM_L2Deposi
/* Library Imports */
import { OVM_CrossDomainEnabled } from "../../../libraries/bridge/OVM_CrossDomainEnabled.sol";
import { Lib_AddressResolver } from "../../../libraries/resolver/Lib_AddressResolver.sol";
import { Lib_AddressManager } from "../../../libraries/resolver/Lib_AddressManager.sol";
/**
* @title OVM_L1ETHGateway
......@@ -36,19 +37,31 @@ contract OVM_L1ETHGateway is iOVM_L1ETHGateway, OVM_CrossDomainEnabled, Lib_Addr
* Constructor *
***************/
// This contract lives behind a proxy, so the constructor parameters will go unused.
constructor()
OVM_CrossDomainEnabled(address(0))
Lib_AddressResolver(address(0))
public
{}
/******************
* Initialization *
******************/
/**
* @param _libAddressManager Address manager for this OE deployment
* @param _ovmEth L2 OVM_ETH implementation of iOVM_DepositedToken
*/
constructor(
function initialize(
address _libAddressManager,
address _ovmEth
)
OVM_CrossDomainEnabled(address(0)) // overridden in constructor code
Lib_AddressResolver(_libAddressManager)
public
{
require(libAddressManager == Lib_AddressManager(0), "Contract has already been initialized.");
libAddressManager = Lib_AddressManager(_libAddressManager);
ovmEth = _ovmEth;
messenger = resolve("Proxy__OVM_L1CrossDomainMessenger"); // overrides OVM_CrossDomainEnabled constructor setting because resolve() is not yet accessible
messenger = resolve("Proxy__OVM_L1CrossDomainMessenger");
}
/**************
......
......@@ -480,6 +480,10 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
assembly {
txDataLength := shr(232, calldataload(nextTransactionPtr))
}
require(
txDataLength <= MAX_ROLLUP_TX_SIZE,
"Transaction data size exceeds maximum for rollup transaction."
);
leaves[leafIndex] = _getSequencerLeafHash(
curContext,
......@@ -937,7 +941,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
internal
view
{
// If there are existing elements, this batch must have the same context
// If there are existing elements, this batch must have the same context
// or a later timestamp and block number.
if (getTotalElements() > 0) {
(,, uint40 lastTimestamp, uint40 lastBlockNumber) = _getBatchExtraData();
......
......@@ -159,7 +159,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
override
public
{
require(transactionContext.ovmNUMBER == 0, "Only be callable at the start of a transaction");
require(transactionContext.ovmNUMBER == 0, "Only callable at the start of a transaction");
// Store our OVM_StateManager instance (significantly easier than attempting to pass the
// address around in calldata).
ovmStateManager = iOVM_StateManager(_ovmStateManager);
......@@ -206,9 +206,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
// Wipe the execution context.
_resetContext();
// Reset the ovmStateManager.
ovmStateManager = iOVM_StateManager(address(0));
}
......@@ -640,7 +637,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
{
// DELEGATECALL does not change anything about the message context.
MessageContext memory nextMessageContext = messageContext;
return _callContract(
nextMessageContext,
_gasLimit,
......@@ -915,7 +912,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
/**
* Handles all interactions which involve the execution manager calling out to untrusted code (both calls and creates).
* Ensures that OVM-related measures are enforced, including L2 gas refunds, nuisance gas, and flagged reversions.
*
*
* @param _nextMessageContext Message context to be used for the external message.
* @param _gasLimit Amount of gas to be passed into this message.
* @param _contract OVM address being called or deployed to
......@@ -1028,7 +1025,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
* Handles the creation-specific safety measures required for OVM contract deployment.
* This function sanitizes the return types for creation messages to match calls (bool, bytes).
* This allows for consistent handling of both types of messages in _handleExternalMessage().
*
*
* @param _gasLimit Amount of gas to be passed into this creation.
* @param _creationCode Code to pass into CREATE for deployment.
* @param _address OVM address being deployed to.
......@@ -1075,7 +1072,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
// Actually execute the EVM create message,
address ethAddress = Lib_EthUtils.createContract(_creationCode);
if (ethAddress == address(0)) {
// If the creation fails, the EVM lets us grab its revert data. This may contain a revert flag
// to be used above in _handleExternalMessage.
......@@ -1813,6 +1810,9 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
messageContext.isStatic = false;
messageRecord.nuisanceGasLeft = 0;
// Reset the ovmStateManager.
ovmStateManager = iOVM_StateManager(address(0));
}
/*****************************
......@@ -1851,7 +1851,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
if (created == address(0)) {
return (false, revertData);
} else {
// The eth_call RPC endpoint for to = undefined will return the deployed bytecode
// The eth_call RPC endpoint for to = undefined will return the deployed bytecode
// in the success case, differing from standard create messages.
return (true, Lib_EthUtils.getCode(created));
}
......
......@@ -33,7 +33,7 @@ import { Abs_FraudContributor } from "./Abs_FraudContributor.sol";
* This contract controls the State Manager and Execution Manager, and uses them to calculate the
* post-state root by applying the transaction. The Fraud Verifier can then check for fraud by comparing
* the calculated post-state root with the proposed post-state root.
*
*
* Compiler used: solc
* Runtime target: EVM
*/
......@@ -170,7 +170,7 @@ contract OVM_StateTransitioner is Lib_AddressResolver, Abs_FraudContributor, iOV
{
return phase == TransitionPhase.COMPLETE;
}
/***********************************
* Public Functions: Pre-Execution *
......@@ -335,7 +335,7 @@ contract OVM_StateTransitioner is Lib_AddressResolver, Abs_FraudContributor, iOV
// We require gas to complete the logic here in run() before/after execution,
// But must ensure the full _tx.gasLimit can be given to the ovmCALL (determinism)
// This includes 1/64 of the gas getting lost because of EIP-150 (lost twice--first
// This includes 1/64 of the gas getting lost because of EIP-150 (lost twice--first
// going into EM, then going into the code contract).
require(
gasleft() >= 100000 + _transaction.gasLimit * 1032 / 1000, // 1032/1000 = 1.032 = (64/63)^2 rounded up
......@@ -354,6 +354,8 @@ contract OVM_StateTransitioner is Lib_AddressResolver, Abs_FraudContributor, iOV
// if that's the case.
ovmExecutionManager.run(_transaction, address(ovmStateManager));
// Prevent the Execution Manager from calling this SM again.
ovmStateManager.setExecutionManager(address(0));
phase = TransitionPhase.POST_EXECUTION;
}
......
......@@ -50,6 +50,7 @@ contract Lib_ResolvedDelegateProxy {
fallback()
external
payable
{
address target = addressManager[address(this)].getAddress((implementationName[address(this)]));
require(
......
......@@ -34,7 +34,7 @@ library Lib_MerkleTree {
"Lib_MerkleTree: Must provide at least one leaf hash."
);
if (_elements.length == 0) {
if (_elements.length == 1) {
return _elements[0];
}
......@@ -203,7 +203,7 @@ library Lib_MerkleTree {
// Borrowed with <3 from https://github.com/ethereum/solidity-examples
uint256 val = _in;
uint256 highest = 0;
for (uint8 i = 128; i >= 1; i >>= 1) {
for (uint256 i = 128; i >= 1; i >>= 1) {
if (val & (uint(1) << i) - 1 << i != 0) {
highest += i;
val >>= i;
......
import { HardhatUserConfig } from 'hardhat/types'
import 'solidity-coverage'
import {
DEFAULT_ACCOUNTS_HARDHAT,
......@@ -17,6 +18,11 @@ const config: HardhatUserConfig = {
accounts: DEFAULT_ACCOUNTS_HARDHAT,
blockGasLimit: RUN_OVM_TEST_GAS * 2,
},
// Add this network to your config!
optimism: {
url: 'http://127.0.0.1:8545',
ovm: true,
},
},
mocha: {
timeout: 50000,
......
......@@ -14,7 +14,7 @@
"build:mainnet": "yarn run build:contracts && yarn run build:typescript && yarn run build:copy && CHAIN_ID=10 yarn run build:dump && yarn run build:typechain",
"build:typescript": "tsc -p ./tsconfig.build.json",
"build:contracts": "hardhat compile --show-stack-traces",
"build:contracts:ovm": "TARGET=ovm hardhat compile --show-stack-traces",
"build:contracts:ovm": "hardhat compile --network optimism",
"build:dump": "ts-node \"bin/take-dump.ts\"",
"build:copy": "yarn run build:copy:artifacts && yarn build:copy:artifacts:ovm && yarn run build:copy:contracts",
"build:copy:artifacts": "copyfiles -u 1 \"artifacts/**/*.json\" \"dist/artifacts\"",
......@@ -63,6 +63,7 @@
"mocha": "^8.3.0",
"random-bytes-seed": "^1.0.3",
"rlp": "^2.2.6",
"solidity-coverage": "^0.7.16",
"ts-generator": "0.0.8",
"typechain": "2.0.0"
},
......
yarn run build:typescript &
yarn run build:contracts:ovm &
yarn run build:contracts &
# avoid race condition between the 2 concurrent hardhat instances
sleep 2
yarn run build:contracts:ovm &
wait
......
......@@ -109,10 +109,23 @@ export const makeContractDeployConfig = async (
},
OVM_L1ETHGateway: {
factory: getContractFactory('OVM_L1ETHGateway'),
params: [
AddressManager.address,
'0x4200000000000000000000000000000000000006',
],
params: [],
},
Proxy__OVM_L1ETHGateway: {
factory: getContractFactory('Lib_ResolvedDelegateProxy'),
params: [AddressManager.address, 'OVM_L1ETHGateway'],
afterDeploy: async (contracts): Promise<void> => {
const l1EthGateway = getContractFactory('OVM_L1ETHGateway')
.connect(config.deploymentSigner)
.attach(contracts.Proxy__OVM_L1ETHGateway.address)
await _sendTx(
l1EthGateway.initialize(
AddressManager.address,
'0x4200000000000000000000000000000000000006',
config.deployOverrides
)
)
},
},
OVM_L1MultiMessageRelayer: {
factory: getContractFactory('OVM_L1MultiMessageRelayer'),
......
......@@ -106,7 +106,6 @@ describe('OVM_ECDSAContractAccount', () => {
// The ovmCALL is the 2nd call because the first call transfers the fee.
const ovmCALL: any = Mock__OVM_ExecutionManager.smocked.ovmCALL.calls[1]
expect(ovmCALL._gasLimit).to.equal(DEFAULT_EIP155_TX.gasLimit)
expect(ovmCALL._address).to.equal(DEFAULT_EIP155_TX.to)
expect(ovmCALL._calldata).to.equal(DEFAULT_EIP155_TX.data)
})
......@@ -130,7 +129,6 @@ describe('OVM_ECDSAContractAccount', () => {
// The ovmCALL is the 2nd call because the first call transfers the fee.
const ovmCALL: any = Mock__OVM_ExecutionManager.smocked.ovmCALL.calls[1]
expect(ovmCALL._gasLimit).to.equal(DEFAULT_EIP155_TX.gasLimit)
expect(ovmCALL._address).to.equal(DEFAULT_EIP155_TX.to)
expect(ovmCALL._calldata).to.equal(DEFAULT_EIP155_TX.data)
})
......
......@@ -13,6 +13,7 @@ const L1_ETH_GATEWAY_NAME = 'Proxy__OVM_L1CrossDomainMessenger'
const ERR_INVALID_MESSENGER = 'OVM_XCHAIN: messenger contract unauthenticated'
const ERR_INVALID_X_DOMAIN_MSG_SENDER =
'OVM_XCHAIN: wrong sender of cross-domain message'
const ERR_ALREADY_INITIALIZED = 'Contract has already been initialized.'
describe('OVM_L1ETHGateway', () => {
// init signers
......@@ -45,14 +46,29 @@ describe('OVM_L1ETHGateway', () => {
{ address: await l1MessengerImpersonator.getAddress() } // This allows us to use an ethers override {from: Mock__OVM_L2CrossDomainMessenger.address} to mock calls
)
// Deploy the contract under test
// Deploy the contract under test and initialize
OVM_L1ETHGateway = await (
await ethers.getContractFactory('OVM_L1ETHGateway')
).deploy(AddressManager.address, Mock__OVM_L2DepositedERC20.address)
).deploy()
await OVM_L1ETHGateway.initialize(
AddressManager.address,
Mock__OVM_L2DepositedERC20.address
)
finalizeDepositGasLimit = await OVM_L1ETHGateway.getFinalizeDepositL2Gas()
})
describe('initialize', () => {
it('Should only be callable once', async () => {
await expect(
OVM_L1ETHGateway.initialize(
ethers.constants.AddressZero,
ethers.constants.AddressZero
)
).to.be.revertedWith(ERR_ALREADY_INITIALIZED)
})
})
describe('finalizeWithdrawal', () => {
it('onlyFromCrossDomainAccount: should revert on calls from a non-crossDomainMessenger L1 account', async () => {
// Deploy new gateway, initialize with random messenger
......@@ -72,7 +88,11 @@ describe('OVM_L1ETHGateway', () => {
OVM_L1ETHGateway = await (
await ethers.getContractFactory('OVM_L1ETHGateway')
).deploy(AddressManager.address, Mock__OVM_L2DepositedERC20.address)
).deploy()
await OVM_L1ETHGateway.initialize(
AddressManager.address,
Mock__OVM_L2DepositedERC20.address
)
Mock__OVM_L1CrossDomainMessenger.smocked.xDomainMessageSender.will.return.with(
NON_ZERO_ADDRESS
......@@ -147,10 +167,14 @@ describe('OVM_L1ETHGateway', () => {
Mock__OVM_L1CrossDomainMessenger.address
)
// Deploy the contract under test:
// Deploy the contract under test and initialize
OVM_L1ETHGateway = await (
await ethers.getContractFactory('OVM_L1ETHGateway')
).deploy(AddressManager.address, Mock__OVM_L2DepositedERC20.address)
).deploy()
await OVM_L1ETHGateway.initialize(
AddressManager.address,
Mock__OVM_L2DepositedERC20.address
)
})
it('deposit() escrows the deposit amount and sends the correct deposit message', async () => {
......
......@@ -13,6 +13,7 @@ import {
NON_ZERO_ADDRESS,
getXDomainCalldata,
} from '../../../../helpers'
import { solidityKeccak256 } from 'ethers/lib/utils'
describe('OVM_L2CrossDomainMessenger', () => {
let signer: Signer
......@@ -157,5 +158,44 @@ describe('OVM_L2CrossDomainMessenger', () => {
OVM_L2CrossDomainMessenger.relayMessage(target, sender, message, 0)
).to.be.revertedWith('Provided message has already been received.')
})
it('should not make a call if the target is the L2 MessagePasser', async () => {
Mock__OVM_L1MessageSender.smocked.getL1MessageSender.will.return.with(
Mock__OVM_L1CrossDomainMessenger.address
)
target = await AddressManager.getAddress('OVM_L2ToL1MessagePasser')
message = Mock__OVM_L2ToL1MessagePasser.interface.encodeFunctionData(
'passMessageToL1(bytes)',
[NON_NULL_BYTES32]
)
const resProm = OVM_L2CrossDomainMessenger.relayMessage(
target,
sender,
message,
0
)
// The call to relayMessage() should succeed.
await expect(resProm).to.not.be.reverted
// There should be no 'relayedMessage' event logged in the receipt.
const logs = (
await Mock__OVM_L2ToL1MessagePasser.provider.getTransactionReceipt(
(await resProm).hash
)
).logs
expect(logs).to.deep.equal([])
// The message should be registered as successful.
expect(
await OVM_L2CrossDomainMessenger.successfulMessages(
solidityKeccak256(
['bytes'],
[getXDomainCalldata(await signer.getAddress(), target, message, 0)]
)
)
).to.be.true
})
})
})
......@@ -129,7 +129,7 @@ describe('[GAS BENCHMARK] OVM_CanonicalTransactionChain', () => {
)
})
describe('appendSequencerBatch', () => {
describe('appendSequencerBatch [ @skip-on-coverage ]', () => {
beforeEach(() => {
OVM_CanonicalTransactionChain = OVM_CanonicalTransactionChain.connect(
sequencer
......
......@@ -754,6 +754,32 @@ describe('OVM_CanonicalTransactionChain', () => {
).to.be.revertedWith('Must append at least one element.')
})
it('should revert when trying to input more data than the max data size', async () => {
const MAX_ROLLUP_TX_SIZE = await OVM_CanonicalTransactionChain.MAX_ROLLUP_TX_SIZE()
const data = '0x' + '12'.repeat(MAX_ROLLUP_TX_SIZE + 1)
const timestamp = await getEthTime(ethers.provider)
const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1
await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: [data],
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp,
blockNumber,
},
],
shouldStartAtElement: 0,
totalElementsToAppend: 1,
})
).to.be.revertedWith(
'Transaction data size exceeds maximum for rollup transaction.'
)
})
describe('Sad path cases', () => {
const target = NON_ZERO_ADDRESS
const gasLimit = 500_000
......
......@@ -101,7 +101,7 @@ describe('OVM_ExecutionManager gas consumption', () => {
).connect(wallet)
})
describe('Measure cost of a very simple contract', async () => {
describe('Measure cost of a very simple contract [ @skip-on-coverage ]', async () => {
it('Gas cost of run', async () => {
const gasCost = await gasMeasurement.getGasCost(
OVM_ExecutionManager,
......
......@@ -21,7 +21,7 @@ const DUMMY_KEY = DUMMY_BYTES32[0]
const DUMMY_VALUE_1 = DUMMY_BYTES32[1]
const DUMMY_VALUE_2 = DUMMY_BYTES32[2]
describe('OVM_StateManager gas consumption', () => {
describe('OVM_StateManager gas consumption [ @skip-on-coverage ]', () => {
let owner: Signer
before(async () => {
;[owner] = await ethers.getSigners()
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -33,6 +33,7 @@
"node-fetch": "^2.6.1"
},
"devDependencies": {
"@ethersproject/abstract-provider": "^5.1.0",
"@nomiclabs/hardhat-ethers": "^2.0.1",
"@types/browser-or-node": "^1.3.0",
"@types/cors": "^2.8.9",
......
......@@ -12,6 +12,8 @@ import {
import {
DecodedSequencerBatchTransaction,
EventArgsSequencerBatchAppended,
SequencerBatchAppendedExtraData,
SequencerBatchAppendedParsedEvent,
TransactionBatchEntry,
TransactionEntry,
EventHandlerSet,
......@@ -21,27 +23,6 @@ import {
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,
......
......@@ -6,23 +6,16 @@ import { BigNumber } from 'ethers'
import {
EventArgsStateBatchAppended,
StateRootBatchEntry,
StateBatchAppendedExtraData,
StateBatchAppendedParsedEvent,
StateRootEntry,
EventHandlerSet,
} from '../../../types'
export const handleEventsStateBatchAppended: EventHandlerSet<
EventArgsStateBatchAppended,
{
timestamp: number
blockNumber: number
submitter: string
l1TransactionHash: string
l1TransactionData: string
},
{
stateRootBatchEntry: StateRootBatchEntry
stateRootEntries: StateRootEntry[]
}
StateBatchAppendedExtraData,
StateBatchAppendedParsedEvent
> = {
getExtraData: async (event) => {
const eventBlock = await event.getBlock()
......
import { JsonRpcProvider } from '@ethersproject/providers'
import { BigNumber } from 'ethers'
import { TransportDB } from '../db/transport-db'
import { TypedEthersEvent } from './event-types'
import {
TransactionBatchEntry,
TransactionEntry,
StateRootBatchEntry,
StateRootEntry,
} from './database-types'
export type GetExtraDataHandler<TEventArgs, TExtraData> = (
event?: TypedEthersEvent<TEventArgs>,
......@@ -22,3 +30,37 @@ export interface EventHandlerSet<TEventArgs, TExtraData, TParsedEvent> {
parseEvent: ParseEventHandler<TEventArgs, TExtraData, TParsedEvent>
storeEvent: StoreEventHandler<TParsedEvent>
}
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 interface StateBatchAppendedExtraData {
timestamp: number
blockNumber: number
submitter: string
l1TransactionHash: string
l1TransactionData: string
}
export interface StateBatchAppendedParsedEvent {
stateRootBatchEntry: StateRootBatchEntry
stateRootEntries: StateRootEntry[]
}
......@@ -2,3 +2,6 @@
// https://etherscan.io/tx/0x6effe006836b841205ace4d99d7ae1b74ee96aac499a3f358b97fccd32ee9af2
export const l1TransactionData =
'0xd0f893440000011fcd00006500000400001900000000603e47620000b67be400000c00000200603e4aef0000b67c2d00003c00000000603e4c020000b67c4a00000200000000603e4f870000b67c9700006301d8914345750baf3939d57f2502c481154979e99b6049e5db5ae3d164376c2c543a5b75aabd10a41dd6305486b3b0a253b5cf247e0615370d7100c13f550d0ccd0189543f0000000000448700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00014300eabe2f5db7a62275de799b9aee03f55f01743563bbee17e722cd2efb0b1fbc1a246ba5723b499d41bd46f7c20c5eab5216e7b5adac96043c2d671de2b47588200089543f000000005ec2631e93a0fb06b5ec6d52c0a2d89a3f9672d6ba64bfa005ce000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000603e49750000000000000000000000000000000000000000000000000000000000000001534e58000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000013ee5cec085b3800000006301f1fc332c1562c12b6224e441f25a10d661e009adabce0a29e41896a85323bdaf6afac2438882c57aaa9e5f137e0365ecc27d27b3a73a108957c6ed4ee5bff8530189543f00000000000d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063017f8f5cdac49108109ad45484957fb00202937447785218f4674332cf42fc6f3e458acda19fa8bd5f14b3209dd235667096cadc57c2c65330b20c6f6db7806a470189543f0000000000318700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000143008d4e5a4defd67ce253e0232e10a592857b6d04b0be36abe1fe4cefa24987df124b96b5840e08d70c536c1a49e28d767289c388b6cfade572989e74a4f28b36660089543f000000005ec3631e93a0fb06b5ec6d52c0a2d89a3f9672d6ba64bfa005ce000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000603e49d40000000000000000000000000000000000000000000000000000000000000001534e580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000141371c5e0331000000006301bcd1432ea3305683f046d921354633808c8a9175fc9b248146b721f4261749d962c7bd3c3b7412d3f8714faf932cd9b44da135b645e8c1d2bb3c44ddbde358910189543f0000000000798700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630169a529e91ff78552b8289c3e42e42a4b69ed57e87e0d3a8739058b34ad1423eb68545b3c1a89683f0b110c66fe0771e0701d24ccdb31de1fef9307b9b3fb7d3401493e0000000000007a4a16a42407aa491564643e1dfc1fd50af29794efd294f093000063013172648acf5ea4cff6ea4893d30aa73983f67773a9ce7084a40e07dccd61cecd0833133b648af4df5ba168234583bc1214eca2687901e6ee71e23fbc42059ad00089543f00000000029c8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630195447e28a93f2dd4cbb2e00f71011ac7e938d280909be4a029054521bc348aae7c226ab01aaec7c2a00880e57ffb25197d05bf0be7c314a91383844824373c6c0089543f0000000000028700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063008070b45c42792670949cbae7e60278d2cd809c9347f26b976a3fc21b3757331904afa0cda74e27122ee02098e26504173be953a608fa01e02b124020e5639d54007a11ff0000000000bdd85eafa37734e4ad237c3a3443d64dc94ae998e7af086c7e000083019cedf37f33c099a4b81e0dc68c4b8d23a1150624d5cca9b938d4aa3c21aaef8c17ff865e8605486a0ecec67b685f1ae38f3c0158e19ed23a986363ccd2ccdb0f0189543f00000000002a8700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000006f05b59d3b20000000006301165d2c69ba74b1b1ee893303b60c48797f883efbcb2019762feb26a9257bea9e7019c339f05e311e79d4799f2bfde302b7c0f82b53ef5b5c9e837a6441e877e00189543f0000000000188700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301d539deb29e2e26049c9168eca08fff60ecf63daf9cedaf27a01d95f6f8b47624185f18f727d156b6ecb4cc3dc75ad29756a1f0447d5c3ab638fd734dd090c9120089543f0000000000258700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630125e4deda3a4e62f65113248e1b3b8a834e6c2d291cedfb71a21e544c3c3217d06ed5d15f56cace11db5e0120e0d5f67a09ca9084cde7728991827851e753b14d0189543f0000000000108700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000143009d2d26177c80858fdba6683fa5d095dacb9634e9deb842286ec79c17d85f0d7b70c617d7878dded6bf0a7ca7fc1371e8bced146f6e7d0da215f67e9e0f92940c0189543f000000005ec4631e93a0fb06b5ec6d52c0a2d89a3f9672d6ba64bfa005ce000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000603e4ae20000000000000000000000000000000000000000000000000000000000000001534e580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000143687087b5b40000000083018e22501ff1c962656f4bfbdab7479cc4ed8c91486c4b19150d03782d7f374467036d7cd2cdba105a4d916dfea70fb8cc2c60121f3baca85c6f2a48909c34d3780189543f00000000001a8700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000008ac7230489e8000000006301019753a47c457cd6cad98373b2092f11a1aea45e9bdd27ab1105609b4c8f87453121c425f6864b1b85910b4c0b82743287f61bc210a46de870b484c07b17cd310189543f0000000000038700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301d08818d883a43fc9f5f64623dca45128884b782fef41d513efa33d4d40139b467641ed4c17071fb9c2bd045023307ed0b23a3fcbbb2f71e81b37e3a7aba1c6560089543f0000000000058700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301c90fa6273070d9ae5af8eea1f687ae507410a258c7861e726d2cf943e00b456f2b3bf75861fe705039179eda8db6ea2781480afd89a98e133f1fe53e379e1eda0189543f00000000000b8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063018b5913d83c37ddc2564db791692072efd3dc8e5fe10584ed96451778789131a14efe012befc05c4ad479a46ea57c7e48f7f64dd7b7c175a48dd445215b2ffa170189543f0000000000418700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000083019d241891335c51ad2cc632ed85dd94ab8eee00da71cd812ea1c40fbcb9c441f56e2f5a7f83e9aa02ae614ee89a4abd10c4332be95a1335be44d5cff6356a903a0089543f0000000000148700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000013f306a2409fc0000000063018dc9bf7f0c853dc0884f0070f8a0413a83f0d9d4d8e5f6f1325deb65684f3485152071dfdeef0a933691bafc850265ccd3624ed00671f68e9cea9bb5b2575c6f01493e000000000000064a16a42407aa491564643e1dfc1fd50af29794efd294f0930000830148b78f666b6d633554897e96f80bb797908467619895cd83eaef7f10b84415d3060967fb0afd49eec5d6759aa9f92c4a5a356369b518210b95c93a0a89ca81e60189543f0000000000088700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000068155a43676e000000000630122d546e170dee47a7952b7c158aacbc38b035f7e183e90a74e7ab91746dbf2bc5aaee131243eedd4781b970b3dd7cc40f6a2bb5fa15684d91e61bf38002c7e9d0189543f0000000000358700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000083016f747cffb6ad3bfaec6d17404d49502ec4bbace678deceb0a6d288dfe353e22b794bb94612e7016f8abf6d15186a3d737536d84f7b43be2ce552a4316280154a0089543f0000000000258700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000c40eb7864285bedec00000630124b164e81e73327bab1cfa0ec9b73e6dc492b4078dcfafb66c11a01134705a9f0d0da528387e2ad59cb192ac51fa9c7b6ebee791e30b27fbf05b956d1c5bc4f20089543f0000000000158700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063011226070ed0b7b181dce226d2ac772265b9831fb462e54a040bd2644ea5092dda6e468fff3a6701d19b413086fb37919617e6bff18a0d9b0791f4bf373d1fbce10089543f0000000000108700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301db43b348b8b2008a01869382c9a91d50ece8adbc0f3a7b2e20b63d55f5db1088624cbc9eb64bcee9508a2352e8e7b03d226c7f06752bccc322bf3af7bc6b68d40189543f0000000000428700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063011a496ca06b19d3b6673c701ae9633e0a986edab74d9fb5b90c9840d129c0ab0967a424474451adb879fe40f49db3ff3505201a5136f88d93a486dffc95a8bbfb0089543f00000000000c8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00008301fbc6c4f4c92ca11aa750c91cd1ee111d69eee808eae4283931a27860599be53344f9763d439d3054e0dbf90667cb801320e802e74ac409610e25c1516caec4ae0089543f0000000000128700daec35af8ff88c16bdf0418774cb3d7599b48a29001400000000000000000000000000000000000000000000001043561a882930000000006301fdeb20b52896ceb6392d4d2f4514cbc187587990d736539a07ca5f59ee697ba84443d3b91894442e03741b67a76afe25ff709e0d318276b404abfc499071b3810189543f0000000000068700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301ebd8f5fda3ce757e953430c5d8c1ad689f2785c7c32f3b607410bd2a1955c3c1228a6bb7dced4e85680ab1d9e2a228c9b0a75114ff636aacd0dbae447e113eb90189543f00000000003f8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301837692935ae091a54c121aaf663f22e86b5c757c1c769ca2be3120f8d20f035a5d6ea340bf095f6a60c36e1e38fb975ad6d895723999abebe38b429637be73e00089543f0000000000208700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00014300bb50bf8b5c9c56a8b515110b42c4013f4e99cf9fcf6874c32e881554562265fa13a1539b789d0364d1e82e7587aad4adb894f4f3320d3517bd90bb08394d42640089543f000000005ec5631e93a0fb06b5ec6d52c0a2d89a3f9672d6ba64bfa005ce000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000603e4cfa0000000000000000000000000000000000000000000000000000000000000001534e580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000143bdb4672850000000006301f96d41806e43f65d8570be44e855870bd2d9b78b512e61264b17105a22267e6338984c7bee80abb6a93f1c4680ae79bc8306cf8598e86d1d25126ce53866c1a10089543f0000000000ac8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630131b960ebca035ef79df2a1e63acdcd5d6e27789b2eb538f9f8232169165473216c51e7febcefc39ca0ed2ab56a316676bff81e5a051651767e0c1fb23588829a00493e0000000000000f4a16a42407aa491564643e1dfc1fd50af29794efd294f093000063015643222807be2acb62072275ed4652806827ecbe9780cf68af1d79a075135c7308230547dd63b8fee254f0a48fae06c7f74b9b1b7c9efa4b6c6801c8d3e98b940089543f0000000000368700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301ee5b0c65075609b827be37d236859f440e182f6c56b3e10736e04f26fab8bf993d3dee5160eb8e6845cbd7198f2afac45c41e93476f9e5b2b401bd1fd6085ad40089543f0000000000018700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301d13c1423797c1e5541f4e730ba701bb6e46bdaa1985cefca4129cb2928baae6c24634a3d4d58d705e65cb6eba169e82ef765a396cc808c273dd4cfa74e37ea680089543f0000000000108700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063019af7bc274b54c2434832f9714315c03020e4465bd283a4b3b25236fa308b5132447f528bae32614e2526c060df7b1139b67fd461b8c0fb688d0517fdc23826ed0189543f00000000000c8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630166b80cd6e820bd1ed6cb92d5a4c0bf178d4078666c1c1a6576f7bdeca1dd3ff40fdbf763431560cf1490d900aff9a42a7b1b0c53c16f93b56039dbc4059de7980089543f0000000000148700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063019eb6f4205fb5ee8009209763b2ee373fcad19b04e9b4411ec7393403e2a815d43b5453d8ff70c0b60ef45917c4789c6afc45c8955cd59432abc2b32bc2c37b5d01493e000000000000024a16a42407aa491564643e1dfc1fd50af29794efd294f09300008301d22b7b46b935bbe378ac97d58bbe84f8b57d9b240f56e9335ed8134214a480c032b15645d64f624cd707628971ece2c06366fd2bbff50cbb40891fe1cf0de2250189543f0000000000098700daec35af8ff88c16bdf0418774cb3d7599b48a29001400000000000000000000000000000000000000000000001043561a882930000000008301f3ecf58a1113fb3387131db1ea3feeb9e2f46e3e48ee1755a1171d3ebcde8ce12d35af50f607a050a62b614074a70d3101211de5f0e40bccdbb49a7206d049ad0089543f0000000000018700daec35af8ff88c16bdf0418774cb3d7599b4295da87d0000000000000000000000000000000000000000000000008ac7230489e800000000630188270db66b963f84c9707597b0c8b7b353042bf61585be66b653a97ead1bbf6a4c373e6831c72dae73ac1a9a6a4e20133f92f57815f0d5a3673d31ae8e7aa0030089543f0000000000168700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301bf64ddfdac64b87045ee2b616ff27224598cd3f74704abdf729f11a5c196436e43b5e9e7fca7b0fa3c3fe149ebf561f14342cfc7c08781a3c85b6cdfa574e0a40089543f0000000000208700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063010beb607a64dcab788fe6c6ad52674b82d41eb785fab9c3c7de72ad96472e9b5c094b2c6b1fe1ff3a4e03a0ed1f0a1a56c9fd10776c61062aa80352c9bc524a240189543f0000000000218700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630152df9af9c07cec85267c8d281bc35087dec67370356cfb4fdcf2afeb322082ad33376cb37f77411963298ba7d127a04236a3d00fe1feb8270c3a42fb090bbe210089543f00000000001b8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00008301244318f4f681328908180b145ca3467846f5cdc40fe6e9ada54f2e7dba843ee414b22618c62cd57adbbc86c5ac079bc69f7e45e87108c53f22ff619501a773be0089543f0000000000108700daec35af8ff88c16bdf0418774cb3d7599b48a29001400000000000000000000000000000000000000000000000340aad21b3b700000000063016715ac2a06087215c0d619320bbbe1f25e9643ec2b7a4dad9813875832360b33592babf1ed961d1c20fd6c9334fc35214ccd20a168c5d53df0b3cfcd40ce46f40089543f0000000000168700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301e3f78d50a6720d493515f979f3242514d8de70a93d4d00caa62b3991fc48e1811c7000a1cec9a13cb59a3bd639a346d6a5834f8f6bfc0b4896eb9f12f52a67750089543f00000000001d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063010d7a3ee143952287c195738b1a238e88fe1cd7c721339f21a2c9fdab99352d862ad25cb00e9d28afad4de9a1c21d8bf16fbc09d47483c928343bd12d90cf0e940089543f0000000000458700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301675ad48eb9a9cb237be82eca75f0f058a53fc42e5952a1009e51523e3e7403ca2bc2086ee05a853fe839bfe3c8123c59373066885185f052c9d6835a6c20d9770189543f0000000000188700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301d104e95267f9651e269ef7ea3a5f0a99c044a0be82bf883c536102918dffed0e38e0e7635046abf595ad5fb1860dc3a13e858bbe1e053436ebf694b7d478f1c00189543f00000000000f8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630170935022f25655a71178c3439cdb3cdc7c2f9857fb65ee2c3af721997e849c55584d650cb4fb8b15f56b708828470224802f1ec9d7fd1afe2f0ad399c5e38ea40189543f00000000004e8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301c5bbd5a56cf2621d47a4acfe87b993fe625218e9b331ab25ea7b07005a40520d70ef48e060693a69c1dcb23300d407658551bcdc270da2a8cdacc91d5cfe25760089543f00000000001d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00014300ff75b419c212ab48019e879247e2aad3a917dc6000e4c0a788af865637b2d8ac422cd67f43ce27c0d0d46c32cdc80409a74029d00c41ab9579adf760671cfd370089543f000000005ec6631e93a0fb06b5ec6d52c0a2d89a3f9672d6ba64bfa005ce000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000603e4e9c0000000000000000000000000000000000000000000000000000000000000001534e58000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000013f1070b03f0180000000630194b0adafa5ed2c77b5b500b13df63ba79d971f383e6716b58df912738766e33f01c11739fd39045d7f2ca3a6ff582391d4094c8910f683a4525b087f5fc5d49a0189543f0000000000a28700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063013a0f2024f85cd3038d8f565ef3e3a7f1ed920bf2340621f1c961c3cf2551a21d234a8d69ab9127dffc3193d3a6af98e5d5f70e166121094ad51d91c93e9e938d0089543f0000000000288700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006300f322135adf6c6959bc012fe5ffb14bb8da6443a80f4e85799ca645ffc369eb09155f05bd65a64741cf089427597a1c35130428c2ecef60dad2b3f1f6da63e3c0007a11ff0000000000bed85eafa37734e4ad237c3a3443d64dc94ae998e7af086c7e00008301ff3ecc7ecf5a169c2b1c4360c63dcf9a8d786d804f400f1bd4bb0fcb9401644f618ca9780aae23bab8a688cf6bf40258e0c44b8089e6bf0989d5aafe9236d3fa0089543f0000000000158700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000001bc16d674ec800000000630142e1b6d027dd1fb9cf43dbb834aaa03547ec356a65a693502546416bd8596250570285bcce68e72ce1bbd7fd3f1b3f8713d06e2b854eb718cafd1fbe3b2ea8ba0089543f0000000000138700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301f343a551e25a7254de23e818c649c30cf974b4f1e7feef4c30eab697f62e71a809006682008e5c25b1816228ce36d77e503e6ce1bdfe3027c6d29787be3ae1330089543f0000000000138700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301bf45e708b0d48a939480d9a16994e7f9b325b55d3a1faa989abca22ec877c799269be034bb0d4dc36a2f684295057e1f1a7de8577114fc660bbe37edbf16f84c0089543f00000000001a8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00008301f4cc4215e75d412821ef74ff9c20918c421227217f7c135f9b56a5bab8411f7061a8b99a11ea37c5346f62e70521f60ed7e664fb4b0f82e4c6a1d3e62c33607c0089543f0000000000048700daec35af8ff88c16bdf0418774cb3d7599b48a290014000000000000000000000000000000000000000000000001158e460913d00000000063013bd03efc62589e24d9d53547a31e18a18e24cb4c820ddbdf2cb3c20323d19ea01ebc043698a7a80ddc632682461ff9cadb95b19ec520f7528dea5bc57017d6df0089543f00000000001a8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301ab3f7535024aaf1bbadd31d33aeedfaf3325d617e8b3b7814080802dd4fe79d55b40120b9f68ece14e45bb347841bd2bad35c0e93f43da8ccf8c0857e4eb1fee0189543f0000000000208700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630145d68cab27031deb7b55e2e6df0df2629c3f07a60a09f433bbd45cd3594855f8508c5ab1201189091b8197d082a7d62abc6e4a28570ef28cfab8bcaf42679a8b0089543f0000000000058700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063013615d80a47143ea2f0102f196e6a2feda49b3be3352c88bb2dec2a9a567e22632fb5097f949412b398ca857b2f105b0f032a69be0f8bd401174a29ae67a8da5a0189543f0000000000148700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630128b5fef41c92369c84e8e742df1253f6eb624cfe5c807de5cb49869a7eaf573416ba55a9f86034b35a11f0c158af9ef07640e71cbcb093cb03a38b75d427ca360089543f0000000000058700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301fc2fc4d368f9f3ef73d9a4114c4a61116f7fd6259c4f172599511882a874993b437960110af8633fd8727372a880194569d295862c321dd1cfd961394e6c21eb0089543f0000000000b48700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301b2c09b2cda9dca2a6c7441d40e24d2a7b481ff76b7759d2a2aebdd7b1b2b3e9d551638ba91ae1511d03be6c90e13d8a931cb46ba48edf1f3c972c2bc6c4b30a50189543f0000000000428700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301ea34d378265585e18e8bac13f67c9733a4055886ddabbd6e467396d964c3dea6361f61256ec3eb9658c88fb1333ae4e74343622e18573a1545ca38af763a4ec40089543f0000000000348700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301563eed133b104dc9d7430fe5082fa1b722b14236053ba0003bff64fb52f073ae6c0c3985fc59c43bc799868fc2222410131a5f08b01dca6bdfc33e9df01c48900089543f0000000000028700daec35af8ff88c16bdf0418774cb3d7599b49741fb2200006301f8773e89f4266cb5afc584dc3e10b4a52b7fefa672ca7f627f758810ed1577ec56cd9dc44571c53fae24ecccd19907791eee25d5e0dd058e5444b02ad91836300089543f0000000000078700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063019ac5f89805eb37c5089ceb5f22bcba2abe7e5da0085adb520d0c2ee4efdf3cab3533f854c08f23f4071c42270b9a074b14bba44a2027e382b2dc0377621fd31100493e000000000000034a16a42407aa491564643e1dfc1fd50af29794efd294f0930000630135234c82daed5f5bb355d7f3f81edc0f5f9f0d4d11365d4e948bd2c02e7031cc291ca1f5c76d8ebe5c1a930a9375acf73f2343cc860aa0633643aecf7420eeb00089543f00000000001d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0001430051884bdaf68fe6c64943a5f3160b5fcd7ef9140463937fc8d1ecdb3c916d7096354237eb3c63430116601c5b887dc44a4d4bce5639be1fa36b91243bb76e2b350189543f000000005ec7631e93a0fb06b5ec6d52c0a2d89a3f9672d6ba64bfa005ce000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000603e508c0000000000000000000000000000000000000000000000000000000000000001534e58000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000013cf0dfffc45f0000000063016e3845ed8e5ae218069cfa7ca98d3ffd86505d0383a3655f8cd46c557bc399c50c81cdfef32719125e7b84b1fd4e7c7535bf5a464a23a86fd9801b146b700d8f0189543f00000000000d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063014c626ef869f07ea4cee4520d5bd24f2c2772a42ce289dfaedaed255446b03c0d2abca85ccfe8233061169c0fcd12d740750f0b65dd707dfe665d4d9cfaf757230189543f00000000000c8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301a0d59339cea6846e08148f7d9daba52402649546b5875c594f2f81209b36794a3e310fa64c0563d53334ae6fe87762426142dfa75b31f1021dd4092f4be895e80089543f00000000002f8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063014bd68c44229ea3cd8c4ff073a9cedbb5ff4387b257b29ce0e18ace691cd97677406b528411c6cd65df52a670dccc257bf290abd1805ba551f151fbd6355bdb980189543f0000000000068700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063016e7f6fe17cb0d1ed5970c49cc0745e3cae90e8e5a2004217472db6a1a1b9ab1124797d03a271feb38e66e690453904f568e61e6623900299f3b8079027fa15570189543f00000000002e8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063015702ed8c76f782970f0579f74a02931fb3e9dca6f3ba750d97d0df7ae60d3d10221ad66f898704bc9987b54d1109da9735c8d7acf2e4a25668134d641ac2d9120089543f00000000000e8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630129bd1bc30f7bfeb956ae7725bcd7144bc42291caf0628d7c8fb75a103f57fd6d4ce006acba63dd5593603368a0b154197d0826b8f9c3d23b93d72dc830c48a0e0189543f00000000001d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000083019cbadc64fb19e3e912ffc2492bf42130d21fb67e4654c11551f3c3dd28e4ded951eae21b5b7f98a7f1e27fdf30fce409363823c97ba03643cbd7e65b964432a90089543f0000000000008700daec35af8ff88c16bdf0418774cb3d7599b48a29001400000000000000000000000000000000000000000000000b38b3bb4459dc00000000630198e2a81ec1c025dc5bcec824708cc1f264134aa6121d90e7c284c77907ee0d0845b88fd9c030bc20002dcbf5a6709af1c42ba4c8ad6e84a55e3e48a1c79974160089543f00000000005b8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063014cc92f396e2790dd5ab926c2e4c1be43000b1098fe7eb2822e0b49ea5ea076f01fb68a84640524d5c525bf989c4a9aff9a02750a81c535c031a8452aed300f420189543f0000000000218700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063019af9087760f24d93e0fbde3bd0e2f2e64fb8d8c36d024fd6cb4a58c34000b2e810e39efa69c5310be3580a4d94bbf069487d65172dc8d39d9bcb75261fd841560189543f0000000000458700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063015402786088ba83e4a68e5f05a7e9c8089c862bfd9c134b9a9ee38aed4a39a08d12d4bf29039655440937a990e14d699101d883246a29b62977aeb9a0e8252f6d0089543f0000000000178700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301bca73ae8381f33c7ec9e0f6a5b518f12854b0061e63a805b3d5941c1516128ce0775f6819e673108d4654a2018289aa951f9340dbee582b9c6b548ec6450149e0189543f0000000000828700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063011bcab33ce1cd156607d9c5a4cefa0d67b82c89462b0d3d4cfd79fa7b2945ee0c14103722c3702f1e247190a3baf4329fd0867d9ceff2bf212e9ff633a89915010089543f00000000001c8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301b9ca39e85b19a0f5f03b067f2bca5be6dc27294107dd4e9ffa9c1af204b6feb235c7e21a385cc3fad8ddfa3aefe7951e2494d63f0a9f057923b39a73008e2a250189543f0000000000388700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063013656758d6e0c9219bb9de64106bb2dcaaec309bc54c0e53993cd37cd9cd03bb15b5102cbef23c0f2e60fcd16e8729059bc6f01546f8dd2c94ee4c9cdae79a0040089543f00000000003f8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630157b7af6659df3a6f211042201fefbaf98780f3b2945398494147dd0a08add01a37fb4cdbccd4df5949381357110e41daf963859999bde4cee42291dadb4fd31b0089543f00000000000d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063012a0b8faa1536ed92b8c2eef51fd0e8a5a82d4a7ada7f2a46ed8e6b6ec29e9a263bd1866f30953a0ceefd68c4a931975bf487ff35fed2b6b3837cc83f0b5486510189543f0000000000218700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000083012aa213c2eaa5a3f0887ba7ab48498bd289bc54a9e59fbff3a8a2a13e13eb9598399c58f16b07789e8c36268985d107091b01fe0afa3faa6fd71baaeef68afb340089543f00000000001f8700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000056bc75e2d6310000000006301da0581ee658208bd90e7e1cc34873f56a3395523a5bdb7dcc1b3245b3cf2b4ad2fd8f550641f2ec4700865d040583e8ff7297dc21f06821332bd5e1aa01ba2770089543f0000000000468700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630114adc64102322203225ec04c16480aa2d7b73e323f4ca68256753726fb81e4450f4318ca276d297a402371994ced771b50eb7409744c8f9182d01599e9c5c6ba0189543f0000000000158700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e'
export const l1StateBatchData =
'0x8ca5cbb900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000017700000000000000000000000000000000000000000000000000000000000000020a16153e7140bd0ac8d077f9a594a5925cd4a16568b27aa8d32132c4f2a0a635685a0371881368047f8ede795d2bbbb05bd86ec4320b115fb22f2fed76db145614a33c9f99602c9a8e8e59929bcdb08d7c607bb26e437a3ef0eaf68672329b1cf2b82861d1ad7b6516ed1552060320224b714cc21f6161f012a5c9f0dfeea60560cc1da3c77cb05da136bc06464958e782f204eb05301de18968cdf0741f6e839fe942abb90acda53506d90772739c1b40bbebdb2340830c82e9fbd4a06af280556426bbec3b6b87131c273400e3b61898d344f59a502e94c7305b9790cb1d7d6967629fd6cd3c82c32342085bd341779df8560220fde3c9dbd26ba4d4c3f1e607984bf3e2c7accadcaf9ea36baaa4b2a1a6f472275e6ca8039d42e478befd6ee4fadbb11ea993409038facf929041e0ceb49559a911b770dd7f2424dee237bcd8524607668aaa247fd4460e09b95b307d42a9d11bed428019f9270e3778d476cd70fe54f255b01aff36232638e1bc94a43c83ccd6958d7220f41abdec3e9c9947989fa5f27a954252b21d3251aa9dddac02e744a8c34aeeadb1e2c92cab32326f3734cd279ade7d37c980e8a83bf11e51fd18f993b6489b97f928ee8647bf0e4b3786404a5a867b784683a0b58ba56174ed6ae18191ed7a1bd7251d33efb8fe68fc788d28d694270a0d28ac258bbb273320cd0a7744490e9120a3a43286988e790abcee4607e715f1ea4da9e32a5fb68908361d84868af6ae3237c165776ee4f76d4ccfcf154202a6a64b830af533b743568f499fbebe0b2e8c8ed2ca2ed67aceef9dbd7d62cb74c180d9ce282b65f91df7399719eef5654f003576664443380a1bf9d02ef7774d624ffc0adee05053a3c62f70c0e0362f17b590eb58cf972ebf4893c102f1a746050e56cf7e27496aa56eb84b778ea284770729dd1d22b0bdbd7522c1ead561a7e090c94712bd58a6fe79db7b6fbbb165486e812a53f7c0ffab7156c29ce53736ed7e31b1ffc952c72324b4fe8e385fbbba398621552dfac3aba74f739740e4f9200af858ca49fc2bbe80e5f14ae48bf5dbeebf3812e3df5d92dcc7d50b384b171f544fa56953be19269dcfcf206fe715b69c0709ea5b84768fb41125a46658d9e5d95203a7589d8053a648e7f8f92a7338c03b72ca2b2b9204b9b69bb902785fc8d3956f10783d98b604d349787a9ad08e54a35d4acce6309b6072744590e4826f7cd9fb6d22a347e2e345446fb42878c19d2f1149cfe8cbf720db9cd0bceb0370046d5ca6f0d5d09bd8685c80b0727298ac24080ede293b40421020de00b59762133b1b01688e106d945d08693bff910a70c9277ddd725dcdaf224191951b7aa710f731bae22430cb9e06a66a9ed951d6cd26cce2af53331071c90c23768f77c97feee69f61a8e172776a2fdfb056c95d4ed37599376260918c10796533b687e33fb9c785b504e64c27ddef60adecbdeffe3827365b934f8868439fe8738ac2cd37a257771d945080de5acaa4e95e4319afcc8039a90ed0fe86c22a204bf3a6c057f31cfb2866cdd5bf479e4fbd8e64260947504b256ec586ec00b1354c006d5d8e78a94a221912293aa65d86f84e48e1b927ace182bd343fe5a924926fa0dc363c2721d8febaee00cfffc3b9af2fbdf0726a608c9339fd374ed9ed62b899ac52e53f5d26493e893af0e73ccd08b7dcb35fa0bcf456f9641186eb46d34fc82725fa226d1644ab075dc73b2263be7e0fc258283ac168128d24fbe6ce3d8b3d5686dfcb79620419ab74cddaeb1a70bd371d7c476ad1f17887dfba85f2a65c85ed7aa78050da9edb9c952db9e16ee106726c3de0dd104c0363a545d0a82d4b86f90ae22bebb26d0249c6e1df5304218b99d9c1831aa8f090889d522ff4dfc85abaaa636fb8786344cf9b8291069b17894a4e8cd68d2213e6ae8cb2195168a71f9b448e9660bbeba976612a67d64e4e241b066f5dc0fe41cac710ba8e6cb23cafa75686fc2d695d46cc95acff9b996744dcf2b11ee31d43d41bb61b089cbdbf88515c2cbdfd434132b72e5fb6e49f50ed4ddd67f65d4836a1a747578a860ec1846e9bc0fa020b9ddfb98089f566c9b803d066279b022b1a847c27e8ddffa7fe15e9e369b88e67aa4458fc77e3c9a73228a45d3f8299a94f55e38a58a572873d5af33afbea7f48bc8013ad5a87871a6d37e082bff20582205f3dc991f6bad09c5811637b4b276ea9715525f92f17c1ded1779d0cc7166a290477fb6c3a15e3d4ff80f1635393a8c6fe713f92732e161442912f2d814379a5488f006ae3c52685667d3d7a4afdfff46c5ad852766b6836b8e4f626760569edc482f9871869d9a6313049de0337beb5bd526ed898348291d7f44fec862f9f12b0d1d91e62939cc9a41fc6387da440c912174c480e0ba34d97fab8f5813215657d4d4292facc682adea5d9d09042c793e0acb678f18287a4f5e1467188c3e4f8159b65ed0f95fcbd132159b4b43c935b15bf85d5e2dfc7952449822ced4608a4474faf0476792de248d17eb261800179f32e8be2385c405b0bd56b5cf9884d360e8e9b83b0bbb3acbf4adb515a29fc33e15209053abe04bf6d52e159434ecaab27c6c492418c5c1761a066f5f8d20f401bcdb091cf273c803205f1bfbd79b8e3fb34bae9cadc299226f9bf0c6be48ff36d8f803e7be8b7e9878645d88eca445ce0f71472433e9629fa6d645fbdfb93266a791dadd59f10a766b05b9c3bdfc7580cdd282ae2e1f416f1ef3f06f5570fc85a18da8070cf53c3f0004d5075c7506ebb776a5c46b47b923620478770348ea4bbd47491af32f24aeca6e065a6070f7fdd9a93052c7293745b033544d191cfdf5506a8834a8a8fbc07ca6342b452cfb96ed91ad1413ff4ee74ab4189f56996c486dba3f0dfc9973095578cc2452d6ff170ffadca25917df624b70873951c3ecdde9e9ca68cc78d5982916a9fc41fafa2d62230ea3ade2ce0158fd4d43c687aaba89662872106874ae586fc5b93e1b45b13d6348fb56be4cc86a1e6bc6dd8e43e1c83b825a5cec533c5306372371b987d02f9822d20a7cde7d4bc158684da5ec67407d4482766effca434dde31fd0fbfa0ee21e7adf3c6c5d535049141705a3344fb7ac2ed5cf51f617edde0fdd6591f8dc590f93652ff078a0536153fea67681318367da75853b0611476b4bea70b596a9618ca5d11046a6f098d38f0d087dc6c39d0920cdca671a349c887cada0c0a2e27bfe7cd3316963779e4a0f5c73b44e400a0b157c61994bf2c64715ddc80c4d2e8b66f88b4f1c2ee254e3cbfdbd6469bb8b615f292ff64f5fb27eb1c5feda880e742537107a1d5839f43fda963899931d3ea2755e0fa3957a103b3fa75436008f1bd84fb2fe20b648fc7cbadb18d9f1eda61544d6d1b058a6ff04462ed2d8fd8ab1a1a58aa1da379b6bd2f861b7e0cf5a62f8eced5dcbb426f14a97ef40922685e2a09ea3e2a66f6d82ee4a76fe3a8c5ca777ed0af104226a1dc7c7c4136536723cb569d2f7611d355b565835612eb4525d592c987eefb07da40c1b6484d448fd7cbec4bfb5803d58c5cd5c50dcae685fe9c8a141a95c26c773a0585016859766293181b0a25c1fcfd2d77bc55fc179def0f71bc4f7b9cad46feabfb62ac4ffcc07ac9288f42786f4707afd3097b7dc4ef42b23090eaa5219623c61372fe37eb58589824edbc0bc4120dbe9e96bed63daec62fe43fc8433b9ccdd416b1f7b8a780c9a0ba91b6e0cbce791e21c0956318d23679a2507993081687b7c8addc14589afda0e9f4a5f35f0ca2b125aeff40dc978f0d62a69f17e8d76cc588f4048a9df1f1dfd7a3672980135a3fbce0db9ec0e519cc011ec4f43fa0158ac6b5b58340a13d56d5b8f50fe281731d1dd4b39b6cc2db4c89de1f8185c9e59c9d65224e026530087e051b016954b2ccefc1f23daddce49cd204e5cf09c142ca8678831c961d4eefeb203ac3e315e792e946842fb8c0106d5a66adba6e2ef7e8a9e3772afe0b7dec69a3dd60988e51ca2fe4d12df05b54ee18e60665c691b4708dab59d2098d6e05fb11c151455d85554bed473ca6b7c5f0a7c3a133d76b8e5acb8fa770f29efb9842be21452f1c9e18d4cc18aad97121ab0587c3d749069bbc5f2eb49f886b7d23dfdf31f49c46d07e2313cc9cda5ec1a936b99b89e4a76b8ef6d24d606f543e3e801556a8f712add4f21c1c8cf4525947c64b4456e469312ad79638ea6d1bcbff764682bbae7df8febf67893712e4026632aaf27b02ef6185725f37eaadadfe1ab79169a63c280d71ab342bfb2ec9d1ef6857a275c5eeda5354185c334647f4c8cfb3b83ea28831ef08c5e72cfc8b0f4646509da56dc7935d58e65e67eb362cd802f957735a86018b987e082379c0fcdc08fb5a1b3e48e8f2e7ba7193503ccfed77563895d603b4c5223f8b49b9a0ba6e6b2471038a6fdb8e4b47a37d3952308502453902ef5dc8c80cdc32741ffe2c3ec2ae3523f6e29f0e9e7e7f8206dc9e3167f33f60b634cbf298634c0b96a328bc50ad2e6b2746c4a2023022161318ae826fdd85b0a89ccc22539968f052a90ec93d8553ed538f333ceddf56a3bcc6a05a3160d2897a15e1cca338e5f1e6630a04113980bf67c855293daef37636e0e6b7c5398cba8a0aa75925deafa0d75918f16290b7ea10832b7950b9f079548d13ad12500ffdd56330ef7bf6a5c71e2eabd5c5b0f42b9531abc4b49df57a77e0e9833039738c013381477e18bc21017080ae1efd8d94470e766510a694b56156d149fb51a9b2d46bfc2690be2485215647c6e6286bb071e715646543c74831bfc2c9a5f5a662933db13351bb5824d59eeaac5d81ea83ec739d9a9df3da4d0ca95acb374bf041c082b9780a716bd07331d3bf3c9f9fff8183a26e8d4541a0440b626737444d708ae57c35f494922e5cd6a4867a194426eda599a0eac5ce980a6bcd6ad6d69f3881dabbd315db4dca1492ec847fb210b19a5240f9086e15cf9fe054735c02e2c9c7316b057228ddf6a20aa3a091cd5d5632c57cd3620d8119f70957c27e4362a24e45447cedca6edb8a2ef759c6dc7012b83b5a6afc58ed4c1bd02ac7c8f63e59417ce8533caf42c967a0b60cb11f2328f0441612b9cf296199245f20f69df79896885522b7b19d7e3b734c908e2fa3772426106267ea638892d93bf252a652cd0e34659d4a1ebbc25a7a5fa2d4d72bcfca7c9af760e415c375553336375715690c5c9fdfbfe213834f6c133b55d52491a9aee7ff76941497e9507f3f0dcd697f0240e98005ef9121500529bb3bc6cd769e700ba13dbdbfd1483357aa6800bb909ac7252e4cbc85e06402720770d0423b78a2cdffbadbb99a6c4b59c017b3a4166afdf5c4255bc9b3057366ed081fbec9f2d613a86ae9e69f5905faf86ebb4289e777807f88ba92a18ef2594249f4115e45f8689356c73bd615fb61ac52ca79e98e84ca300a5e435f5087b01c2d7017c9d30cd13f11651b56b42c99024ef9aec4304b55305f1520ab8fc0f40ebb62dd4c2ea357f989ce4ddd3917ea869934a652911f8da911e0f85f3fa0a28be725992a4d3d8ef2acc1b3112be96daf3865474808a2067d1befb1843784a9fa1a696cd886167b0280b1e9d83d73c37b3188d478ea7ed62da9883c491ee2241255038a007d120294d3b222694c4ca3603c4364482fec79f8c5b6cfb2f9f48ea20b39b8681f1233e5e1a63eb806fa7894177e5fa346cd5d2c54e2c6e8d9433d12445d9d7e490e727e701c8cf725f297a6f4670dde599b2b97079614922a88d57cf6ab4116970c8256bad6a1b7625eb2bf62a03c9675a9c042f367ec27771db0a3a37fe93261b1c2d8b71dceffe79a44196a45d548bd42d2fb02b71e9cbcd7a82a9f043094496f2f9b11c982c1d07ff6596c17d188b9712e0aee992349a9673ed6190f6d6c448296133f6f1bea685d41284a6004af9284ff2dbe1c5c0bbeb3d80904a49a1b42a63eecba80c69b745bfe50b72669d82bce1f3cdaa84202ea87903387ea377c226441e0252a723fd072f4d42865069e02225e6c37cfd88306d6a496e4105013c70cb275a786acfd484c3c27704544eafa8e3ede4044ae9a5e1753a82089af7ef7d692664eff896753fde9d6a2559d6f939069400e1570d0351bff9ca5d87a17b2bda70d5a3d1b70a49571d24ea7bb86cf66bb1626f23cdccc30d9eb8ec4472551e74428750ab642affca47e4f175a571db2d1f513637df004d0ae0562e3f1696354c0596f5af449399cfebf237af08e47c6c7359d5d9e0b405eaf2c9ec8829f9fee7f83de4ae01e4f9ae2055b8e0c6f854ff4e05f2d0b5be441635b6a214b6d9fde9d547e7e7bf9546680b9a841b37d32b97ba710f7ef5a5bce28cc23bfc23f83d08e09273b3141ed139e91a30aaaffe8a007b899671c9d7d31e65ebf516d8f51bbd57c944bfdbdfd066e18ec3e7cccdd46e75a3d11372e2c5e3b1f383c8fa97b77bba8ede2c0d114d5718ff5562e6efed6bb0f97fc558719ab2a146ea883bd4803a5c2e431fc68d11c18cdc0834b5aee2ba0c89cdb5e436c4e1a9bad0975c63d86c957a2afce6c6bb49cf997660622d2adad757707bd992456a6293fccb1814f47ddc3c758d42cdf6391c77c24e8b9fd6c1b065531215f6afefe4fe343b2d571a5c8bb4997d8ab1dadf65ae381edd1c6ef7300882663bb44e210814ffc6cf20faacee48a0216419b82fb5db363c3bc2f1b4358fd139e6d8c1341483964049e9c04fca6cf9c0703c94979a7509f2fee5bf7f0fb548e04cc606e479325ae12673d0e2a5723941997f78890f65a9d4daa710ab5688f8a1529fdb5417ba102aec98798b63aae78d6328bdfe912874c80c28ae15c025be0fc291a4d7888844a6ca951f07314ad23ef9445d2c50907887253c104e38cc97dde20be744c424553783ea4ff87f3fbe3a70a9285ec09b199669d1ed2e91aa19306d9fbfdf2e505312b719963058ca41d499655796f678209fa31308a38f530a126ec71bbeaf609ce77a288ac650de5e38adcd3a35ca966bcef063646ca5c6ede34ab764700083bc2fd85de8e2e7bd5281d2f7e50ab8fa456797131dbd92e6fcd5d076a94bfb090e862269d401839d1c7ebf8e0259503a6bbfe092497671e329f3b04a2f378c2716c1aec5c9a76dd7a14a119c94e18aff3a946d55469c696fef11db9596645964e3f3c4f5b001d717b9f806979bd67bb6a09ea800ecd3f1f2dd23196a9b954dc8b36a328f397bb7e1c9c24730d193923c6edd757cff06649eda0ae8ac5fa79a03685f796ac35885d9e254a441091756a4bb4febb188b44cd31b3dda73e8f98815ea5c187483527305971518c095130af522ddd0bb72ff897b0c631e8fa07dd61da7d276043ad22cbe334fb28bf2ff926758572a4a852a4275d98f56c2b2671dd7bc2b0444d5da6a42b9fa5339853f3f62c1cdc81f5cf8d318b8e622de861a642c42c6ca8cf87b25cfd9b15f966fb42903fb05584f70eccc7bba96e89f70cf1f7a4e6afcaf44c738e5fd1f9dbac961d5775f2fc9acb8d3ee0db5d1a21d600f25c20f5c92eaab06166d3ab1e2c96b024bb087dd917559ea30034e781b0a2e546cf48ff93677d071f7197c5055e7102eb3caab9494e1cd951e89625e30c3b3cdf22ebff9d0074d16a5f15a8256f3d094fc3fded20c052e3b61de7a6cf3e784edc344a3ccc2c3101de110d9ba4a1cff5ec20a7fc7524041955565e05dacfe3134eaf8ed5c87ee3af43b06dc3f465034bf2ab822d1fc2878d8c80ae09769c1c120b9dfc734db72a82bf8d625aead7cdc073f9da2d2dc1f7fdbdeb20078f22f5b13581ff8d8485f181f347cad3aaa19e32abbe18a50ec3fea633465298735f11819fe380421480dfaac513bc766be29f5bf9363db935b96d0d57c7a342b53fd4fedcbc012a03992883453801591b110f7407331dd37ff003826006491118c09a38c5ba5ad11060b4f456b98fed3bf08fbe056d8e34eed716c3d04773012cee8cb0706a1d0cdcf7fea82d3431ee4f30231b5a82d4baa107368bb2b382d48c61360a1b6c4cdc22de348677913011422679133586aa0fa045b4431e1cc31752c1abc17218f0300b41350c1bb00be1045bfbf933707c54010124b883b7013254ffe24b21795763608e53538e2a4a29bd8c652395a8bf2ea0dc958f003c62d10152b39a120309de4342e2fdb0dcea8d28659cfc09bb9b839894dcc27112612bf238b7d59eee89c1e1b4a43ecfdc415428e17ca1b8af7cd0556f5a8e56261ee4c27a9ccbcf1ffea388e42127ea0e2b68516b21ec79adb3fcbe36336d93b03168705c467746d466f74bc9bea78473caf6c658bdb2d960652c399b640a58cfa9c3942049f56a0f2058a03e36ec1c60cc758e2f2ba0e624ee7a5d0dbd7067d7c6ba6b0e0aa4037b5fd414c64ab44e0a4621a24a8c104a444a8b3ea21830ebaa79adfe08463c0d0e49e4af43dc28e5e75a1fa338903bdc0c414a5b22d799370a0f125b018b9b4d9065a0882892665372e63d20a7b8031c5146a049149af30b084ee8788a47698220e264a86c04dddb9f392b6beef7bcf0de1f2e4a5a9caf43cf5c842d77d9eba3d95cda5d71e0e8e55a1785c45840eb1a8df044a6ab7c406719cbca2a6babd8655234f0b4bae9a55002abba8c80db9a414ccbb46e4679d778c49e641e6c4a623ba909d3d915f5f0203ebcc5359a002e163bae0a1fed8f5ea6f530f98fb3c1d608519a0490e4d8bc9856a52bfbab79bf752862ea7d6d448c2dca45438234360dff43c4de4a2690daa91fb347be6fbca9eb8220296a2101c88f0c6e19f2a9172db66dd36a2a6a962f740d66c46ea1558a201fdeba7b383b26a6afc87d676287d682e585d54a69c34eeab6b0e49cbe4d9e3e9eb6d33b09cab6589ed29e5496d15fd8d37b22d057212a7aafbc705eaba0cbb246126601c59165654099d50c4ca59796a1b36c61eb425e05df556dccd41e701209422b65e538a452a0f52885593181ddb82e15dc07afd564914b6032a68b9439500fe3b4ed679ff68b6e704c97e6927959f42da1b39f29d77f7aa6cd30415586991c7ed42ed0998746998ae28b3df1f8951c2c32f5c89a35afb9e954ae68c90dc532ac23bef98a066a6e681a8ab0a32f2ef523b28276d80b467992a070fe4ffbe8ee09eed45b829661a2a8a152c158ddb90728f9d512f38c42ac0f8efeed5ab805a557524b30901c324e69af0e1649992831b3cc5de52f1766341394fb5df556d74d4812826cac417b22b09f46ba0e380e47146623ca445c88fd0d9bc93a4517413eed4cba273b12f6ae874a4a6bc9b8f7ad91e70027d16f0ea38c1d72b2f05d610e1f94b26643b3dedeed1729db27ece245fc56a460ed983ae2db9eb0d2762d87fb12a45da1de4860ab6076d4ea96f4e330b24876abd780fcf1c3088670f4e42e4460ab1b184fef13a3230c82f0669846131f5fde3c209f09a3c3bbc683b1e3b2ef00f5904e90aa90cee102ffd081cd11fc74d3f8c4ae7a7b6a25dee1fa2221b30be6ad5657e21c8e8d2d3963a6b958724fb9bfbb70a61a26d7087fa7cc7be36a2b141fd42b8daaf64f8c7e7021d890d8fe3d52b795b6e6c549acd76dbda2e03cad4102113fb4bac8e42951d92016e1106292f135e4f034902781e34975d231bd017c3e7d4e3c30359fbbe79b9130ca3eb0c836d54556217460700a96846649300bb0f8528b29127ed01b647cf82d72f42470cc5a2500bad79f5761226fcdf25145d6c9f2baea49fc802dcb9612fbbe7420d0512aebdbc897db0e64855c828cdf0773298c36f49f8ba66e249c93533d704dcd3843caf3d45c832c153c493097eef8acda5da5ce753df21d078e866672ebae4211aaaddea7b0e0997cde9df247f2dca2501698751a8e0fa0a7461ff3d4d2d259a414ae78b4014051deddd464d6d77fcff171ecbde719d781a623e9f2ba319b07fb217b54e66b9fd54e0335ac6d0d634cd039e175d92704575f2a69a9d54de18143495fa9a819300848b45fc8759702d1fa38d2c40989d718a63cb89c65001b1c5e39ac5360525384f3b2ab6e9a26d16d05b7ceefb540f675cc2c912bd55845132a196964c3eeb4608510c697a56496c00e7ccbf6281fb861318d8339cf6aeb1a16e4754b4a865990824101f4d31f978d0087d4e2699ad51057ca10d907f286010f8b6191f86878797d6098b40d8aacae68477f18f19eb78d5c228903eb747ee141801d00bddd0c101d21e7d6fdba072b6079fbf838e52e33c424417f33edbff7def59cdea33e1de9d2ec4b4a9cf9a44b9066a9bbd073672da75e93f71b3e1aa9fd69ad0c45c000ba17f4d1c64c80d877dbe424c4c81504f6de7d599a2f060169195f59ee2df00ff87510d53c7cc9c7b1da8ff1e6fad04dafb135add30bd8b32a4ef96a6b0e6c49ed8bb81563812f48fd94b38060fa40219d559646132fe9c3f0deb86e81e0f46fe2ad40c1d4b47b330ea7c2e4d70a781e976bdd86fa9f2e889d604954334307f642154b38cd48392f2350d13e56186e4106881eb3e4dd7e6eaacac7b704a3e12af8335ec08e723f6c90951cc68da5e720ed58933e650f93aa78019006d4d10a5146d6ee412a56ca687771fb7b8ad1fe9bf368117abba071c87b62a81cbf0c1465036c3c840b534ddb9d2e210cfb83767f125012b697f3e7131f7b93d8ec5cdf8b53423fa2d612941d280b929104490fd9f9aafd2d1473f11a5b53954f2eeabbff6cd85d5dc4307026e6e90cd60d9211bbadb3f091a4368a6c3218ab1e9d64defbe6004e5d2463ba48e0b43dbdd138be9009e699b243d128be4ea5bd28b24540f3e9701f5f8d9f043a411c541d9d15027ededcfa2bbfe634fd0d5b5bbf2cc5584cef21cce73ab4eb84983bb79b78208153132e784f044d5042c1b8cfe06c4587d2b30627bfb0fdfd1db6925100583c661bb1632907f3f3e522332dbaeb6e7dffbe4eca8b5db76d42e5ebdd2c8cdf90adcde0079a2429d79f6c1309bc55e354c2a87ec2d3f366f91a5cced192e193c5fd1cf2c7b1fab9ff8ad8b32118737e23416a2e57af5f2b2ca3b041ac99420f9f98a9b6e09bcdbd58ec23bdf60ddbba1afd551b02d94c9cb4e6cc59c12683881226381dd382f1f13776837acb4939f0bb73f6b20b064e51e4b7e00be53cc51ee80039dd15988a2683ce7ce25e8ce0031eb604cf0cafca9c4698d0cf02c5770d94304e0ceb9a86b750ee6da7179389dafd8f4aa4fd63551c08c6f72d2ae851357f42e801abe7ce54325fb4500a0211e11ce85d12dc661f568217093c7530e624b1205e4c111b6c62698d90d617c5e9ee8624a74ad37e64210c89207e184fc1ec339deb37d7e6bb676cfaed806dc09a804f1f4e4f4e8aac3a118fd1ac33e2da4c48e77e1c151fc6a3351d2c90c80ee385efcf27ff1d0cc29d5e77016f20fa9fb524970e549ec606d571eb46e320fd7b1706b80491ddea7a307521bfea93baea892434dcdbed9a0a862798d119dd4a33df9e19f45ac06a125cbe1f35668977c706877dcc7afe87df3908b410dfeb83dcb5be35b2edfbaacc02e18689deb6e075dffe214d88b6f91f41232207d99de54eb304cd1781721a47c7f5bab1051a8e82ad9973ca6172ba48f0bc3acbfe2547d15f28ecbcbecf40fe3691f27757343764d9e36f568946e7ad5846c09edca85fed685107916efa60bf380f3abdbc6864506ae2745230763909d2a4028200914e719478b979fa8aff9d8611f149c0f7e38216b582e5ac3d42d75450f05e935aacf4aeab539a4636fcd3ca9823f5a4498e8a908f59edbfa4d18d8d0f9af5a513d213f88594f9bbd60169d9439f65751cf768fdc9591939067dd648410f59a9e51969cd1e96f957853710978fa5ee5e0faa2cdc3c84e688f6efa013f076d95a0aedf1922c4a3d8b3780c7a8881dae23bce8ad71f2c250e024f55a0fc3b752a50aea95668f8861b9456b1b351378af1cc9bdfdccb892775def1f501ccfc596bbe864ff68d30b3cf1064bc31397e61fed2858593c12858b40acb6b8b2a64cd5f0b0b7949079220f4e36cef17d3bb7c4de84a5434820861f8ec35cf3625b5639ddd983f97c49fa7e8a231e40e91a39088c22a95431d926eed60b94f22f24c87e0e9eced837c4e5f2ab86859625e0ebdd5ab138a0459f6b6fd542928fecd7e30b07dd5d6459206cbc0874a4f7452d365945fefe7d80f50365bc40d7be27ddc1df0b168bfff690d1f12cf5c2f060640afd0ff39fac543ffb5c992c86369b5d7265a55205dd3b6e36899d13a4454a3144688b0c59fa2df98f3066fae4689e36c6b0e818ee25ba5c725278f059026000ca2540706c4da56e4d34aec9c244d38108d696653e0106f0b93e83fc16af3510c2f3707512b5992e9de13f9635e751c5b6325ca8536de726534be66934139af510a8cba7f9e2b036f62908c79d92c148fa7d8d0fc6e71b69593eaa99976a20d6f54d3378635ca9e19d84c19af82e6b35f87ccd7ce267a8d5f7fee18d1f99f4a0632653796c7596b41da964651e682ce34ea1e8625c3da93f3bc5d45666442ac5595942477dd6e55dad6312299f3afdcb05e0c5d7b08feca5c6dec0e4ad2d6230e57e397ba8cc5676e889d454707cf1bc522fb0e422f486909dc5b2c256c5493a8bb686431ad54525733eeeda77f97a977ba64c82d83c23f87859b19570d015a6d4ed7c397f1f4c6fc585c4e35ebb5fd69240eb8b74c84724eeb6a7af6c58f2a88212d927d8fd3cdde85e190f87baca9cca14875ad520ecda4d178edff3ce2b9dd77d61b56df04781cee624755e494408fda0610253387473c1cae4ea866eb7aa474a76d4dc8e1591d6dd087aa670e0058ddf032b7e2d6f16facc02cad068fc9782788899619fd558f0e69527d81bb2e5eb9182254ed56977f740e38b58630cac01dd809c3f1e3db7f2080b0cac37180164d00aec0f07f535abb11c51cf1b5bec776d5d62867933e25af17810c7e8e639ad6d0bd5b2a0b206786258f40a736cdd9e6f3e6a865df69d42c12e32378f738811cecf71cb040d9aab3af61097f8b6feb739ff62c73f6ad39c4a9badc717449ec893d7e6464d3ea64a417b391244f054fe22430d08e7e8c987f01a9a765785150b052e5382a20a14f693d31395e4c7049f81b5556f8629530a4260c0ca08f9e78eaca2e6aabeb80077b366d0d20ff20ef3cbb31a7b50c4adaa1c66f390beab2f30c1bd914230449ac3aeca590f64ac81f755e201faa97a858d14eeaa54eb1f59ccf345130bed5fd4e8b82254bb2934379a7e4f17d29954fb423a354212aca42fe3b2e15f3442a991787099c176e7d7489f2bcfb23e6cd17ef795b8598b50b1e7eeb734bd33631af8ed49e0943f552b95b18c437e7c6393d05952924272edec5fecb41a20582a1c8c1f76569eb42c649fadb6858853c32f2a3786a28423681b416e0bfd0aabd53ce81a7c8ac45655c50f9f47d465759cc99094530b8c2dabe878133f3931d19c7114b0e57d81b29213ac674b0a9e33ebf404d666a77ceaf31d5cf4ca1b64226c1012daab937605dfb4fe1fbdea0a95c8e6563adeba3ce403b1d6574d3d4e2300aea451b142ba46fc6a337645e6939193edd21211029b97de00ed70ea365d8637fa2a4fff058fb055680360a947c4db4de722bf47bfaf1f4b3be4cf31052d2384bc3915d2dcf7b7d236cf4355b1b632b13be897ab0b50d5f4ab3f81287468f5676165ad651c1e138c10b81e1b446c2fc213eb08415040bd9bfd5585887b33f6639d2056019a802f1fc44ccd93feaea5efa2f7783c96058a62e632cbb79adac30b7c8d25e22f2f5dc2ba6f091b7f74993e6fc3e61c73600474bb0bc172a7f40f7fc13d84f648ec8b5dc0f8beaa0a7dd70ff94eaef25223e532c9ad95536db3e3c839588dfe8b2342ec9d7d398e6a0b5afc95edc9c7ddb8a10de4b0d5c54559b6d471625e7b12ca38145cf21a17cca0931ba8f97c9d795aa70448722db456bc8a229d56e31298b745ce72e8edef52f73ec305b6d57bc7fbe63891c21ee80d93346dcb7ccf5d4ef12d046449ef448436229b87fd300072f9697e4e6634683823062916b709da8bf18c5780cdf20d57548d509ddd8debeb96bb119311a0dbaf436f872bb021b20bb7e896c02a80d84d81a898fe96ff4ac60c9d5872c3c1047795e2c51feeff22722e87743b99e5679152d0050facad7a4c07aac273397918c6afdc179ae29b4958560fef7a2c7cc096de7aa91227a2fb35d7e7a9bb123e9759e7f0ff5f4a58639ae86eb3139747828d570d900ba51755bb09fa8ec4526a6c42bc02cf011c0083ee0eb2767f6e33449499ecc08b2082b2b01c1d39b5cf2051e6af0b01a5ac6b5dd996f03e6bc035d10f18bddb6551defc3d76aec606128fbcbedd75fd72e8aaea286ac2239e11c6cd58ef3ffc6a04b9667263e81e4de10eef57d9c00a52e7fbb2517a8d2103da19d5301664519a204f7084df24c69b9e430987b8fc85a7e1fbe5a65e8d75aa47f0a2378c4cdc88a148598ae43270dc46789e01fb07a582adff706b5098c4c55e7c34c57d024fb8e24ffcbded7aecb4e1d19b784913dbe091ac901a0ec03df4583e6d4860e71257044b2705f278533e734e997948fa894448576027b078ac9e68d0ab998f4c523cd6b094d3ca22313e4ecc674298dce027df4c9b4501f2a24cd41a02e5ebafbff6f04fe4cc2b12ae3175f3af36ff6949b3266c5e29b8815a24340188f33b3bdd43cab16421e592b35a0ef5cf197c49ea4cdacf04becf9b09135be23b8229217e01a813478202d0ef37f4f808016af5b8ab98342b3f556c79f8c6a8c43a265f646a1d411cb453d13c24325ade8d436cdfff23b723d4eb448306640d3852688a841b209f26c97af9c9851c099a1d00da8167b1920ed0dd2784ea9f1998d97e475604ee0eb989e456890e127f1d25a347a148721f0a4f601f6d151c57c8d43fb5dcf099a4efd7e4724c974d5f486a0cd797a618477552951a0071b7be1c25334eeac803ef6d154c336fefd0eaabcdb4a2ec4132273f4c5a5ff704242098ad06959301382988fd2dff00958b0298b2113f2424cd70372f96fb013e1982c1277ab599c2f11aaf76d182fba0a81aa861a7968c5cb208e05ce53678cb800d138987a89779b06e518c9277e21c7b7b29890a2a4ef71778c6f3f58a7d943c3bd6f0551646b503c4851b270cde08aed3c6d3580f5c55af81424a0f73670a334da76370521f95e0fef6670ead27f238040bb594e880736517f60b27e1df83f71b5371e8734a08ef1fa7f1fe3dc93d2785bec84ce5d6929cb1da1d486578f663a99e0fc27cbd3c4224b5671f3cf0879a6e912b356dd1c344763d2c519357ba96e7829a82834325862f5f64369978702861469259e86fcdddaa305e5b64e2544e40de03847b0df47c4b246e649c7552044bd993200b3e07136c72db8eee00b4d08371748378ce456dbacc247ca793bf452cf0fff6e7ce733776c8c9da4bf6a6847221f9488bf1065728ef53ee07d7cc17d089c08484861c43451416004a3da9e8436606031cac6d9b953426158331888b04bc308b13097ea7150d89966def2dbbbebb3ecbedf2edb41596f9e111f11b9f6438d5980b1efb1e9df0398839799869eec4b9b14311949981688318b819dbdad59401606c9aacfb743bd2233c05c16e2aeca7c1ed4c98c4aaf094c01d1b96290d1152dff4277bcc881bb7727e1bc12e364ac298eb707ed628f1fe55b55c85118b28e7895c422a7c7b0b2cea4b63218d7aa155dbbc4c916db3bfb521008df4ea8590fb04e9fef23781ca5b9336f98aa99a815cd950131c8aff8f698458695bc03b359cb513183ead3096767e6f38796ba3e0dc2930f66cf1d2fcc8de4068fa3b33e22435cb6a39ccc7a5a92a4b560e4caa34fb4bd18dd4a8940d6748fbd309b95ed98955ad4535f6a8599d80b413937b4e36b92e8b7e8251315ba9e64744ee51a83e6d5b99bdb2cb210aa7dbfc79daf0c43ad8aa6d25219f252169f55821f8de3a65a63eff1dba22f34a8c47cf0739e31f1773e36d06e1228d5ba75b50a5835768af32654a11e77832805ffc6113c9c6b4e67766400fa5ca6141eb115577d2d518577bee7f2d51c3b1906e51ba80587f755ebc962f5e5ef3aed056e5c9f59097a1d82b4f3c0e2f22caa1770d5b3ed5bce07025370939dfde5664dad1460cb12b8f43ae50fb3b2787e2811c59ee46706f9caa19aa173e1c70242b694285cc02f6925b1783af50c8dfa0848c7faaf30ca9476f9ff37b27970064a952dbf1c98882099907ab89a8ecb6cfbb3ca6422a6680da7e4f24a1c50cbfe6362f12eb0ad014d1715e4bbb810ca8892ea40c2e03096586657035a866c0b6d098fdf55c679e1b83af9e182e2226040455ba705b4c01bb1ae97189257ae197ac25e9ea70f52a72ee65dba4134da88e0460c367b8d5e85f0a7e43deb703c01609cc383aca31c9feaf71c8ed198210751c3133a60778abb78c1fa8ea459ae5a844d7e845a5a4ab149a3dd83b51014385b638847174dd82db684d8dedef336c7faa07c46de87a29aa8c7e8dd8117d25126be83fb314369ac9de5d2eff765ed8477da939dc26d68abbf15914a5deef9253b42a515a79c71d0e42e43958383cb6fc1048374f4bef04547779cf0c432602ca21322f15cf0df5cd2d59ecd09d1516f9d95f2a9343947027ab3ba27e48166ef56fdbd4f74a93ffeadd34cbc9049515360399f153c35d976fffc3d1b63d581b952935a37306930ab741c9e1eb704a25aa3df3a7e47da597fab699ad64c8cdb56b055e2bbf11a9a0dd9a54268d447a7c97374556122191c8662ec1b926e83d21ea7ba9aec5e9a852e81083e74184cba738e74cd9c3fc54efbea1f0626c7c02cd65c90b529a10a7e39138a19e992736ad2eb359c5744c5feb42285ef79dc941401c97a9bb9db7b91d63ac1d753b58413860be7a964cdc7f4b396e0a3f2525af55be7bcfda6df089e66a7ca872ceee23fe7428b84ec7a24aecdfec24f4160959a5f66b68509881c742df2a42aa2dd087ae77822dbb906d262dd2b612f7b19293ebb4c2f29822da6703451b99736db1fd6957a9614e5044f8074f37ca57128c040dac22d029e862c4d89dc8b88f25df0f4b2a46d77f78b8fbaebba74b0c40718814c9bf4d2bedb7ba91fce687cba745c2c62d9f8a633d6972b60f17ce6b3c370c8502608df3e3c065ae61e44d255f36b924736d57040cf481df3100f8258acf4f4914c6b56a1cd8cb77d22d88b33146686d1329812b19fa39bb3fa1afc6ecb2668d148eae2bcda295a62e3f65dfc1cc61812e56fb6ed258d3aa29d8bc5242c60ba45cb77d73df2e41e3eb875c27818494f223b759ce96387cdbf2ac653bfcfd9c9d6091d8fd098332edd406a9f001b1cc6039d30d0bc486645c005acbcec0cb17197f77c610cd3e8ea50757ed5f9339bddf00ffd45d1bc6ea19abfcfa59ad91300879a44c7e5a7fc8363628090303f42c7e698634b8ef2c6560ee6f7615d6fc819a7c77002a6951c49df051911f61fa7ab45afc1be87627c9f5dc3636934ea29df5a4504b3174b55b4a442b780655a2f1f41c67f05d0c4990d45f2d1e2bf059371a70865c8b6ecd880044eeda9fa253d175d591d751b7071285f50efb8bd15c4a4d4a98b36066f2888a0f0588ad72783efaa621c1aa5d0739aad11dbe574c75784643bbf5b734732787ca8e7b42d6401591103fbc9c8939bd807f2d3ff496a11125bd9770363891ba34ce32b750eef9b45c0d48f409bd8f29d1f40025956df43ca7fc4c77725a6d584c50b8fa982b748e234f335f0ff46b806829096b23952a901482a8245569fda4c22ea2cacf0088d4613690cae88d56b1fee3c5af69e753d26c2ab41acd37436325f924ae29527c75502d6e25ea4d77ba7048a34e93d1fab02bdfd13096a48af79cd43d2ce3bda5f4f810503d3c28509217649ec0da8d513fa7f7b8956ba2056e15f2fc4761f37a805ba7c6930512a53ea6ec9f7c351a41603c7e6a1d6d44741eef58aea127fde141de6d1aab743d1d1bc91c3637409f407704291162dde7c1411ce25b7513757ddada345ddeaa933fe31d14cd47cf725d348d5b5a704caf6e4ad45e74d11785b3c3670cd5cfccad8a97c3847085460f1dfeca096eb0395e4dbac857618c9779a89d76695e32eddfa63df28dc81454ef5a6667221a2b246e5108711b17bc665551410e36c482acee2055c33d16de02e521cd445c9a8dfe911c392d7c8968106297bacc424ab00873412982a96d5df52f73a4371d8d189ee8362790cfcdb240ac7fee45621351f6de354e6d480998dc702b168e016a025969745606391409fcfd34964a884f30380a326c785428cd23c2b1e37a3e80c54e1485db990015b77b9d38c86d1aa3b424cb50c1411060d116d2708439a4d2e6861854d86ea2c7d8eae3bf9690638c7ed5ef1c5f41b80ad150eae93096def040a3d6c785e0e300b02e8d4ec1fd7ac4bcb13b897e306af58b29d8bed962e7b6d8caa6637848f51686ce85e793bdf4b0635264a0914aea5c620049ba2fd1f75b344fca8328d1ef893b549277e83bc5b5d665473aeaf5d4d1fafe6d167408278dc0a1cf7e430857d67e67445aa96eaa29482ad5f6a01bc9eb212a560d401cd1c845ec1d8f998e366a0b3c45e674e190ced2e0f50f249b628bef881a395153d9055e7b63bda19b44b472f9f29ba028415124e90eb30ff870401b272bfea8945f358cfda3dc65e076526b060e97997da19b29a89acf4f54620de667f59f2dc1c1b144b18bd34e40e93b057ed6b719082be2d847498a2702ceab39157139cecfeea46866add60f470c25adc00ef6dbfce69e359326ea725fcad726cb1a022f6e506c0f0f9744e22cf6427c392afc6861b97eb863bfe62cdbf25bb71b2d7d0805760e18098c0fcf7d2de8170b1f7dd2b0ffa333daaa7b2bfcc0c09dc228cce55d6c4b0dd36d8b1f1c18860cc6027667e6dbede6ddfab2786c7626bb721e8e9ce08f991fcb0392dffccdb4aa9bf49077442e81e82b47f80cc005f6acfdaff7a81c702e0e0b3ff128d9a52e815aae267af33cb1389088467ce4d6d13f5d2ae5d58492dadedd5630fc9db3448b5a7392676f8c7b2478a5a2e54e3d46d816b8675fdcb8d0370b41bc3e5071cb7114ebfc9cafcdf6dbc49ca72642207149970d96c1960c3e43d27b8ca8901912d83705e204251a341b6660b95e8ed74a701de0bac6476e47be479546feebee3a9b790b005414403e12987f8e422e8112ca256b20a47baec8b1ccab46d2118ec7af172e5551f24b27142334442b6bace845ffeee3aee6fce8eff54d7feb01b288e95fd4c195609d7df1935773325ab9fa24111206ae7096b4e84fd6d4ce9060608d03eeaec851cccb5d94a2c067ce9ae20df4405e4590ba2606f823e60e74555474f11c3c4bf4ef71077d348a90cb070a2700e74c18fc25683c2fdbb67c48f496296476e19d192731b79f1992b1725efa498a479cb6ae718f069b7e871112c0a53679919508f98d24f5a3b9d7ca75b9d89cd20970ff2aa1e2b9b08242609111a16ec523f6276451d41a3e7d8407487a24d0204fa7e0992517f77e7fda9ee5323fb96d61b4cf68e2c83d1d70317e75c05a895daf1c59268d99ac8951bd35b0236030df61074018ff83e75357fee6a8abed1291392cbdbf89013bce4343a2fb36a67e206c3505eab47384fcb8535ce2a410c6f19670a0583524fae7ce63232aa539d0f8623f81448c0be56f8557512ef0ccbefcf811ec47b76d0e54c7e3d337029e21709c6f1a72d76c290fd55c654feeb41cd0eee0ebb1555ee2b109cfaf4213abefaf859557ccdd79e0a4112c6705dee7ce16dd6fff4497816dbc3aa94955aa84ff84ffd6e13c872281f72556d32fa2b3a38d76e4bc98b70c0fefe34c1f26c4f43e7f756095d88118a59ef54ceb7bd0df02bf751c79ab097fd289b155aaf423979f73f4fbed7450572183c689c8e6a7e2a7fc6957e75ea45c3a0c174e1dcdcab75642ab4b0d146af194d4efebb4af17c41c9a26ce2dbe01b9774e61212ac5228eea727b913859ebe890c681f5384811a39355fa787210247b9e385a839697d6ba40e0d4238b6fae73171f50972de6e8b9c476308b98782b81c0bc8bb0cc42e018854ca2693a83b3deac0af3395e78ca2d01ea6912676a61fb82a87fa42befadaa2bedefb66e88ebf05028b7f7bc16104f742c98821ceb9841ec3c239e4746ab8c5472cc0bc3b3247cdbb18b96a2279d0d1af1b954ff2c1525900714fe12ddbec5ccb9aa5baa1dbae2b629b312a374b20d86503bbfe474d9bf1db4c4990ff235cf777910325119f4c39884e1a3d74e5e3c58dbf8376f737fd8d28ad454c8491b0a1473ea936bf5139a51aafe344709f322e3618a94a34af278a6c380456b080688a11e21a8ac31ca07bed843ec16f010a68bc2d441dbc5b9c0b66b79af22ef9a742d07b17c91f6aeae44eaa9609e6c946785479696877e943b7eb2de6817de47dd6e3f3ce5676c1be10769e133a8074b61aaaa28f226bbf66bbf1f49a7fe2d78bab994dca022c5d29c4296600f0b9061623b82b0fe2ad39e04fec095aafcea9a914bda8c17c5fff0dea3a15e04282519fa20c851736802e54aae80080cde21c822d96e7fa743ef098247537cfec314201f3d550659c072def81258928fedc53303e077cf77f20bb40d408201c9e69d856bf6d3aec26d64edf80a5d6f56ea47d160a9ec57d800c38103277a986a1d24685bf02043d81d1fc8ed084af6c249a74417acc7fbf1ffc658beee6fa387247b3e2327cdd8ae7504df9eed1fe2bc5d8ebd15366967950689b30e5ebf5e5b7313385c2b7f53da33598d3730cf92a8f45825c35c5596b40200d823f3cdd5be14e5b3cb4af1ee524490ee3432e5d7a46992949ff61c39bd42e88104282846d86a8df44e00019cdce26158f504da5037a5a448e1591e834fd8ca871ce99d496b9f4300cce3c8cd92485b07a7d196ece563bfbc905e76a9488e606aa9c2458b3233161a1cc94cf4d5f1ef37f461c3024c5d5bcd095ec3e9d520421feb8f9d081e1222e3b02b7f2fa58c6550172a4fa451b7735039ca334279d2c5db3bb89095d761ae33623f48ac6964fb073584d71a44fc5c0c6e956a1f338794dc9e1c06845613ead6871e75270e95a16bb9fa49f7b14d95fe1f4194200236cf26df1b11507d1c79249f2b9f9e022640b0b87747f7a50e4f7608d1dbc1e3487f7c3dcade90a79ba2cd64cb2a22b8fdd9b4edf185a380930a51a36798441280ff148b2f7c01ea01fdc2ce5dc0d4177839dd7a2292124df62c56c0c4df4d9d2eaa6058603e385857e75e678f096d121b247f43aa685edb0e311d03b9f78b6425f4ec4410ac58ff3f5de2cd2c5273808a9868431776ff0fb40e7aa5aad58dc9f52f567b1df41263d7613375e8e5294f16ae8c3476b43c59ef47191dd02532b2c12fd1584d72766651660dcfcdb11844ff1d51937a280096519143b29df97a4744e0081cf1d70937a2fc934971c6c86f56d21831f12849797882fbe8b28ed9f7388a6c7e6ddcd2b07673934d2ef33ef6e7800208304fb9e65f37a4ed9c4b618c759e4029ddb86bd116717883e959c0a803edbe4b477bb5ecd59352e78d8a187c0468014861805ea1d0cbe0c8f4a566c98d7193465ba3cbc5fa25d756277aa5202b14daee87368746e59b20f8efc9d60e183341fc5f80a7d9a9ac74eac6dfe3d14da27280150e9c9197b210a2c73ff400a64ecbb67da91e073c022b5f42dfe0fe1d67fbe2fe1cb53f2f1326fbb9457a27bfa9adcf85e2c3fd4c43bec70f4b3bc1018d99236ad3aebc08e2b05fbf71ea607de74cb9cd938cd389261eb5876a6105c899d1f09434dfbf8e40469c06ab2dcfe43f2509b139271198e106697a2cb21ffd954beffad87e3f5cb7c3b6b5cdc245f1525573fc5bfd9d0efae1e8b1349a7920f353a394d3f3d701947b4102c4b40c88702b5f4d761c10a75b6758efb4549e7ab6ee0ea5e3e93a17dc9bcff5ed2cfca8f29c81858d0c110161d15d7fdff4696ef9f4f1d7fde62d6c498d08fc70389e866c18ccd84aa73a0913e11981247baeed0183400f772eea5ac023ea1fdaff399330cca59f8812dfb51a5b1dbd02714f7641b9581706ef5fed59b1a6ff2694d8896a996fcbfb7aa4d46cae110987ed5d327113204e1184153cd0d8c0c24e7c9bea8ab66e1082a2d42fb6e717068bb0400bc19d1da4a4976d7a9d7589421df3e5d4566ea7de9a0a0879f707caa418a8c091a0cc5d402274a34857fb1cbe626f51d53ffa0a227717c71bc75762427e5edc33ea8aec891c9397a986579c303db541c653b85151e45a1f721733b7a8dfb37c86ba5f5f68e757d78547bc74eb7f55d1722dc812b7117ff89b59d1aa22bb9cd6a7d46ad23e570e4a9beaac59a896f9b7773b6d4f5ef14c8620de3849118c8fcce6c4ba21a5cab125d524a69b20d625c30f97b67a81c5feb6b52c9699995372ac6904b29a4695cf6d12ad3e13aa05ef9ae3420c2aacdf08aa4a5bf92dceb7ccd08621db2fa659d37a2ca3adec26c30eb996a9555e17e5831ddbbccd89ce8b6e59ec51d31e4025661c6aa8367f1313d37ccb4b48186e5c3f2d707f046f17c00a48ac3ad86b5e1166cdb37f13311e99cb4c47ff97495a9897063605f24678011f4f96c64cde8d3db3d8ef45ccb45f973aa6c509aebf9277c1c233a3a54f8611018cd5e17a9990b581db6c2e5640da07bcf5557c95df2978f1cb0d558bac5652a8213ecc52fcfbb5972d00a96d34623f4a0a733eaee9722c24fd87e2fea8af653bf03148a0116bd739732b0a93cfe8e4860c011302a4b41dcbe06f2b825c06b51629fb4152e8d80a3a83c109d67f288842a340088ecac2afe0f8c8a0f05d6617e8b5da2c24a1a1827a2c15a060473dbf66729c24ac7ee293388e0ba24ffeb2604a79db32f5e6c8f9e44afa995517d79323d4c9ef2e874dbe5c1325da5e1d9a8a8ebf8f352485382dbfaec6e4ae8bc322dc6517edd916c12c67d9c0e115a10730d833d6aebaf0e2f8358f0435153c2c3873dc2d74fcc7f2882c4b11dcb93f9fa9fde6227174354057a8f77c88558de90cb5fdea44dc518e89473c056008e27904ec4161a81e9368a9b26b5af381444832b9f64b37b987d85f89e3d39bd353b75e3b0f92abaa2b9ce963bce06dcba9545000085bfe4fdf978ac3a6363186b2efaf411e740be28e156cf708568bd3e4744cf481f33bbfcf7a95d4d3b180c4ddd43d2931ad351bc2e86ba155449708923953412450f3381f6a022544739bc5b24a8d242628c37d6f87d3d56aa3e1962d06b36bf52cd8524c0130807fdd49734f67f3c901a2f3b924b1ae5c9c570ba56c6808fbb54eec9c4ac0f8d9c4988838fc97998de9ae1cecf83f4c2f4c31c0e218fd26be2eb3a0dca4859c28bd81ba57c816e81c73ba76d0299116e940bf4777363a2cb7ed9f6424e821f3ba0910ba86c93db44c8f598e29cddbd1c3a059895b22f7b684e8bb4014b2a2048405405817c313ef1c95ae9ae05037506a508e4df6d012f90018145fa9577a605f9b9afbb3b'
import { BigNumber, ethers } from 'ethers'
import { expect } from '../../../../setup'
import {
SequencerBatchAppendedExtraData,
validateBatchTransaction,
handleEventsSequencerBatchAppended,
} from '../../../../../src/services/l1-ingestion/handlers/sequencer-batch-appended'
import { SequencerBatchAppendedExtraData } from '../../../../../src/types'
import { l1TransactionData } from '../../../examples/l1-data'
import { blocksOnL2 } from '../../../examples/l2-data'
......
import { BigNumber } from 'ethers'
import { Block } from '@ethersproject/abstract-provider'
import { expect } from '../../../../setup'
import { handleEventsStateBatchAppended } from '../../../../../src/services/l1-ingestion/handlers/state-batch-appended'
import { StateBatchAppendedExtraData } from '../../../../../src/types'
import { l1StateBatchData } from '../../../examples/l1-data'
describe('Event Handlers: OVM_CanonicalTransactionChain.StateBatchAppended', () => {
describe('getExtraData', () => {
it('should return event block and transaction', async () => {
// Source: https://etherscan.io/tx/0x4ca72484e93cdb50fe1089984db152258c2bbffc2534dcafbfe032b596bd5b49
const l1Transaction = {
hash:
'0x4ca72484e93cdb50fe1089984db152258c2bbffc2534dcafbfe032b596bd5b49',
from: '0xfd7d4de366850c08ee2cba32d851385a3071ec8d',
data: l1StateBatchData,
}
// Source: https://etherscan.io/block/12106615
const eventBlock: Block = {
timestamp: 1616680530,
number: 12106615,
hash:
'0x9c40310e19e943ad38e170329465c4489f6aba5895e9cacdac236be181aea31f',
parentHash:
'0xc7707a04c287a22ff4e43e5d9316e45ab342dcd405e7e0284eb51ce71a3a29ac',
miner: '0xea674fdde714fd979de3edf0f56aa9716b898ec8',
nonce: '0x40e6174f521a7cd8',
difficulty: 5990647962682594,
gasLimit: BigNumber.from(548976),
gasUsed: BigNumber.from(12495850),
extraData: '0x65746865726d696e652d6575726f70652d7765737433',
transactions: [l1Transaction.hash],
}
const input1: [any] = [
{
getBlock: () => eventBlock,
getTransaction: () => l1Transaction,
},
]
const output1 = await handleEventsStateBatchAppended.getExtraData(
...input1
)
expect(output1.timestamp).to.equal(eventBlock.timestamp)
expect(output1.blockNumber).to.equal(eventBlock.number)
expect(output1.submitter).to.equal(l1Transaction.from)
expect(output1.l1TransactionHash).to.equal(l1Transaction.hash)
expect(output1.l1TransactionData).to.equal(l1Transaction.data)
})
})
describe('parseEvent', () => {
it('should have a ctcIndex equal to null', () => {
// Source: https://etherscan.io/tx/0x4ca72484e93cdb50fe1089984db152258c2bbffc2534dcafbfe032b596bd5b49#eventlog
const event = {
args: {
_batchIndex: BigNumber.from(144),
_batchRoot:
'AD2039C6E9A8EE58817252CF16AB720BF3ED20CC4B53184F5B11DE09639AA123',
_batchSize: BigNumber.from(522),
_prevTotalElements: BigNumber.from(96000),
_extraData:
'00000000000000000000000000000000000000000000000000000000605C33E2000000000000000000000000FD7D4DE366850C08EE2CBA32D851385A3071EC8D',
},
}
const extraData: StateBatchAppendedExtraData = {
l1TransactionData: l1StateBatchData,
timestamp: 1616680530,
blockNumber: 12106615,
submitter: '0xfd7d4de366850c08ee2cba32d851385a3071ec8d',
l1TransactionHash:
'0x4ca72484e93cdb50fe1089984db152258c2bbffc2534dcafbfe032b596bd5b49',
}
const input1: [any, StateBatchAppendedExtraData] = [event, extraData]
const output1 = handleEventsStateBatchAppended.parseEvent(...input1)
expect(output1.stateRootEntries.length).to.eq(
event.args._batchSize.toNumber()
)
output1.stateRootEntries.forEach((entry, i) => {
expect(entry.index).to.eq(
event.args._prevTotalElements.add(BigNumber.from(i)).toNumber()
)
expect(entry.batchIndex).to.eq(event.args._batchIndex.toNumber())
expect(entry.confirmed).to.be.true
})
const batchEntry = output1.stateRootBatchEntry
expect(batchEntry.index).to.eq(event.args._batchIndex.toNumber())
expect(batchEntry.blockNumber).to.eq(extraData.blockNumber)
expect(batchEntry.timestamp).to.eq(extraData.timestamp)
expect(batchEntry.submitter).to.eq(extraData.submitter)
expect(batchEntry.size).to.eq(event.args._batchSize.toNumber())
expect(batchEntry.root).to.eq(event.args._batchRoot)
expect(batchEntry.prevTotalElements).to.eq(
event.args._prevTotalElements.toNumber()
)
expect(batchEntry.extraData).to.eq(event.args._extraData)
expect(batchEntry.l1TransactionHash).to.eq(extraData.l1TransactionHash)
})
})
})
......@@ -75,7 +75,7 @@ const getOvmSolcPath = async (version: string): Promise<string> => {
subtask(
TASK_COMPILE_SOLIDITY_RUN_SOLC,
async (args: { input: any; solcPath: string }, hre, runSuper) => {
if ((hre.network as any).ovm !== true) {
if (hre.network.ovm !== true) {
return runSuper(args)
}
......@@ -127,9 +127,9 @@ subtask(
)
extendEnvironment((hre) => {
if (process.env.TARGET === 'ovm') {
;(hre.network as any).ovm = true
// Quick check to make sure we don't accidentally perform this transform multiple times.
if (hre.network.config.ovm) {
hre.network.ovm = hre.network.config.ovm
let artifactsPath = hre.config.paths.artifacts
if (!artifactsPath.endsWith('-ovm')) {
artifactsPath = artifactsPath + '-ovm'
......@@ -144,6 +144,5 @@ extendEnvironment((hre) => {
hre.config.paths.artifacts = artifactsPath
hre.config.paths.cache = cachePath
;(hre as any).artifacts = new Artifacts(artifactsPath)
;(hre.network as any).ovm = true
}
})
......@@ -12,4 +12,26 @@ declare module 'hardhat/types/config' {
solcVersion?: string
}
}
interface HardhatNetworkUserConfig {
ovm?: boolean
}
interface HttpNetworkUserConfig {
ovm?: boolean
}
interface HardhatNetworkConfig {
ovm: boolean
}
interface HttpNetworkConfig {
ovm: boolean
}
}
declare module 'hardhat/types/runtime' {
interface Network {
ovm: boolean
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
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