There are 4 different mechanisms for following the status of a transaction. In addition to using the Boba Blockexplorer (for L2) and Etherscan (for L1), you can use:
Third-party analytics
A typescript messenger
The Boba watcher-api
Subgraphs have been migrated to Goldsky. Please see subgraph.md for more information.
Internally in all the services and also in the gateway, the status of transactions is monitored through a messenger. Here is an example for how that is done.
import { CrossChainMessenger, MessageStatus, MessageDirection, } from'@eth-optimism/sdk'constmessenger=newCrossChainMessenger({ l1SignerOrProvider: l1Wallet, l2SignerOrProvider: l2Wallet, l1ChainId:network.chainId, l2ChainId, })withdrawalTest('{tag:other} should withdraw tokens from L2 to the depositor',async () => {consttx=awaitmessenger.withdrawERC20(L1__ERC20.address,L2__ERC20.address,500 )awaitmessenger.waitForMessageStatus( tx,MessageStatus.READY_FOR_RELAY )awaitmessenger.waitForMessageReceipt(tx)expect(awaitL1__ERC20.balanceOf(env.l1Wallet.address)).to.deep.equal(BigNumber.from(999500) )expect(awaitL2__ERC20.balanceOf(env.l2Wallet.address)).to.deep.equal(BigNumber.from(0) ) } )
The system is documented here. For example, to get L2 transactions between two blocks, use get.l2.transactions:
get.l2.transactions
Request Body
{ address:"ACCOUNT", from:"NUMBER", to:"NUMBER"}
Response Body
[ { hash:"TRANSACTION_HASH", blockNumber:"BLOCK_NUMBER", from:"FROM_ACCOUNT", to:"TO_ACCOUNT", timestamp:"BLOCK_TIMESTAMP", exitL2:"EXIT_L2",// True or False crossDomainMessage: { crossDomainMessage:"CROSS_DOMAIN_MESSAGE",// whether the transaction sent cross domain message crossDomainMessageFinalize:"CROSS_DOMAIN_MESSAGE_FINALIZED",// whether the cross domain message is finalized on L1 crossDomainMessageSendTime:"CROSS_DOMAIN_MESSAGE_FINALIZED_TIME",// when the cross domain message is finalized crossDomainMessageEstimateFinalizedTime:"ESTIMATE_CROSS_DOMAIN_MESSAGE_FINALIZED_TIME", fast:"FAST_RELAY",// Whether the message is using the fast message relayer l1Hash:"L1_HASH",// L1 hash of the cross domain message l1BlockNumber:"L1_BLOCK_NUMBER", l1BlockHash:"L1_BLOCK_HASH", l1From:"L1_FROM", l1To:"L1_TO" }, stateRoot: { stateRootHash:"L1_STATE_ROOT_HASH", stateRootBlockNumber:"L1_STATE_ROOT_BLOCK_NUMBER", stateRootBlockHash:"L1_STATE_ROOT_BLOCK_HASH", stateRootBlockTimestamp:"L1_STATE_ROOT_BLOCK_TIMESTAMP" }, action: { sender:"EXIT_SENDER",// The address of L2 token sender to:"EXIT_RECEIVER",// The address of L1 token receiver token:"EXIT_TOKEN",// L2 token address amount:"EXIT_AMOUNT",// L2 exit amount, which doesn't consider fee receive:"EXIT_RECEIVE",// L1 received amount feeRate:"EXIT_FEE", relay:"FAST_RELAY", status:"STATUS"// pending || succeeded || reverted } }]