The Optimism Data Transport Layer is a long-running software service (written in TypeScript) designed to reliably index Optimism transaction data from Layer 1 (Ethereum). Specifically, this service indexes:
- Transactions that have been enqueued for submission to the CanonicalTransactionChain via [
CanonicalTransactionChain.enqueue
]. - Transactions that have been included in the CanonicalTransactionChain via [
CanonicalTransactionChain.appendQueueBatch
] or [CanonicalTransactionChain.appendSequencerBatch
]. - State roots (transaction results) that have been published to the StateCommitmentChain via [
StateCommitmentChain.appendStateBatch
].
We run two sub-services, the L1IngestionService
and the L1TransportServer
. The L1IngestionService
is responsible for querying for the various events and transaction data necessary to accurately index information from our Layer 1 (Ethereum) smart contracts. The L1TransportServer
simply provides an API for accessing this information.
See an example config at .env.example; copy into a .env
file before running.
L1_TRANSPORT__L1_RPC_ENDPOINT
can be the JSON RPC endpoint of any L1 Ethereum node. L1_TRANSPORT__ADDRESS_MANAGER
should be the contract addresss of the Address Manager on the corresponding network; find their values in the Regenesis repo.
After cloning and switching to the repository, install dependencies:
$ yarn
Use the following commands to build, use, test, and lint:
$ yarn build
$ yarn start
$ yarn test
$ yarn lint
We're using dotenv
for our configuration.
Copy .env.example
into .env
, feel free to modify it.
Here's the list of environment variables you can change:
Variable | Default | Description |
---|---|---|
DATA_TRANSPORT_LAYER__DB_PATH | ./db | Path to the database for this service. |
DATA_TRANSPORT_LAYER__ADDRESS_MANAGER | - | Address of the AddressManager contract on L1. See regenesis repo to find this address for mainnet or kovan. |
DATA_TRANSPORT_LAYER__POLLING_INTERVAL | 5000 | Period of time between execution loops. |
DATA_TRANSPORT_LAYER__DANGEROUSLY_CATCH_ALL_ERRORS | false | If true, will catch all errors without throwing. |
DATA_TRANSPORT_LAYER__CONFIRMATIONS | 12 | Number of confirmations to wait before accepting transactions as "canonical". |
DATA_TRANSPORT_LAYER__SERVER_HOSTNAME | localhost | Host to run the API on. |
DATA_TRANSPORT_LAYER__SERVER_PORT | 7878 | Port to run the API on. |
DATA_TRANSPORT_LAYER__SYNC_FROM_L1 | true | Whether or not to sync from L1. |
DATA_TRANSPORT_LAYER__L1_RPC_ENDPOINT | - | RPC endpoint for an L1 node. |
DATA_TRANSPORT_LAYER__LOGS_PER_POLLING_INTERVAL | 2000 | Logs to sync per polling interval. |
DATA_TRANSPORT_LAYER__SYNC_FROM_L2 | false | Whether or not to sync from L2. |
DATA_TRANSPORT_LAYER__L2_RPC_ENDPOINT | - | RPC endpoint for an L2 node. |
DATA_TRANSPORT_LAYER__TRANSACTIONS_PER_POLLING_INTERVAL | 1000 | Number of L2 transactions to query per polling interval. |
DATA_TRANSPORT_LAYER__L2_CHAIN_ID | - | L2 chain ID. |
DATA_TRANSPORT_LAYER__LEGACY_SEQUENCER_COMPATIBILITY | false | Whether or not to enable "legacy" sequencer sync (without the custom eth_getBlockRange endpoint) |
DATA_TRANSPORT_LAYER__NODE_ENV | development | Environment the service is running in: production, development, or test. |
DATA_TRANSPORT_LAYER__ETH_NETWORK_NAME | - | L1 Ethereum network the service is deployed to: mainnet, kovan, goerli. |
DATA_TRANSPORT_LAYER__L1_GAS_PRICE_BACKEND | l1 | Where to pull the l1 gas price from (l1 or l2) |
DATA_TRANSPORT_LAYER__DEFAULT_BACKEND | l1 | Where to sync transactions from (l1 or l2) |
To enable proper error tracking via Sentry on deployed instances, make sure NODE_ENV
and ETH_NETWORK_NAME
are set in addition to SENTRY_DSN
.
This section describes the HTTP API for accessing indexed Layer 1 data.
GET /eth/context/latest
{
"blockNumber": number,
"timestamp": number
}
GET /eth/gasprice
Defaults to pulling L1 gas price from config option DATA_TRANSPORT_LAYER__L1_GAS_PRICE_BACKEND
. Can be overridden with query parameter backend
(/eth/gasprice?backend=l1
).
{
"gasPrice": string
}
GET /enqueue/index/{index: number}
{
"index": number,
"target": string,
"data": string,
"gasLimit": number,
"origin": string,
"blockNumber": number,
"timestamp": number
}
GET /transaction/index/{index: number}
{
"transaction": {
"index": number,
"batchIndex": number,
"data": string,
"blockNumber": number,
"timestamp": number,
"gasLimit": number,
"target": string,
"origin": string,
"queueOrigin": string,
"type": string | null,
"decoded": {
"sig": {
"r": string,
"s": string,
"v": string
},
"gasLimit": number,
"gasPrice": number,
"nonce": number,
"target": string,
"data": string
} | null,
"queueIndex": number | null,
},
"batch": {
"index": number,
"blockNumber": number,
"timestamp": number,
"submitter": string,
"size": number,
"root": string,
"prevTotalElements": number,
"extraData": string
}
}
GET /batch/transaction/index/{index: number}
{
"batch": {
"index": number,
"blockNumber": number,
"timestamp": number,
"submitter": string,
"size": number,
"root": string,
"prevTotalElements": number,
"extraData": string
},
"transactions": [
{
"index": number,
"batchIndex": number,
"data": string,
"blockNumber": number,
"timestamp": number,
"gasLimit": number,
"target": string,
"origin": string,
"queueOrigin": string,
"type": string | null,
"decoded": {
"sig": {
"r": string,
"s": string,
"v": string
},
"gasLimit": number,
"gasPrice": number,
"nonce": number,
"target": string,
"data": string
} | null,
"queueIndex": number | null,
}
]
}
GET /stateroot/index/{index: number}
{
"stateRoot": {
"index": number,
"batchIndex": number,
"value": string
},
"batch": {
"index": number,
"blockNumber": number,
"timestamp": number,
"submitter": string,
"size": number,
"root": string,
"prevTotalElements": number,
"extraData": string
},
}
GET /batch/stateroot/index/{index: number}
{
"batch": {
"index": number,
"blockNumber": number,
"timestamp": number,
"submitter": string,
"size": number,
"root": string,
"prevTotalElements": number,
"extraData": string
},
"stateRoots": [
{
"index": number,
"batchIndex": number,
"value": string
}
]
}