Commit 2a82bcae authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into fix/dead-validate

parents d5a69806 7d378361
...@@ -880,7 +880,7 @@ jobs: ...@@ -880,7 +880,7 @@ jobs:
name: Test name: Test
command: | command: |
mkdir -p /test-results mkdir -p /test-results
DB_USER=postgres gotestsum --junitfile /test-results/tests.xml -- -parallel=4 ./... DB_USER=postgres gotestsum --format=standard-verbose --junitfile /test-results/tests.xml -- -parallel=4 ./...
working_directory: indexer working_directory: indexer
- run: - run:
name: Build name: Build
......
...@@ -74,9 +74,17 @@ pull_request_rules: ...@@ -74,9 +74,17 @@ pull_request_rules:
More details can be found on the `Queue: Embarked in merge train` More details can be found on the `Queue: Embarked in merge train`
check-run. check-run.
- name: Add indexer tag and ecopod reviewers - name: Add A-cannon label
conditions:
- 'files~=^cannon/'
actions:
label:
add:
- A-cannon
- name: Add A-indexer label and ecopod reviewers
conditions: conditions:
- 'files~=^indexer/' - 'files~=^indexer/'
- '#label<5'
actions: actions:
label: label:
add: add:
...@@ -84,9 +92,139 @@ pull_request_rules: ...@@ -84,9 +92,139 @@ pull_request_rules:
request_reviews: request_reviews:
users: users:
- roninjin10 - roninjin10
- name: Add sdk tag and ecopod reviewers - name: Add A-op-batcher label
conditions:
- 'files~=^op-batcher/'
actions:
label:
add:
- A-op-batcher
- name: Add A-op-bindings label
conditions:
- 'files~=^op-bindings/'
actions:
label:
add:
- A-op-bindings
- name: Add A-op-bootnode label
conditions:
- 'files~=^op-bootnode/'
- '#label<5'
actions:
label:
add:
- A-op-bootnode
- name: Add A-op-chain-ops label
conditions:
- 'files~=^op-chain-ops/'
actions:
label:
add:
- A-op-chain-ops
- name: Add A-op-challenger label
conditions:
- 'files~=^op-challenger/'
actions:
label:
add:
- A-op-challenger
- name: Add A-op-e2e label
conditions:
- 'files~=^op-e2e/'
- '#label<5'
actions:
label:
add:
- A-op-e2e
- name: Add A-op-exporter label
conditions:
- 'files~=^op-exporter/'
- '#label<5'
actions:
label:
add:
- A-op-exporter
- name: Add A-op-heartbeat label
conditions:
- 'files~=^op-heartbeat/'
- '#label<5'
actions:
label:
add:
- A-op-heartbeat
- name: Add A-op-node label
conditions:
- 'files~=^op-node/'
actions:
label:
add:
- A-op-node
- name: Add A-op-preimage label
conditions:
- 'files~=^op-preimage/'
- '#label<5'
actions:
label:
add:
- A-op-preimage
- name: Add A-op-program label
conditions:
- 'files~=^op-program/'
actions:
label:
add:
- A-op-program
- name: Add A-op-proposer label
conditions:
- 'files~=^op-proposer/'
actions:
label:
add:
- A-op-proposer
- name: Add A-op-service label
conditions:
- 'files~=^op-service/'
- '#label<5'
actions:
label:
add:
- A-op-service
- name: Add A-op-signer label
conditions:
- 'files~=^op-signer/'
- '#label<5'
actions:
label:
add:
- A-op-signer
- name: Add A-op-wheel label
conditions:
- 'files~=^op-wheel/'
- '#label<5'
actions:
label:
add:
- A-op-wheel
- name: Add A-ops-bedrock label
conditions:
- 'files~=^ops-bedrock/'
- '#label<5'
actions:
label:
add:
- A-ops-bedrock
- name: Add A-ops label
conditions:
- 'files~=^ops/'
- '#label<5'
actions:
label:
add:
- A-ops
- name: Add A-pkg-sdk label and ecopod reviewers
conditions: conditions:
- 'files~=^packages/sdk/' - 'files~=^packages/sdk/'
- '#label<5'
actions: actions:
label: label:
add: add:
...@@ -94,9 +232,10 @@ pull_request_rules: ...@@ -94,9 +232,10 @@ pull_request_rules:
request_reviews: request_reviews:
users: users:
- roninjin10 - roninjin10
- name: Add common-ts tag and ecopod reviewers - name: Add A-pkg-common-ts label and ecopod reviewers
conditions: conditions:
- 'files~=^packages/common-ts/' - 'files~=^packages/common-ts/'
- '#label<5'
actions: actions:
label: label:
add: add:
...@@ -104,3 +243,33 @@ pull_request_rules: ...@@ -104,3 +243,33 @@ pull_request_rules:
request_reviews: request_reviews:
users: users:
- roninjin10 - roninjin10
- name: Add A-pkg-contracts-bedrock label
conditions:
- 'files~=^packages/contracts-bedrock/'
actions:
label:
add:
- A-pkg-contracts-bedrock
- name: Add M-docs label
conditions:
- 'files~=^(technical-documents|specs)\/'
- '#label<5'
actions:
label:
add:
- M-docs
- name: Add M-deletion label when files are removed
conditions:
- 'removed-files~=^/'
actions:
label:
add:
- M-deletion
- name: Add M-ci label when ci files are modified
conditions:
- 'files~=^\.(github|circleci|husky)\/'
- '#label<5'
actions:
label:
add:
- M-ci
...@@ -10,8 +10,9 @@ jobs: ...@@ -10,8 +10,9 @@ jobs:
- uses: actions/stale@v4 - uses: actions/stale@v4
with: with:
stale-pr-message: 'This PR is stale because it has been open 14 days with no activity. Remove stale label or comment or this will be closed in 5 days.' stale-pr-message: 'This PR is stale because it has been open 14 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
stale-issue-label: 'S-stale'
exempt-pr-labels: exempt-stale exempt-pr-labels: exempt-stale
days-before-issue-stale: 999 days-before-issue-stale: 999
days-before-pr-stale: 14 days-before-pr-stale: 14
days-before-close: 5 days-before-close: 5
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
...@@ -209,13 +209,20 @@ This makes them a great tool for contributors! ...@@ -209,13 +209,20 @@ This makes them a great tool for contributors!
#### Filtering for Work #### Filtering for Work
To find tickets available for external contribution, take a look at the [`M-community`][M-community] label. To find tickets available for external contribution, take a look at the https://github.com/ethereum-optimism/optimism/labels/M-community label.
You can filter by the [`D-good-first-issue`][D-good-first-issue] You can filter by the https://github.com/ethereum-optimism/optimism/labels/D-good-first-issue
label to find issues that are intended to be easy to implement or fix. label to find issues that are intended to be easy to implement or fix.
Also, all labels can be seen by visiting the [labels page][labels] Also, all labels can be seen by visiting the [labels page][labels]
[labels]: https://github.com/ethereum-optimism/optimism/labels [labels]: https://github.com/ethereum-optimism/optimism/labels
[M-community]: https://github.com/ethereum-optimism/optimism/labels/M-community
[D-good-first-issue]: https://github.com/ethereum-optimism/optimism/labels/D-good-first-issue #### Modifying Labels
When altering label names or deleting labels there are a few things you must be aware of.
- This may affect the mergify bot's use of labels. See the [mergify config](.github/mergify.yml).
- If the https://github.com/ethereum-optimism/labels/S-stale label is altered, the [close-stale](.github/workflows/close-stale.yml) workflow should be updated.
- If the https://github.com/ethereum-optimism/labels/M-dependabot label is altered, the [dependabot config](.github/dependabot.yml) file should be adjusted.
- Saved label filters for project boards will not automatically update. These should be updated if label names change.
...@@ -22,4 +22,4 @@ FROM alpine:3.16 ...@@ -22,4 +22,4 @@ FROM alpine:3.16
COPY --from=builder /app/indexer/indexer /usr/local/bin COPY --from=builder /app/indexer/indexer /usr/local/bin
CMD ["indexer"] CMD ["indexer", "--config", "/app/indexer/indexer.toml"]
...@@ -46,15 +46,6 @@ func (mbv *MockBridgeTransfersView) L1BridgeDepositWithFilter(filter database.Br ...@@ -46,15 +46,6 @@ func (mbv *MockBridgeTransfersView) L1BridgeDepositWithFilter(filter database.Br
return &deposit, nil return &deposit, nil
} }
func (mbv *MockBridgeTransfersView) L1BridgeDepositsByAddress(address common.Address) ([]*database.L1BridgeDepositWithTransactionHashes, error) {
return []*database.L1BridgeDepositWithTransactionHashes{
{
L1BridgeDeposit: deposit,
L1TransactionHash: common.HexToHash("0x123"),
},
}, nil
}
func (mbv *MockBridgeTransfersView) L2BridgeWithdrawal(address common.Hash) (*database.L2BridgeWithdrawal, error) { func (mbv *MockBridgeTransfersView) L2BridgeWithdrawal(address common.Hash) (*database.L2BridgeWithdrawal, error) {
return &withdrawal, nil return &withdrawal, nil
} }
...@@ -63,15 +54,27 @@ func (mbv *MockBridgeTransfersView) L2BridgeWithdrawalWithFilter(filter database ...@@ -63,15 +54,27 @@ func (mbv *MockBridgeTransfersView) L2BridgeWithdrawalWithFilter(filter database
return &withdrawal, nil return &withdrawal, nil
} }
func (mbv *MockBridgeTransfersView) L2BridgeWithdrawalsByAddress(address common.Address) ([]*database.L2BridgeWithdrawalWithTransactionHashes, error) { func (mbv *MockBridgeTransfersView) L1BridgeDepositsByAddress(address common.Address, cursor string, limit int) (*database.L1BridgeDepositsResponse, error) {
return []*database.L2BridgeWithdrawalWithTransactionHashes{ return &database.L1BridgeDepositsResponse{
{ Deposits: []*database.L1BridgeDepositWithTransactionHashes{
L2BridgeWithdrawal: withdrawal, {
L2TransactionHash: common.HexToHash("0x789"), L1BridgeDeposit: deposit,
L1TransactionHash: common.HexToHash("0x123"),
},
}, },
}, nil }, nil
} }
func (mbv *MockBridgeTransfersView) L2BridgeWithdrawalsByAddress(address common.Address, cursor string, limit int) (*database.L2BridgeWithdrawalsResponse, error) {
return &database.L2BridgeWithdrawalsResponse{
Withdrawals: []*database.L2BridgeWithdrawalWithTransactionHashes{
{
L2BridgeWithdrawal: withdrawal,
L2TransactionHash: common.HexToHash("0x789"),
},
},
}, nil
}
func TestHealthz(t *testing.T) { func TestHealthz(t *testing.T) {
logger := testlog.Logger(t, log.LvlInfo) logger := testlog.Logger(t, log.LvlInfo)
api := NewApi(&MockBridgeTransfersView{}, logger) api := NewApi(&MockBridgeTransfersView{}, logger)
......
...@@ -2,6 +2,7 @@ package routes ...@@ -2,6 +2,7 @@ package routes
import ( import (
"net/http" "net/http"
"strconv"
"github.com/ethereum-optimism/optimism/indexer/database" "github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -29,9 +30,9 @@ type DepositResponse struct { ...@@ -29,9 +30,9 @@ type DepositResponse struct {
// TODO this is original spec but maybe include the l2 block info too for the relayed tx // TODO this is original spec but maybe include the l2 block info too for the relayed tx
// FIXME make a pure function that returns a struct instead of newWithdrawalResponse // FIXME make a pure function that returns a struct instead of newWithdrawalResponse
func newDepositResponse(deposits []*database.L1BridgeDepositWithTransactionHashes) DepositResponse { func newDepositResponse(deposits *database.L1BridgeDepositsResponse) DepositResponse {
items := make([]DepositItem, len(deposits)) items := make([]DepositItem, len(deposits.Deposits))
for _, deposit := range deposits { for _, deposit := range deposits.Deposits {
item := DepositItem{ item := DepositItem{
Guid: deposit.L1BridgeDeposit.TransactionSourceHash.String(), Guid: deposit.L1BridgeDeposit.TransactionSourceHash.String(),
Block: Block{ Block: Block{
...@@ -71,16 +72,30 @@ func newDepositResponse(deposits []*database.L1BridgeDepositWithTransactionHashe ...@@ -71,16 +72,30 @@ func newDepositResponse(deposits []*database.L1BridgeDepositWithTransactionHashe
} }
return DepositResponse{ return DepositResponse{
Cursor: "42042042-4204-4204-4204-420420420420", // TODO Cursor: deposits.Cursor,
HasNextPage: false, // TODO HasNextPage: deposits.HasNextPage,
Items: items, Items: items,
} }
} }
func (h Routes) L1DepositsHandler(w http.ResponseWriter, r *http.Request) { func (h Routes) L1DepositsHandler(w http.ResponseWriter, r *http.Request) {
address := common.HexToAddress(chi.URLParam(r, "address")) address := common.HexToAddress(chi.URLParam(r, "address"))
cursor := r.URL.Query().Get("cursor")
limitQuery := r.URL.Query().Get("limit")
deposits, err := h.BridgeTransfersView.L1BridgeDepositsByAddress(address) defaultLimit := 100
limit := defaultLimit
if limitQuery != "" {
parsedLimit, err := strconv.Atoi(limitQuery)
if err != nil {
http.Error(w, "Limit could not be parsed into a number", http.StatusBadRequest)
h.Logger.Error("Invalid limit")
h.Logger.Error(err.Error())
}
limit = parsedLimit
}
deposits, err := h.BridgeTransfersView.L1BridgeDepositsByAddress(address, cursor, limit)
if err != nil { if err != nil {
http.Error(w, "Internal server error reading deposits", http.StatusInternalServerError) http.Error(w, "Internal server error reading deposits", http.StatusInternalServerError)
h.Logger.Error("Unable to read deposits from DB") h.Logger.Error("Unable to read deposits from DB")
......
...@@ -2,6 +2,7 @@ package routes ...@@ -2,6 +2,7 @@ package routes
import ( import (
"net/http" "net/http"
"strconv"
"github.com/ethereum-optimism/optimism/indexer/database" "github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -42,9 +43,9 @@ type WithdrawalResponse struct { ...@@ -42,9 +43,9 @@ type WithdrawalResponse struct {
} }
// FIXME make a pure function that returns a struct instead of newWithdrawalResponse // FIXME make a pure function that returns a struct instead of newWithdrawalResponse
func newWithdrawalResponse(withdrawals []*database.L2BridgeWithdrawalWithTransactionHashes) WithdrawalResponse { func newWithdrawalResponse(withdrawals *database.L2BridgeWithdrawalsResponse) WithdrawalResponse {
items := make([]WithdrawalItem, len(withdrawals)) items := make([]WithdrawalItem, len(withdrawals.Withdrawals))
for _, withdrawal := range withdrawals { for _, withdrawal := range withdrawals.Withdrawals {
item := WithdrawalItem{ item := WithdrawalItem{
Guid: withdrawal.L2BridgeWithdrawal.TransactionWithdrawalHash.String(), Guid: withdrawal.L2BridgeWithdrawal.TransactionWithdrawalHash.String(),
Block: Block{ Block: Block{
...@@ -96,23 +97,34 @@ func newWithdrawalResponse(withdrawals []*database.L2BridgeWithdrawalWithTransac ...@@ -96,23 +97,34 @@ func newWithdrawalResponse(withdrawals []*database.L2BridgeWithdrawalWithTransac
} }
return WithdrawalResponse{ return WithdrawalResponse{
Cursor: "42042042-0420-4204-2042-420420420420", // TODO Cursor: withdrawals.Cursor,
HasNextPage: true, // TODO HasNextPage: withdrawals.HasNextPage,
Items: items, Items: items,
} }
} }
func (h Routes) L2WithdrawalsHandler(w http.ResponseWriter, r *http.Request) { func (h Routes) L2WithdrawalsHandler(w http.ResponseWriter, r *http.Request) {
address := common.HexToAddress(chi.URLParam(r, "address")) address := common.HexToAddress(chi.URLParam(r, "address"))
cursor := r.URL.Query().Get("cursor")
limitQuery := r.URL.Query().Get("limit")
withdrawals, err := h.BridgeTransfersView.L2BridgeWithdrawalsByAddress(address) defaultLimit := 100
limit := defaultLimit
if limitQuery != "" {
parsedLimit, err := strconv.Atoi(limitQuery)
if err != nil {
http.Error(w, "Limit could not be parsed into a number", http.StatusBadRequest)
h.Logger.Error("Invalid limit")
h.Logger.Error(err.Error())
}
limit = parsedLimit
}
withdrawals, err := h.BridgeTransfersView.L2BridgeWithdrawalsByAddress(address, cursor, limit)
if err != nil { if err != nil {
http.Error(w, "Internal server error fetching withdrawals", http.StatusInternalServerError) http.Error(w, "Internal server error reading withdrawals", http.StatusInternalServerError)
h.Logger.Error("Unable to read deposits from DB") h.Logger.Error("Unable to read withdrawals from DB")
h.Logger.Error(err.Error()) h.Logger.Error(err.Error())
return
} }
response := newWithdrawalResponse(withdrawals) response := newWithdrawalResponse(withdrawals)
jsonResponse(w, h.Logger, response, http.StatusOK) jsonResponse(w, h.Logger, response, http.StatusOK)
......
...@@ -2,6 +2,7 @@ package database ...@@ -2,6 +2,7 @@ package database
import ( import (
"errors" "errors"
"fmt"
"gorm.io/gorm" "gorm.io/gorm"
...@@ -59,11 +60,11 @@ type L2BridgeWithdrawalWithTransactionHashes struct { ...@@ -59,11 +60,11 @@ type L2BridgeWithdrawalWithTransactionHashes struct {
type BridgeTransfersView interface { type BridgeTransfersView interface {
L1BridgeDeposit(common.Hash) (*L1BridgeDeposit, error) L1BridgeDeposit(common.Hash) (*L1BridgeDeposit, error)
L1BridgeDepositWithFilter(BridgeTransfer) (*L1BridgeDeposit, error) L1BridgeDepositWithFilter(BridgeTransfer) (*L1BridgeDeposit, error)
L1BridgeDepositsByAddress(common.Address) ([]*L1BridgeDepositWithTransactionHashes, error) L1BridgeDepositsByAddress(common.Address, string, int) (*L1BridgeDepositsResponse, error)
L2BridgeWithdrawal(common.Hash) (*L2BridgeWithdrawal, error) L2BridgeWithdrawal(common.Hash) (*L2BridgeWithdrawal, error)
L2BridgeWithdrawalWithFilter(BridgeTransfer) (*L2BridgeWithdrawal, error) L2BridgeWithdrawalWithFilter(BridgeTransfer) (*L2BridgeWithdrawal, error)
L2BridgeWithdrawalsByAddress(common.Address) ([]*L2BridgeWithdrawalWithTransactionHashes, error) L2BridgeWithdrawalsByAddress(common.Address, string, int) (*L2BridgeWithdrawalsResponse, error)
} }
type BridgeTransfersDB interface { type BridgeTransfersDB interface {
...@@ -122,9 +123,20 @@ func (db *bridgeTransfersDB) L1BridgeDepositWithFilter(filter BridgeTransfer) (* ...@@ -122,9 +123,20 @@ func (db *bridgeTransfersDB) L1BridgeDepositWithFilter(filter BridgeTransfer) (*
return &deposit, nil return &deposit, nil
} }
type L1BridgeDepositsResponse struct {
Deposits []*L1BridgeDepositWithTransactionHashes
Cursor string
HasNextPage bool
}
// L1BridgeDepositsByAddress retrieves a list of deposits intiated by the specified address, coupled with the L1/L2 transaction // L1BridgeDepositsByAddress retrieves a list of deposits intiated by the specified address, coupled with the L1/L2 transaction
// hashes that complete the bridge transaction. // hashes that complete the bridge transaction.
func (db *bridgeTransfersDB) L1BridgeDepositsByAddress(address common.Address) ([]*L1BridgeDepositWithTransactionHashes, error) { 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(` depositsQuery := db.gorm.Table("l1_bridge_deposits").Select(`
l1_bridge_deposits.*, l1_bridge_deposits.*,
l1_contract_events.transaction_hash AS l1_transaction_hash, l1_contract_events.transaction_hash AS l1_transaction_hash,
...@@ -134,8 +146,11 @@ l1_transaction_deposits.l2_transaction_hash`) ...@@ -134,8 +146,11 @@ l1_transaction_deposits.l2_transaction_hash`)
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_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") depositsQuery = depositsQuery.Joins("INNER JOIN l1_contract_events ON l1_transaction_deposits.initiated_l1_event_guid = l1_contract_events.guid")
// add in cursoring options if cursor != "" {
filteredQuery := depositsQuery.Where(&Transaction{FromAddress: address}).Order("l1_bridge_deposits.timestamp DESC").Limit(100) 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)
deposits := []*L1BridgeDepositWithTransactionHashes{} deposits := []*L1BridgeDepositWithTransactionHashes{}
result := filteredQuery.Scan(&deposits) result := filteredQuery.Scan(&deposits)
...@@ -146,7 +161,24 @@ l1_transaction_deposits.l2_transaction_hash`) ...@@ -146,7 +161,24 @@ l1_transaction_deposits.l2_transaction_hash`)
return nil, result.Error return nil, result.Error
} }
return deposits, nil hasNextPage := false
if len(deposits) > limit {
hasNextPage = true
deposits = deposits[:limit]
}
nextCursor := ""
if hasNextPage {
nextCursor = deposits[len(deposits)-1].L1TransactionHash.String()
}
response := &L1BridgeDepositsResponse{
Deposits: deposits,
Cursor: nextCursor,
HasNextPage: hasNextPage,
}
return response, nil
} }
/** /**
...@@ -186,9 +218,20 @@ func (db *bridgeTransfersDB) L2BridgeWithdrawalWithFilter(filter BridgeTransfer) ...@@ -186,9 +218,20 @@ func (db *bridgeTransfersDB) L2BridgeWithdrawalWithFilter(filter BridgeTransfer)
return &withdrawal, nil return &withdrawal, nil
} }
type L2BridgeWithdrawalsResponse struct {
Withdrawals []*L2BridgeWithdrawalWithTransactionHashes
Cursor string
HasNextPage bool
}
// L2BridgeDepositsByAddress retrieves a list of deposits intiated by the specified address, coupled with the L1/L2 transaction hashes // L2BridgeDepositsByAddress retrieves a list of deposits intiated by the specified address, coupled with the L1/L2 transaction hashes
// that complete the bridge transaction. The hashes that correspond to with the Bedrock multistep withdrawal process are also surfaced // that complete the bridge transaction. The hashes that correspond to with the Bedrock multistep withdrawal process are also surfaced
func (db *bridgeTransfersDB) L2BridgeWithdrawalsByAddress(address common.Address) ([]*L2BridgeWithdrawalWithTransactionHashes, error) { func (db *bridgeTransfersDB) L2BridgeWithdrawalsByAddress(address common.Address, cursor string, limit int) (*L2BridgeWithdrawalsResponse, error) {
defaultLimit := 100
if limit <= 0 {
limit = defaultLimit
}
withdrawalsQuery := db.gorm.Table("l2_bridge_withdrawals").Select(` withdrawalsQuery := db.gorm.Table("l2_bridge_withdrawals").Select(`
l2_bridge_withdrawals.*, l2_bridge_withdrawals.*,
l2_contract_events.transaction_hash AS l2_transaction_hash, l2_contract_events.transaction_hash AS l2_transaction_hash,
...@@ -200,8 +243,11 @@ finalized_l1_contract_events.transaction_hash AS finalized_l1_transaction_hash`) ...@@ -200,8 +243,11 @@ finalized_l1_contract_events.transaction_hash AS finalized_l1_transaction_hash`)
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 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") 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")
// add in cursoring options if cursor != "" {
filteredQuery := withdrawalsQuery.Where(&Transaction{FromAddress: address}).Order("l2_bridge_withdrawals.timestamp DESC").Limit(100) withdrawalsQuery = withdrawalsQuery.Where("l2_bridge_withdrawals.id < ?", cursor)
}
filteredQuery := withdrawalsQuery.Where(&Transaction{FromAddress: address}).Order("l2_bridge_withdrawals.timestamp DESC").Limit(limit + 1)
withdrawals := []*L2BridgeWithdrawalWithTransactionHashes{} withdrawals := []*L2BridgeWithdrawalWithTransactionHashes{}
result := filteredQuery.Scan(&withdrawals) result := filteredQuery.Scan(&withdrawals)
...@@ -212,5 +258,28 @@ finalized_l1_contract_events.transaction_hash AS finalized_l1_transaction_hash`) ...@@ -212,5 +258,28 @@ finalized_l1_contract_events.transaction_hash AS finalized_l1_transaction_hash`)
return nil, result.Error return nil, result.Error
} }
return withdrawals, nil hasNextPage := false
if len(withdrawals) > limit {
hasNextPage = true
withdrawals = withdrawals[:limit]
}
nextCursor := ""
if hasNextPage {
nextCursor = fmt.Sprintf("%d", withdrawals[len(withdrawals)-1].L2TransactionHash)
}
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
response := &L2BridgeWithdrawalsResponse{
Withdrawals: withdrawals,
Cursor: nextCursor,
HasNextPage: hasNextPage,
}
return response, nil
} }
...@@ -28,7 +28,7 @@ CREATE TABLE IF NOT EXISTS l2_block_headers ( ...@@ -28,7 +28,7 @@ CREATE TABLE IF NOT EXISTS l2_block_headers (
rlp_bytes VARCHAR NOT NULL rlp_bytes VARCHAR NOT NULL
); );
/** /**
* EVENT DATA * EVENT DATA
*/ */
......
...@@ -4,13 +4,13 @@ import ( ...@@ -4,13 +4,13 @@ import (
"context" "context"
"math/big" "math/big"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
) )
type FaultDisputeGameCaller interface { type FaultDisputeGameCaller interface {
...@@ -53,7 +53,7 @@ func (fc *FaultCaller) LogGameInfo(ctx context.Context) { ...@@ -53,7 +53,7 @@ func (fc *FaultCaller) LogGameInfo(ctx context.Context) {
fc.log.Error("failed to get claim count", "err", err) fc.log.Error("failed to get claim count", "err", err)
return return
} }
fc.log.Info("Game info", "claims", claimLen, "status", GameStatusString(status)) fc.log.Info("Game info", "claims", claimLen, "status", status)
} }
// GetGameStatus returns the current game status. // GetGameStatus returns the current game status.
...@@ -78,17 +78,3 @@ func (fc *FaultCaller) LogClaimDataLength(ctx context.Context) { ...@@ -78,17 +78,3 @@ func (fc *FaultCaller) LogClaimDataLength(ctx context.Context) {
} }
fc.log.Info("Number of claims", "length", claimLen) fc.log.Info("Number of claims", "length", claimLen)
} }
// GameStatusString returns the current game status as a string.
func GameStatusString(status types.GameStatus) string {
switch status {
case types.GameStatusInProgress:
return "In Progress"
case types.GameStatusChallengerWon:
return "Challenger Won"
case types.GameStatusDefenderWon:
return "Defender Won"
default:
return "Unknown"
}
}
...@@ -32,7 +32,7 @@ type Executor struct { ...@@ -32,7 +32,7 @@ type Executor struct {
logger log.Logger logger log.Logger
l1 string l1 string
l2 string l2 string
inputs localGameInputs inputs LocalGameInputs
cannon string cannon string
server string server string
network string network string
...@@ -45,7 +45,7 @@ type Executor struct { ...@@ -45,7 +45,7 @@ type Executor struct {
cmdExecutor cmdExecutor cmdExecutor cmdExecutor
} }
func NewExecutor(logger log.Logger, cfg *config.Config, inputs localGameInputs) *Executor { func NewExecutor(logger log.Logger, cfg *config.Config, inputs LocalGameInputs) *Executor {
return &Executor{ return &Executor{
logger: logger, logger: logger,
l1: cfg.L1EthRpc, l1: cfg.L1EthRpc,
...@@ -92,11 +92,11 @@ func (e *Executor) GenerateProof(ctx context.Context, dir string, i uint64) erro ...@@ -92,11 +92,11 @@ func (e *Executor) GenerateProof(ctx context.Context, dir string, i uint64) erro
"--l1", e.l1, "--l1", e.l1,
"--l2", e.l2, "--l2", e.l2,
"--datadir", dataDir, "--datadir", dataDir,
"--l1.head", e.inputs.l1Head.Hex(), "--l1.head", e.inputs.L1Head.Hex(),
"--l2.head", e.inputs.l2Head.Hex(), "--l2.head", e.inputs.L2Head.Hex(),
"--l2.outputroot", e.inputs.l2OutputRoot.Hex(), "--l2.outputroot", e.inputs.L2OutputRoot.Hex(),
"--l2.claim", e.inputs.l2Claim.Hex(), "--l2.claim", e.inputs.L2Claim.Hex(),
"--l2.blocknumber", e.inputs.l2BlockNumber.Text(10), "--l2.blocknumber", e.inputs.L2BlockNumber.Text(10),
) )
if e.network != "" { if e.network != "" {
args = append(args, "--network", e.network) args = append(args, "--network", e.network)
......
...@@ -29,12 +29,12 @@ func TestGenerateProof(t *testing.T) { ...@@ -29,12 +29,12 @@ func TestGenerateProof(t *testing.T) {
cfg.CannonL2 = "http://localhost:9999" cfg.CannonL2 = "http://localhost:9999"
cfg.CannonSnapshotFreq = 500 cfg.CannonSnapshotFreq = 500
inputs := localGameInputs{ inputs := LocalGameInputs{
l1Head: common.Hash{0x11}, L1Head: common.Hash{0x11},
l2Head: common.Hash{0x22}, L2Head: common.Hash{0x22},
l2OutputRoot: common.Hash{0x33}, L2OutputRoot: common.Hash{0x33},
l2Claim: common.Hash{0x44}, L2Claim: common.Hash{0x44},
l2BlockNumber: big.NewInt(3333), L2BlockNumber: big.NewInt(3333),
} }
captureExec := func(t *testing.T, cfg config.Config, proofAt uint64) (string, string, map[string]string) { captureExec := func(t *testing.T, cfg config.Config, proofAt uint64) (string, string, map[string]string) {
executor := NewExecutor(testlog.Logger(t, log.LvlInfo), &cfg, inputs) executor := NewExecutor(testlog.Logger(t, log.LvlInfo), &cfg, inputs)
...@@ -94,10 +94,10 @@ func TestGenerateProof(t *testing.T) { ...@@ -94,10 +94,10 @@ func TestGenerateProof(t *testing.T) {
require.NotContains(t, args, "--l2.genesis") require.NotContains(t, args, "--l2.genesis")
// Local game inputs // Local game inputs
require.Equal(t, inputs.l1Head.Hex(), args["--l1.head"]) require.Equal(t, inputs.L1Head.Hex(), args["--l1.head"])
require.Equal(t, inputs.l2Head.Hex(), args["--l2.head"]) require.Equal(t, inputs.L2Head.Hex(), args["--l2.head"])
require.Equal(t, inputs.l2OutputRoot.Hex(), args["--l2.outputroot"]) require.Equal(t, inputs.L2OutputRoot.Hex(), args["--l2.outputroot"])
require.Equal(t, inputs.l2Claim.Hex(), args["--l2.claim"]) require.Equal(t, inputs.L2Claim.Hex(), args["--l2.claim"])
require.Equal(t, "3333", args["--l2.blocknumber"]) require.Equal(t, "3333", args["--l2.blocknumber"])
}) })
......
...@@ -11,12 +11,12 @@ import ( ...@@ -11,12 +11,12 @@ import (
ethtypes "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types"
) )
type localGameInputs struct { type LocalGameInputs struct {
l1Head common.Hash L1Head common.Hash
l2Head common.Hash L2Head common.Hash
l2OutputRoot common.Hash L2OutputRoot common.Hash
l2Claim common.Hash L2Claim common.Hash
l2BlockNumber *big.Int L2BlockNumber *big.Int
} }
type L2DataSource interface { type L2DataSource interface {
...@@ -32,30 +32,30 @@ type GameInputsSource interface { ...@@ -32,30 +32,30 @@ type GameInputsSource interface {
}, error) }, error)
} }
func fetchLocalInputs(ctx context.Context, gameAddr common.Address, caller GameInputsSource, l2Client L2DataSource) (localGameInputs, error) { func fetchLocalInputs(ctx context.Context, gameAddr common.Address, caller GameInputsSource, l2Client L2DataSource) (LocalGameInputs, error) {
opts := &bind.CallOpts{Context: ctx} opts := &bind.CallOpts{Context: ctx}
l1Head, err := caller.L1Head(opts) l1Head, err := caller.L1Head(opts)
if err != nil { if err != nil {
return localGameInputs{}, fmt.Errorf("fetch L1 head for game %v: %w", gameAddr, err) return LocalGameInputs{}, fmt.Errorf("fetch L1 head for game %v: %w", gameAddr, err)
} }
proposals, err := caller.Proposals(opts) proposals, err := caller.Proposals(opts)
if err != nil { if err != nil {
return localGameInputs{}, fmt.Errorf("fetch proposals: %w", err) return LocalGameInputs{}, fmt.Errorf("fetch proposals: %w", err)
} }
claimedOutput := proposals.Disputed claimedOutput := proposals.Disputed
agreedOutput := proposals.Starting agreedOutput := proposals.Starting
agreedHeader, err := l2Client.HeaderByNumber(ctx, agreedOutput.L2BlockNumber) agreedHeader, err := l2Client.HeaderByNumber(ctx, agreedOutput.L2BlockNumber)
if err != nil { if err != nil {
return localGameInputs{}, fmt.Errorf("fetch L2 block header %v: %w", agreedOutput.L2BlockNumber, err) return LocalGameInputs{}, fmt.Errorf("fetch L2 block header %v: %w", agreedOutput.L2BlockNumber, err)
} }
l2Head := agreedHeader.Hash() l2Head := agreedHeader.Hash()
return localGameInputs{ return LocalGameInputs{
l1Head: l1Head, L1Head: l1Head,
l2Head: l2Head, L2Head: l2Head,
l2OutputRoot: agreedOutput.OutputRoot, L2OutputRoot: agreedOutput.OutputRoot,
l2Claim: claimedOutput.OutputRoot, L2Claim: claimedOutput.OutputRoot,
l2BlockNumber: claimedOutput.L2BlockNumber, L2BlockNumber: claimedOutput.L2BlockNumber,
}, nil }, nil
} }
...@@ -39,11 +39,11 @@ func TestFetchLocalInputs(t *testing.T) { ...@@ -39,11 +39,11 @@ func TestFetchLocalInputs(t *testing.T) {
inputs, err := fetchLocalInputs(ctx, gameAddr, l1Client, l2Client) inputs, err := fetchLocalInputs(ctx, gameAddr, l1Client, l2Client)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, l1Client.l1Head, inputs.l1Head) require.Equal(t, l1Client.l1Head, inputs.L1Head)
require.Equal(t, l2Client.header.Hash(), inputs.l2Head) require.Equal(t, l2Client.header.Hash(), inputs.L2Head)
require.EqualValues(t, l1Client.starting.OutputRoot, inputs.l2OutputRoot) require.EqualValues(t, l1Client.starting.OutputRoot, inputs.L2OutputRoot)
require.EqualValues(t, l1Client.disputed.OutputRoot, inputs.l2Claim) require.EqualValues(t, l1Client.disputed.OutputRoot, inputs.L2Claim)
require.Equal(t, l1Client.disputed.L2BlockNumber, inputs.l2BlockNumber) require.Equal(t, l1Client.disputed.L2BlockNumber, inputs.L2BlockNumber)
} }
type mockGameInputsSource struct { type mockGameInputsSource struct {
......
...@@ -60,16 +60,20 @@ func NewTraceProvider(ctx context.Context, logger log.Logger, cfg *config.Config ...@@ -60,16 +60,20 @@ func NewTraceProvider(ctx context.Context, logger log.Logger, cfg *config.Config
if err != nil { if err != nil {
return nil, fmt.Errorf("create caller for game %v: %w", cfg.GameAddress, err) return nil, fmt.Errorf("create caller for game %v: %w", cfg.GameAddress, err)
} }
l1Head, err := fetchLocalInputs(ctx, cfg.GameAddress, gameCaller, l2Client) localInputs, err := fetchLocalInputs(ctx, cfg.GameAddress, gameCaller, l2Client)
if err != nil { if err != nil {
return nil, fmt.Errorf("fetch local game inputs: %w", err) return nil, fmt.Errorf("fetch local game inputs: %w", err)
} }
return NewTraceProviderFromInputs(logger, cfg, localInputs), nil
}
func NewTraceProviderFromInputs(logger log.Logger, cfg *config.Config, localInputs LocalGameInputs) *CannonTraceProvider {
return &CannonTraceProvider{ return &CannonTraceProvider{
logger: logger, logger: logger,
dir: cfg.CannonDatadir, dir: cfg.CannonDatadir,
prestate: cfg.CannonAbsolutePreState, prestate: cfg.CannonAbsolutePreState,
generator: NewExecutor(logger, cfg, l1Head), generator: NewExecutor(logger, cfg, localInputs),
}, nil }
} }
func (p *CannonTraceProvider) Get(ctx context.Context, i uint64) (common.Hash, error) { func (p *CannonTraceProvider) Get(ctx context.Context, i uint64) (common.Hash, error) {
......
...@@ -4,8 +4,9 @@ import ( ...@@ -4,8 +4,9 @@ import (
"context" "context"
"time" "time"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
) )
type GameInfo interface { type GameInfo interface {
...@@ -51,9 +52,9 @@ func progressGame(ctx context.Context, logger log.Logger, agreeWithProposedOutpu ...@@ -51,9 +52,9 @@ func progressGame(ctx context.Context, logger log.Logger, agreeWithProposedOutpu
expectedStatus = types.GameStatusDefenderWon expectedStatus = types.GameStatusDefenderWon
} }
if expectedStatus == status { if expectedStatus == status {
logger.Info("Game won", "status", GameStatusString(status)) logger.Info("Game won", "status", status)
} else { } else {
logger.Error("Game lost", "status", GameStatusString(status)) logger.Error("Game lost", "status", status)
} }
return true return true
} else { } else {
......
...@@ -5,10 +5,11 @@ import ( ...@@ -5,10 +5,11 @@ import (
"errors" "errors"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum-optimism/optimism/op-node/testlog"
) )
func TestMonitorExitsWhenContextDone(t *testing.T) { func TestMonitorExitsWhenContextDone(t *testing.T) {
...@@ -48,7 +49,6 @@ func TestProgressGame_LogErrorWhenGameLost(t *testing.T) { ...@@ -48,7 +49,6 @@ func TestProgressGame_LogErrorWhenGameLost(t *testing.T) {
agreeWithOutput bool agreeWithOutput bool
logLevel log.Lvl logLevel log.Lvl
logMsg string logMsg string
statusText string
}{ }{
{ {
name: "GameLostAsDefender", name: "GameLostAsDefender",
...@@ -56,7 +56,6 @@ func TestProgressGame_LogErrorWhenGameLost(t *testing.T) { ...@@ -56,7 +56,6 @@ func TestProgressGame_LogErrorWhenGameLost(t *testing.T) {
agreeWithOutput: false, agreeWithOutput: false,
logLevel: log.LvlError, logLevel: log.LvlError,
logMsg: "Game lost", logMsg: "Game lost",
statusText: "Challenger Won",
}, },
{ {
name: "GameLostAsChallenger", name: "GameLostAsChallenger",
...@@ -64,7 +63,6 @@ func TestProgressGame_LogErrorWhenGameLost(t *testing.T) { ...@@ -64,7 +63,6 @@ func TestProgressGame_LogErrorWhenGameLost(t *testing.T) {
agreeWithOutput: true, agreeWithOutput: true,
logLevel: log.LvlError, logLevel: log.LvlError,
logMsg: "Game lost", logMsg: "Game lost",
statusText: "Defender Won",
}, },
{ {
name: "GameWonAsDefender", name: "GameWonAsDefender",
...@@ -72,7 +70,6 @@ func TestProgressGame_LogErrorWhenGameLost(t *testing.T) { ...@@ -72,7 +70,6 @@ func TestProgressGame_LogErrorWhenGameLost(t *testing.T) {
agreeWithOutput: false, agreeWithOutput: false,
logLevel: log.LvlInfo, logLevel: log.LvlInfo,
logMsg: "Game won", logMsg: "Game won",
statusText: "Defender Won",
}, },
{ {
name: "GameWonAsChallenger", name: "GameWonAsChallenger",
...@@ -80,7 +77,6 @@ func TestProgressGame_LogErrorWhenGameLost(t *testing.T) { ...@@ -80,7 +77,6 @@ func TestProgressGame_LogErrorWhenGameLost(t *testing.T) {
agreeWithOutput: true, agreeWithOutput: true,
logLevel: log.LvlInfo, logLevel: log.LvlInfo,
logMsg: "Game won", logMsg: "Game won",
statusText: "Challenger Won",
}, },
} }
for _, test := range tests { for _, test := range tests {
...@@ -94,7 +90,7 @@ func TestProgressGame_LogErrorWhenGameLost(t *testing.T) { ...@@ -94,7 +90,7 @@ func TestProgressGame_LogErrorWhenGameLost(t *testing.T) {
require.Equal(t, 0, gameInfo.logCount, "should not log latest game state") require.Equal(t, 0, gameInfo.logCount, "should not log latest game state")
errLog := handler.FindLog(test.logLevel, test.logMsg) errLog := handler.FindLog(test.logLevel, test.logMsg)
require.NotNil(t, errLog, "should log game result") require.NotNil(t, errLog, "should log game result")
require.Equal(t, test.statusText, errLog.GetContextValue("status")) require.Equal(t, test.status, errLog.GetContextValue("status"))
}) })
} }
} }
......
...@@ -20,6 +20,20 @@ const ( ...@@ -20,6 +20,20 @@ const (
GameStatusDefenderWon GameStatusDefenderWon
) )
// String returns the string representation of the game status.
func (s GameStatus) String() string {
switch s {
case GameStatusInProgress:
return "In Progress"
case GameStatusChallengerWon:
return "Challenger Won"
case GameStatusDefenderWon:
return "Defender Won"
default:
return "Unknown"
}
}
// PreimageOracleData encapsulates the preimage oracle data // PreimageOracleData encapsulates the preimage oracle data
// to load into the onchain oracle. // to load into the onchain oracle.
type PreimageOracleData struct { type PreimageOracleData struct {
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"os" "os"
"path/filepath" "path/filepath"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/fault/cannon" "github.com/ethereum-optimism/optimism/op-challenger/fault/cannon"
...@@ -12,8 +13,10 @@ import ( ...@@ -12,8 +13,10 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/testlog" "github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
) )
type CannonGameHelper struct { type CannonGameHelper struct {
...@@ -21,7 +24,7 @@ type CannonGameHelper struct { ...@@ -21,7 +24,7 @@ type CannonGameHelper struct {
} }
func (g *CannonGameHelper) StartChallenger(ctx context.Context, rollupCfg *rollup.Config, l2Genesis *core.Genesis, l1Endpoint string, l2Endpoint string, name string, options ...challenger.Option) *challenger.Helper { func (g *CannonGameHelper) StartChallenger(ctx context.Context, rollupCfg *rollup.Config, l2Genesis *core.Genesis, l1Endpoint string, l2Endpoint string, name string, options ...challenger.Option) *challenger.Helper {
opts := []challenger.Option{g.createConfigOption(rollupCfg, l2Genesis, l2Endpoint)} opts := []challenger.Option{createConfigOption(g.t, rollupCfg, l2Genesis, g.addr, l2Endpoint)}
opts = append(opts, options...) opts = append(opts, options...)
c := challenger.NewChallenger(g.t, ctx, l1Endpoint, name, opts...) c := challenger.NewChallenger(g.t, ctx, l1Endpoint, name, opts...)
g.t.Cleanup(func() { g.t.Cleanup(func() {
...@@ -31,10 +34,10 @@ func (g *CannonGameHelper) StartChallenger(ctx context.Context, rollupCfg *rollu ...@@ -31,10 +34,10 @@ func (g *CannonGameHelper) StartChallenger(ctx context.Context, rollupCfg *rollu
} }
func (g *CannonGameHelper) CreateHonestActor(ctx context.Context, rollupCfg *rollup.Config, l2Genesis *core.Genesis, l1Client bind.ContractCaller, l1Endpoint string, l2Endpoint string, options ...challenger.Option) *HonestHelper { func (g *CannonGameHelper) CreateHonestActor(ctx context.Context, rollupCfg *rollup.Config, l2Genesis *core.Genesis, l1Client bind.ContractCaller, l1Endpoint string, l2Endpoint string, options ...challenger.Option) *HonestHelper {
opts := []challenger.Option{g.createConfigOption(rollupCfg, l2Genesis, l2Endpoint)} opts := []challenger.Option{createConfigOption(g.t, rollupCfg, l2Genesis, g.addr, l2Endpoint)}
opts = append(opts, options...) opts = append(opts, options...)
cfg := challenger.NewChallengerConfig(g.t, l1Endpoint, opts...) cfg := challenger.NewChallengerConfig(g.t, l1Endpoint, opts...)
provider, err := cannon.NewTraceProvider(ctx, testlog.Logger(g.t, log.LvlTrace).New("role", "CorrectTrace"), cfg, l1Client) provider, err := cannon.NewTraceProvider(ctx, testlog.Logger(g.t, log.LvlInfo).New("role", "CorrectTrace"), cfg, l1Client)
g.require.NoError(err, "create cannon trace provider") g.require.NoError(err, "create cannon trace provider")
return &HonestHelper{ return &HonestHelper{
...@@ -45,28 +48,29 @@ func (g *CannonGameHelper) CreateHonestActor(ctx context.Context, rollupCfg *rol ...@@ -45,28 +48,29 @@ func (g *CannonGameHelper) CreateHonestActor(ctx context.Context, rollupCfg *rol
} }
} }
func (g *CannonGameHelper) createConfigOption(rollupCfg *rollup.Config, l2Genesis *core.Genesis, l2Endpoint string) challenger.Option { func createConfigOption(t *testing.T, rollupCfg *rollup.Config, l2Genesis *core.Genesis, gameAddr common.Address, l2Endpoint string) challenger.Option {
return func(c *config.Config) { return func(c *config.Config) {
c.GameAddress = g.addr require := require.New(t)
c.GameAddress = gameAddr
c.TraceType = config.TraceTypeCannon c.TraceType = config.TraceTypeCannon
c.AgreeWithProposedOutput = false c.AgreeWithProposedOutput = false
c.CannonL2 = l2Endpoint c.CannonL2 = l2Endpoint
c.CannonBin = "../cannon/bin/cannon" c.CannonBin = "../cannon/bin/cannon"
c.CannonDatadir = g.t.TempDir() c.CannonDatadir = t.TempDir()
c.CannonServer = "../op-program/bin/op-program" c.CannonServer = "../op-program/bin/op-program"
c.CannonAbsolutePreState = "../op-program/bin/prestate.json" c.CannonAbsolutePreState = "../op-program/bin/prestate.json"
c.CannonSnapshotFreq = 10_000_000 c.CannonSnapshotFreq = 10_000_000
genesisBytes, err := json.Marshal(l2Genesis) genesisBytes, err := json.Marshal(l2Genesis)
g.require.NoError(err, "marshall l2 genesis config") require.NoError(err, "marshall l2 genesis config")
genesisFile := filepath.Join(c.CannonDatadir, "l2-genesis.json") genesisFile := filepath.Join(c.CannonDatadir, "l2-genesis.json")
g.require.NoError(os.WriteFile(genesisFile, genesisBytes, 0644)) require.NoError(os.WriteFile(genesisFile, genesisBytes, 0644))
c.CannonL2GenesisPath = genesisFile c.CannonL2GenesisPath = genesisFile
rollupBytes, err := json.Marshal(rollupCfg) rollupBytes, err := json.Marshal(rollupCfg)
g.require.NoError(err, "marshall rollup config") require.NoError(err, "marshall rollup config")
rollupFile := filepath.Join(c.CannonDatadir, "rollup.json") rollupFile := filepath.Join(c.CannonDatadir, "rollup.json")
g.require.NoError(os.WriteFile(rollupFile, rollupBytes, 0644)) require.NoError(os.WriteFile(rollupFile, rollupBytes, 0644))
c.CannonRollupConfigPath = rollupFile c.CannonRollupConfigPath = rollupFile
} }
} }
...@@ -42,7 +42,7 @@ func (g *FaultGameHelper) WaitForClaimCount(ctx context.Context, count int64) { ...@@ -42,7 +42,7 @@ func (g *FaultGameHelper) WaitForClaimCount(ctx context.Context, count int64) {
g.t.Log("Waiting for claim count", "current", actual, "expected", count, "game", g.addr) g.t.Log("Waiting for claim count", "current", actual, "expected", count, "game", g.addr)
return actual.Cmp(big.NewInt(count)) == 0, nil return actual.Cmp(big.NewInt(count)) == 0, nil
}) })
g.require.NoError(err) g.require.NoErrorf(err, "Did not find expected claim count %v", count)
} }
type ContractClaim struct { type ContractClaim struct {
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"math"
"math/big" "math/big"
"testing" "testing"
"time" "time"
...@@ -13,11 +14,16 @@ import ( ...@@ -13,11 +14,16 @@ import (
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/fault/alphabet" "github.com/ethereum-optimism/optimism/op-challenger/fault/alphabet"
"github.com/ethereum-optimism/optimism/op-challenger/fault/cannon"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -125,9 +131,57 @@ func (h *FactoryHelper) StartAlphabetGame(ctx context.Context, claimedAlphabet s ...@@ -125,9 +131,57 @@ func (h *FactoryHelper) StartAlphabetGame(ctx context.Context, claimedAlphabet s
} }
func (h *FactoryHelper) StartCannonGame(ctx context.Context, rootClaim common.Hash) *CannonGameHelper { func (h *FactoryHelper) StartCannonGame(ctx context.Context, rootClaim common.Hash) *CannonGameHelper {
l2BlockNumber := h.waitForProposals(ctx) l2BlockNumber, l1Head := h.prepareCannonGame(ctx)
l1Head := h.checkpointL1Block(ctx) return h.createCannonGame(ctx, l2BlockNumber, l1Head, rootClaim)
}
func (h *FactoryHelper) StartCannonGameWithCorrectRoot(ctx context.Context, rollupCfg *rollup.Config, l2Genesis *core.Genesis, l1Endpoint string, l2Endpoint string, options ...challenger.Option) (*CannonGameHelper, *HonestHelper) {
l2BlockNumber, l1Head := h.prepareCannonGame(ctx)
challengerOpts := []challenger.Option{createConfigOption(h.t, rollupCfg, l2Genesis, common.Address{0xaa}, l2Endpoint)}
challengerOpts = append(challengerOpts, options...)
cfg := challenger.NewChallengerConfig(h.t, l1Endpoint, challengerOpts...)
opts := &bind.CallOpts{Context: ctx}
outputIdx, err := h.l2oo.GetL2OutputIndexAfter(opts, new(big.Int).SetUint64(l2BlockNumber))
h.require.NoError(err, "Fetch challenged output index")
challengedOutput, err := h.l2oo.GetL2Output(opts, outputIdx)
h.require.NoError(err, "Fetch challenged output")
agreedOutput, err := h.l2oo.GetL2Output(opts, new(big.Int).Sub(outputIdx, common.Big1))
h.require.NoError(err, "Fetch agreed output")
l1BlockInfo, err := h.blockOracle.Load(opts, l1Head)
h.require.NoError(err, "Fetch L1 block info")
l2Client, err := ethclient.DialContext(ctx, cfg.CannonL2)
if err != nil {
h.require.NoErrorf(err, "Failed to dial l2 client %v", l2Endpoint)
}
defer l2Client.Close()
agreedHeader, err := l2Client.HeaderByNumber(ctx, agreedOutput.L2BlockNumber)
if err != nil {
h.require.NoErrorf(err, "Failed to fetch L2 block header %v", agreedOutput.L2BlockNumber)
}
inputs := cannon.LocalGameInputs{
L1Head: l1BlockInfo.Hash,
L2Head: agreedHeader.Hash(),
L2OutputRoot: agreedOutput.OutputRoot,
L2Claim: challengedOutput.OutputRoot,
L2BlockNumber: challengedOutput.L2BlockNumber,
}
provider := cannon.NewTraceProviderFromInputs(testlog.Logger(h.t, log.LvlInfo).New("role", "CorrectTrace"), cfg, inputs)
rootClaim, err := provider.Get(ctx, math.MaxUint64)
h.require.NoError(err, "Compute correct root hash")
game := h.createCannonGame(ctx, l2BlockNumber, l1Head, rootClaim)
honestHelper := &HonestHelper{
t: h.t,
require: h.require,
game: &game.FaultGameHelper,
correctTrace: provider,
}
return game, honestHelper
}
func (h *FactoryHelper) createCannonGame(ctx context.Context, l2BlockNumber uint64, l1Head *big.Int, rootClaim common.Hash) *CannonGameHelper {
ctx, cancel := context.WithTimeout(ctx, 1*time.Minute) ctx, cancel := context.WithTimeout(ctx, 1*time.Minute)
defer cancel() defer cancel()
...@@ -171,6 +225,12 @@ func (h *FactoryHelper) StartChallenger(ctx context.Context, l1Endpoint string, ...@@ -171,6 +225,12 @@ func (h *FactoryHelper) StartChallenger(ctx context.Context, l1Endpoint string,
return c return c
} }
func (h *FactoryHelper) prepareCannonGame(ctx context.Context) (uint64, *big.Int) {
l2BlockNumber := h.waitForProposals(ctx)
l1Head := h.checkpointL1Block(ctx)
return l2BlockNumber, l1Head
}
// waitForProposals waits until there are at least two proposals in the output oracle // waitForProposals waits until there are at least two proposals in the output oracle
// This is the minimum required for creating a game. // This is the minimum required for creating a game.
// Returns the l2 block number of the latest available proposal // Returns the l2 block number of the latest available proposal
......
...@@ -290,6 +290,54 @@ func TestCannonDefendStep(t *testing.T) { ...@@ -290,6 +290,54 @@ func TestCannonDefendStep(t *testing.T) {
game.LogGameData(ctx) game.LogGameData(ctx)
} }
func TestCannonChallengeWithCorrectRoot(t *testing.T) {
t.Skip("Not currently handling this case as the correct approach will change when output root bisection is added")
InitParallel(t)
ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t)
t.Cleanup(sys.Close)
l1Endpoint := sys.NodeEndpoint("l1")
l2Endpoint := sys.NodeEndpoint("sequencer")
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys.cfg.L1Deployments, l1Client)
game, correctTrace := disputeGameFactory.StartCannonGameWithCorrectRoot(ctx, sys.RollupConfig, sys.L2GenesisCfg, l1Endpoint, l2Endpoint, func(c *config.Config) {
c.TxMgrConfig.PrivateKey = e2eutils.EncodePrivKeyToString(sys.cfg.Secrets.Mallory)
})
require.NotNil(t, game)
game.LogGameData(ctx)
game.StartChallenger(ctx, sys.RollupConfig, sys.L2GenesisCfg, l1Endpoint, l2Endpoint, "Challenger", func(c *config.Config) {
c.AgreeWithProposedOutput = true // Agree with the proposed output, so disagree with the root claim
c.TxMgrConfig.PrivateKey = e2eutils.EncodePrivKeyToString(sys.cfg.Secrets.Alice)
})
maxDepth := game.MaxDepth(ctx)
for claimCount := int64(1); claimCount < maxDepth; {
game.LogGameData(ctx)
claimCount++
// Wait for the challenger to counter
game.WaitForClaimCount(ctx, claimCount)
// Defend everything because we have the same trace as the honest proposer
correctTrace.Defend(ctx, claimCount-1)
claimCount++
game.LogGameData(ctx)
game.WaitForClaimCount(ctx, claimCount)
}
game.LogGameData(ctx)
// Wait for the challenger to call step and counter our invalid claim
game.WaitForClaimAtMaxDepth(ctx, true)
sys.TimeTravelClock.AdvanceTime(game.GameDuration(ctx))
require.NoError(t, wait.ForNextBlock(ctx, l1Client))
game.WaitForGameStatus(ctx, disputegame.StatusChallengerWins)
game.LogGameData(ctx)
}
func startFaultDisputeSystem(t *testing.T) (*System, *ethclient.Client) { func startFaultDisputeSystem(t *testing.T) (*System, *ethclient.Client) {
cfg := DefaultSystemConfig(t) cfg := DefaultSystemConfig(t)
delete(cfg.Nodes, "verifier") delete(cfg.Nodes, "verifier")
......
...@@ -56,7 +56,11 @@ log = logging.getLogger(__name__) ...@@ -56,7 +56,11 @@ log = logging.getLogger(__name__)
def main(): def main():
patterns = sys.argv[1].split(',') patterns = sys.argv[1].split(',')
patterns = patterns + REBUILD_ALL_PATTERNS
# temporarily only run indexer tests if indexer is changed because the tests are flaky
if len(patterns) != 1 or patterns[0] != "indexer":
patterns = patterns + REBUILD_ALL_PATTERNS
fp = os.path.realpath(__file__) fp = os.path.realpath(__file__)
monorepo_path = os.path.realpath(os.path.join(fp, '..', '..')) monorepo_path = os.path.realpath(os.path.join(fp, '..', '..'))
......
...@@ -67,7 +67,7 @@ contract Multichain is SafeBuilder { ...@@ -67,7 +67,7 @@ contract Multichain is SafeBuilder {
/// @notice L2OutputOracle implementation to upgrade to /// @notice L2OutputOracle implementation to upgrade to
address internal constant L2OutputOracleImplementation = 0xaBd96C062c6B640d5670455E9d1cD98383Dd23CA; address internal constant L2OutputOracleImplementation = 0xaBd96C062c6B640d5670455E9d1cD98383Dd23CA;
/// @notice OptimismMintableERC20Factory to upgrade to /// @notice OptimismMintableERC20Factory to upgrade to
address internal constant OptimismMintableERC20FactoryImplementation = 0xE220F7D7fF39837003A1835fCefFa8bCA4098582; address internal constant OptimismMintableERC20FactoryImplementation = 0xdfe97868233d1aa22e815a266982f2cf17685a27;
/// @notice OptimismPortal implementation to upgrade to /// @notice OptimismPortal implementation to upgrade to
address internal constant OptimismPortalImplementation = 0x345D27c7B6C90fef5beA9631037C36119f4bF93e; address internal constant OptimismPortalImplementation = 0x345D27c7B6C90fef5beA9631037C36119f4bF93e;
/// @notice SystemConfig implementation to upgrade to /// @notice SystemConfig implementation to upgrade to
...@@ -82,7 +82,7 @@ contract Multichain is SafeBuilder { ...@@ -82,7 +82,7 @@ contract Multichain is SafeBuilder {
string internal constant L1CrossDomainMessengerVersion = "1.5.1"; string internal constant L1CrossDomainMessengerVersion = "1.5.1";
string internal constant L1StandardBridgeVersion = "1.2.1"; string internal constant L1StandardBridgeVersion = "1.2.1";
string internal constant L2OutputOracleVersion = "1.4.1"; string internal constant L2OutputOracleVersion = "1.4.1";
string internal constant OptimismMintableERC20FactoryVersion = "1.1.2"; string internal constant OptimismMintableERC20FactoryVersion = "1.3.0";
string internal constant OptimismPortalVersion = "1.8.1"; string internal constant OptimismPortalVersion = "1.8.1";
string internal constant SystemConfigVersion = "1.6.0"; string internal constant SystemConfigVersion = "1.6.0";
string internal constant L1ERC721BridgeVersion = "1.2.1"; string internal constant L1ERC721BridgeVersion = "1.2.1";
...@@ -393,7 +393,13 @@ contract Multichain is SafeBuilder { ...@@ -393,7 +393,13 @@ contract Multichain is SafeBuilder {
target: _proxyAdmin, target: _proxyAdmin,
allowFailure: false, allowFailure: false,
callData: abi.encodeCall( callData: abi.encodeCall(
ProxyAdmin.upgrade, (payable(prox.OptimismMintableERC20Factory), OptimismMintableERC20FactoryImplementation) ProxyAdmin.upgradeAndCall,
(
payable(prox.OptimismMintableERC20Factory), // proxy
OptimismMintableERC20FactoryImplementation, // implementation
abi.encodeCall( // data
OptimismMintableERC20Factory.initialize, (prox.L1StandardBridge))
)
) )
}); });
......
# @eth-optimism/web3.js-plugin
## 0.1.1
### Patch Changes
- [#6848](https://github.com/ethereum-optimism/optimism/pull/6848) [`b08fccd9e`](https://github.com/ethereum-optimism/optimism/commit/b08fccd9e21c499f9fefd4d58fb8a36bfa0d800a) Thanks [@spacesailor24](https://github.com/spacesailor24)! - Correct use of web3js-plugin to web3.js-plugin in README. Rename OptimismFeeEstimationPlugin export to OptimismPlugin
# @eth-optimism/web3js-plugin # @eth-optimism/web3.js-plugin
This web3.js plugin adds utility functions for estimating L1 and L2 gas for OP chains by wrapping the [GasPriceOracle](../contracts-bedrock/contracts/l2/GasPriceOracle.sol) contract This web3.js plugin adds utility functions for estimating L1 and L2 gas for OP chains by wrapping the [GasPriceOracle](../contracts-bedrock/contracts/l2/GasPriceOracle.sol) contract
...@@ -13,22 +13,22 @@ This plugin is intended to be [registered](https://docs.web3js.org/guides/web3_p ...@@ -13,22 +13,22 @@ This plugin is intended to be [registered](https://docs.web3js.org/guides/web3_p
### Installing the Plugin ### Installing the Plugin
```bash ```bash
pnpm install @eth-optimism/web3js-plugin pnpm install @eth-optimism/web3.js-plugin
``` ```
```bash ```bash
npm install @eth-optimism/web3js-plugin npm install @eth-optimism/web3.js-plugin
``` ```
```bash ```bash
yarn add @eth-optimism/web3js-plugin yarn add @eth-optimism/web3.js-plugin
``` ```
### Registering the Plugin ### Registering the Plugin
```typescript ```typescript
import Web3 from 'web3' import Web3 from 'web3'
import OptimismFeeEstimationPlugin from '@eth-optimism/web3js-plugin' import OptimismFeeEstimationPlugin from '@eth-optimism/web3.js-plugin'
const web3 = new Web3('http://yourProvider.com') const web3 = new Web3('http://yourProvider.com')
web3.registerPlugin(new OptimismFeeEstimationPlugin()) web3.registerPlugin(new OptimismFeeEstimationPlugin())
...@@ -65,8 +65,8 @@ async estimateFees(transaction: Transaction, returnFormat?: ReturnFormat) ...@@ -65,8 +65,8 @@ async estimateFees(transaction: Transaction, returnFormat?: ReturnFormat)
#### Parameters #### Parameters
- `transaction: Transaction` - An unsigned web3.js [transaction](https://docs.web3js.org/api/web3-types/interface/Transaction) object - `transaction: Transaction` - An unsigned web3.js [transaction](https://docs.web3js.org/api/web3-types/interface/Transaction) object
- `returnFormat?: ReturnFormat` - A web3.js [DataFormat](https://docs.web3js.org/api/web3-types#DataFormat) object that specifies how to format number and bytes values - `returnFormat?: ReturnFormat` - A web3.js [DataFormat][1] object that specifies how to format number and bytes values
- If `returnFormat` is not provided, [DEFAULT_RETURN_FORMAT](https://docs.web3js.org/api/web3-types#DEFAULT_RETURN_FORMAT) is used which will format numbers to `BigInt`s - If `returnFormat` is not provided, [DEFAULT_RETURN_FORMAT][2] is used which will format numbers to `BigInt`s
#### Returns #### Returns
...@@ -168,8 +168,8 @@ async getL1Fee(transaction: Transaction, returnFormat?: ReturnFormat) ...@@ -168,8 +168,8 @@ async getL1Fee(transaction: Transaction, returnFormat?: ReturnFormat)
#### Parameters #### Parameters
- `transaction: Transaction` - An unsigned web3.js [transaction](https://docs.web3js.org/api/web3-types/interface/Transaction) object - `transaction: Transaction` - An unsigned web3.js [transaction](https://docs.web3js.org/api/web3-types/interface/Transaction) object
- `returnFormat?: ReturnFormat` - A web3.js [DataFormat](https://docs.web3js.org/api/web3-types#DataFormat) object that specifies how to format number and bytes values - `returnFormat?: ReturnFormat` - A web3.js [DataFormat][1] object that specifies how to format number and bytes values
- If `returnFormat` is not provided, [DEFAULT_RETURN_FORMAT](https://docs.web3js.org/api/web3-types#DEFAULT_RETURN_FORMAT) is used which will format numbers to `BigInt`s - If `returnFormat` is not provided, [DEFAULT_RETURN_FORMAT][2] is used which will format numbers to `BigInt`s
#### Returns #### Returns
...@@ -235,8 +235,8 @@ async getL2Fee(transaction: Transaction, returnFormat?: ReturnFormat) ...@@ -235,8 +235,8 @@ async getL2Fee(transaction: Transaction, returnFormat?: ReturnFormat)
- A web3.js [Numbers](https://docs.web3js.org/api/web3-types#Numbers) - A web3.js [Numbers](https://docs.web3js.org/api/web3-types#Numbers)
- A web3.js [BlockTags](https://docs.web3js.org/api/web3-types/enum/BlockTags) - A web3.js [BlockTags](https://docs.web3js.org/api/web3-types/enum/BlockTags)
- If not provided, `BlockTags.LATEST` is used - If not provided, `BlockTags.LATEST` is used
- `returnFormat?: ReturnFormat` - A web3.js [DataFormat](https://docs.web3js.org/api/web3-types#DataFormat) object that specifies how to format number and bytes values - `returnFormat?: ReturnFormat` - A web3.js [DataFormat][1] object that specifies how to format number and bytes values
- If `returnFormat` is not provided, [DEFAULT_RETURN_FORMAT](https://docs.web3js.org/api/web3-types#DEFAULT_RETURN_FORMAT) is used which will format numbers to `BigInt`s - If `returnFormat` is not provided, [DEFAULT_RETURN_FORMAT][2] is used which will format numbers to `BigInt`s
#### Returns #### Returns
...@@ -301,8 +301,8 @@ async getBaseFee(returnFormat?: ReturnFormat) ...@@ -301,8 +301,8 @@ async getBaseFee(returnFormat?: ReturnFormat)
#### Parameters #### Parameters
- `returnFormat?: ReturnFormat` - A web3.js [DataFormat](https://docs.web3js.org/api/web3-types#DataFormat) object that specifies how to format number and bytes values - `returnFormat?: ReturnFormat` - A web3.js [DataFormat][1] object that specifies how to format number and bytes values
- If `returnFormat` is not provided, [DEFAULT_RETURN_FORMAT](https://docs.web3js.org/api/web3-types#DEFAULT_RETURN_FORMAT) is used which will format numbers to `BigInt`s - If `returnFormat` is not provided, [DEFAULT_RETURN_FORMAT][2] is used which will format numbers to `BigInt`s
#### Returns #### Returns
...@@ -337,8 +337,8 @@ async getDecimals(returnFormat?: ReturnFormat) ...@@ -337,8 +337,8 @@ async getDecimals(returnFormat?: ReturnFormat)
#### Parameters #### Parameters
- `returnFormat?: ReturnFormat` - A web3.js [DataFormat](https://docs.web3js.org/api/web3-types#DataFormat) object that specifies how to format number and bytes values - `returnFormat?: ReturnFormat` - A web3.js [DataFormat][3] object that specifies how to format number and bytes values
- If `returnFormat` is not provided, [DEFAULT_RETURN_FORMAT](https://docs.web3js.org/api/web3-types#DEFAULT_RETURN_FORMAT) is used which will format numbers to `BigInt`s - If `returnFormat` is not provided, [DEFAULT_RETURN_FORMAT][2] is used which will format numbers to `BigInt`s
#### Returns #### Returns
...@@ -373,8 +373,8 @@ async getGasPrice(returnFormat?: ReturnFormat) ...@@ -373,8 +373,8 @@ async getGasPrice(returnFormat?: ReturnFormat)
#### Parameters #### Parameters
- `returnFormat?: ReturnFormat` - A web3.js [DataFormat](https://docs.web3js.org/api/web3-types#DataFormat) object that specifies how to format number and bytes values - `returnFormat?: ReturnFormat` - A web3.js [DataFormat][3] object that specifies how to format number and bytes values
- If `returnFormat` is not provided, [DEFAULT_RETURN_FORMAT](https://docs.web3js.org/api/web3-types#DEFAULT_RETURN_FORMAT) is used which will format numbers to `BigInt`s - If `returnFormat` is not provided, [DEFAULT_RETURN_FORMAT][2] is used which will format numbers to `BigInt`s
#### Returns #### Returns
...@@ -410,8 +410,8 @@ async getL1GasUsed(transaction: Transaction, returnFormat?: ReturnFormat) ...@@ -410,8 +410,8 @@ async getL1GasUsed(transaction: Transaction, returnFormat?: ReturnFormat)
#### Parameters #### Parameters
- `transaction: Transaction` - An unsigned web3.js [transaction](https://docs.web3js.org/api/web3-types/interface/Transaction) object - `transaction: Transaction` - An unsigned web3.js [transaction](https://docs.web3js.org/api/web3-types/interface/Transaction) object
- `returnFormat?: ReturnFormat` - A web3.js [DataFormat](https://docs.web3js.org/api/web3-types#DataFormat) object that specifies how to format number and bytes values - `returnFormat?: ReturnFormat` - A web3.js [DataFormat][3] object that specifies how to format number and bytes values
- If `returnFormat` is not provided, [DEFAULT_RETURN_FORMAT](https://docs.web3js.org/api/web3-types#DEFAULT_RETURN_FORMAT) is used which will format numbers to `BigInt`s - If `returnFormat` is not provided, [DEFAULT_RETURN_FORMAT][2] is used which will format numbers to `BigInt`s
#### Returns #### Returns
...@@ -470,8 +470,8 @@ async getL1BaseFee(returnFormat?: ReturnFormat) ...@@ -470,8 +470,8 @@ async getL1BaseFee(returnFormat?: ReturnFormat)
#### Parameters #### Parameters
- `returnFormat?: ReturnFormat` - A web3.js [DataFormat](https://docs.web3js.org/api/web3-types#DataFormat) object that specifies how to format number and bytes values - `returnFormat?: ReturnFormat` - A web3.js [DataFormat][3] object that specifies how to format number and bytes values
- If `returnFormat` is not provided, [DEFAULT_RETURN_FORMAT](https://docs.web3js.org/api/web3-types#DEFAULT_RETURN_FORMAT) is used which will format numbers to `BigInt`s - If `returnFormat` is not provided, [DEFAULT_RETURN_FORMAT][2] is used which will format numbers to `BigInt`s
#### Returns #### Returns
...@@ -506,8 +506,8 @@ async getOverhead(returnFormat?: ReturnFormat) ...@@ -506,8 +506,8 @@ async getOverhead(returnFormat?: ReturnFormat)
#### Parameters #### Parameters
- `returnFormat?: ReturnFormat` - A web3.js [DataFormat](https://docs.web3js.org/api/web3-types#DataFormat) object that specifies how to format number and bytes values - `returnFormat?: ReturnFormat` - A web3.js [DataFormat][3] object that specifies how to format number and bytes values
- If `returnFormat` is not provided, [DEFAULT_RETURN_FORMAT](https://docs.web3js.org/api/web3-types#DEFAULT_RETURN_FORMAT) is used which will format numbers to `BigInt`s - If `returnFormat` is not provided, [DEFAULT_RETURN_FORMAT][2] is used which will format numbers to `BigInt`s
#### Returns #### Returns
...@@ -542,8 +542,8 @@ async getScalar(returnFormat?: ReturnFormat) ...@@ -542,8 +542,8 @@ async getScalar(returnFormat?: ReturnFormat)
#### Parameters #### Parameters
- `returnFormat?: ReturnFormat` - A web3.js [DataFormat](https://docs.web3js.org/api/web3-types#DataFormat) object that specifies how to format number and bytes values - `returnFormat?: ReturnFormat` - A web3.js [DataFormat][1] object that specifies how to format number and bytes values
- If `returnFormat` is not provided, [DEFAULT_RETURN_FORMAT](https://docs.web3js.org/api/web3-types#DEFAULT_RETURN_FORMAT) is used which will format numbers to `BigInt`s - If `returnFormat` is not provided, [DEFAULT_RETURN_FORMAT][2] is used which will format numbers to `BigInt`s
#### Returns #### Returns
...@@ -592,3 +592,7 @@ console.log(version) // 1.0.0 ...@@ -592,3 +592,7 @@ console.log(version) // 1.0.0
- As of version `4.0.3` of web3.js, both `input` and `data` parameters are automatically added to a transaction objects causing the gas estimations to be inflated. This was corrected in [this](https://github.com/web3/web3.js/pull/6294) PR, but has yet to be released - As of version `4.0.3` of web3.js, both `input` and `data` parameters are automatically added to a transaction objects causing the gas estimations to be inflated. This was corrected in [this](https://github.com/web3/web3.js/pull/6294) PR, but has yet to be released
- For the plugin function `getL2Fee`, you should be able to get the fee estimates using the state of the blockchain at a specified block, however, this doesn't seem to be working with web3.js and requires further investigation - For the plugin function `getL2Fee`, you should be able to get the fee estimates using the state of the blockchain at a specified block, however, this doesn't seem to be working with web3.js and requires further investigation
[1]: https://docs.web3js.org/api/web3-types#DataFormat
[2]: https://docs.web3js.org/api/web3-types#DEFAULT_RETURN_FORMAT
[3]: https://docs.web3js.org/api/web3-types#DataFormat
\ No newline at end of file
{ {
"name": "@eth-optimism/web3.js-plugin", "name": "@eth-optimism/web3.js-plugin",
"version": "0.1.0", "version": "0.1.1",
"description": "A Web3.js plugin for doing OP-Chain gas estimation", "description": "A Web3.js plugin for doing OP-Chain gas estimation",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
......
...@@ -8,7 +8,7 @@ import { ...@@ -8,7 +8,7 @@ import {
optimistAddress, optimistAddress,
} from '@eth-optimism/contracts-ts' } from '@eth-optimism/contracts-ts'
import { OptimismFeeEstimationPlugin } from './plugin' import { OptimismPlugin } from './plugin'
const defaultProvider = 'https://mainnet.optimism.io' const defaultProvider = 'https://mainnet.optimism.io'
const provider = z const provider = z
...@@ -21,17 +21,17 @@ if (provider === defaultProvider) ...@@ -21,17 +21,17 @@ if (provider === defaultProvider)
'Warning: Using default public provider, this could cause tests to fail due to rate limits. Set the VITE_L2_RPC_URL env to override default provider' 'Warning: Using default public provider, this could cause tests to fail due to rate limits. Set the VITE_L2_RPC_URL env to override default provider'
) )
describe('OptimismFeeEstimationPlugin', () => { describe('OptimismPlugin', () => {
let web3: Web3 let web3: Web3
beforeAll(() => { beforeAll(() => {
web3 = new Web3(provider) web3 = new Web3(provider)
web3.registerPlugin(new OptimismFeeEstimationPlugin()) web3.registerPlugin(new OptimismPlugin())
}) })
test('should be registered under .op namespace', () => test('should be registered under .op namespace', () =>
expect(web3.op).toMatchInlineSnapshot(` expect(web3.op).toMatchInlineSnapshot(`
OptimismFeeEstimationPlugin { OptimismPlugin {
"_accountProvider": { "_accountProvider": {
"create": [Function], "create": [Function],
"decrypt": [Function], "decrypt": [Function],
...@@ -62,7 +62,7 @@ describe('OptimismFeeEstimationPlugin', () => { ...@@ -62,7 +62,7 @@ describe('OptimismFeeEstimationPlugin', () => {
Symbol(kCapture): false, Symbol(kCapture): false,
}, },
"_provider": HttpProvider { "_provider": HttpProvider {
"clientUrl": "https://mainnet.optimism.io", "clientUrl": "https://opt-mainnet.g.alchemy.com/v2/OVlbpe9COlhG-ijOXGvL_phb5ns6p9-w",
"httpProviderOptions": undefined, "httpProviderOptions": undefined,
}, },
"useRpcCallSpecification": undefined, "useRpcCallSpecification": undefined,
...@@ -88,7 +88,7 @@ describe('OptimismFeeEstimationPlugin', () => { ...@@ -88,7 +88,7 @@ describe('OptimismFeeEstimationPlugin', () => {
Symbol(kCapture): false, Symbol(kCapture): false,
}, },
"_provider": HttpProvider { "_provider": HttpProvider {
"clientUrl": "https://mainnet.optimism.io", "clientUrl": "https://opt-mainnet.g.alchemy.com/v2/OVlbpe9COlhG-ijOXGvL_phb5ns6p9-w",
"httpProviderOptions": undefined, "httpProviderOptions": undefined,
}, },
"useRpcCallSpecification": undefined, "useRpcCallSpecification": undefined,
......
...@@ -17,7 +17,7 @@ import { ...@@ -17,7 +17,7 @@ import {
} from '@eth-optimism/contracts-ts' } from '@eth-optimism/contracts-ts'
import { RLP } from '@ethereumjs/rlp' import { RLP } from '@ethereumjs/rlp'
export class OptimismFeeEstimationPlugin extends Web3PluginBase { export class OptimismPlugin extends Web3PluginBase {
public pluginNamespace = 'op' public pluginNamespace = 'op'
private _gasPriceOracleContract: private _gasPriceOracleContract:
...@@ -323,6 +323,6 @@ export class OptimismFeeEstimationPlugin extends Web3PluginBase { ...@@ -323,6 +323,6 @@ export class OptimismFeeEstimationPlugin extends Web3PluginBase {
// Module Augmentation to add op namespace to root {web3} instance // Module Augmentation to add op namespace to root {web3} instance
declare module 'web3' { declare module 'web3' {
interface Web3Context { interface Web3Context {
op: OptimismFeeEstimationPlugin op: OptimismPlugin
} }
} }
This diff is collapsed.
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