diff --git a/indexer/database/bridge_transfers.go b/indexer/database/bridge_transfers.go index 6e1cb1e19f2c..cedc805f84b6 100644 --- a/indexer/database/bridge_transfers.go +++ b/indexer/database/bridge_transfers.go @@ -139,10 +139,10 @@ func (db *bridgeTransfersDB) L1BridgeDepositsByAddress(address common.Address, c // Coalesce l1 transaction deposits that are ETH receives into bridge deposits. ethTransactionDeposits := db.gorm.Model(&L1TransactionDeposit{}) - ethTransactionDeposits = ethTransactionDeposits.Where(`from_address = ? AND data = '0x' AND amount > 0`, address.String()) + ethTransactionDeposits = ethTransactionDeposits.Where(Transaction{FromAddress: address}).Where(`data = '0x' AND amount > 0`) ethTransactionDeposits = ethTransactionDeposits.Joins("INNER JOIN l1_contract_events ON l1_contract_events.guid = initiated_l1_event_guid") ethTransactionDeposits = ethTransactionDeposits.Select(` -from_address, to_address, amount, data, source_hash AS transaction_source_hash, +from_address, to_address, amount, data, source_hash AS transaction_source_hash, l2_transaction_hash, l1_contract_events.transaction_hash AS l1_transaction_hash, l1_transaction_deposits.timestamp, NULL AS cross_domain_message_hash, ? AS local_token_address, ? AS remote_token_address`, ethAddressString, ethAddressString) @@ -151,13 +151,15 @@ l1_transaction_deposits.timestamp, NULL AS cross_domain_message_hash, ? AS local depositsQuery = depositsQuery.Joins("INNER JOIN l1_contract_events ON l1_contract_events.guid = l1_transaction_deposits.initiated_l1_event_guid") depositsQuery = depositsQuery.Select(` l1_bridge_deposits.from_address, l1_bridge_deposits.to_address, l1_bridge_deposits.amount, l1_bridge_deposits.data, transaction_source_hash, -l2_transaction_hash, l1_contract_events.transaction_hash as l1_transaction_hash, +l2_transaction_hash, l1_contract_events.transaction_hash AS l1_transaction_hash, l1_bridge_deposits.timestamp, cross_domain_message_hash, local_token_address, remote_token_address`) // Since all bridge deposits share have the same primary key corresponding to the transaction // deposit, we can simply order by the timestamp in the transaction deposits table which will // order all deposits (bridge & transactions) uniformly + // TODO: cursoring options + query := db.gorm.Table("(?) AS deposits", depositsQuery) query = query.Joins("UNION (?)", ethTransactionDeposits) query = query.Select("*").Order("timestamp DESC").Limit(limit) @@ -240,25 +242,41 @@ func (db *bridgeTransfersDB) L2BridgeWithdrawalsByAddress(address common.Address limit = defaultLimit } - withdrawalsQuery := db.gorm.Table("l2_bridge_withdrawals").Select(` -l2_bridge_withdrawals.*, -l2_contract_events.transaction_hash AS l2_transaction_hash, -proven_l1_contract_events.transaction_hash AS proven_l1_transaction_hash, -finalized_l1_contract_events.transaction_hash AS finalized_l1_transaction_hash`) - - withdrawalsQuery = withdrawalsQuery.Joins("INNER JOIN l2_transaction_withdrawals ON l2_bridge_withdrawals.transaction_withdrawal_hash = l2_transaction_withdrawals.withdrawal_hash") - withdrawalsQuery = withdrawalsQuery.Joins("INNER JOIN l2_contract_events ON l2_transaction_withdrawals.initiated_l2_event_guid = l2_contract_events.guid") - withdrawalsQuery = withdrawalsQuery.Joins("LEFT JOIN l1_contract_events AS proven_l1_contract_events ON l2_transaction_withdrawals.proven_l1_event_guid = proven_l1_contract_events.guid") - withdrawalsQuery = withdrawalsQuery.Joins("LEFT JOIN l1_contract_events AS finalized_l1_contract_events ON l2_transaction_withdrawals.finalized_l1_event_guid = finalized_l1_contract_events.guid") + // TODO join with l1_tokens and l2_tokens + ethAddressString := predeploys.LegacyERC20ETHAddr.String() - if cursor != "" { - withdrawalsQuery = withdrawalsQuery.Where("l2_bridge_withdrawals.id < ?", cursor) - } + // Coalesce l2 transaction withdrawals that are ETH receives + ethTransactionWithdrawals := db.gorm.Model(&L2TransactionWithdrawal{}) + ethTransactionWithdrawals = ethTransactionWithdrawals.Where(Transaction{FromAddress: address}).Where(`data = '0x' AND amount > 0`) + ethTransactionWithdrawals = ethTransactionWithdrawals.Joins("INNER JOIN l2_contract_events ON l2_contract_events.guid = l2_transaction_withdrawals.initiated_l2_event_guid") + ethTransactionWithdrawals = ethTransactionWithdrawals.Joins("LEFT JOIN l1_contract_events AS proven_l1_events ON proven_l1_events.guid = l2_transaction_withdrawals.proven_l1_event_guid") + ethTransactionWithdrawals = ethTransactionWithdrawals.Joins("LEFT JOIN l1_contract_events AS finalized_l1_events ON finalized_l1_events.guid = l2_transaction_withdrawals.finalized_l1_event_guid") + ethTransactionWithdrawals = ethTransactionWithdrawals.Select(` +from_address, to_address, amount, data, withdrawal_hash AS transaction_withdrawal_hash, +l2_contract_events.transaction_hash AS l2_transaction_hash, proven_l1_events.transaction_hash AS proven_l1_transaction_hash, finalized_l1_events.transaction_hash AS finalized_l1_transaction_hash, +l2_transaction_withdrawals.timestamp, NULL AS cross_domain_message_hash, ? AS local_token_address, ? AS remote_token_address`, ethAddressString, ethAddressString) + + withdrawalsQuery := db.gorm.Model(&L2BridgeWithdrawal{}) + withdrawalsQuery = withdrawalsQuery.Joins("INNER JOIN l2_transaction_withdrawals ON withdrawal_hash = l2_bridge_withdrawals.transaction_withdrawal_hash") + withdrawalsQuery = withdrawalsQuery.Joins("INNER JOIN l2_contract_events ON l2_contract_events.guid = l2_transaction_withdrawals.initiated_l2_event_guid") + withdrawalsQuery = withdrawalsQuery.Joins("LEFT JOIN l1_contract_events AS proven_l1_events ON proven_l1_events.guid = l2_transaction_withdrawals.proven_l1_event_guid") + withdrawalsQuery = withdrawalsQuery.Joins("LEFT JOIN l1_contract_events AS finalized_l1_events ON finalized_l1_events.guid = l2_transaction_withdrawals.finalized_l1_event_guid") + withdrawalsQuery = withdrawalsQuery.Select(` +l2_bridge_withdrawals.from_address, l2_bridge_withdrawals.to_address, l2_bridge_withdrawals.amount, l2_bridge_withdrawals.data, transaction_withdrawal_hash, +l2_contract_events.transaction_hash AS l2_transaction_hash, proven_l1_events.transaction_hash AS proven_l1_transaction_hash, finalized_l1_events.transaction_hash AS finalized_l1_transaction_hash, +l2_bridge_withdrawals.timestamp, cross_domain_message_hash, local_token_address, remote_token_address`) + + // Since all bridge withdrawals share have the same primary key corresponding to the transaction + // withdrawal, we can simply order by the timestamp in the transaction withdrawal table which will + // order all deposits (bridge & transactions) uniformly - filteredQuery := withdrawalsQuery.Where(&Transaction{FromAddress: address}).Order("l2_bridge_withdrawals.timestamp DESC").Limit(limit + 1) + // TODO: cursoring options + query := db.gorm.Table("(?) AS withdrawals", withdrawalsQuery) + query = query.Joins("UNION (?)", ethTransactionWithdrawals) + query = query.Select("*").Order("timestamp DESC").Limit(limit) withdrawals := []L2BridgeWithdrawalWithTransactionHashes{} - result := filteredQuery.Scan(&withdrawals) + result := query.Scan(&withdrawals) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { return nil, nil diff --git a/indexer/e2e_tests/bridge_transfers_e2e_test.go b/indexer/e2e_tests/bridge_transfers_e2e_test.go index 63c6d9d325bc..3e02a7cb3c7b 100644 --- a/indexer/e2e_tests/bridge_transfers_e2e_test.go +++ b/indexer/e2e_tests/bridge_transfers_e2e_test.go @@ -341,7 +341,7 @@ func TestE2EBridgeTransfersStandardBridgeETHWithdrawal(t *testing.T) { require.NotNil(t, crossDomainBridgeMessage.RelayedMessageEventGUID) } -func TestE2EBridgeTransfersL2ToL1MessagePasserReceive(t *testing.T) { +func TestE2EBridgeTransfersL2ToL1MessagePasserETHReceive(t *testing.T) { testSuite := createE2ETestSuite(t) optimismPortal, err := bindings.NewOptimismPortal(testSuite.OpCfg.L1Deployments.OptimismPortalProxy, testSuite.L1Client) @@ -378,6 +378,7 @@ func TestE2EBridgeTransfersL2ToL1MessagePasserReceive(t *testing.T) { aliceWithdrawals, err := testSuite.DB.BridgeTransfers.L2BridgeWithdrawalsByAddress(aliceAddr, "", 0) require.NoError(t, err) + require.Len(t, aliceWithdrawals.Withdrawals, 1) require.Equal(t, l2ToL1MessagePasserWithdrawTx.Hash().String(), aliceWithdrawals.Withdrawals[0].L2TransactionHash.String()) msgPassed, err := withdrawals.ParseMessagePassed(l2ToL1WithdrawReceipt) diff --git a/indexer/processors/bridge/l2_bridge_processor.go b/indexer/processors/bridge/l2_bridge_processor.go index e23da874d1d0..266225618530 100644 --- a/indexer/processors/bridge/l2_bridge_processor.go +++ b/indexer/processors/bridge/l2_bridge_processor.go @@ -26,7 +26,6 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, fromHeight return err } - ethWithdrawals := []database.L2BridgeWithdrawal{} messagesPassed := make(map[logKey]*contracts.L2ToL1MessagePasserMessagePassed, len(l2ToL1MPMessagesPassed)) transactionWithdrawals := make([]database.L2TransactionWithdrawal, len(l2ToL1MPMessagesPassed)) for i := range l2ToL1MPMessagesPassed { @@ -39,13 +38,6 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, fromHeight GasLimit: messagePassed.GasLimit, Tx: messagePassed.Tx, } - - if len(messagePassed.Tx.Data) == 0 && messagePassed.Tx.Amount.Int.BitLen() > 0 { - ethWithdrawals = append(ethWithdrawals, database.L2BridgeWithdrawal{ - TransactionWithdrawalHash: messagePassed.WithdrawalHash, - BridgeTransfer: database.BridgeTransfer{Tx: transactionWithdrawals[i].Tx, TokenPair: database.ETHTokenPair}, - }) - } } if len(messagesPassed) > 0 { @@ -53,12 +45,6 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, fromHeight if err := db.BridgeTransactions.StoreL2TransactionWithdrawals(transactionWithdrawals); err != nil { return err } - if len(ethWithdrawals) > 0 { - log.Info("detected L2ToL1MessagePasser ETH transfers", "size", len(ethWithdrawals)) - if err := db.BridgeTransfers.StoreL2BridgeWithdrawals(ethWithdrawals); err != nil { - return err - } - } } // (2) L2CrossDomainMessenger