Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 56 additions & 22 deletions indexer/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ func (d *Database) AddIndexedL1Block(block *IndexedL1Block) error {
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
`

const updateWithdrawalStatement = `
UPDATE withdrawals SET (br_withdrawal_finalized_tx_hash, br_withdrawal_finalized_log_index, br_withdrawal_finalized_success) = ($1, $2, $3)
WHERE br_withdrawal_hash = $4
`

return txn(d.db, func(tx *sql.Tx) error {
_, err := tx.Exec(
insertBlockStatement,
Expand All @@ -209,26 +215,39 @@ func (d *Database) AddIndexedL1Block(block *IndexedL1Block) error {
return err
}

if len(block.Deposits) == 0 {
return nil
if len(block.Deposits) > 0 {
for _, deposit := range block.Deposits {
_, err = tx.Exec(
insertDepositStatement,
NewGUID(),
deposit.FromAddress.String(),
deposit.ToAddress.String(),
deposit.L1Token.String(),
deposit.L2Token.String(),
deposit.Amount.String(),
deposit.TxHash.String(),
deposit.LogIndex,
block.Hash.String(),
deposit.Data,
)
if err != nil {
return err
}
}
}

for _, deposit := range block.Deposits {
_, err = tx.Exec(
insertDepositStatement,
NewGUID(),
deposit.FromAddress.String(),
deposit.ToAddress.String(),
deposit.L1Token.String(),
deposit.L2Token.String(),
deposit.Amount.String(),
deposit.TxHash.String(),
deposit.LogIndex,
block.Hash.String(),
deposit.Data,
)
if err != nil {
return err
if len(block.FinalizedWithdrawals) > 0 {
for _, wd := range block.FinalizedWithdrawals {
_, err = tx.Exec(
updateWithdrawalStatement,
wd.TxHash.String(),
wd.LogIndex,
wd.Success,
wd.WithdrawalHash.String(),
)
if err != nil {
return err
}
}
}

Expand Down Expand Up @@ -459,19 +478,21 @@ func (d *Database) GetWithdrawalBatch(hash common.Hash) (*StateBatchJSON, error)

// GetWithdrawalsByAddress returns the list of Withdrawals indexed for the given
// address paginated by the given params.
func (d *Database) GetWithdrawalsByAddress(address common.Address, page PaginationParam) (*PaginatedWithdrawals, error) {
func (d *Database) GetWithdrawalsByAddress(address common.Address, page PaginationParam, state FinalizationState) (*PaginatedWithdrawals, error) {
selectWithdrawalsStatement := fmt.Sprintf(`
SELECT
withdrawals.guid, withdrawals.from_address, withdrawals.to_address,
withdrawals.amount, withdrawals.tx_hash, withdrawals.data,
withdrawals.l1_token, withdrawals.l2_token,
l2_tokens.name, l2_tokens.symbol, l2_tokens.decimals,
l2_blocks.number, l2_blocks.timestamp, withdrawals.br_withdrawal_hash
l2_blocks.number, l2_blocks.timestamp, withdrawals.br_withdrawal_hash,
withdrawals.br_withdrawal_finalized_tx_hash, withdrawals.br_withdrawal_finalized_log_index,
withdrawals.br_withdrawal_finalized_success
FROM withdrawals
INNER JOIN l2_blocks ON withdrawals.block_hash=l2_blocks.hash
INNER JOIN l2_tokens ON withdrawals.l2_token=l2_tokens.address
WHERE withdrawals.from_address = $1 %s ORDER BY l2_blocks.timestamp LIMIT $2 OFFSET $3;
`, FinalizationStateAny.SQL())
`, state.SQL())
var withdrawals []WithdrawalJSON

