Commit 19eb157f authored by Hamdi Allam's avatar Hamdi Allam

Legacy block processor

parent 7662a3b3
...@@ -37,7 +37,6 @@ func Grouped(start, end *big.Int, size uint64) []Range { ...@@ -37,7 +37,6 @@ func Grouped(start, end *big.Int, size uint64) []Range {
if end.Cmp(start) < 0 || size == 0 { if end.Cmp(start) < 0 || size == 0 {
return nil return nil
} }
bigMaxDiff := big.NewInt(int64(size - 1)) bigMaxDiff := big.NewInt(int64(size - 1))
groups := []Range{} groups := []Range{}
......
...@@ -7,7 +7,7 @@ import ( ...@@ -7,7 +7,7 @@ import (
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
geth_log "github.com/ethereum/go-ethereum/log" log "github.com/ethereum/go-ethereum/log"
) )
const ( const (
...@@ -34,6 +34,9 @@ type L1Contracts struct { ...@@ -34,6 +34,9 @@ type L1Contracts struct {
L1CrossDomainMessengerProxy common.Address `toml:"l1-cross-domain-messenger"` L1CrossDomainMessengerProxy common.Address `toml:"l1-cross-domain-messenger"`
L1StandardBridgeProxy common.Address `toml:"l1-standard-bridge"` L1StandardBridgeProxy common.Address `toml:"l1-standard-bridge"`
// Pre-Bedrock Legacy Contracts
LegacyCanonicalTransactionChain common.Address `toml:"l1-canonical-transaction-chain"`
// Some more contracts -- L1ERC721Bridge, ProxyAdmin, SystemConfig, etc // Some more contracts -- L1ERC721Bridge, ProxyAdmin, SystemConfig, etc
// Ignore the auxiliary contracts? // Ignore the auxiliary contracts?
...@@ -69,6 +72,10 @@ type ChainConfig struct { ...@@ -69,6 +72,10 @@ type ChainConfig struct {
L1Contracts L1Contracts `toml:"l1-contracts"` L1Contracts L1Contracts `toml:"l1-contracts"`
L1StartingHeight uint `toml:"l1-starting-height"` L1StartingHeight uint `toml:"l1-starting-height"`
// Bedrock starting heights only applicable for OP-Mainnet & OP-Goerli
L1BedrockStartingHeight uint `toml:"-"`
L2BedrockStartingHeight uint `toml:"-"`
// These configuration options will be removed once // These configuration options will be removed once
// native reorg handling is implemented // native reorg handling is implemented
L1ConfirmationDepth uint `toml:"l1-confirmation-depth"` L1ConfirmationDepth uint `toml:"l1-confirmation-depth"`
...@@ -103,8 +110,8 @@ type ServerConfig struct { ...@@ -103,8 +110,8 @@ type ServerConfig struct {
} }
// LoadConfig loads the `indexer.toml` config file from a given path // LoadConfig loads the `indexer.toml` config file from a given path
func LoadConfig(logger geth_log.Logger, path string) (Config, error) { func LoadConfig(log log.Logger, path string) (Config, error) {
logger.Debug("loading config", "path", path) log.Debug("loading config", "path", path)
var conf Config var conf Config
data, err := os.ReadFile(path) data, err := os.ReadFile(path)
...@@ -113,9 +120,9 @@ func LoadConfig(logger geth_log.Logger, path string) (Config, error) { ...@@ -113,9 +120,9 @@ func LoadConfig(logger geth_log.Logger, path string) (Config, error) {
} }
data = []byte(os.ExpandEnv(string(data))) data = []byte(os.ExpandEnv(string(data)))
logger.Debug("parsed config file", "data", string(data)) log.Debug("parsed config file", "data", string(data))
if _, err := toml.Decode(string(data), &conf); err != nil { if _, err := toml.Decode(string(data), &conf); err != nil {
logger.Info("failed to decode config file", "err", err) log.Info("failed to decode config file", "err", err)
return conf, err return conf, err
} }
...@@ -124,31 +131,34 @@ func LoadConfig(logger geth_log.Logger, path string) (Config, error) { ...@@ -124,31 +131,34 @@ func LoadConfig(logger geth_log.Logger, path string) (Config, error) {
if !ok { if !ok {
return conf, fmt.Errorf("unknown preset: %d", conf.Chain.Preset) return conf, fmt.Errorf("unknown preset: %d", conf.Chain.Preset)
} }
conf.Chain.L1Contracts = knownPreset.L1Contracts conf.Chain.L1Contracts = knownPreset.L1Contracts
conf.Chain.L1StartingHeight = knownPreset.L1StartingHeight conf.Chain.L1StartingHeight = knownPreset.L1StartingHeight
conf.Chain.L1BedrockStartingHeight = knownPreset.L1BedrockStartingHeight
conf.Chain.L2BedrockStartingHeight = knownPreset.L1BedrockStartingHeight
} }
// Set polling defaults if not set // Set polling defaults if not set
if conf.Chain.L1PollingInterval == 0 { if conf.Chain.L1PollingInterval == 0 {
logger.Info("setting default L1 polling interval", "interval", defaultLoopInterval) log.Info("setting default L1 polling interval", "interval", defaultLoopInterval)
conf.Chain.L1PollingInterval = defaultLoopInterval conf.Chain.L1PollingInterval = defaultLoopInterval
} }
if conf.Chain.L2PollingInterval == 0 { if conf.Chain.L2PollingInterval == 0 {
logger.Info("setting default L2 polling interval", "interval", defaultLoopInterval) log.Info("setting default L2 polling interval", "interval", defaultLoopInterval)
conf.Chain.L2PollingInterval = defaultLoopInterval conf.Chain.L2PollingInterval = defaultLoopInterval
} }
if conf.Chain.L1HeaderBufferSize == 0 { if conf.Chain.L1HeaderBufferSize == 0 {
logger.Info("setting default L1 header buffer", "size", defaultHeaderBufferSize) log.Info("setting default L1 header buffer", "size", defaultHeaderBufferSize)
conf.Chain.L1HeaderBufferSize = defaultHeaderBufferSize conf.Chain.L1HeaderBufferSize = defaultHeaderBufferSize
} }
if conf.Chain.L2HeaderBufferSize == 0 { if conf.Chain.L2HeaderBufferSize == 0 {
logger.Info("setting default L2 header buffer", "size", defaultHeaderBufferSize) log.Info("setting default L2 header buffer", "size", defaultHeaderBufferSize)
conf.Chain.L2HeaderBufferSize = defaultHeaderBufferSize conf.Chain.L2HeaderBufferSize = defaultHeaderBufferSize
} }
logger.Info("loaded config") log.Info("loaded config")
return conf, nil return conf, nil
} }
...@@ -14,9 +14,11 @@ var presetConfigs = map[int]ChainConfig{ ...@@ -14,9 +14,11 @@ var presetConfigs = map[int]ChainConfig{
L2OutputOracleProxy: common.HexToAddress("0xdfe97868233d1aa22e815a266982f2cf17685a27"), L2OutputOracleProxy: common.HexToAddress("0xdfe97868233d1aa22e815a266982f2cf17685a27"),
L1CrossDomainMessengerProxy: common.HexToAddress("0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1"), L1CrossDomainMessengerProxy: common.HexToAddress("0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1"),
L1StandardBridgeProxy: common.HexToAddress("0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1"), L1StandardBridgeProxy: common.HexToAddress("0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1"),
// LegacyCanonicalTransactionChain: common.HexToAddress("0x5e4e65926ba27467555eb562121fac00d24e9dd2"), LegacyCanonicalTransactionChain: common.HexToAddress("0x5e4e65926ba27467555eb562121fac00d24e9dd2"),
}, },
L1StartingHeight: 13596466, L1StartingHeight: 13596466,
L1BedrockStartingHeight: 17422590,
L2BedrockStartingHeight: 105235063,
}, },
// OP Goerli // OP Goerli
420: { 420: {
...@@ -25,8 +27,11 @@ var presetConfigs = map[int]ChainConfig{ ...@@ -25,8 +27,11 @@ var presetConfigs = map[int]ChainConfig{
L2OutputOracleProxy: common.HexToAddress("0xE6Dfba0953616Bacab0c9A8ecb3a9BBa77FC15c0"), L2OutputOracleProxy: common.HexToAddress("0xE6Dfba0953616Bacab0c9A8ecb3a9BBa77FC15c0"),
L1CrossDomainMessengerProxy: common.HexToAddress("0x5086d1eEF304eb5284A0f6720f79403b4e9bE294"), L1CrossDomainMessengerProxy: common.HexToAddress("0x5086d1eEF304eb5284A0f6720f79403b4e9bE294"),
L1StandardBridgeProxy: common.HexToAddress("0x636Af16bf2f682dD3109e60102b8E1A089FedAa8"), L1StandardBridgeProxy: common.HexToAddress("0x636Af16bf2f682dD3109e60102b8E1A089FedAa8"),
LegacyCanonicalTransactionChain: common.HexToAddress("0x2ebA8c4EfDB39A8Cd8f9eD65c50ec079f7CEBD81"),
}, },
L1StartingHeight: 7017096, L1StartingHeight: 7017096,
L1BedrockStartingHeight: 8300214,
L2BedrockStartingHeight: 4061224,
}, },
// Base Mainnet // Base Mainnet
8453: { 8453: {
......
...@@ -29,9 +29,10 @@ type ETL struct { ...@@ -29,9 +29,10 @@ type ETL struct {
headerBufferSize uint64 headerBufferSize uint64
headerTraversal *node.HeaderTraversal headerTraversal *node.HeaderTraversal
ethClient node.EthClient
contracts []common.Address contracts []common.Address
etlBatches chan ETLBatch etlBatches chan ETLBatch
EthClient node.EthClient
} }
type ETLBatch struct { type ETLBatch struct {
...@@ -104,7 +105,7 @@ func (etl *ETL) processBatch(headers []types.Header) error { ...@@ -104,7 +105,7 @@ func (etl *ETL) processBatch(headers []types.Header) error {
} }
headersWithLog := make(map[common.Hash]bool, len(headers)) headersWithLog := make(map[common.Hash]bool, len(headers))
logs, err := etl.ethClient.FilterLogs(ethereum.FilterQuery{FromBlock: firstHeader.Number, ToBlock: lastHeader.Number, Addresses: etl.contracts}) logs, err := etl.EthClient.FilterLogs(ethereum.FilterQuery{FromBlock: firstHeader.Number, ToBlock: lastHeader.Number, Addresses: etl.contracts})
if err != nil { if err != nil {
batchLog.Info("unable to extract logs", "err", err) batchLog.Info("unable to extract logs", "err", err)
return err return err
......
...@@ -66,9 +66,10 @@ func NewL1ETL(cfg Config, log log.Logger, db *database.DB, metrics Metricer, cli ...@@ -66,9 +66,10 @@ func NewL1ETL(cfg Config, log log.Logger, db *database.DB, metrics Metricer, cli
log: log, log: log,
metrics: metrics, metrics: metrics,
headerTraversal: node.NewHeaderTraversal(client, fromHeader, cfg.ConfirmationDepth), headerTraversal: node.NewHeaderTraversal(client, fromHeader, cfg.ConfirmationDepth),
ethClient: client,
contracts: cSlice, contracts: cSlice,
etlBatches: etlBatches, etlBatches: etlBatches,
EthClient: client,
} }
return &L1ETL{ETL: etl, db: db, mu: new(sync.Mutex)}, nil return &L1ETL{ETL: etl, db: db, mu: new(sync.Mutex)}, nil
......
...@@ -50,9 +50,10 @@ func NewL2ETL(cfg Config, log log.Logger, db *database.DB, metrics Metricer, cli ...@@ -50,9 +50,10 @@ func NewL2ETL(cfg Config, log log.Logger, db *database.DB, metrics Metricer, cli
log: log, log: log,
metrics: metrics, metrics: metrics,
headerTraversal: node.NewHeaderTraversal(client, fromHeader, cfg.ConfirmationDepth), headerTraversal: node.NewHeaderTraversal(client, fromHeader, cfg.ConfirmationDepth),
ethClient: client,
contracts: l2Contracts, contracts: l2Contracts,
etlBatches: etlBatches, etlBatches: etlBatches,
EthClient: client,
} }
return &L2ETL{ETL: etl, db: db}, nil return &L2ETL{ETL: etl, db: db}, nil
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
# Can configure them manually or use a preset l2 ChainId for known chains including OP Mainnet, OP Goerli, Base, Base Goerli, Zora, and Zora goerli # Can configure them manually or use a preset l2 ChainId for known chains including OP Mainnet, OP Goerli, Base, Base Goerli, Zora, and Zora goerli
[chain] [chain]
# OP Goerli
preset = $INDEXER_CHAIN_PRESET preset = $INDEXER_CHAIN_PRESET
# L1 Config # L1 Config
......
...@@ -29,7 +29,7 @@ CREATE TABLE IF NOT EXISTS l2_block_headers ( ...@@ -29,7 +29,7 @@ CREATE TABLE IF NOT EXISTS l2_block_headers (
hash VARCHAR PRIMARY KEY, hash VARCHAR PRIMARY KEY,
parent_hash VARCHAR NOT NULL UNIQUE, parent_hash VARCHAR NOT NULL UNIQUE,
number UINT256 NOT NULL UNIQUE, number UINT256 NOT NULL UNIQUE,
timestamp INTEGER NOT NULL UNIQUE CHECK (timestamp > 0), timestamp INTEGER NOT NULL,
-- Raw Data -- Raw Data
rlp_bytes VARCHAR NOT NULL rlp_bytes VARCHAR NOT NULL
......
...@@ -29,6 +29,8 @@ type EthClient interface { ...@@ -29,6 +29,8 @@ type EthClient interface {
BlockHeaderByHash(common.Hash) (*types.Header, error) BlockHeaderByHash(common.Hash) (*types.Header, error)
BlockHeadersByRange(*big.Int, *big.Int) ([]types.Header, error) BlockHeadersByRange(*big.Int, *big.Int) ([]types.Header, error)
TxByHash(common.Hash) (*types.Transaction, error)
StorageHash(common.Address, *big.Int) (common.Hash, error) StorageHash(common.Address, *big.Int) (common.Hash, error)
FilterLogs(ethereum.FilterQuery) ([]types.Log, error) FilterLogs(ethereum.FilterQuery) ([]types.Log, error)
} }
...@@ -147,6 +149,23 @@ func (c *client) BlockHeadersByRange(startHeight, endHeight *big.Int) ([]types.H ...@@ -147,6 +149,23 @@ func (c *client) BlockHeadersByRange(startHeight, endHeight *big.Int) ([]types.H
return headers, nil return headers, nil
} }
func (c *client) TxByHash(hash common.Hash) (*types.Transaction, error) {
ctxwt, cancel := context.WithTimeout(context.Background(), defaultRequestTimeout)
defer cancel()
var tx *types.Transaction
err := c.rpc.CallContext(ctxwt, &tx, "eth_getTransactionByHash", hash)
if err != nil {
return nil, err
} else if tx == nil {
return nil, ethereum.NotFound
}
// NOTE: why does ethclient do something different here
return tx, nil
}
// StorageHash returns the sha3 of the storage root for the specified account // StorageHash returns the sha3 of the storage root for the specified account
func (c *client) StorageHash(address common.Address, blockNumber *big.Int) (common.Hash, error) { func (c *client) StorageHash(address common.Address, blockNumber *big.Int) (common.Hash, error) {
ctxwt, cancel := context.WithTimeout(context.Background(), defaultRequestTimeout) ctxwt, cancel := context.WithTimeout(context.Background(), defaultRequestTimeout)
......
...@@ -124,15 +124,77 @@ func (b *BridgeProcessor) Start(ctx context.Context) error { ...@@ -124,15 +124,77 @@ func (b *BridgeProcessor) Start(ctx context.Context) error {
fromL2Height = new(big.Int).Add(b.LatestL2Header.Number, bigint.One) fromL2Height = new(big.Int).Add(b.LatestL2Header.Number, bigint.One)
} }
l1BedrockStartingHeight := big.NewInt(int64(b.chainConfig.L1BedrockStartingHeight))
l2BedrockStartingHeight := big.NewInt(int64(b.chainConfig.L2BedrockStartingHeight))
batchLog := b.log.New("epoch_start_number", fromL1Height, "epoch_end_number", toL1Height) batchLog := b.log.New("epoch_start_number", fromL1Height, "epoch_end_number", toL1Height)
batchLog.Info("unobserved epochs") batchLog.Info("unobserved epochs")
err = b.db.Transaction(func(tx *database.DB) error { err = b.db.Transaction(func(tx *database.DB) error {
l1BridgeLog := b.log.New("bridge", "l1")
l2BridgeLog := b.log.New("bridge", "l2")
// In the event where we have a large number of un-observed blocks, group the block range // In the event where we have a large number of un-observed blocks, group the block range
// on the order of 10k blocks at a time. If this turns out to be a bottleneck, we can // on the order of 10k blocks at a time. If this turns out to be a bottleneck, we can
// parallelize these operations for significant improvements as well // parallelize these operations for significant improvements as well
l1BridgeLog := b.log.New("bridge", "l1")
l2BridgeLog := b.log.New("bridge", "l2")
// FOR OP-MAINNET, OP-GOERLI ONLY! Specially handle the existence of pre-bedrock blocks
// - Since this processor indexes bridge events on available epochs between L1 & L2, we
// can detect when we've switched into bedrock state by comparing the l1 bedrock starting
// height to the range of L1 blocks we are indexing.
if l1BedrockStartingHeight.Cmp(fromL1Height) > 0 {
legacyFromL1Height, legacyToL1Height := fromL1Height, toL1Height
legacyFromL2Height, legacyToL2Height := fromL2Height, toL2Height
if l1BedrockStartingHeight.Cmp(toL1Height) <= 0 {
legacyToL1Height = new(big.Int).Sub(l1BedrockStartingHeight, big.NewInt(1))
legacyToL1Height = new(big.Int).Sub(l2BedrockStartingHeight, big.NewInt(1))
}
l1BridgeLog := l1BridgeLog.New("mode", "legacy")
l2BridgeLog := l2BridgeLog.New("mode", "legacy")
l1BlockGroups := bigint.Grouped(legacyFromL1Height, legacyToL1Height, 10_000)
l2BlockGroups := bigint.Grouped(legacyFromL2Height, legacyToL2Height, 10_000)
// Now that all initiated events have been indexed, it is ensured that all finalization can find their counterpart.
for _, group := range l1BlockGroups {
log := l1BridgeLog.New("from_l1_block_number", group.Start, "to_l1_block_number", group.End)
log.Info("scanning for initiated bridge events")
if err := bridge.LegacyL1ProcessInitiatedBridgeEvents(log, tx, b.chainConfig, group.Start, group.End); err != nil {
return err
}
}
for _, group := range l2BlockGroups {
log := l2BridgeLog.New("from_l2_block_number", group.Start, "to_l2_block_number", group.End)
log.Info("scanning for initiated bridge events")
if err := bridge.LegacyL2ProcessInitiatedBridgeEvents(log, tx, group.Start, group.End); err != nil {
return err
}
}
// Now that all initiated events have been indexed, it is ensured that all finalization can find their counterpart.
for _, group := range l1BlockGroups {
log := l1BridgeLog.New("from_l1_block_number", group.Start, "to_l1_block_number", group.End)
log.Info("scanning for finalized bridge events")
if err := bridge.LegacyL1ProcessFinalizedBridgeEvents(log, tx, b.l1Etl.EthClient, b.chainConfig, group.Start, group.End); err != nil {
return err
}
}
for _, group := range l2BlockGroups {
log := l2BridgeLog.New("from_l2_block_number", group.Start, "to_l2_block_number", group.End)
log.Info("scanning for finalized bridge events")
if err := bridge.LegacyL2ProcessFinalizedBridgeEvents(log, tx, group.Start, group.End); err != nil {
return err
}
}
if legacyToL1Height.Cmp(toL1Height) == 0 {
// a-ok! entire batch was legacy blocks
return nil
}
batchLog.Info("detected switch to bedrock", "l1_bedrock_starting_height", l1BedrockStartingHeight, "l2_bedrock_starting_height", l2BedrockStartingHeight)
fromL1Height = l1BedrockStartingHeight
fromL2Height = l2BedrockStartingHeight
}
l1BlockGroups := bigint.Grouped(fromL1Height, toL1Height, 10_000) l1BlockGroups := bigint.Grouped(fromL1Height, toL1Height, 10_000)
l2BlockGroups := bigint.Grouped(fromL2Height, toL2Height, 10_000) l2BlockGroups := bigint.Grouped(fromL2Height, toL2Height, 10_000)
...@@ -181,4 +243,5 @@ func (b *BridgeProcessor) Start(ctx context.Context) error { ...@@ -181,4 +243,5 @@ func (b *BridgeProcessor) Start(ctx context.Context) error {
b.LatestL2Header = latestEpoch.L2BlockHeader.RLPHeader.Header() b.LatestL2Header = latestEpoch.L2BlockHeader.RLPHeader.Header()
} }
} }
} }
package bridge
import (
"errors"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/indexer/config"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/node"
"github.com/ethereum-optimism/optimism/indexer/processors/contracts"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
)
// Legacy Bridge Initiation
func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, chainConfig config.ChainConfig, fromHeight *big.Int, toHeight *big.Int) error {
// (1) CanonicalTransactionChain
ctcTxDepositEvents, err := contracts.LegacyCTCDepositEvents(chainConfig.L1Contracts.LegacyCanonicalTransactionChain, db, fromHeight, toHeight)
if err != nil {
return err
}
ctcTxDeposits := make(map[logKey]*contracts.LegacyCTCDepositEvent, len(ctcTxDepositEvents))
transactionDeposits := make([]database.L1TransactionDeposit, len(ctcTxDepositEvents))
for i := range ctcTxDepositEvents {
deposit := ctcTxDepositEvents[i]
ctcTxDeposits[logKey{deposit.Event.BlockHash, deposit.Event.LogIndex}] = &deposit
transactionDeposits[i] = database.L1TransactionDeposit{
SourceHash: deposit.TxHash,
L2TransactionHash: deposit.TxHash, // TODO: check that this is right
InitiatedL1EventGUID: deposit.Event.GUID,
GasLimit: deposit.GasLimit,
Tx: deposit.Tx,
}
}
if len(ctcTxDepositEvents) > 0 {
log.Info("detected legacy transaction deposits", "size", len(transactionDeposits))
if err := db.BridgeTransactions.StoreL1TransactionDeposits(transactionDeposits); err != nil {
return err
}
}
// (2) L1CrossDomainMessenger
crossDomainSentMessages, err := contracts.CrossDomainMessengerSentMessageEvents("l1", chainConfig.L1Contracts.L1CrossDomainMessengerProxy, db, fromHeight, toHeight)
if err != nil {
return err
}
sentMessages := make(map[logKey]*contracts.CrossDomainMessengerSentMessageEvent, len(crossDomainSentMessages))
l1BridgeMessages := make([]database.L1BridgeMessage, len(crossDomainSentMessages))
for i := range crossDomainSentMessages {
sentMessage := crossDomainSentMessages[i]
sentMessages[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex}] = &sentMessage
// extract the deposit hash from the previous TransactionDepositedEvent
ctcTxDeposit, ok := ctcTxDeposits[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex - 1}]
if !ok {
log.Error("missing transaction deposit for cross domain message", "tx_hash", sentMessage.Event.TransactionHash.String())
return fmt.Errorf("missing preceding TransactionEnqueued for SentMessage event. tx_hash = %s", sentMessage.Event.TransactionHash.String())
}
l1BridgeMessages[i] = database.L1BridgeMessage{TransactionSourceHash: ctcTxDeposit.TxHash, BridgeMessage: sentMessage.BridgeMessage}
}
if len(l1BridgeMessages) > 0 {
log.Info("detected legacy L1CrossDomainMessenger messages", "size", len(l1BridgeMessages))
if err := db.BridgeMessages.StoreL1BridgeMessages(l1BridgeMessages); err != nil {
return err
}
}
// (3) L1StandardBridge
initiatedBridges, err := contracts.L1StandardBridgeLegacyDepositInitiatedEvents(chainConfig.L1Contracts.L1StandardBridgeProxy, db, fromHeight, toHeight)
if err != nil {
return err
}
l1BridgeDeposits := make([]database.L1BridgeDeposit, len(initiatedBridges))
for i := range initiatedBridges {
initiatedBridge := initiatedBridges[i]
// extract the cross domain message hash & deposit source hash from the following events
// Unlike bedrock, the bridge events are emitted AFTER sending the cross domain message
// - Event Flow: TransactionEnqueued -> SentMessage -> DepositInitiated
sentMessage, ok := sentMessages[logKey{initiatedBridge.Event.BlockHash, initiatedBridge.Event.LogIndex - 1}]
if !ok {
log.Error("missing cross domain message for bridge transfer", "tx_hash", initiatedBridge.Event.TransactionHash.String())
return fmt.Errorf("expected SentMessage preceding TransactionEnqeueud event. tx_hash = %s", initiatedBridge.Event.TransactionHash.String())
}
ctcTxDeposit, ok := ctcTxDeposits[logKey{initiatedBridge.Event.BlockHash, initiatedBridge.Event.LogIndex - 2}]
if !ok {
log.Error("missing transaction deposit for bridge transfer", "tx_hash", initiatedBridge.Event.TransactionHash.String())
return fmt.Errorf("expected TransactionEnqueued preceding BridgeInitiated event. tx_hash = %s", initiatedBridge.Event.TransactionHash.String())
}
initiatedBridge.BridgeTransfer.CrossDomainMessageHash = &sentMessage.BridgeMessage.MessageHash
l1BridgeDeposits[i] = database.L1BridgeDeposit{
TransactionSourceHash: ctcTxDeposit.TxHash,
BridgeTransfer: initiatedBridge.BridgeTransfer,
}
}
if len(l1BridgeDeposits) > 0 {
log.Info("detected legacy L1StandardBridge deposits", "size", len(l1BridgeDeposits))
if err := db.BridgeTransfers.StoreL1BridgeDeposits(l1BridgeDeposits); err != nil {
return err
}
}
// a-ok!
return nil
}
func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, fromHeight *big.Int, toHeight *big.Int) error {
// (1) L2CrossDomainMessenger -> This is the root-most contract for which bridge events since withdrawals must be initiated from the L2CrossDomainMessenger
crossDomainSentMessages, err := contracts.CrossDomainMessengerSentMessageEvents("l2", predeploys.L2CrossDomainMessengerAddr, db, fromHeight, toHeight)
if err != nil {
return err
}
sentMessages := make(map[logKey]*contracts.CrossDomainMessengerSentMessageEvent, len(crossDomainSentMessages))
l2BridgeMessages := make([]database.L2BridgeMessage, len(crossDomainSentMessages))
l2TransactionWithdrawals := make([]database.L2TransactionWithdrawal, len(crossDomainSentMessages))
for i := range crossDomainSentMessages {
sentMessage := crossDomainSentMessages[i]
sentMessages[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex}] = &sentMessage
// To ensure consitency in our schema, we duplicate this as the "root" transaction withdrawal. We re-use the same withdrawal hash as the storage
// key for the proof sha3(calldata + sender) can be derived from the fields. (NOTE: should we just use the storage key here?)
l2BridgeMessages[i] = database.L2BridgeMessage{TransactionWithdrawalHash: sentMessage.BridgeMessage.MessageHash, BridgeMessage: sentMessage.BridgeMessage}
l2TransactionWithdrawals[i] = database.L2TransactionWithdrawal{
WithdrawalHash: sentMessage.BridgeMessage.MessageHash,
InitiatedL2EventGUID: sentMessage.Event.GUID,
Nonce: sentMessage.BridgeMessage.Nonce,
GasLimit: sentMessage.BridgeMessage.GasLimit,
Tx: database.Transaction{
FromAddress: sentMessage.BridgeMessage.Tx.FromAddress,
ToAddress: sentMessage.BridgeMessage.Tx.ToAddress,
Amount: big.NewInt(0),
Data: sentMessage.BridgeMessage.Tx.Data,
Timestamp: sentMessage.Event.Timestamp,
},
}
}
if len(l2BridgeMessages) > 0 {
log.Info("detected legacy transaction withdrawals (via L2CrossDomainMessenger)", "size", len(l2BridgeMessages))
if err := db.BridgeTransactions.StoreL2TransactionWithdrawals(l2TransactionWithdrawals); err != nil {
return err
}
if err := db.BridgeMessages.StoreL2BridgeMessages(l2BridgeMessages); err != nil {
return err
}
}
// (2) L2StandardBridge
initiatedBridges, err := contracts.L2StandardBridgeLegacyWithdrawalInitiatedEvents(predeploys.L2StandardBridgeAddr, db, fromHeight, toHeight)
if err != nil {
return err
}
l2BridgeWithdrawals := make([]database.L2BridgeWithdrawal, len(initiatedBridges))
for i := range initiatedBridges {
initiatedBridge := initiatedBridges[i]
// extract the cross domain message hash & deposit source hash from the following events
// Unlike bedrock, the bridge events are emitted AFTER sending the cross domain message
// - Event Flow: TransactionEnqueued -> SentMessage -> DepositInitiated
sentMessage, ok := sentMessages[logKey{initiatedBridge.Event.BlockHash, initiatedBridge.Event.LogIndex - 1}]
if !ok {
log.Error("missing cross domain message for bridge transfer", "tx_hash", initiatedBridge.Event.TransactionHash.String())
return fmt.Errorf("expected SentMessage preceding DepositInitiated event. tx_hash = %s", initiatedBridge.Event.TransactionHash)
}
initiatedBridge.BridgeTransfer.CrossDomainMessageHash = &sentMessage.BridgeMessage.MessageHash
l2BridgeWithdrawals[i] = database.L2BridgeWithdrawal{
TransactionWithdrawalHash: sentMessage.BridgeMessage.MessageHash,
BridgeTransfer: initiatedBridge.BridgeTransfer,
}
}
if len(l2BridgeWithdrawals) > 0 {
log.Info("detected legacy L2StandardBridge withdrawals", "size", len(l2BridgeWithdrawals))
if err := db.BridgeTransfers.StoreL2BridgeWithdrawals(l2BridgeWithdrawals); err != nil {
return err
}
}
// a-ok
return nil
}
// Legacy Bridge Finalization
func LegacyL1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Client node.EthClient, chainConfig config.ChainConfig, fromHeight *big.Int, toHeight *big.Int) error {
// (1) L1CrossDomainMessenger -> This is the root-most contract from which bridge events are finalized since withdrawals must be initiated from the
// L2CrossDomainMessenger. Since there's no two-step withdrawal process, we mark the transaction as proven/finalized in the same step
crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l1", chainConfig.L1Contracts.L1CrossDomainMessengerProxy, db, fromHeight, toHeight)
if err != nil {
return err
}
skippedPreRegenesisMessages := 0
for i := range crossDomainRelayedMessages {
relayedMessage := crossDomainRelayedMessages[i]
message, err := db.BridgeMessages.L2BridgeMessage(relayedMessage.MessageHash)
if err != nil {
return err
} else if message == nil {
// Before surfacing an error about a missing withdrawal, we need to handle an edge case
// for OP-Mainnet pre-regensis withdrawals that no longer exist on L2.
tx, err := l1Client.TxByHash(relayedMessage.Event.TransactionHash)
if err != nil {
return err
} else if tx == nil {
return errors.New("missing l1 tx hash for relayed message")
}
relayMessageData := tx.Data()[4:]
inputs, err := contracts.CrossDomainMessengerLegacyRelayMessageEncoding.Inputs.Unpack(relayMessageData)
if err != nil || inputs == nil {
return fmt.Errorf("unable to extract XDomainCallData from relayMessage transaction: %w", err)
}
// NOTE: Since OP-Mainnet is the only network to go through a regensis we can simply harcode the
// the starting message nonce at genesis (100k). Any relayed withdrawal on L1 with a lesser nonce
// is a clear indicator of a pre-regenesis withdrawal.
if inputs[3].(*big.Int).Int64() < 100_000 {
// skip pre-regenesis withdrawals
skippedPreRegenesisMessages++
continue
} else {
log.Error("missing indexed legacy L2CrossDomainMessenger message", "message_hash", relayedMessage.MessageHash, "tx_hash", relayedMessage.Event.TransactionHash)
return fmt.Errorf("missing indexed L2CrossDomainMessager message")
}
}
// Mark the associated tx withdrawal as proven/finalized with the same event
if err := db.BridgeTransactions.MarkL2TransactionWithdrawalProvenEvent(relayedMessage.MessageHash, relayedMessage.Event.GUID); err != nil {
return err
}
if err := db.BridgeTransactions.MarkL2TransactionWithdrawalFinalizedEvent(relayedMessage.MessageHash, relayedMessage.Event.GUID, true); err != nil {
return err
}
if err := db.BridgeMessages.MarkRelayedL2BridgeMessage(relayedMessage.MessageHash, relayedMessage.Event.GUID); err != nil {
return err
}
}
if len(crossDomainRelayedMessages) > 0 {
relayedMessages := len(crossDomainRelayedMessages) - skippedPreRegenesisMessages
if skippedPreRegenesisMessages > 0 {
// Logged as a warning just for visibility
log.Warn("skipped pre-regensis relayed L2CrossDomainMessenger withdrawals", "size", skippedPreRegenesisMessages)
}
if relayedMessages > 0 {
log.Info("relayed legacy L2CrossDomainMessenger messages", "size", len(crossDomainRelayedMessages))
}
}
// (2) L1StandardBridge
// no-op for now as there's nothing actionable to do here besides sanity checks that we'll skip for now
// a-ok!
return nil
}
func LegacyL2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, fromHeight *big.Int, toHeight *big.Int) error {
// (1) L2CrossDomainMessenger
crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l2", predeploys.L2CrossDomainMessengerAddr, db, fromHeight, toHeight)
if err != nil {
return err
}
for i := range crossDomainRelayedMessages {
relayedMessage := crossDomainRelayedMessages[i]
message, err := db.BridgeMessages.L1BridgeMessage(relayedMessage.MessageHash)
if err != nil {
return err
} else if message == nil {
log.Error("missing indexed legacy L1CrossDomainMessenger message", "message_hash", relayedMessage.MessageHash, "tx_hash", relayedMessage.Event.TransactionHash)
return fmt.Errorf("missing indexed L1CrossDomainMessager message")
}
if err := db.BridgeMessages.MarkRelayedL1BridgeMessage(relayedMessage.MessageHash, relayedMessage.Event.GUID); err != nil {
return err
}
}
if len(crossDomainRelayedMessages) > 0 {
log.Info("relayed legacy L1CrossDomainMessenger messages", "size", len(crossDomainRelayedMessages))
}
// (2) L2StandardBridge
// no-op for now as there's nothing actionable to do here besides sanity checks that we'll skip for now
// a-ok!
return nil
}
...@@ -18,7 +18,7 @@ var ( ...@@ -18,7 +18,7 @@ var (
bytesType, _ = abi.NewType("bytes", "", nil) bytesType, _ = abi.NewType("bytes", "", nil)
addressType, _ = abi.NewType("address", "", nil) addressType, _ = abi.NewType("address", "", nil)
legacyCrossDomainMessengerRelayMessageMethod = abi.NewMethod( CrossDomainMessengerLegacyRelayMessageEncoding = abi.NewMethod(
"relayMessage", "relayMessage",
"relayMessage", "relayMessage",
abi.Function, abi.Function,
...@@ -26,10 +26,13 @@ var ( ...@@ -26,10 +26,13 @@ var (
false, // isConst false, // isConst
true, // payable true, // payable
abi.Arguments{ // inputs abi.Arguments{ // inputs
{Name: "sender", Type: addressType},
{Name: "target", Type: addressType}, {Name: "target", Type: addressType},
{Name: "sender", Type: addressType},
{Name: "data", Type: bytesType}, {Name: "data", Type: bytesType},
{Name: "nonce", Type: uint256Type}, {Name: "nonce", Type: uint256Type},
// The actual transaction on the legacy L1CrossDomainMessenger
// actually has a trailing proof argument but is ignored for the
// `XDomainCallData` encoding
}, },
abi.Arguments{}, // outputs abi.Arguments{}, // outputs
) )
...@@ -58,7 +61,6 @@ func CrossDomainMessengerSentMessageEvents(chainSelector string, contractAddress ...@@ -58,7 +61,6 @@ func CrossDomainMessengerSentMessageEvents(chainSelector string, contractAddress
return nil, err return nil, err
} }
if len(sentMessageEvents) == 0 { if len(sentMessageEvents) == 0 {
// prevent the following db queries if we dont need them
return nil, nil return nil, nil
} }
...@@ -68,10 +70,13 @@ func CrossDomainMessengerSentMessageEvents(chainSelector string, contractAddress ...@@ -68,10 +70,13 @@ func CrossDomainMessengerSentMessageEvents(chainSelector string, contractAddress
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(sentMessageEvents) != len(sentMessageExtensionEvents) { if len(sentMessageExtensionEvents) > len(sentMessageEvents) {
return nil, fmt.Errorf("mismatch in SentMessage events. %d sent messages & %d sent message extensions", len(sentMessageEvents), len(sentMessageExtensionEvents)) return nil, fmt.Errorf("mismatch. %d sent messages & %d sent message extensions", len(sentMessageEvents), len(sentMessageExtensionEvents))
} }
// We handle version zero messages uniquely since the first version of cross domain messages
// do not have the SentMessageExtension1 event emitted, introduced in version 1.
numVersionZeroMessages := len(sentMessageEvents) - len(sentMessageExtensionEvents)
crossDomainSentMessages := make([]CrossDomainMessengerSentMessageEvent, len(sentMessageEvents)) crossDomainSentMessages := make([]CrossDomainMessengerSentMessageEvent, len(sentMessageEvents))
for i := range sentMessageEvents { for i := range sentMessageEvents {
sentMessage := bindings.CrossDomainMessengerSentMessage{Raw: *sentMessageEvents[i].RLPLog} sentMessage := bindings.CrossDomainMessengerSentMessage{Raw: *sentMessageEvents[i].RLPLog}
...@@ -79,13 +84,24 @@ func CrossDomainMessengerSentMessageEvents(chainSelector string, contractAddress ...@@ -79,13 +84,24 @@ func CrossDomainMessengerSentMessageEvents(chainSelector string, contractAddress
if err != nil { if err != nil {
return nil, err return nil, err
} }
version, _ := DecodeVersionedNonce(sentMessage.MessageNonce)
if i < numVersionZeroMessages && version != 0 {
return nil, fmt.Errorf("expected version zero nonce. nonce %d tx_hash %s", sentMessage.MessageNonce, sentMessage.Raw.TxHash)
}
// In version zero, to value is bridged through the cross domain messenger.
value := big.NewInt(0)
if version > 0 {
sentMessageExtension := bindings.CrossDomainMessengerSentMessageExtension1{Raw: *sentMessageExtensionEvents[i].RLPLog} sentMessageExtension := bindings.CrossDomainMessengerSentMessageExtension1{Raw: *sentMessageExtensionEvents[i].RLPLog}
err = UnpackLog(&sentMessageExtension, sentMessageExtensionEvents[i].RLPLog, sentMessageExtensionEventAbi.Name, crossDomainMessengerAbi) err = UnpackLog(&sentMessageExtension, sentMessageExtensionEvents[i].RLPLog, sentMessageExtensionEventAbi.Name, crossDomainMessengerAbi)
if err != nil { if err != nil {
return nil, err return nil, err
} }
value = sentMessageExtension.Value
}
messageHash, err := CrossDomainMessageHash(crossDomainMessengerAbi, &sentMessage, sentMessageExtension.Value) messageHash, err := CrossDomainMessageHash(crossDomainMessengerAbi, &sentMessage, value)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -100,13 +116,12 @@ func CrossDomainMessengerSentMessageEvents(chainSelector string, contractAddress ...@@ -100,13 +116,12 @@ func CrossDomainMessengerSentMessageEvents(chainSelector string, contractAddress
Tx: database.Transaction{ Tx: database.Transaction{
FromAddress: sentMessage.Sender, FromAddress: sentMessage.Sender,
ToAddress: sentMessage.Target, ToAddress: sentMessage.Target,
Amount: sentMessageExtension.Value, Amount: value,
Data: sentMessage.Message, Data: sentMessage.Message,
Timestamp: sentMessageEvents[i].Timestamp, Timestamp: sentMessageEvents[i].Timestamp,
}, },
}, },
} }
} }
return crossDomainSentMessages, nil return crossDomainSentMessages, nil
...@@ -148,11 +163,11 @@ func CrossDomainMessageHash(abi *abi.ABI, sentMsg *bindings.CrossDomainMessenger ...@@ -148,11 +163,11 @@ func CrossDomainMessageHash(abi *abi.ABI, sentMsg *bindings.CrossDomainMessenger
switch version { switch version {
case 0: case 0:
// Legacy Message // Legacy Message
inputBytes, err := legacyCrossDomainMessengerRelayMessageMethod.Inputs.Pack(sentMsg.Sender, sentMsg.Target, sentMsg.Message, sentMsg.MessageNonce) inputBytes, err := CrossDomainMessengerLegacyRelayMessageEncoding.Inputs.Pack(sentMsg.Target, sentMsg.Sender, sentMsg.Message, sentMsg.MessageNonce)
if err != nil { if err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
msgBytes := append(legacyCrossDomainMessengerRelayMessageMethod.ID, inputBytes...) msgBytes := append(CrossDomainMessengerLegacyRelayMessageEncoding.ID, inputBytes...)
return crypto.Keccak256Hash(msgBytes), nil return crypto.Keccak256Hash(msgBytes), nil
case 1: case 1:
// Current Message // Current Message
......
package contracts
import (
"math/big"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum/go-ethereum/common"
)
type LegacyBridgeEvent struct {
Event *database.ContractEvent
BridgeTransfer database.BridgeTransfer
}
func L1StandardBridgeLegacyDepositInitiatedEvents(contractAddress common.Address, db *database.DB, fromHeight, toHeight *big.Int) ([]LegacyBridgeEvent, error) {
l1StandardBridgeAbi, err := bindings.L1StandardBridgeMetaData.GetAbi()
if err != nil {
return nil, err
}
// The L1StandardBridge contains the legacy events
ethDepositEventAbi := l1StandardBridgeAbi.Events["ETHDepositInitiated"]
erc20DepositEventAbi := l1StandardBridgeAbi.Events["ERC20DepositInitiated"]
// Grab both ETH & ERC20 Events
ethDepositEvents, err := db.ContractEvents.L1ContractEventsWithFilter(database.ContractEvent{ContractAddress: contractAddress, EventSignature: ethDepositEventAbi.ID}, fromHeight, toHeight)
if err != nil {
return nil, err
}
erc20DepositEvents, err := db.ContractEvents.L1ContractEventsWithFilter(database.ContractEvent{ContractAddress: contractAddress, EventSignature: erc20DepositEventAbi.ID}, fromHeight, toHeight)
if err != nil {
return nil, err
}
// Represent the ETH deposits via the ETH ERC20 predeploy address
deposits := make([]LegacyBridgeEvent, len(ethDepositEvents)+len(erc20DepositEvents))
for i := range ethDepositEvents {
bridgeEvent := bindings.L1StandardBridgeETHDepositInitiated{Raw: *ethDepositEvents[i].RLPLog}
err := UnpackLog(&bridgeEvent, &bridgeEvent.Raw, ethDepositEventAbi.Name, l1StandardBridgeAbi)
if err != nil {
return nil, err
}
deposits[i] = LegacyBridgeEvent{
Event: &ethDepositEvents[i].ContractEvent,
BridgeTransfer: database.BridgeTransfer{
TokenPair: database.ETHTokenPair,
Tx: database.Transaction{
FromAddress: bridgeEvent.From,
ToAddress: bridgeEvent.To,
Amount: bridgeEvent.Amount,
Data: bridgeEvent.ExtraData,
Timestamp: ethDepositEvents[i].Timestamp,
},
},
}
}
for i := range erc20DepositEvents {
bridgeEvent := bindings.L1StandardBridgeERC20DepositInitiated{Raw: *erc20DepositEvents[i].RLPLog}
err := UnpackLog(&bridgeEvent, &bridgeEvent.Raw, erc20DepositEventAbi.Name, l1StandardBridgeAbi)
if err != nil {
return nil, err
}
deposits[len(ethDepositEvents)+i] = LegacyBridgeEvent{
Event: &erc20DepositEvents[i].ContractEvent,
BridgeTransfer: database.BridgeTransfer{
TokenPair: database.TokenPair{LocalTokenAddress: bridgeEvent.L1Token, RemoteTokenAddress: bridgeEvent.L2Token},
Tx: database.Transaction{
FromAddress: bridgeEvent.From,
ToAddress: bridgeEvent.To,
Amount: bridgeEvent.Amount,
Data: bridgeEvent.ExtraData,
Timestamp: erc20DepositEvents[i].Timestamp,
},
},
}
}
return deposits, nil
}
func L2StandardBridgeLegacyWithdrawalInitiatedEvents(contractAddress common.Address, db *database.DB, fromHeight, toHeight *big.Int) ([]LegacyBridgeEvent, error) {
l2StandardBridgeAbi, err := bindings.L2StandardBridgeMetaData.GetAbi()
if err != nil {
return nil, err
}
withdrawalInitiatedEventAbi := l2StandardBridgeAbi.Events["WithdrawalInitiated"]
withdrawalEvents, err := db.ContractEvents.L2ContractEventsWithFilter(database.ContractEvent{ContractAddress: contractAddress, EventSignature: withdrawalInitiatedEventAbi.ID}, fromHeight, toHeight)
withdrawals := make([]LegacyBridgeEvent, len(withdrawalEvents))
for i := range withdrawalEvents {
bridgeEvent := bindings.L2StandardBridgeWithdrawalInitiated{Raw: *withdrawalEvents[i].RLPLog}
err := UnpackLog(&bridgeEvent, &bridgeEvent.Raw, withdrawalInitiatedEventAbi.Name, l2StandardBridgeAbi)
if err != nil {
return nil, err
}
withdrawals[i] = LegacyBridgeEvent{
Event: &withdrawalEvents[i].ContractEvent,
BridgeTransfer: database.BridgeTransfer{
TokenPair: database.ETHTokenPair,
Tx: database.Transaction{
FromAddress: bridgeEvent.From,
ToAddress: bridgeEvent.To,
Amount: bridgeEvent.Amount,
Data: bridgeEvent.ExtraData,
Timestamp: withdrawalEvents[i].Timestamp,
},
},
}
}
return withdrawals, nil
}
package contracts
import (
"math/big"
"github.com/ethereum-optimism/optimism/indexer/database"
legacy_bindings "github.com/ethereum-optimism/optimism/op-bindings/legacy-bindings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
type LegacyCTCDepositEvent struct {
Event *database.ContractEvent
Tx database.Transaction
TxHash common.Hash
GasLimit *big.Int
}
func LegacyCTCDepositEvents(contractAddress common.Address, db *database.DB, fromHeight, toHeight *big.Int) ([]LegacyCTCDepositEvent, error) {
ctcAbi, err := legacy_bindings.CanonicalTransactionChainMetaData.GetAbi()
if err != nil {
return nil, err
}
transactionEnqueuedEventAbi := ctcAbi.Events["TransactionEnqueued"]
contractEventFilter := database.ContractEvent{ContractAddress: contractAddress, EventSignature: transactionEnqueuedEventAbi.ID}
events, err := db.ContractEvents.L1ContractEventsWithFilter(contractEventFilter, fromHeight, toHeight)
if err != nil {
return nil, err
}
ctcTxDeposits := make([]LegacyCTCDepositEvent, len(events))
for i := range events {
txEnqueued := legacy_bindings.CanonicalTransactionChainTransactionEnqueued{Raw: *events[i].RLPLog}
err = UnpackLog(&txEnqueued, events[i].RLPLog, transactionEnqueuedEventAbi.Name, ctcAbi)
if err != nil {
return nil, err
}
zeroAmt := big.NewInt(0)
ctcTxDeposits[i] = LegacyCTCDepositEvent{
Event: &events[i].ContractEvent,
GasLimit: txEnqueued.GasLimit,
TxHash: types.NewTransaction(0, txEnqueued.Target, zeroAmt, txEnqueued.GasLimit.Uint64(), nil, txEnqueued.Data).Hash(),
Tx: database.Transaction{
FromAddress: txEnqueued.L1TxOrigin,
ToAddress: txEnqueued.Target,
Amount: zeroAmt,
Data: txEnqueued.Data,
Timestamp: events[i].Timestamp,
},
}
}
return ctcTxDeposits, nil
}
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