Skip to content

Commit

Permalink
coalesce eth sends and bridge transfers
Browse files Browse the repository at this point in the history
  • Loading branch information
hamdiallam committed Aug 22, 2023
1 parent b4243cc commit 97ae6b1
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 44 deletions.
52 changes: 31 additions & 21 deletions indexer/database/bridge_transfers.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,7 @@ func (db *bridgeTransfersDB) L1BridgeDeposit(txSourceHash common.Hash) (*L1Bridg
return &deposit, nil
}

// L1BridgeDepositByCrossDomainMessengerNonce retrieves tokens deposited, specified by the associated `L1CrossDomainMessenger` nonce.
// All tokens bridged via the StandardBridge flows through the L1CrossDomainMessenger
// L1BridgeDepositWithFilter queries for a bridge deposit with set fields in the `BridgeTransfer` filter
func (db *bridgeTransfersDB) L1BridgeDepositWithFilter(filter BridgeTransfer) (*L1BridgeDeposit, error) {
var deposit L1BridgeDeposit
result := db.gorm.Where(&filter).Take(&deposit)
Expand All @@ -127,31 +126,43 @@ type L1BridgeDepositsResponse struct {
HasNextPage bool
}

// L1BridgeDepositsByAddress retrieves a list of deposits intiated by the specified address, coupled with the L1/L2 transaction
// hashes that complete the bridge transaction.
// L1BridgeDepositsByAddress retrieves a list of deposits intiated by the specified address,
// coupled with the L1/L2 transaction hashes that complete the bridge transaction.
func (db *bridgeTransfersDB) L1BridgeDepositsByAddress(address common.Address, cursor string, limit int) (*L1BridgeDepositsResponse, error) {
defaultLimit := 100
if limit <= 0 {
limit = defaultLimit
}

depositsQuery := db.gorm.Table("l1_bridge_deposits").Select(`
l1_bridge_deposits.*,
l1_contract_events.transaction_hash AS l1_transaction_hash,
l1_transaction_deposits.l2_transaction_hash`)

// TODO join with l1_tokens and l2_tokens
depositsQuery = depositsQuery.Joins("INNER JOIN l1_transaction_deposits ON l1_bridge_deposits.transaction_source_hash = l1_transaction_deposits.source_hash")
depositsQuery = depositsQuery.Joins("INNER JOIN l1_contract_events ON l1_transaction_deposits.initiated_l1_event_guid = l1_contract_events.guid")

if cursor != "" {
depositsQuery = depositsQuery.Where("l1_bridge_deposits.transaction_source_hash < ?", cursor)
}

filteredQuery := depositsQuery.Where(&Transaction{FromAddress: address}).Order("l1_bridge_deposits.transaction_source_hash DESC").Limit(limit + 1)

ethAddressString := predeploys.LegacyERC20ETHAddr.String()

// 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.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,
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)

depositsQuery := db.gorm.Model(&L1BridgeDeposit{})
depositsQuery = depositsQuery.Joins("INNER JOIN l1_transaction_deposits ON l1_transaction_deposits.source_hash = transaction_source_hash")
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,
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

query := db.gorm.Table("(?) AS deposits", depositsQuery)
query = query.Joins("UNION (?)", ethTransactionDeposits)
query = query.Select("*").Order("timestamp DESC").Limit(limit)
deposits := []L1BridgeDepositWithTransactionHashes{}
result := filteredQuery.Scan(&deposits)
result := query.Debug().Find(&deposits)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
Expand Down Expand Up @@ -201,8 +212,7 @@ func (db *bridgeTransfersDB) L2BridgeWithdrawal(txWithdrawalHash common.Hash) (*
return &withdrawal, nil
}

// L2BridgeWithdrawalByCrossDomainMessengerNonce retrieves tokens withdrawn, specified by the associated `L2CrossDomainMessenger` nonce.
// All tokens bridged via the StandardBridge flows through the L2CrossDomainMessenger
// L2BridgeWithdrawalWithFilter queries for a bridge withdrawal with set fields in the `BridgeTransfer` filter
func (db *bridgeTransfersDB) L2BridgeWithdrawalWithFilter(filter BridgeTransfer) (*L2BridgeWithdrawal, error) {
var withdrawal L2BridgeWithdrawal
result := db.gorm.Where(filter).Take(&withdrawal)
Expand Down
2 changes: 2 additions & 0 deletions indexer/e2e_tests/bridge_transfers_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ func TestE2EBridgeTransfersOptimismPortalETHReceive(t *testing.T) {

aliceDeposits, err := testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, "", 0)
require.NoError(t, err)
require.NotNil(t, aliceDeposits)
require.Len(t, aliceDeposits.Deposits, 1)
require.Equal(t, portalDepositTx.Hash(), aliceDeposits.Deposits[0].L1TransactionHash)

deposit := aliceDeposits.Deposits[0].L1BridgeDeposit
Expand Down
10 changes: 2 additions & 8 deletions indexer/migrations/20230523_create_schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,7 @@ CREATE TABLE IF NOT EXISTS l2_bridge_messages(

-- StandardBridge
CREATE TABLE IF NOT EXISTS l1_bridge_deposits (
transaction_source_hash VARCHAR PRIMARY KEY REFERENCES l1_transaction_deposits(source_hash),

-- We allow the cross_domain_message_hash to be NULL-able to account
-- for scenarios where ETH is simply sent to the OptimismPortal contract
transaction_source_hash VARCHAR PRIMARY KEY REFERENCES l1_transaction_deposits(source_hash),
cross_domain_message_hash VARCHAR UNIQUE REFERENCES l1_bridge_messages(message_hash),

-- Deposit information
Expand All @@ -201,10 +198,7 @@ CREATE TABLE IF NOT EXISTS l1_bridge_deposits (
);
CREATE TABLE IF NOT EXISTS l2_bridge_withdrawals (
transaction_withdrawal_hash VARCHAR PRIMARY KEY REFERENCES l2_transaction_withdrawals(withdrawal_hash),

-- We allow the cross_domain_message_hash to be NULL-able to account for
-- scenarios where ETH is simply sent to the L2ToL1MessagePasser contract
cross_domain_message_hash VARCHAR UNIQUE REFERENCES l2_bridge_messages(message_hash),
cross_domain_message_hash VARCHAR NOT NULL UNIQUE REFERENCES l2_bridge_messages(message_hash),

-- Withdrawal information
from_address VARCHAR NOT NULL,
Expand Down
15 changes: 0 additions & 15 deletions indexer/processors/bridge/l1_bridge_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, chainConfig
return err
}

ethDeposits := []database.L1BridgeDeposit{}
portalDeposits := make(map[logKey]*contracts.OptimismPortalTransactionDepositEvent, len(optimismPortalTxDeposits))
transactionDeposits := make([]database.L1TransactionDeposit, len(optimismPortalTxDeposits))
for i := range optimismPortalTxDeposits {
Expand All @@ -39,27 +38,13 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, chainConfig
GasLimit: depositTx.GasLimit,
Tx: depositTx.Tx,
}

// catch ETH transfers to the portal contract.
if len(depositTx.DepositTx.Data) == 0 && depositTx.DepositTx.Value.BitLen() > 0 {
ethDeposits = append(ethDeposits, database.L1BridgeDeposit{
TransactionSourceHash: depositTx.DepositTx.SourceHash,
BridgeTransfer: database.BridgeTransfer{Tx: transactionDeposits[i].Tx, TokenPair: database.ETHTokenPair},
})
}
}

if len(transactionDeposits) > 0 {
log.Info("detected transaction deposits", "size", len(transactionDeposits))
if err := db.BridgeTransactions.StoreL1TransactionDeposits(transactionDeposits); err != nil {
return err
}
if len(ethDeposits) > 0 {
log.Info("detected portal ETH transfers", "size", len(ethDeposits))
if err := db.BridgeTransfers.StoreL1BridgeDeposits(ethDeposits); err != nil {
return err
}
}
}

// (2) L1CrossDomainMessenger
Expand Down

0 comments on commit 97ae6b1

Please sign in to comment.