err := txn(d.db, func(tx *sql.Tx) error {
Expand All @@ -485,20 +506,33 @@ func (d *Database) GetWithdrawalsByAddress(address common.Address, page Paginati
var withdrawal WithdrawalJSON
var l2Token Token
var wdHash sql.NullString
var finTxHash sql.NullString
var finLogIndex sql.NullInt32
var finSuccess sql.NullBool
if err := rows.Scan(
&withdrawal.GUID, &withdrawal.FromAddress, &withdrawal.ToAddress,
&withdrawal.Amount, &withdrawal.TxHash, &withdrawal.Data,
&withdrawal.L1Token, &l2Token.Address,
&l2Token.Name, &l2Token.Symbol, &l2Token.Decimals,
&withdrawal.BlockNumber, &withdrawal.BlockTimestamp,
&wdHash,
&wdHash, &finTxHash, &finLogIndex, &finSuccess,
); err != nil {
return err
}
withdrawal.L2Token = &l2Token
if wdHash.Valid {
withdrawal.BedrockWithdrawalHash = &wdHash.String
}
if finTxHash.Valid {
withdrawal.BedrockFinalizedTxHash = &finTxHash.String
}
if finLogIndex.Valid {
idx := int(finLogIndex.Int32)
withdrawal.BedrockFinalizedLogIndex = &idx
}
if finSuccess.Valid {
withdrawal.BedrockFinalizedSuccess = &finSuccess.Bool
}
withdrawals = append(withdrawals, withdrawal)
}

Expand Down
11 changes: 6 additions & 5 deletions indexer/db/l1block.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import (

// IndexedL1Block contains the L1 block including the deposits in it.
type IndexedL1Block struct {
Hash common.Hash
ParentHash common.Hash
Number uint64
Timestamp uint64
Deposits []Deposit
Hash common.Hash
ParentHash common.Hash
Number uint64
Timestamp uint64
Deposits []Deposit
FinalizedWithdrawals []FinalizedWithdrawal
}

// String returns the block hash for the indexed l1 block.
Expand Down
4 changes: 2 additions & 2 deletions indexer/db/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ CREATE TABLE IF NOT EXISTS airdrops (
const updateWithdrawalsTable = `
ALTER TABLE withdrawals ADD COLUMN IF NOT EXISTS br_withdrawal_hash VARCHAR NULL;
ALTER TABLE withdrawals ADD COLUMN IF NOT EXISTS br_withdrawal_finalized_tx_hash VARCHAR NULL;
ALTER TABLE withdrawals ADD COLUMN IF NOT EXISTS br_withdrawal_finalized_log_index BOOLEAN NULL;
ALTER TABLE withdrawals ADD COLUMN IF NOT EXISTS br_withdrawal_success BOOLEAN NULL;
ALTER TABLE withdrawals ADD COLUMN IF NOT EXISTS br_withdrawal_finalized_log_index INTEGER NULL;
ALTER TABLE withdrawals ADD COLUMN IF NOT EXISTS br_withdrawal_finalized_success BOOLEAN NULL;
CREATE INDEX IF NOT EXISTS withdrawals_br_withdrawal_hash ON withdrawals(br_withdrawal_hash);
`

Expand Down
33 changes: 18 additions & 15 deletions indexer/db/withdrawal.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,22 @@ func (w Withdrawal) String() string {

// WithdrawalJSON contains Withdrawal data suitable for JSON serialization.
type WithdrawalJSON struct {
GUID string `json:"guid"`
FromAddress string `json:"from"`
ToAddress string `json:"to"`
L1Token string `json:"l1Token"`
L2Token *Token `json:"l2Token"`
Amount string `json:"amount"`
Data []byte `json:"data"`
LogIndex uint64 `json:"logIndex"`
BlockNumber uint64 `json:"blockNumber"`
BlockTimestamp string `json:"blockTimestamp"`
TxHash string `json:"transactionHash"`
Batch *StateBatchJSON `json:"batch"`
BedrockWithdrawalHash *string `json:"bedrockWithdrawalHash"`
GUID string `json:"guid"`
FromAddress string `json:"from"`
ToAddress string `json:"to"`
L1Token string `json:"l1Token"`
L2Token *Token `json:"l2Token"`
Amount string `json:"amount"`
Data []byte `json:"data"`
LogIndex uint64 `json:"logIndex"`
BlockNumber uint64 `json:"blockNumber"`
BlockTimestamp string `json:"blockTimestamp"`
TxHash string `json:"transactionHash"`
Batch *StateBatchJSON `json:"batch"`
BedrockWithdrawalHash *string `json:"bedrockWithdrawalHash"`
BedrockFinalizedTxHash *string `json:"bedrockFinalizedTxHash"`
BedrockFinalizedLogIndex *int `json:"bedrockFinalizedLogIndex"`
BedrockFinalizedSuccess *bool `json:"bedrockFinalizedSuccess"`
}

type FinalizationState int
Expand All @@ -64,9 +67,9 @@ func ParseFinalizationState(in string) FinalizationState {
func (f FinalizationState) SQL() string {
switch f {
case FinalizationStateFinalized:
return "AND withdrawals.l1_block_hash IS NOT NULL"
return "AND withdrawals.br_withdrawal_finalized_tx_hash IS NOT NULL"
case FinalizationStateUnfinalized:
return "AND withdrawals.l2_block_hash IS NULL"
return "AND withdrawals.br_withdrawal_finalized_tx_hash IS NULL"
}

return ""
Expand Down
105 changes: 105 additions & 0 deletions indexer/services/addresses.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package services

import (
"github.com/ethereum-optimism/optimism/indexer/bindings/legacy/scc"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
)

type AddressManager interface {
L1StandardBridge() (common.Address, *bindings.L1StandardBridge)
StateCommitmentChain() (common.Address, *scc.StateCommitmentChain)
OptimismPortal() (common.Address, *bindings.OptimismPortal)
}

type LegacyAddresses struct {
l1SB *bindings.L1StandardBridge
l1SBAddr common.Address
scc *scc.StateCommitmentChain
sccAddr common.Address
}

var _ AddressManager = (*LegacyAddresses)(nil)

func NewLegacyAddresses(client bind.ContractBackend, addrMgrAddr common.Address) (AddressManager, error) {
mgr, err := bindings.NewAddressManager(addrMgrAddr, client)
if err != nil {
return nil, err
}

l1SBAddr, err := mgr.GetAddress(nil, "Proxy__OVM_L1StandardBridge")
if err != nil {
return nil, err
}
sccAddr, err := mgr.GetAddress(nil, "StateCommitmentChain")
if err != nil {
return nil, err
}
l1SB, err := bindings.NewL1StandardBridge(l1SBAddr, client)
if err != nil {
return nil, err
}
sccContract, err := scc.NewStateCommitmentChain(sccAddr, client)
if err != nil {
return nil, err
}

return &LegacyAddresses{
l1SB: l1SB,
l1SBAddr: l1SBAddr,
scc: sccContract,
sccAddr: sccAddr,
}, nil
}

func (a *LegacyAddresses) L1StandardBridge() (common.Address, *bindings.L1StandardBridge) {
return a.l1SBAddr, a.l1SB
}

func (a *LegacyAddresses) StateCommitmentChain() (common.Address, *scc.StateCommitmentChain) {
return a.sccAddr, a.scc
}

func (a *LegacyAddresses) OptimismPortal() (common.Address, *bindings.OptimismPortal) {
panic("OptimismPortal not configured on legacy networks - this is a programmer error")
}

type BedrockAddresses struct {
l1SB *bindings.L1StandardBridge
l1SBAddr common.Address
portal *bindings.OptimismPortal
portalAddr common.Address
}

var _ AddressManager = (*BedrockAddresses)(nil)

func NewBedrockAddresses(client bind.ContractBackend, l1SBAddr, portalAddr common.Address) (AddressManager, error) {
l1SB, err := bindings.NewL1StandardBridge(l1SBAddr, client)
if err != nil {
return nil, err
}
portal, err := bindings.NewOptimismPortal(portalAddr, client)
if err != nil {
return nil, err
}

return &BedrockAddresses{
l1SB: l1SB,
l1SBAddr: l1SBAddr,
portal: portal,
portalAddr: portalAddr,
}, nil
}

func (b *BedrockAddresses) L1StandardBridge() (common.Address, *bindings.L1StandardBridge) {
return b.l1SBAddr, b.l1SB
}

func (b *BedrockAddresses) StateCommitmentChain() (common.Address, *scc.StateCommitmentChain) {
panic("SCC not configured on legacy networks - this is a programmer error")
}

func (b *BedrockAddresses) OptimismPortal() (common.Address, *bindings.OptimismPortal) {
return b.portalAddr, b.portal
}
11 changes: 10 additions & 1 deletion indexer/services/l1/bridge/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,17 @@ import (
"github.com/ethereum/go-ethereum/common"
)

// DepositsMap is a collection of deposit objects keyed
// on block hashes.
type DepositsMap map[common.Hash][]db.Deposit
type WithdrawalsMap map[common.Hash][]db.Withdrawal // Finalizations

// WithdrawalsMap is a collection of withdrawal objects keyed
// on block hashes.
type WithdrawalsMap map[common.Hash][]db.Withdrawal

// FinalizedWithdrawalsMap is a collection of finalized withdrawal
// objected keyed on block hashes.
type FinalizedWithdrawalsMap map[common.Hash][]db.FinalizedWithdrawal

type Bridge interface {
Address() common.Address
Expand Down
63 changes: 63 additions & 0 deletions indexer/services/l1/bridge/portal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package bridge

import (
"context"

"github.com/ethereum-optimism/optimism/indexer/db"
"github.com/ethereum-optimism/optimism/indexer/services"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-service/backoff"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
)

type Portal struct {
address common.Address
contract *bindings.OptimismPortal
}

func NewPortal(addrs services.AddressManager) *Portal {
address, contract := addrs.OptimismPortal()

return &Portal{
address: address,
contract: contract,
}
}

func (p *Portal) Address() common.Address {
return p.address
}

func (p *Portal) GetFinalizedWithdrawalsByBlockRange(ctx context.Context, start, end uint64) (FinalizedWithdrawalsMap, error) {
wdsByBlockHash := make(FinalizedWithdrawalsMap)
opts := &bind.FilterOpts{
Context: ctx,
Start: start,
End: &end,
}

var iter *bindings.OptimismPortalWithdrawalFinalizedIterator
err := backoff.Do(3, backoff.Exponential(), func() error {
var err error
iter, err = p.contract.FilterWithdrawalFinalized(opts, nil)
return err
})
if err != nil {
return nil, err
}

defer iter.Close()
for iter.Next() {
wdsByBlockHash[iter.Event.Raw.BlockHash] = append(
wdsByBlockHash[iter.Event.Raw.BlockHash], db.FinalizedWithdrawal{
TxHash: iter.Event.Raw.TxHash,
WithdrawalHash: iter.Event.WithdrawalHash,
Success: iter.Event.Success,
LogIndex: iter.Event.Raw.Index,
},
)
}

return wdsByBlockHash, iter.Error()
}
Loading