Commit a490ddea authored by protolambda's avatar protolambda

Merge branch 'develop' into indexer-lifecycle

parents b58f6b44 07d22c8f
...@@ -31,7 +31,7 @@ golang-docker: ...@@ -31,7 +31,7 @@ golang-docker:
# We don't use a buildx builder here, and just load directly into regular docker, for convenience. # We don't use a buildx builder here, and just load directly into regular docker, for convenience.
GIT_COMMIT=$$(git rev-parse HEAD) \ GIT_COMMIT=$$(git rev-parse HEAD) \
GIT_DATE=$$(git show -s --format='%ct') \ GIT_DATE=$$(git show -s --format='%ct') \
IMAGE_TAGS=$$GIT_COMMIT,latest \ IMAGE_TAGS=$$(git rev-parse HEAD),latest \
docker buildx bake \ docker buildx bake \
--progress plain \ --progress plain \
--load \ --load \
...@@ -189,4 +189,3 @@ install-geth: ...@@ -189,4 +189,3 @@ install-geth:
go install -v github.com/ethereum/go-ethereum/cmd/geth@$(shell cat .gethrc); \ go install -v github.com/ethereum/go-ethereum/cmd/geth@$(shell cat .gethrc); \
echo "Installed geth!"; true) echo "Installed geth!"; true)
.PHONY: install-geth .PHONY: install-geth
...@@ -288,21 +288,23 @@ def debug_dumpBlock(url): ...@@ -288,21 +288,23 @@ def debug_dumpBlock(url):
def wait_for_rpc_server(url): def wait_for_rpc_server(url):
log.info(f'Waiting for RPC server at {url}') log.info(f'Waiting for RPC server at {url}')
conn = http.client.HTTPConnection(url)
headers = {'Content-type': 'application/json'} headers = {'Content-type': 'application/json'}
body = '{"id":1, "jsonrpc":"2.0", "method": "eth_chainId", "params":[]}' body = '{"id":1, "jsonrpc":"2.0", "method": "eth_chainId", "params":[]}'
while True: while True:
try: try:
conn = http.client.HTTPConnection(url)
conn.request('POST', '/', body, headers) conn.request('POST', '/', body, headers)
response = conn.getresponse() response = conn.getresponse()
conn.close()
if response.status < 300: if response.status < 300:
log.info(f'RPC server at {url} ready') log.info(f'RPC server at {url} ready')
return return
except Exception as e: except Exception as e:
log.info(f'Waiting for RPC server at {url}') log.info(f'Waiting for RPC server at {url}')
time.sleep(1) time.sleep(1)
finally:
if conn:
conn.close()
CommandPreset = namedtuple('Command', ['name', 'args', 'cwd', 'timeout']) CommandPreset = namedtuple('Command', ['name', 'args', 'cwd', 'timeout'])
......
...@@ -12,6 +12,7 @@ ignore: ...@@ -12,6 +12,7 @@ ignore:
- "op-bindings/bindings/*.go" - "op-bindings/bindings/*.go"
- "**/*.t.sol" - "**/*.t.sol"
- "packages/contracts-bedrock/test/**/*.sol" - "packages/contracts-bedrock/test/**/*.sol"
- "packages/contracts-bedrock/scripts/**/*.sol"
- "packages/contracts-bedrock/contracts/vendor/WETH9.sol" - "packages/contracts-bedrock/contracts/vendor/WETH9.sol"
- 'packages/contracts-bedrock/contracts/EAS/**/*.sol' - 'packages/contracts-bedrock/contracts/EAS/**/*.sol'
coverage: coverage:
......
...@@ -34,12 +34,12 @@ export interface WithdrawalItem { ...@@ -34,12 +34,12 @@ export interface WithdrawalItem {
from: string; from: string;
to: string; to: string;
transactionHash: string; transactionHash: string;
messageHash: string; crossDomainMessageHash: string;
timestamp: number /* uint64 */; timestamp: number /* uint64 */;
l2BlockHash: string; l2BlockHash: string;
amount: string; amount: string;
proofTransactionHash: string; l1ProvenTxHash: string;
claimTransactionHash: string; l1FinalizedTxHash: string;
l1TokenAddress: string; l1TokenAddress: string;
l2TokenAddress: string; l2TokenAddress: string;
} }
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"sync/atomic" "sync/atomic"
"time"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware" "github.com/go-chi/chi/v5/middleware"
...@@ -76,7 +77,7 @@ func (a *APIService) initFromConfig(ctx context.Context, cfg *Config) error { ...@@ -76,7 +77,7 @@ func (a *APIService) initFromConfig(ctx context.Context, cfg *Config) error {
if err := a.startMetricsServer(cfg.MetricsServer); err != nil { if err := a.startMetricsServer(cfg.MetricsServer); err != nil {
return fmt.Errorf("failed to start metrics server: %w", err) return fmt.Errorf("failed to start metrics server: %w", err)
} }
a.initRouter() a.initRouter(cfg.HTTPServer)
if err := a.startServer(cfg.HTTPServer); err != nil { if err := a.startServer(cfg.HTTPServer); err != nil {
return fmt.Errorf("failed to start API server: %w", err) return fmt.Errorf("failed to start API server: %w", err)
} }
...@@ -133,7 +134,7 @@ func (a *APIService) initDB(ctx context.Context, connector DBConnector) error { ...@@ -133,7 +134,7 @@ func (a *APIService) initDB(ctx context.Context, connector DBConnector) error {
return nil return nil
} }
func (a *APIService) initRouter() { func (a *APIService) initRouter(apiConfig config.ServerConfig) {
apiRouter := chi.NewRouter() apiRouter := chi.NewRouter()
h := routes.NewRoutes(a.log, a.bv, apiRouter) h := routes.NewRoutes(a.log, a.bv, apiRouter)
...@@ -141,6 +142,7 @@ func (a *APIService) initRouter() { ...@@ -141,6 +142,7 @@ func (a *APIService) initRouter() {
// (2) Inject routing middleware // (2) Inject routing middleware
apiRouter.Use(chiMetricsMiddleware(promRecorder)) apiRouter.Use(chiMetricsMiddleware(promRecorder))
apiRouter.Use(middleware.Timeout(time.Duration(apiConfig.WriteTimeout) * time.Second))
apiRouter.Use(middleware.Recoverer) apiRouter.Use(middleware.Recoverer)
apiRouter.Use(middleware.Heartbeat(HealthPath)) apiRouter.Use(middleware.Heartbeat(HealthPath))
......
...@@ -169,8 +169,8 @@ func TestL2BridgeWithdrawalsByAddressHandler(t *testing.T) { ...@@ -169,8 +169,8 @@ func TestL2BridgeWithdrawalsByAddressHandler(t *testing.T) {
assert.Equal(t, resp.Items[0].To, withdrawal.Tx.ToAddress.String()) assert.Equal(t, resp.Items[0].To, withdrawal.Tx.ToAddress.String())
assert.Equal(t, resp.Items[0].TransactionHash, common.HexToHash("0x789").String()) assert.Equal(t, resp.Items[0].TransactionHash, common.HexToHash("0x789").String())
assert.Equal(t, resp.Items[0].Amount, withdrawal.Tx.Amount.String()) assert.Equal(t, resp.Items[0].Amount, withdrawal.Tx.Amount.String())
assert.Equal(t, resp.Items[0].ProofTransactionHash, common.HexToHash("0x123").String()) assert.Equal(t, resp.Items[0].L1ProvenTxHash, common.HexToHash("0x123").String())
assert.Equal(t, resp.Items[0].ClaimTransactionHash, common.HexToHash("0x123").String()) assert.Equal(t, resp.Items[0].L1FinalizedTxHash, common.HexToHash("0x123").String())
assert.Equal(t, resp.Items[0].L1TokenAddress, withdrawal.TokenPair.RemoteTokenAddress.String()) assert.Equal(t, resp.Items[0].L1TokenAddress, withdrawal.TokenPair.RemoteTokenAddress.String())
assert.Equal(t, resp.Items[0].L2TokenAddress, withdrawal.TokenPair.LocalTokenAddress.String()) assert.Equal(t, resp.Items[0].L2TokenAddress, withdrawal.TokenPair.LocalTokenAddress.String())
assert.Equal(t, resp.Items[0].Timestamp, withdrawal.Tx.Timestamp) assert.Equal(t, resp.Items[0].Timestamp, withdrawal.Tx.Timestamp)
......
package models package models
import (
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum/go-ethereum/common"
)
// DepositItem ... Deposit item model for API responses // DepositItem ... Deposit item model for API responses
type DepositItem struct { type DepositItem struct {
Guid string `json:"guid"` Guid string `json:"guid"`
...@@ -23,18 +28,18 @@ type DepositResponse struct { ...@@ -23,18 +28,18 @@ type DepositResponse struct {
// WithdrawalItem ... Data model for API JSON response // WithdrawalItem ... Data model for API JSON response
type WithdrawalItem struct { type WithdrawalItem struct {
Guid string `json:"guid"` Guid string `json:"guid"`
From string `json:"from"` From string `json:"from"`
To string `json:"to"` To string `json:"to"`
TransactionHash string `json:"transactionHash"` TransactionHash string `json:"transactionHash"`
MessageHash string `json:"messageHash"` CrossDomainMessageHash string `json:"crossDomainMessageHash"`
Timestamp uint64 `json:"timestamp"` Timestamp uint64 `json:"timestamp"`
L2BlockHash string `json:"l2BlockHash"` L2BlockHash string `json:"l2BlockHash"`
Amount string `json:"amount"` Amount string `json:"amount"`
ProofTransactionHash string `json:"proofTransactionHash"` L1ProvenTxHash string `json:"l1ProvenTxHash"`
ClaimTransactionHash string `json:"claimTransactionHash"` L1FinalizedTxHash string `json:"l1FinalizedTxHash"`
L1TokenAddress string `json:"l1TokenAddress"` L1TokenAddress string `json:"l1TokenAddress"`
L2TokenAddress string `json:"l2TokenAddress"` L2TokenAddress string `json:"l2TokenAddress"`
} }
// WithdrawalResponse ... Data model for API JSON response // WithdrawalResponse ... Data model for API JSON response
...@@ -43,3 +48,38 @@ type WithdrawalResponse struct { ...@@ -43,3 +48,38 @@ type WithdrawalResponse struct {
HasNextPage bool `json:"hasNextPage"` HasNextPage bool `json:"hasNextPage"`
Items []WithdrawalItem `json:"items"` Items []WithdrawalItem `json:"items"`
} }
// FIXME make a pure function that returns a struct instead of newWithdrawalResponse
// newWithdrawalResponse ... Converts a database.L2BridgeWithdrawalsResponse to an api.WithdrawalResponse
func CreateWithdrawalResponse(withdrawals *database.L2BridgeWithdrawalsResponse) WithdrawalResponse {
items := make([]WithdrawalItem, len(withdrawals.Withdrawals))
for i, withdrawal := range withdrawals.Withdrawals {
cdh := withdrawal.L2BridgeWithdrawal.CrossDomainMessageHash
if cdh == nil { // Zero value indicates that the withdrawal didn't have a cross domain message
cdh = &common.Hash{0}
}
item := WithdrawalItem{
Guid: withdrawal.L2BridgeWithdrawal.TransactionWithdrawalHash.String(),
L2BlockHash: withdrawal.L2BlockHash.String(),
Timestamp: withdrawal.L2BridgeWithdrawal.Tx.Timestamp,
From: withdrawal.L2BridgeWithdrawal.Tx.FromAddress.String(),
To: withdrawal.L2BridgeWithdrawal.Tx.ToAddress.String(),
TransactionHash: withdrawal.L2TransactionHash.String(),
Amount: withdrawal.L2BridgeWithdrawal.Tx.Amount.String(),
CrossDomainMessageHash: cdh.String(),
L1ProvenTxHash: withdrawal.ProvenL1TransactionHash.String(),
L1FinalizedTxHash: withdrawal.FinalizedL1TransactionHash.String(),
L1TokenAddress: withdrawal.L2BridgeWithdrawal.TokenPair.RemoteTokenAddress.String(),
L2TokenAddress: withdrawal.L2BridgeWithdrawal.TokenPair.LocalTokenAddress.String(),
}
items[i] = item
}
return WithdrawalResponse{
Cursor: withdrawals.Cursor,
HasNextPage: withdrawals.HasNextPage,
Items: items,
}
}
package models_test
import (
"fmt"
"reflect"
"testing"
"github.com/ethereum-optimism/optimism/indexer/api/models"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)
func TestCreateWithdrawal(t *testing.T) {
// (1) Create a dummy database response object
cdh := common.HexToHash("0x2")
dbWithdrawals := &database.L2BridgeWithdrawalsResponse{
Withdrawals: []database.L2BridgeWithdrawalWithTransactionHashes{
{
L2BridgeWithdrawal: database.L2BridgeWithdrawal{
TransactionWithdrawalHash: common.HexToHash("0x1"),
BridgeTransfer: database.BridgeTransfer{
CrossDomainMessageHash: &cdh,
Tx: database.Transaction{
FromAddress: common.HexToAddress("0x3"),
ToAddress: common.HexToAddress("0x4"),
Timestamp: 5,
},
TokenPair: database.TokenPair{
LocalTokenAddress: common.HexToAddress("0x6"),
RemoteTokenAddress: common.HexToAddress("0x7"),
},
},
},
},
},
}
// (2) Create and validate response object
response := models.CreateWithdrawalResponse(dbWithdrawals)
require.NotEmpty(t, response.Items)
require.Len(t, response.Items, 1)
// (3) Use reflection to check that all fields in WithdrawalItem are populated correctly
item := response.Items[0]
structType := reflect.TypeOf(item)
structVal := reflect.ValueOf(item)
fieldNum := structVal.NumField()
for i := 0; i < fieldNum; i++ {
field := structVal.Field(i)
fieldName := structType.Field(i).Name
isSet := field.IsValid() && !field.IsZero()
require.True(t, isSet, fmt.Sprintf("%s in not set", fieldName))
}
}
...@@ -4,38 +4,9 @@ import ( ...@@ -4,38 +4,9 @@ import (
"net/http" "net/http"
"github.com/ethereum-optimism/optimism/indexer/api/models" "github.com/ethereum-optimism/optimism/indexer/api/models"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
) )
// FIXME make a pure function that returns a struct instead of newWithdrawalResponse
// newWithdrawalResponse ... Converts a database.L2BridgeWithdrawalsResponse to an api.WithdrawalResponse
func newWithdrawalResponse(withdrawals *database.L2BridgeWithdrawalsResponse) models.WithdrawalResponse {
items := make([]models.WithdrawalItem, len(withdrawals.Withdrawals))
for i, withdrawal := range withdrawals.Withdrawals {
item := models.WithdrawalItem{
Guid: withdrawal.L2BridgeWithdrawal.TransactionWithdrawalHash.String(),
L2BlockHash: withdrawal.L2BlockHash.String(),
Timestamp: withdrawal.L2BridgeWithdrawal.Tx.Timestamp,
From: withdrawal.L2BridgeWithdrawal.Tx.FromAddress.String(),
To: withdrawal.L2BridgeWithdrawal.Tx.ToAddress.String(),
TransactionHash: withdrawal.L2TransactionHash.String(),
Amount: withdrawal.L2BridgeWithdrawal.Tx.Amount.String(),
ProofTransactionHash: withdrawal.ProvenL1TransactionHash.String(),
ClaimTransactionHash: withdrawal.FinalizedL1TransactionHash.String(),
L1TokenAddress: withdrawal.L2BridgeWithdrawal.TokenPair.RemoteTokenAddress.String(),
L2TokenAddress: withdrawal.L2BridgeWithdrawal.TokenPair.LocalTokenAddress.String(),
}
items[i] = item
}
return models.WithdrawalResponse{
Cursor: withdrawals.Cursor,
HasNextPage: withdrawals.HasNextPage,
Items: items,
}
}
// L2WithdrawalsHandler ... Handles /api/v0/withdrawals/{address} GET requests // L2WithdrawalsHandler ... Handles /api/v0/withdrawals/{address} GET requests
func (h Routes) L2WithdrawalsHandler(w http.ResponseWriter, r *http.Request) { func (h Routes) L2WithdrawalsHandler(w http.ResponseWriter, r *http.Request) {
addressValue := chi.URLParam(r, "address") addressValue := chi.URLParam(r, "address")
...@@ -69,7 +40,7 @@ func (h Routes) L2WithdrawalsHandler(w http.ResponseWriter, r *http.Request) { ...@@ -69,7 +40,7 @@ func (h Routes) L2WithdrawalsHandler(w http.ResponseWriter, r *http.Request) {
h.logger.Error("Unable to read withdrawals from DB", "err", err.Error()) h.logger.Error("Unable to read withdrawals from DB", "err", err.Error())
return return
} }
response := newWithdrawalResponse(withdrawals) response := models.CreateWithdrawalResponse(withdrawals)
err = jsonResponse(w, response, http.StatusOK) err = jsonResponse(w, response, http.StatusOK)
if err != nil { if err != nil {
......
...@@ -134,10 +134,11 @@ type DBConfig struct { ...@@ -134,10 +134,11 @@ type DBConfig struct {
Password string `toml:"password"` Password string `toml:"password"`
} }
// Configures the a server // Configures the server
type ServerConfig struct { type ServerConfig struct {
Host string `toml:"host"` Host string `toml:"host"`
Port int `toml:"port"` Port int `toml:"port"`
WriteTimeout int `toml:"timeout"`
} }
// LoadConfig loads the `indexer.toml` config file from a given path // LoadConfig loads the `indexer.toml` config file from a given path
......
...@@ -29,6 +29,7 @@ name = "$INDEXER_DB_NAME" ...@@ -29,6 +29,7 @@ name = "$INDEXER_DB_NAME"
[http] [http]
host = "127.0.0.1" host = "127.0.0.1"
port = 8080 port = 8080
timeout = 10
[metrics] [metrics]
host = "127.0.0.1" host = "127.0.0.1"
......
...@@ -290,8 +290,10 @@ func (bs *BatcherService) Stop(ctx context.Context) error { ...@@ -290,8 +290,10 @@ func (bs *BatcherService) Stop(ctx context.Context) error {
bs.Log.Info("Stopping batcher") bs.Log.Info("Stopping batcher")
var result error var result error
if err := bs.driver.StopBatchSubmittingIfRunning(ctx); err != nil { if bs.driver != nil {
result = errors.Join(result, fmt.Errorf("failed to stop batch submitting: %w", err)) if err := bs.driver.StopBatchSubmittingIfRunning(ctx); err != nil {
result = errors.Join(result, fmt.Errorf("failed to stop batch submitting: %w", err))
}
} }
if bs.rpcServer != nil { if bs.rpcServer != nil {
...@@ -328,7 +330,7 @@ func (bs *BatcherService) Stop(ctx context.Context) error { ...@@ -328,7 +330,7 @@ func (bs *BatcherService) Stop(ctx context.Context) error {
if result == nil { if result == nil {
bs.stopped.Store(true) bs.stopped.Store(true)
bs.driver.Log.Info("Batch Submitter stopped") bs.Log.Info("Batch Submitter stopped")
} }
return result return result
} }
......
...@@ -9,7 +9,7 @@ import ( ...@@ -9,7 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/solc" "github.com/ethereum-optimism/optimism/op-bindings/solc"
) )
const AlphabetVMStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contract\":\"test/FaultDisputeGame.t.sol:AlphabetVM\",\"label\":\"oracle\",\"offset\":0,\"slot\":\"0\",\"type\":\"t_contract(IPreimageOracle)1001\"}],\"types\":{\"t_contract(IPreimageOracle)1001\":{\"encoding\":\"inplace\",\"label\":\"contract IPreimageOracle\",\"numberOfBytes\":\"20\"}}}" const AlphabetVMStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contract\":\"test/mocks/AlphabetVM.sol:AlphabetVM\",\"label\":\"oracle\",\"offset\":0,\"slot\":\"0\",\"type\":\"t_contract(IPreimageOracle)1001\"}],\"types\":{\"t_contract(IPreimageOracle)1001\":{\"encoding\":\"inplace\",\"label\":\"contract IPreimageOracle\",\"numberOfBytes\":\"20\"}}}"
var AlphabetVMStorageLayout = new(solc.StorageLayout) var AlphabetVMStorageLayout = new(solc.StorageLayout)
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -112,7 +112,7 @@ func entrypoint(ctx *cli.Context) error { ...@@ -112,7 +112,7 @@ func entrypoint(ctx *cli.Context) error {
name, _ := toDeployConfigName(chainConfig) name, _ := toDeployConfigName(chainConfig)
config, err := genesis.NewDeployConfigWithNetwork(name, deployConfig) config, err := genesis.NewDeployConfigWithNetwork(name, deployConfig)
if err != nil { if err != nil {
log.Warn("Cannot find deploy config for network", "name", chainConfig.Name, "deploy-config-name", name, "path", deployConfig) log.Warn("Cannot find deploy config for network", "name", chainConfig.Name, "deploy-config-name", name, "path", deployConfig, "err", err)
} }
if config != nil { if config != nil {
......
...@@ -25,7 +25,7 @@ type Responder interface { ...@@ -25,7 +25,7 @@ type Responder interface {
} }
type ClaimLoader interface { type ClaimLoader interface {
FetchClaims(ctx context.Context) ([]types.Claim, error) GetAllClaims(ctx context.Context) ([]types.Claim, error)
} }
type Agent struct { type Agent struct {
...@@ -136,7 +136,7 @@ func (a *Agent) tryResolve(ctx context.Context) bool { ...@@ -136,7 +136,7 @@ func (a *Agent) tryResolve(ctx context.Context) bool {
var errNoResolvableClaims = errors.New("no resolvable claims") var errNoResolvableClaims = errors.New("no resolvable claims")
func (a *Agent) tryResolveClaims(ctx context.Context) error { func (a *Agent) tryResolveClaims(ctx context.Context) error {
claims, err := a.loader.FetchClaims(ctx) claims, err := a.loader.GetAllClaims(ctx)
if err != nil { if err != nil {
return fmt.Errorf("failed to fetch claims: %w", err) return fmt.Errorf("failed to fetch claims: %w", err)
} }
...@@ -189,7 +189,7 @@ func (a *Agent) resolveClaims(ctx context.Context) error { ...@@ -189,7 +189,7 @@ func (a *Agent) resolveClaims(ctx context.Context) error {
// newGameFromContracts initializes a new game state from the state in the contract // newGameFromContracts initializes a new game state from the state in the contract
func (a *Agent) newGameFromContracts(ctx context.Context) (types.Game, error) { func (a *Agent) newGameFromContracts(ctx context.Context) (types.Game, error) {
claims, err := a.loader.FetchClaims(ctx) claims, err := a.loader.GetAllClaims(ctx)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to fetch claims: %w", err) return nil, fmt.Errorf("failed to fetch claims: %w", err)
} }
......
...@@ -124,7 +124,7 @@ type stubClaimLoader struct { ...@@ -124,7 +124,7 @@ type stubClaimLoader struct {
claims []types.Claim claims []types.Claim
} }
func (s *stubClaimLoader) FetchClaims(ctx context.Context) ([]types.Claim, error) { func (s *stubClaimLoader) GetAllClaims(ctx context.Context) ([]types.Claim, error) {
s.callCount++ s.callCount++
return s.claims, nil return s.claims, nil
} }
......
package contracts
import (
"context"
"fmt"
"math/big"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum/go-ethereum/common"
)
const (
methodGameDuration = "GAME_DURATION"
methodMaxGameDepth = "MAX_GAME_DEPTH"
methodAbsolutePrestate = "ABSOLUTE_PRESTATE"
methodStatus = "status"
methodClaimCount = "claimDataLen"
methodClaim = "claimData"
)
type FaultDisputeGameContract struct {
multiCaller *batching.MultiCaller
contract *batching.BoundContract
}
func NewFaultDisputeGameContract(addr common.Address, caller *batching.MultiCaller) (*FaultDisputeGameContract, error) {
fdgAbi, err := bindings.FaultDisputeGameMetaData.GetAbi()
if err != nil {
return nil, fmt.Errorf("failed to load fault dispute game ABI: %w", err)
}
return &FaultDisputeGameContract{
multiCaller: caller,
contract: batching.NewBoundContract(fdgAbi, addr),
}, nil
}
func (f *FaultDisputeGameContract) GetGameDuration(ctx context.Context) (uint64, error) {
result, err := f.multiCaller.SingleCallLatest(ctx, f.contract.Call(methodGameDuration))
if err != nil {
return 0, fmt.Errorf("failed to fetch game duration: %w", err)
}
return result.GetUint64(0), nil
}
func (f *FaultDisputeGameContract) GetMaxGameDepth(ctx context.Context) (uint64, error) {
result, err := f.multiCaller.SingleCallLatest(ctx, f.contract.Call(methodMaxGameDepth))
if err != nil {
return 0, fmt.Errorf("failed to fetch max game depth: %w", err)
}
return result.GetBigInt(0).Uint64(), nil
}
func (f *FaultDisputeGameContract) GetAbsolutePrestateHash(ctx context.Context) (common.Hash, error) {
result, err := f.multiCaller.SingleCallLatest(ctx, f.contract.Call(methodAbsolutePrestate))
if err != nil {
return common.Hash{}, fmt.Errorf("failed to fetch absolute prestate hash: %w", err)
}
return result.GetHash(0), nil
}
func (f *FaultDisputeGameContract) GetStatus(ctx context.Context) (gameTypes.GameStatus, error) {
result, err := f.multiCaller.SingleCallLatest(ctx, f.contract.Call(methodStatus))
if err != nil {
return 0, fmt.Errorf("failed to fetch status: %w", err)
}
return gameTypes.GameStatusFromUint8(result.GetUint8(0))
}
func (f *FaultDisputeGameContract) GetClaimCount(ctx context.Context) (uint64, error) {
result, err := f.multiCaller.SingleCallLatest(ctx, f.contract.Call(methodClaimCount))
if err != nil {
return 0, fmt.Errorf("failed to fetch claim count: %w", err)
}
return result.GetBigInt(0).Uint64(), nil
}
func (f *FaultDisputeGameContract) GetClaim(ctx context.Context, idx uint64) (types.Claim, error) {
result, err := f.multiCaller.SingleCallLatest(ctx, f.contract.Call(methodClaim, new(big.Int).SetUint64(idx)))
if err != nil {
return types.Claim{}, fmt.Errorf("failed to fetch claim %v: %w", idx, err)
}
return f.decodeClaim(result, int(idx)), nil
}
func (f *FaultDisputeGameContract) GetAllClaims(ctx context.Context) ([]types.Claim, error) {
count, err := f.GetClaimCount(ctx)
if err != nil {
return nil, fmt.Errorf("failed to load claim count: %w", err)
}
calls := make([]*batching.ContractCall, count)
for i := uint64(0); i < count; i++ {
calls[i] = f.contract.Call(methodClaim, new(big.Int).SetUint64(i))
}
results, err := f.multiCaller.CallLatest(ctx, calls...)
if err != nil {
return nil, fmt.Errorf("failed to fetch claim data: %w", err)
}
var claims []types.Claim
for idx, result := range results {
claims = append(claims, f.decodeClaim(result, idx))
}
return claims, nil
}
func (f *FaultDisputeGameContract) decodeClaim(result *batching.CallResult, contractIndex int) types.Claim {
parentIndex := result.GetUint32(0)
countered := result.GetBool(1)
claim := result.GetHash(2)
position := result.GetBigInt(3)
clock := result.GetBigInt(4)
return types.Claim{
ClaimData: types.ClaimData{
Value: claim,
Position: types.NewPositionFromGIndex(position),
},
Countered: countered,
Clock: clock.Uint64(),
ContractIndex: contractIndex,
ParentContractIndex: int(parentIndex),
}
}
package contracts
import (
"context"
"math"
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
batchingTest "github.com/ethereum-optimism/optimism/op-service/sources/batching/test"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)
func TestSimpleGetters(t *testing.T) {
tests := []struct {
method string
args []interface{}
result interface{}
expected interface{} // Defaults to expecting the same as result
call func(game *FaultDisputeGameContract) (any, error)
}{
{
method: methodStatus,
result: types.GameStatusChallengerWon,
call: func(game *FaultDisputeGameContract) (any, error) {
return game.GetStatus(context.Background())
},
},
{
method: methodGameDuration,
result: uint64(5566),
call: func(game *FaultDisputeGameContract) (any, error) {
return game.GetGameDuration(context.Background())
},
},
{
method: methodMaxGameDepth,
result: big.NewInt(128),
expected: uint64(128),
call: func(game *FaultDisputeGameContract) (any, error) {
return game.GetMaxGameDepth(context.Background())
},
},
{
method: methodAbsolutePrestate,
result: common.Hash{0xab},
call: func(game *FaultDisputeGameContract) (any, error) {
return game.GetAbsolutePrestateHash(context.Background())
},
},
{
method: methodClaimCount,
result: big.NewInt(9876),
expected: uint64(9876),
call: func(game *FaultDisputeGameContract) (any, error) {
return game.GetClaimCount(context.Background())
},
},
}
for _, test := range tests {
test := test
t.Run(test.method, func(t *testing.T) {
stubRpc, game := setup(t)
stubRpc.SetResponse(test.method, nil, []interface{}{test.result})
status, err := test.call(game)
require.NoError(t, err)
expected := test.expected
if expected == nil {
expected = test.result
}
require.Equal(t, expected, status)
})
}
}
func TestGetClaim(t *testing.T) {
stubRpc, game := setup(t)
idx := big.NewInt(2)
parentIndex := uint32(1)
countered := true
value := common.Hash{0xab}
position := big.NewInt(2)
clock := big.NewInt(1234)
stubRpc.SetResponse(methodClaim, []interface{}{idx}, []interface{}{parentIndex, countered, value, position, clock})
status, err := game.GetClaim(context.Background(), idx.Uint64())
require.NoError(t, err)
require.Equal(t, faultTypes.Claim{
ClaimData: faultTypes.ClaimData{
Value: value,
Position: faultTypes.NewPositionFromGIndex(position),
},
Countered: true,
Clock: 1234,
ContractIndex: int(idx.Uint64()),
ParentContractIndex: 1,
}, status)
}
func TestGetAllClaims(t *testing.T) {
stubRpc, game := setup(t)
claim0 := faultTypes.Claim{
ClaimData: faultTypes.ClaimData{
Value: common.Hash{0xaa},
Position: faultTypes.NewPositionFromGIndex(big.NewInt(1)),
},
Countered: true,
Clock: 1234,
ContractIndex: 0,
ParentContractIndex: math.MaxUint32,
}
claim1 := faultTypes.Claim{
ClaimData: faultTypes.ClaimData{
Value: common.Hash{0xab},
Position: faultTypes.NewPositionFromGIndex(big.NewInt(2)),
},
Countered: true,
Clock: 4455,
ContractIndex: 1,
ParentContractIndex: 0,
}
claim2 := faultTypes.Claim{
ClaimData: faultTypes.ClaimData{
Value: common.Hash{0xbb},
Position: faultTypes.NewPositionFromGIndex(big.NewInt(6)),
},
Countered: false,
Clock: 7777,
ContractIndex: 2,
ParentContractIndex: 1,
}
expectedClaims := []faultTypes.Claim{claim0, claim1, claim2}
stubRpc.SetResponse(methodClaimCount, nil, []interface{}{big.NewInt(int64(len(expectedClaims)))})
for _, claim := range expectedClaims {
expectGetClaim(stubRpc, claim)
}
claims, err := game.GetAllClaims(context.Background())
require.NoError(t, err)
require.Equal(t, expectedClaims, claims)
}
func expectGetClaim(stubRpc *batchingTest.AbiBasedRpc, claim faultTypes.Claim) {
stubRpc.SetResponse(
methodClaim,
[]interface{}{big.NewInt(int64(claim.ContractIndex))},
[]interface{}{
uint32(claim.ParentContractIndex),
claim.Countered,
claim.Value,
claim.Position.ToGIndex(),
big.NewInt(int64(claim.Clock)),
})
}
func setup(t *testing.T) (*batchingTest.AbiBasedRpc, *FaultDisputeGameContract) {
fdgAbi, err := bindings.FaultDisputeGameMetaData.GetAbi()
require.NoError(t, err)
address := common.HexToAddress("0x24112842371dFC380576ebb09Ae16Cb6B6caD7CB")
stubRpc := batchingTest.NewAbiBasedRpc(t, fdgAbi, address)
caller := batching.NewMultiCaller(stubRpc, 100)
game, err := NewFaultDisputeGameContract(address, caller)
require.NoError(t, err)
return stubRpc, game
}
...@@ -5,22 +5,23 @@ import ( ...@@ -5,22 +5,23 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/responder" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/responder"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-challenger/metrics" "github.com/ethereum-optimism/optimism/op-challenger/metrics"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
"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/log" "github.com/ethereum/go-ethereum/log"
) )
type actor func(ctx context.Context) error type actor func(ctx context.Context) error
type GameInfo interface { type GameInfo interface {
GetGameStatus(context.Context) (gameTypes.GameStatus, error) GetStatus(context.Context) (gameTypes.GameStatus, error)
GetClaimCount(context.Context) (uint64, error) GetClaimCount(context.Context) (uint64, error)
} }
...@@ -42,18 +43,16 @@ func NewGamePlayer( ...@@ -42,18 +43,16 @@ func NewGamePlayer(
dir string, dir string,
addr common.Address, addr common.Address,
txMgr txmgr.TxManager, txMgr txmgr.TxManager,
client bind.ContractCaller, client *ethclient.Client,
creator resourceCreator, creator resourceCreator,
) (*GamePlayer, error) { ) (*GamePlayer, error) {
logger = logger.New("game", addr) logger = logger.New("game", addr)
contract, err := bindings.NewFaultDisputeGameCaller(addr, client) loader, err := contracts.NewFaultDisputeGameContract(addr, batching.NewMultiCaller(client.Client(), 100))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to bind the fault dispute game contract: %w", err) return nil, fmt.Errorf("failed to create fault dispute game contract wrapper: %w", err)
} }
loader := NewLoader(contract) status, err := loader.GetStatus(ctx)
status, err := loader.GetGameStatus(ctx)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to fetch game status: %w", err) return nil, fmt.Errorf("failed to fetch game status: %w", err)
} }
...@@ -72,7 +71,7 @@ func NewGamePlayer( ...@@ -72,7 +71,7 @@ func NewGamePlayer(
}, nil }, nil
} }
gameDepth, err := loader.FetchGameDepth(ctx) gameDepth, err := loader.GetMaxGameDepth(ctx)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to fetch the game depth: %w", err) return nil, fmt.Errorf("failed to fetch the game depth: %w", err)
} }
...@@ -114,7 +113,7 @@ func (g *GamePlayer) ProgressGame(ctx context.Context) gameTypes.GameStatus { ...@@ -114,7 +113,7 @@ func (g *GamePlayer) ProgressGame(ctx context.Context) gameTypes.GameStatus {
if err := g.act(ctx); err != nil { if err := g.act(ctx); err != nil {
g.logger.Error("Error when acting on game", "err", err) g.logger.Error("Error when acting on game", "err", err)
} }
status, err := g.loader.GetGameStatus(ctx) status, err := g.loader.GetStatus(ctx)
if err != nil { if err != nil {
g.logger.Warn("Unable to retrieve game status", "err", err) g.logger.Warn("Unable to retrieve game status", "err", err)
return gameTypes.GameStatusInProgress return gameTypes.GameStatusInProgress
...@@ -148,7 +147,7 @@ func (g *GamePlayer) logGameStatus(ctx context.Context, status gameTypes.GameSta ...@@ -148,7 +147,7 @@ func (g *GamePlayer) logGameStatus(ctx context.Context, status gameTypes.GameSta
} }
type PrestateLoader interface { type PrestateLoader interface {
FetchAbsolutePrestateHash(ctx context.Context) (common.Hash, error) GetAbsolutePrestateHash(ctx context.Context) (common.Hash, error)
} }
// ValidateAbsolutePrestate validates the absolute prestate of the fault game. // ValidateAbsolutePrestate validates the absolute prestate of the fault game.
...@@ -157,7 +156,7 @@ func ValidateAbsolutePrestate(ctx context.Context, trace types.TraceProvider, lo ...@@ -157,7 +156,7 @@ func ValidateAbsolutePrestate(ctx context.Context, trace types.TraceProvider, lo
if err != nil { if err != nil {
return fmt.Errorf("failed to get the trace provider's absolute prestate: %w", err) return fmt.Errorf("failed to get the trace provider's absolute prestate: %w", err)
} }
onchainPrestate, err := loader.FetchAbsolutePrestateHash(ctx) onchainPrestate, err := loader.GetAbsolutePrestateHash(ctx)
if err != nil { if err != nil {
return fmt.Errorf("failed to get the onchain absolute prestate: %w", err) return fmt.Errorf("failed to get the onchain absolute prestate: %w", err)
} }
......
...@@ -181,7 +181,7 @@ func (s *stubGameState) Act(ctx context.Context) error { ...@@ -181,7 +181,7 @@ func (s *stubGameState) Act(ctx context.Context) error {
return s.actErr return s.actErr
} }
func (s *stubGameState) GetGameStatus(ctx context.Context) (gameTypes.GameStatus, error) { func (s *stubGameState) GetStatus(ctx context.Context) (gameTypes.GameStatus, error) {
return s.status, nil return s.status, nil
} }
...@@ -234,7 +234,7 @@ func newMockPrestateLoader(prestateError bool, prestate common.Hash) *mockLoader ...@@ -234,7 +234,7 @@ func newMockPrestateLoader(prestateError bool, prestate common.Hash) *mockLoader
prestate: prestate, prestate: prestate,
} }
} }
func (m *mockLoader) FetchAbsolutePrestateHash(ctx context.Context) (common.Hash, error) { func (m *mockLoader) GetAbsolutePrestateHash(ctx context.Context) (common.Hash, error) {
if m.prestateError { if m.prestateError {
return common.Hash{}, mockLoaderError return common.Hash{}, mockLoaderError
} }
......
...@@ -12,8 +12,8 @@ import ( ...@@ -12,8 +12,8 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/game/types" "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-challenger/metrics" "github.com/ethereum-optimism/optimism/op-challenger/metrics"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
"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/log" "github.com/ethereum/go-ethereum/log"
) )
...@@ -33,7 +33,7 @@ func RegisterGameTypes( ...@@ -33,7 +33,7 @@ func RegisterGameTypes(
m metrics.Metricer, m metrics.Metricer,
cfg *config.Config, cfg *config.Config,
txMgr txmgr.TxManager, txMgr txmgr.TxManager,
client bind.ContractCaller, client *ethclient.Client,
) { ) {
if cfg.TraceTypeEnabled(config.TraceTypeCannon) { if cfg.TraceTypeEnabled(config.TraceTypeCannon) {
resourceCreator := func(addr common.Address, gameDepth uint64, dir string) (faultTypes.TraceProvider, faultTypes.OracleUpdater, error) { resourceCreator := func(addr common.Address, gameDepth uint64, dir string) (faultTypes.TraceProvider, faultTypes.OracleUpdater, error) {
......
...@@ -203,6 +203,9 @@ type SystemConfig struct { ...@@ -203,6 +203,9 @@ type SystemConfig struct {
// Target L1 tx size for the batcher transactions // Target L1 tx size for the batcher transactions
BatcherTargetL1TxSizeBytes uint64 BatcherTargetL1TxSizeBytes uint64
// Max L1 tx size for the batcher transactions
BatcherMaxL1TxSizeBytes uint64
// SupportL1TimeTravel determines if the L1 node supports quickly skipping forward in time // SupportL1TimeTravel determines if the L1 node supports quickly skipping forward in time
SupportL1TimeTravel bool SupportL1TimeTravel bool
} }
...@@ -684,13 +687,17 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste ...@@ -684,13 +687,17 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste
if os.Getenv("OP_E2E_USE_SPAN_BATCH") == "true" { if os.Getenv("OP_E2E_USE_SPAN_BATCH") == "true" {
batchType = derive.SpanBatchType batchType = derive.SpanBatchType
} }
batcherMaxL1TxSizeBytes := cfg.BatcherMaxL1TxSizeBytes
if batcherMaxL1TxSizeBytes == 0 {
batcherMaxL1TxSizeBytes = 240_000
}
batcherCLIConfig := &bss.CLIConfig{ batcherCLIConfig := &bss.CLIConfig{
L1EthRpc: sys.EthInstances["l1"].WSEndpoint(), L1EthRpc: sys.EthInstances["l1"].WSEndpoint(),
L2EthRpc: sys.EthInstances["sequencer"].WSEndpoint(), L2EthRpc: sys.EthInstances["sequencer"].WSEndpoint(),
RollupRpc: sys.RollupNodes["sequencer"].HTTPEndpoint(), RollupRpc: sys.RollupNodes["sequencer"].HTTPEndpoint(),
MaxPendingTransactions: 0, MaxPendingTransactions: 0,
MaxChannelDuration: 1, MaxChannelDuration: 1,
MaxL1TxSize: 240_000, MaxL1TxSize: batcherMaxL1TxSizeBytes,
CompressorConfig: compressor.CLIConfig{ CompressorConfig: compressor.CLIConfig{
TargetL1TxSizeBytes: cfg.BatcherTargetL1TxSizeBytes, TargetL1TxSizeBytes: cfg.BatcherTargetL1TxSizeBytes,
TargetNumFrames: 1, TargetNumFrames: 1,
......
...@@ -1553,3 +1553,14 @@ func TestRequiredProtocolVersionChangeAndHalt(t *testing.T) { ...@@ -1553,3 +1553,14 @@ func TestRequiredProtocolVersionChangeAndHalt(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
t.Log("verified that op-geth closed!") t.Log("verified that op-geth closed!")
} }
func TestIncorrectBatcherConfiguration(t *testing.T) {
InitParallel(t)
cfg := DefaultSystemConfig(t)
// make the batcher configuration invalid
cfg.BatcherMaxL1TxSizeBytes = 1
_, err := cfg.Start(t)
require.Error(t, err, "Expected error on invalid batcher configuration")
}
...@@ -199,18 +199,18 @@ func checkSpanBatch(ctx context.Context, cfg *rollup.Config, log log.Logger, l1B ...@@ -199,18 +199,18 @@ func checkSpanBatch(ctx context.Context, cfg *rollup.Config, log log.Logger, l1B
if batch.GetTimestamp() < nextTimestamp { if batch.GetTimestamp() < nextTimestamp {
if batch.GetTimestamp() > l2SafeHead.Time { if batch.GetTimestamp() > l2SafeHead.Time {
// batch timestamp cannot be between safe head and next timestamp // batch timestamp cannot be between safe head and next timestamp
log.Warn("batch has misaligned timestamp") log.Warn("batch has misaligned timestamp, block time is too short")
return BatchDrop return BatchDrop
} }
if (l2SafeHead.Time-batch.GetTimestamp())%cfg.BlockTime != 0 { if (l2SafeHead.Time-batch.GetTimestamp())%cfg.BlockTime != 0 {
log.Warn("batch has misaligned timestamp") log.Warn("batch has misaligned timestamp, not overlapped exactly")
return BatchDrop return BatchDrop
} }
parentNum = l2SafeHead.Number - (l2SafeHead.Time-batch.GetTimestamp())/cfg.BlockTime - 1 parentNum = l2SafeHead.Number - (l2SafeHead.Time-batch.GetTimestamp())/cfg.BlockTime - 1
var err error var err error
parentBlock, err = l2Fetcher.L2BlockRefByNumber(ctx, parentNum) parentBlock, err = l2Fetcher.L2BlockRefByNumber(ctx, parentNum)
if err != nil { if err != nil {
log.Error("failed to fetch L2 block", "number", parentNum, "err", err) log.Warn("failed to fetch L2 block", "number", parentNum, "err", err)
// unable to validate the batch for now. retry later. // unable to validate the batch for now. retry later.
return BatchUndecided return BatchUndecided
} }
...@@ -332,7 +332,7 @@ func checkSpanBatch(ctx context.Context, cfg *rollup.Config, log log.Logger, l1B ...@@ -332,7 +332,7 @@ func checkSpanBatch(ctx context.Context, cfg *rollup.Config, log log.Logger, l1B
safeBlockNum := parentNum + i + 1 safeBlockNum := parentNum + i + 1
safeBlockPayload, err := l2Fetcher.PayloadByNumber(ctx, safeBlockNum) safeBlockPayload, err := l2Fetcher.PayloadByNumber(ctx, safeBlockNum)
if err != nil { if err != nil {
log.Error("failed to fetch L2 block payload", "number", parentNum, "err", err) log.Warn("failed to fetch L2 block payload", "number", parentNum, "err", err)
// unable to validate the batch for now. retry later. // unable to validate the batch for now. retry later.
return BatchUndecided return BatchUndecided
} }
......
This diff is collapsed.
package sources package batching
import ( import (
"context" "context"
......
package sources package batching
import ( import (
"context" "context"
......
package batching
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
)
type BoundContract struct {
abi *abi.ABI
addr common.Address
}
func NewBoundContract(abi *abi.ABI, addr common.Address) *BoundContract {
return &BoundContract{
abi: abi,
addr: addr,
}
}
func (b *BoundContract) Call(method string, args ...interface{}) *ContractCall {
return NewContractCall(b.abi, b.addr, method, args...)
}
type ContractCall struct {
Abi *abi.ABI
Addr common.Address
Method string
Args []interface{}
}
func NewContractCall(abi *abi.ABI, addr common.Address, method string, args ...interface{}) *ContractCall {
return &ContractCall{
Abi: abi,
Addr: addr,
Method: method,
Args: args,
}
}
func (c *ContractCall) Pack() ([]byte, error) {
return c.Abi.Pack(c.Method, c.Args...)
}
func (c *ContractCall) ToCallArgs() (interface{}, error) {
data, err := c.Pack()
if err != nil {
return nil, fmt.Errorf("failed to pack arguments: %w", err)
}
msg := ethereum.CallMsg{
To: &c.Addr,
Data: data,
}
return toCallArg(msg), nil
}
func (c *ContractCall) Unpack(hex hexutil.Bytes) (*CallResult, error) {
out, err := c.Abi.Unpack(c.Method, hex)
if err != nil {
return nil, fmt.Errorf("failed to unpack data: %w", err)
}
return &CallResult{out: out}, nil
}
func toCallArg(msg ethereum.CallMsg) interface{} {
arg := map[string]interface{}{
"from": msg.From,
"to": msg.To,
}
if len(msg.Data) > 0 {
arg["input"] = hexutil.Bytes(msg.Data)
}
if msg.Value != nil {
arg["value"] = (*hexutil.Big)(msg.Value)
}
if msg.Gas != 0 {
arg["gas"] = hexutil.Uint64(msg.Gas)
}
if msg.GasPrice != nil {
arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
}
return arg
}
type CallResult struct {
out []interface{}
}
func (c *CallResult) GetUint8(i int) uint8 {
return *abi.ConvertType(c.out[i], new(uint8)).(*uint8)
}
func (c *CallResult) GetUint32(i int) uint32 {
return *abi.ConvertType(c.out[i], new(uint32)).(*uint32)
}
func (c *CallResult) GetUint64(i int) uint64 {
return *abi.ConvertType(c.out[i], new(uint64)).(*uint64)
}
func (c *CallResult) GetBool(i int) bool {
return *abi.ConvertType(c.out[i], new(bool)).(*bool)
}
func (c *CallResult) GetHash(i int) common.Hash {
return *abi.ConvertType(c.out[i], new([32]byte)).(*[32]byte)
}
func (c *CallResult) GetBigInt(i int) *big.Int {
return *abi.ConvertType(c.out[i], new(*big.Int)).(**big.Int)
}
package batching
import (
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/stretchr/testify/require"
)
func TestContractCall_ToCallArgs(t *testing.T) {
addr := common.Address{0xbd}
testAbi, err := bindings.ERC20MetaData.GetAbi()
require.NoError(t, err)
call := NewContractCall(testAbi, addr, "approve", common.Address{0xcc}, big.NewInt(1234444))
args, err := call.ToCallArgs()
require.NoError(t, err)
argMap, ok := args.(map[string]interface{})
require.True(t, ok)
require.Equal(t, argMap["from"], common.Address{})
require.Equal(t, argMap["to"], &addr)
expectedData, err := call.Pack()
require.NoError(t, err)
require.Equal(t, argMap["input"], hexutil.Bytes(expectedData))
require.NotContains(t, argMap, "value")
require.NotContains(t, argMap, "gas")
require.NotContains(t, argMap, "gasPrice")
}
func TestContractCall_Pack(t *testing.T) {
addr := common.Address{0xbd}
testAbi, err := bindings.ERC20MetaData.GetAbi()
require.NoError(t, err)
sender := common.Address{0xcc}
amount := big.NewInt(1234444)
call := NewContractCall(testAbi, addr, "approve", sender, amount)
actual, err := call.Pack()
require.NoError(t, err)
expected, err := testAbi.Pack("approve", sender, amount)
require.NoError(t, err)
require.Equal(t, actual, expected)
}
func TestContractCall_PackInvalid(t *testing.T) {
addr := common.Address{0xbd}
testAbi, err := bindings.ERC20MetaData.GetAbi()
require.NoError(t, err)
// Second arg should be a *big.Int so packing should fail
call := NewContractCall(testAbi, addr, "approve", common.Address{0xcc}, uint32(123))
_, err = call.Pack()
require.Error(t, err)
}
func TestContractCall_Unpack(t *testing.T) {
addr := common.Address{0xbd}
testAbi, err := bindings.ERC20MetaData.GetAbi()
require.NoError(t, err)
call := NewContractCall(testAbi, addr, "balanceOf", common.Address{0xcc})
outputs := testAbi.Methods["balanceOf"].Outputs
expected := big.NewInt(1234)
packed, err := outputs.Pack(expected)
require.NoError(t, err)
unpacked, err := call.Unpack(packed)
require.NoError(t, err)
require.Equal(t, unpacked.GetBigInt(0), expected)
}
func TestContractCall_UnpackInvalid(t *testing.T) {
addr := common.Address{0xbd}
testAbi, err := bindings.ERC20MetaData.GetAbi()
require.NoError(t, err)
call := NewContractCall(testAbi, addr, "balanceOf", common.Address{0xcc})
// Input data is the wrong format and won't unpack successfully
inputPacked, err := call.Pack()
require.NoError(t, err)
_, err = call.Unpack(inputPacked)
require.Error(t, err)
}
func TestCallResult_GetValues(t *testing.T) {
tests := []struct {
name string
getter func(result *CallResult, i int) interface{}
expected interface{}
}{
{
name: "GetUint8",
getter: func(result *CallResult, i int) interface{} {
return result.GetUint8(i)
},
expected: uint8(12),
},
{
name: "GetUint32",
getter: func(result *CallResult, i int) interface{} {
return result.GetUint32(i)
},
expected: uint32(12346),
},
{
name: "GetUint64",
getter: func(result *CallResult, i int) interface{} {
return result.GetUint64(i)
},
expected: uint64(12346),
},
{
name: "GetBool",
getter: func(result *CallResult, i int) interface{} {
return result.GetBool(i)
},
expected: true,
},
{
name: "GetHash",
getter: func(result *CallResult, i int) interface{} {
return result.GetHash(i)
},
expected: ([32]byte)(common.Hash{0xaa, 0xbb, 0xcc}),
},
{
name: "GetBigInt",
getter: func(result *CallResult, i int) interface{} {
return result.GetBigInt(i)
},
expected: big.NewInt(2398423),
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
callResult := &CallResult{[]interface{}{nil, 0, "abc", test.expected, "xyz", 3, nil}}
actual := test.getter(callResult, 3)
require.EqualValues(t, test.expected, actual)
})
}
}
package batching
import (
"context"
"fmt"
"io"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rpc"
)
type EthRpc interface {
CallContext(ctx context.Context, out interface{}, method string, args ...interface{}) error
BatchCallContext(ctx context.Context, b []rpc.BatchElem) error
}
type MultiCaller struct {
rpc EthRpc
batchSize int
}
func NewMultiCaller(rpc EthRpc, batchSize int) *MultiCaller {
return &MultiCaller{
rpc: rpc,
batchSize: batchSize,
}
}
func (m *MultiCaller) SingleCallLatest(ctx context.Context, call *ContractCall) (*CallResult, error) {
results, err := m.CallLatest(ctx, call)
if err != nil {
return nil, err
}
return results[0], nil
}
func (m *MultiCaller) CallLatest(ctx context.Context, calls ...*ContractCall) ([]*CallResult, error) {
keys := make([]interface{}, len(calls))
for i := 0; i < len(calls); i++ {
args, err := calls[i].ToCallArgs()
if err != nil {
return nil, err
}
keys[i] = args
}
fetcher := NewIterativeBatchCall[interface{}, *hexutil.Bytes](
keys,
func(args interface{}) (*hexutil.Bytes, rpc.BatchElem) {
out := new(hexutil.Bytes)
return out, rpc.BatchElem{
Method: "eth_call",
Args: []interface{}{args, "latest"},
Result: &out,
}
},
m.rpc.BatchCallContext,
m.rpc.CallContext,
m.batchSize)
for {
if err := fetcher.Fetch(ctx); err == io.EOF {
break
} else if err != nil {
return nil, fmt.Errorf("failed to fetch claims: %w", err)
}
}
results, err := fetcher.Result()
if err != nil {
return nil, fmt.Errorf("failed to get batch call results: %w", err)
}
callResults := make([]*CallResult, len(results))
for i, result := range results {
call := calls[i]
out, err := call.Unpack(*result)
if err != nil {
return nil, fmt.Errorf("failed to unpack result: %w", err)
}
callResults[i] = out
}
return callResults, nil
}
package test
import (
"context"
"encoding/json"
"errors"
"fmt"
"testing"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require"
"golang.org/x/exp/slices"
)
type expectedCall struct {
args []interface{}
packedArgs []byte
outputs []interface{}
}
func (e *expectedCall) String() string {
return fmt.Sprintf("{args: %v, outputs: %v}", e.args, e.outputs)
}
type AbiBasedRpc struct {
t *testing.T
abi *abi.ABI
addr common.Address
expectedCalls map[string][]*expectedCall
}
func NewAbiBasedRpc(t *testing.T, contractAbi *abi.ABI, addr common.Address) *AbiBasedRpc {
return &AbiBasedRpc{
t: t,
abi: contractAbi,
addr: addr,
expectedCalls: make(map[string][]*expectedCall),
}
}
func (l *AbiBasedRpc) SetResponse(method string, expected []interface{}, output []interface{}) {
if expected == nil {
expected = []interface{}{}
}
if output == nil {
output = []interface{}{}
}
abiMethod, ok := l.abi.Methods[method]
require.Truef(l.t, ok, "No method: %v", method)
packedArgs, err := abiMethod.Inputs.Pack(expected...)
require.NoErrorf(l.t, err, "Invalid expected arguments for method %v: %v", method, expected)
l.expectedCalls[method] = append(l.expectedCalls[method], &expectedCall{
args: expected,
packedArgs: packedArgs,
outputs: output,
})
}
func (l *AbiBasedRpc) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error {
var errs []error
for _, elem := range b {
elem.Error = l.CallContext(ctx, elem.Result, elem.Method, elem.Args...)
errs = append(errs, elem.Error)
}
return errors.Join(errs...)
}
func (l *AbiBasedRpc) CallContext(_ context.Context, out interface{}, method string, args ...interface{}) error {
require.Equal(l.t, "eth_call", method)
require.Len(l.t, args, 2)
require.Equal(l.t, "latest", args[1])
callOpts, ok := args[0].(map[string]any)
require.True(l.t, ok)
require.Equal(l.t, &l.addr, callOpts["to"])
data, ok := callOpts["input"].(hexutil.Bytes)
require.True(l.t, ok)
abiMethod, err := l.abi.MethodById(data[0:4])
require.NoError(l.t, err)
argData := data[4:]
args, err = abiMethod.Inputs.Unpack(argData)
require.NoError(l.t, err)
require.Len(l.t, args, len(abiMethod.Inputs))
expectedCalls, ok := l.expectedCalls[abiMethod.Name]
require.Truef(l.t, ok, "Unexpected call to %v", abiMethod.Name)
var call *expectedCall
for _, candidate := range expectedCalls {
if slices.Equal(candidate.packedArgs, argData) {
call = candidate
break
}
}
require.NotNilf(l.t, call, "No expected calls to %v with arguments: %v\nExpected calls: %v", abiMethod.Name, args, expectedCalls)
output, err := abiMethod.Outputs.Pack(call.outputs...)
require.NoErrorf(l.t, err, "Invalid outputs for method %v: %v", abiMethod.Name, call.outputs)
// I admit I do not understand Go reflection.
// So leverage json.Unmarshal to set the out value correctly.
j, err := json.Marshal(hexutil.Bytes(output))
require.NoError(l.t, err)
require.NoError(l.t, json.Unmarshal(j, out))
return nil
}
package batching
import (
"context"
"github.com/ethereum/go-ethereum/rpc"
)
type BatchCallContextFn func(ctx context.Context, b []rpc.BatchElem) error
type CallContextFn func(ctx context.Context, result any, method string, args ...any) error
...@@ -4,16 +4,17 @@ import ( ...@@ -4,16 +4,17 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
) )
type DebugClient struct { type DebugClient struct {
callContext CallContextFn callContext batching.CallContextFn
} }
func NewDebugClient(callContext CallContextFn) *DebugClient { func NewDebugClient(callContext batching.CallContextFn) *DebugClient {
return &DebugClient{callContext} return &DebugClient{callContext}
} }
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"io" "io"
"sync" "sync"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
...@@ -391,7 +392,7 @@ type receiptsFetchingJob struct { ...@@ -391,7 +392,7 @@ type receiptsFetchingJob struct {
receiptHash common.Hash receiptHash common.Hash
txHashes []common.Hash txHashes []common.Hash
fetcher *IterativeBatchCall[common.Hash, *types.Receipt] fetcher *batching.IterativeBatchCall[common.Hash, *types.Receipt]
// [OPTIONAL] RethDB path to fetch receipts from // [OPTIONAL] RethDB path to fetch receipts from
rethDbPath string rethDbPath string
...@@ -424,7 +425,7 @@ type ReceiptsRequester interface { ...@@ -424,7 +425,7 @@ type ReceiptsRequester interface {
func (job *receiptsFetchingJob) runFetcher(ctx context.Context) error { func (job *receiptsFetchingJob) runFetcher(ctx context.Context) error {
if job.fetcher == nil { if job.fetcher == nil {
// start new work // start new work
job.fetcher = NewIterativeBatchCall[common.Hash, *types.Receipt]( job.fetcher = batching.NewIterativeBatchCall[common.Hash, *types.Receipt](
job.txHashes, job.txHashes,
makeReceiptRequest, makeReceiptRequest,
job.client.BatchCallContext, job.client.BatchCallContext,
......
package sources package sources
import ( import (
"context"
"fmt" "fmt"
"math/big" "math/big"
"strings" "strings"
...@@ -18,10 +17,6 @@ import ( ...@@ -18,10 +17,6 @@ import (
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
) )
type BatchCallContextFn func(ctx context.Context, b []rpc.BatchElem) error
type CallContextFn func(ctx context.Context, result any, method string, args ...any) error
// Note: these types are used, instead of the geth types, to enable: // Note: these types are used, instead of the geth types, to enable:
// - batched calls of many block requests (standard bindings do extra uncle-header fetches, cannot be batched nicely) // - batched calls of many block requests (standard bindings do extra uncle-header fetches, cannot be batched nicely)
// - ignore uncle data (does not even exist anymore post-Merge) // - ignore uncle data (does not even exist anymore post-Merge)
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
}, },
"dependencies": { "dependencies": {
"@eth-optimism/core-utils": "workspace:*", "@eth-optimism/core-utils": "workspace:*",
"@sentry/node": "^7.75.0", "@sentry/node": "^7.77.0",
"bcfg": "^0.2.1", "bcfg": "^0.2.1",
"body-parser": "^1.20.2", "body-parser": "^1.20.2",
"commander": "^11.1.0", "commander": "^11.1.0",
......
This diff is collapsed.
...@@ -40,6 +40,6 @@ ...@@ -40,6 +40,6 @@
"eip1559DenominatorCanyon": 250, "eip1559DenominatorCanyon": 250,
"eip1559Elasticity": 10, "eip1559Elasticity": 10,
"systemConfigStartBlock": 8300214, "systemConfigStartBlock": 8300214,
"requiredProtocolVersion": "0x0000000000000000000000000000000000000000000000000000000000000000", "requiredProtocolVersion": "0x0000000000000000000000000000000000000003000000010000000000000000",
"recommendedProtocolVersion": "0x0000000000000000000000000000000000000000000000000000000000000000" "recommendedProtocolVersion": "0x0000000000000000000000000000000000000003000000010000000000000000"
} }
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
"l1StartingBlockTag": "earliest", "l1StartingBlockTag": "earliest",
"l1ChainID": 900, "l1ChainID": 900,
"l2ChainID": 901, "l2ChainID": 901,
"l2BlockTime": 1, "l2BlockTime": 2,
"l1BlockTime": 2, "l1BlockTime": 12,
"maxSequencerDrift": 300, "maxSequencerDrift": 300,
"sequencerWindowSize": 15, "sequencerWindowSize": 15,
"channelTimeout": 40, "channelTimeout": 40,
...@@ -14,8 +14,8 @@ ...@@ -14,8 +14,8 @@
"batchInboxAddress": "0xff00000000000000000000000000000000000000", "batchInboxAddress": "0xff00000000000000000000000000000000000000",
"batchSenderAddress": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", "batchSenderAddress": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC",
"l2OutputOracleSubmissionInterval": 6, "l2OutputOracleSubmissionInterval": 6,
"l2OutputOracleStartingTimestamp": 0, "l2OutputOracleStartingTimestamp": 1,
"l2OutputOracleStartingBlockNumber": 0, "l2OutputOracleStartingBlockNumber": 1,
"gasPriceOracleOverhead": 2100, "gasPriceOracleOverhead": 2100,
"gasPriceOracleScalar": 1000000, "gasPriceOracleScalar": 1000000,
"l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
"governanceTokenName": "Optimism", "governanceTokenName": "Optimism",
"governanceTokenSymbol": "OP", "governanceTokenSymbol": "OP",
"governanceTokenOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", "governanceTokenOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc",
"finalizationPeriodSeconds": 2, "finalizationPeriodSeconds": 36,
"eip1559Denominator": 50, "eip1559Denominator": 50,
"eip1559DenominatorCanyon": 250, "eip1559DenominatorCanyon": 250,
"eip1559Elasticity": 10, "eip1559Elasticity": 10,
......
...@@ -44,8 +44,8 @@ ...@@ -44,8 +44,8 @@
"lint": "pnpm lint:fix && pnpm lint:check" "lint": "pnpm lint:fix && pnpm lint:check"
}, },
"devDependencies": { "devDependencies": {
"@typescript-eslint/eslint-plugin": "^6.9.0", "@typescript-eslint/eslint-plugin": "^6.9.1",
"@typescript-eslint/parser": "^6.9.0", "@typescript-eslint/parser": "^6.9.1",
"tsx": "^3.14.0", "tsx": "^3.14.0",
"typescript": "^5.2.2" "typescript": "^5.2.2"
} }
......
{
"faucetAdmin": "0x212E789D4523D4BAF464f8Fb2A9B9dff2B36e5A6",
"faucetDrippieOwner": "0x10ab157483dd308f8B38aCF2ad823dfD255F56b5",
"faucetDripV1Value": 20000000000000000000,
"faucetDripV1Interval": 3600,
"faucetDripV1Threshold": 100000000000000000000,
"faucetDripV2Interval": 604800,
"faucetDripV2Threshold": 20000000000000000000,
"faucetDripV2Value": 500000000000000000000,
"faucetAdminDripV1Interval": 86400,
"faucetAdminDripV1Threshold": 100000000000000000,
"faucetAdminDripV1Value": 1000000000000000000,
"faucetGelatoTreasury": "0x644CB00854EDC55FE8CCC9c1967BABb22F08Ad2f",
"faucetGelatoRecipient": "0x0E9b4649eB0760A4F01646636E032D68cFDe58FF",
"faucetGelatoBalanceV1DripInterval": 86400,
"faucetGelatoBalanceV1Value": 1000000000000000000,
"faucetGelatoThreshold": 100000000000000000,
"faucetOnchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOnchainAuthModuleTtl": 86400,
"faucetOnchainAuthModuleAmount": 1000000000000000000,
"faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOffchainAuthModuleTtl": 86400,
"faucetOffchainAuthModuleAmount": 50000000000000000
}
{
"faucetAdmin": "0x212E789D4523D4BAF464f8Fb2A9B9dff2B36e5A6",
"faucetDrippieOwner": "0x10ab157483dd308f8B38aCF2ad823dfD255F56b5",
"faucetDripV1Value": 20000000000000000000,
"faucetDripV1Interval": 3600,
"faucetDripV1Threshold": 100000000000000000000,
"faucetDripV2Interval": 604800,
"faucetDripV2Threshold": 20000000000000000000,
"faucetDripV2Value": 500000000000000000000,
"faucetAdminDripV1Interval": 86400,
"faucetAdminDripV1Threshold": 100000000000000000,
"faucetAdminDripV1Value": 1000000000000000000,
"faucetGelatoTreasury": "0x644CB00854EDC55FE8CCC9c1967BABb22F08Ad2f",
"faucetGelatoRecipient": "0x0E9b4649eB0760A4F01646636E032D68cFDe58FF",
"faucetGelatoBalanceV1DripInterval": 86400,
"faucetGelatoBalanceV1Value": 1000000000000000000,
"faucetGelatoThreshold": 100000000000000000,
"faucetOnchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOnchainAuthModuleTtl": 86400,
"faucetOnchainAuthModuleAmount": 1000000000000000000,
"faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOffchainAuthModuleTtl": 86400,
"faucetOffchainAuthModuleAmount": 50000000000000000
}
{
"faucetAdmin": "0x212E789D4523D4BAF464f8Fb2A9B9dff2B36e5A6",
"faucetDrippieOwner": "0x10ab157483dd308f8B38aCF2ad823dfD255F56b5",
"faucetDripV1Value": 20000000000000000000,
"faucetDripV1Interval": 3600,
"faucetDripV1Threshold": 100000000000000000000,
"faucetDripV2Interval": 604800,
"faucetDripV2Threshold": 20000000000000000000,
"faucetDripV2Value": 500000000000000000000,
"faucetAdminDripV1Interval": 86400,
"faucetAdminDripV1Threshold": 100000000000000000,
"faucetAdminDripV1Value": 1000000000000000000,
"faucetGelatoTreasury": "0x644CB00854EDC55FE8CCC9c1967BABb22F08Ad2f",
"faucetGelatoRecipient": "0x0E9b4649eB0760A4F01646636E032D68cFDe58FF",
"faucetGelatoBalanceV1DripInterval": 86400,
"faucetGelatoBalanceV1Value": 1000000000000000000,
"faucetGelatoThreshold": 100000000000000000,
"faucetOnchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOnchainAuthModuleTtl": 86400,
"faucetOnchainAuthModuleAmount": 1000000000000000000,
"faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOffchainAuthModuleTtl": 86400,
"faucetOffchainAuthModuleAmount": 50000000000000000
}
{
"faucetAdmin": "0x212E789D4523D4BAF464f8Fb2A9B9dff2B36e5A6",
"faucetDrippieOwner": "0x10ab157483dd308f8B38aCF2ad823dfD255F56b5",
"faucetDripV1Value": 20000000000000000000,
"faucetDripV1Interval": 3600,
"faucetDripV1Threshold": 100000000000000000000,
"faucetDripV2Interval": 604800,
"faucetDripV2Threshold": 20000000000000000000,
"faucetDripV2Value": 500000000000000000000,
"faucetAdminDripV1Interval": 86400,
"faucetAdminDripV1Threshold": 100000000000000000,
"faucetAdminDripV1Value": 1000000000000000000,
"faucetGelatoTreasury": "0x644CB00854EDC55FE8CCC9c1967BABb22F08Ad2f",
"faucetGelatoRecipient": "0x0E9b4649eB0760A4F01646636E032D68cFDe58FF",
"faucetGelatoBalanceV1DripInterval": 86400,
"faucetGelatoBalanceV1Value": 1000000000000000000,
"faucetGelatoThreshold": 100000000000000000,
"faucetOnchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOnchainAuthModuleTtl": 86400,
"faucetOnchainAuthModuleAmount": 1000000000000000000,
"faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOffchainAuthModuleTtl": 86400,
"faucetOffchainAuthModuleAmount": 50000000000000000
}
{
"faucetAdmin": "0x212E789D4523D4BAF464f8Fb2A9B9dff2B36e5A6",
"faucetDrippieOwner": "0x10ab157483dd308f8B38aCF2ad823dfD255F56b5",
"faucetDripV1Value": 20000000000000000000,
"faucetDripV1Interval": 3600,
"faucetDripV1Threshold": 100000000000000000000,
"faucetDripV2Interval": 604800,
"faucetDripV2Threshold": 20000000000000000000,
"faucetDripV2Value": 500000000000000000000,
"faucetAdminDripV1Interval": 86400,
"faucetAdminDripV1Threshold": 100000000000000000,
"faucetAdminDripV1Value": 1000000000000000000,
"faucetGelatoTreasury": "0x644CB00854EDC55FE8CCC9c1967BABb22F08Ad2f",
"faucetGelatoRecipient": "0x0E9b4649eB0760A4F01646636E032D68cFDe58FF",
"faucetGelatoBalanceV1DripInterval": 86400,
"faucetGelatoBalanceV1Value": 1000000000000000000,
"faucetGelatoThreshold": 100000000000000000,
"faucetOnchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOnchainAuthModuleTtl": 86400,
"faucetOnchainAuthModuleAmount": 1000000000000000000,
"faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOffchainAuthModuleTtl": 86400,
"faucetOffchainAuthModuleAmount": 50000000000000000
}
{
"faucetAdmin": "0x212E789D4523D4BAF464f8Fb2A9B9dff2B36e5A6",
"faucetDrippieOwner": "0x10ab157483dd308f8B38aCF2ad823dfD255F56b5",
"faucetDripV1Value": 20000000000000000000,
"faucetDripV1Interval": 3600,
"faucetDripV1Threshold": 100000000000000000000,
"faucetDripV2Interval": 604800,
"faucetDripV2Threshold": 20000000000000000000,
"faucetDripV2Value": 500000000000000000000,
"faucetAdminDripV1Interval": 86400,
"faucetAdminDripV1Threshold": 100000000000000000,
"faucetAdminDripV1Value": 1000000000000000000,
"faucetGelatoTreasury": "0x644CB00854EDC55FE8CCC9c1967BABb22F08Ad2f",
"faucetGelatoRecipient": "0x0E9b4649eB0760A4F01646636E032D68cFDe58FF",
"faucetGelatoBalanceV1DripInterval": 86400,
"faucetGelatoBalanceV1Value": 1000000000000000000,
"faucetGelatoThreshold": 100000000000000000,
"faucetOnchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOnchainAuthModuleTtl": 86400,
"faucetOnchainAuthModuleAmount": 1000000000000000000,
"faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOffchainAuthModuleTtl": 86400,
"faucetOffchainAuthModuleAmount": 50000000000000000
}
{ {
"faucetAdmin": "0x8F0EBDaA1cF7106bE861753B0f9F5c0250fE0819", "faucetAdmin": "0x212E789D4523D4BAF464f8Fb2A9B9dff2B36e5A6",
"faucetDrippieOwner": "0xEa193Fd9565284E7534dDDA15b07B119e7792644", "faucetDrippieOwner": "0x10ab157483dd308f8B38aCF2ad823dfD255F56b5",
"faucetDripV1Value": 20000000000000000000, "faucetDripV1Value": 20000000000000000000,
"faucetDripV1Interval": 3600, "faucetDripV1Interval": 3600,
"faucetDripV1Threshold": 100000000000000000000, "faucetDripV1Threshold": 100000000000000000000,
...@@ -11,14 +11,14 @@ ...@@ -11,14 +11,14 @@
"faucetAdminDripV1Threshold": 100000000000000000, "faucetAdminDripV1Threshold": 100000000000000000,
"faucetAdminDripV1Value": 1000000000000000000, "faucetAdminDripV1Value": 1000000000000000000,
"faucetGelatoTreasury": "0x644CB00854EDC55FE8CCC9c1967BABb22F08Ad2f", "faucetGelatoTreasury": "0x644CB00854EDC55FE8CCC9c1967BABb22F08Ad2f",
"faucetGelatoRecipient": "0x789e58a4B08A23a7f60141959C6ABbdC0D0C4Aba", "faucetGelatoRecipient": "0x0E9b4649eB0760A4F01646636E032D68cFDe58FF",
"faucetGelatoBalanceV1DripInterval": 86400, "faucetGelatoBalanceV1DripInterval": 86400,
"faucetGelatoBalanceV1Value": 1000000000000000000, "faucetGelatoBalanceV1Value": 1000000000000000000,
"faucetGelatoThreshold": 100000000000000000, "faucetGelatoThreshold": 100000000000000000,
"faucetOnchainAuthModuleAdmin": "0x8F0EBDaA1cF7106bE861753B0f9F5c0250fE0819", "faucetOnchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOnchainAuthModuleTtl": 86400, "faucetOnchainAuthModuleTtl": 86400,
"faucetOnchainAuthModuleAmount": 1000000000000000000, "faucetOnchainAuthModuleAmount": 1000000000000000000,
"faucetOffchainAuthModuleAdmin": "0x8F0EBDaA1cF7106bE861753B0f9F5c0250fE0819", "faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOffchainAuthModuleTtl": 86400, "faucetOffchainAuthModuleTtl": 86400,
"faucetOffchainAuthModuleAmount": 50000000000000000 "faucetOffchainAuthModuleAmount": 50000000000000000
} }
{
"faucetAdmin": "0x212E789D4523D4BAF464f8Fb2A9B9dff2B36e5A6",
"faucetDrippieOwner": "0x10ab157483dd308f8B38aCF2ad823dfD255F56b5",
"faucetDripV1Value": 20000000000000000000,
"faucetDripV1Interval": 3600,
"faucetDripV1Threshold": 100000000000000000000,
"faucetDripV2Interval": 604800,
"faucetDripV2Threshold": 20000000000000000000,
"faucetDripV2Value": 500000000000000000000,
"faucetAdminDripV1Interval": 86400,
"faucetAdminDripV1Threshold": 100000000000000000,
"faucetAdminDripV1Value": 1000000000000000000,
"faucetGelatoTreasury": "0x644CB00854EDC55FE8CCC9c1967BABb22F08Ad2f",
"faucetGelatoRecipient": "0x0E9b4649eB0760A4F01646636E032D68cFDe58FF",
"faucetGelatoBalanceV1DripInterval": 86400,
"faucetGelatoBalanceV1Value": 1000000000000000000,
"faucetGelatoThreshold": 100000000000000000,
"faucetOnchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOnchainAuthModuleTtl": 86400,
"faucetOnchainAuthModuleAmount": 1000000000000000000,
"faucetOffchainAuthModuleAdmin": "0xFe44Ae787A632c45ACea658492dDBebE39f002aC",
"faucetOffchainAuthModuleTtl": 86400,
"faucetOffchainAuthModuleAmount": 50000000000000000
}
...@@ -41,7 +41,7 @@ import { Chains } from "scripts/Chains.sol"; ...@@ -41,7 +41,7 @@ import { Chains } from "scripts/Chains.sol";
import { IBigStepper } from "src/dispute/interfaces/IBigStepper.sol"; import { IBigStepper } from "src/dispute/interfaces/IBigStepper.sol";
import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol"; import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol";
import { AlphabetVM } from "../test/FaultDisputeGame.t.sol"; import { AlphabetVM } from "test/mocks/AlphabetVM.sol";
import "src/libraries/DisputeTypes.sol"; import "src/libraries/DisputeTypes.sol";
/// @title Deploy /// @title Deploy
...@@ -52,13 +52,12 @@ import "src/libraries/DisputeTypes.sol"; ...@@ -52,13 +52,12 @@ import "src/libraries/DisputeTypes.sol";
contract Deploy is Deployer { contract Deploy is Deployer {
DeployConfig cfg; DeployConfig cfg;
/// @notice The name of the script, used to ensure the right deploy artifacts /// @inheritdoc Deployer
/// are used.
function name() public pure override returns (string memory name_) { function name() public pure override returns (string memory name_) {
name_ = "Deploy"; name_ = "Deploy";
} }
function setUp() public override { function setUp() public virtual override {
super.setUp(); super.setUp();
string memory path = string.concat(vm.projectRoot(), "/deploy-config/", deploymentContext, ".json"); string memory path = string.concat(vm.projectRoot(), "/deploy-config/", deploymentContext, ".json");
...@@ -97,7 +96,7 @@ contract Deploy is Deployer { ...@@ -97,7 +96,7 @@ contract Deploy is Deployer {
/// @notice The create2 salt used for deployment of the contract implementations. /// @notice The create2 salt used for deployment of the contract implementations.
/// Using this helps to reduce config across networks as the implementation /// Using this helps to reduce config across networks as the implementation
/// addresses will be the same across networks when deployed with create2. /// addresses will be the same across networks when deployed with create2.
function implSalt() public returns (bytes32) { function implSalt() internal returns (bytes32) {
return keccak256(bytes(vm.envOr("IMPL_SALT", string("ethers phoenix")))); return keccak256(bytes(vm.envOr("IMPL_SALT", string("ethers phoenix"))));
} }
......
...@@ -4,8 +4,8 @@ pragma solidity ^0.8.0; ...@@ -4,8 +4,8 @@ pragma solidity ^0.8.0;
import { Script } from "forge-std/Script.sol"; import { Script } from "forge-std/Script.sol";
import { console2 as console } from "forge-std/console2.sol"; import { console2 as console } from "forge-std/console2.sol";
import { stdJson } from "forge-std/StdJson.sol"; import { stdJson } from "forge-std/StdJson.sol";
import { Executables } from "./Executables.sol"; import { Executables } from "scripts/Executables.sol";
import { Chains } from "./Chains.sol"; import { Chains } from "scripts/Chains.sol";
/// @title DeployConfig /// @title DeployConfig
/// @notice Represents the configuration required to deploy the system. It is expected /// @notice Represents the configuration required to deploy the system. It is expected
...@@ -33,8 +33,11 @@ contract DeployConfig is Script { ...@@ -33,8 +33,11 @@ contract DeployConfig is Script {
uint256 public finalizationPeriodSeconds; uint256 public finalizationPeriodSeconds;
address public proxyAdminOwner; address public proxyAdminOwner;
address public baseFeeVaultRecipient; address public baseFeeVaultRecipient;
uint256 public baseFeeVaultMinimumWithdrawalAmount;
address public l1FeeVaultRecipient; address public l1FeeVaultRecipient;
uint256 public l1FeeVaultMinimumWithdrawalAmount;
address public sequencerFeeVaultRecipient; address public sequencerFeeVaultRecipient;
uint256 public sequencerFeeVaultMinimumWithdrawalAmount;
string public governanceTokenName; string public governanceTokenName;
string public governanceTokenSymbol; string public governanceTokenSymbol;
address public governanceTokenOwner; address public governanceTokenOwner;
...@@ -79,8 +82,11 @@ contract DeployConfig is Script { ...@@ -79,8 +82,11 @@ contract DeployConfig is Script {
finalizationPeriodSeconds = stdJson.readUint(_json, "$.finalizationPeriodSeconds"); finalizationPeriodSeconds = stdJson.readUint(_json, "$.finalizationPeriodSeconds");
proxyAdminOwner = stdJson.readAddress(_json, "$.proxyAdminOwner"); proxyAdminOwner = stdJson.readAddress(_json, "$.proxyAdminOwner");
baseFeeVaultRecipient = stdJson.readAddress(_json, "$.baseFeeVaultRecipient"); baseFeeVaultRecipient = stdJson.readAddress(_json, "$.baseFeeVaultRecipient");
baseFeeVaultMinimumWithdrawalAmount = stdJson.readUint(_json, "$.baseFeeVaultMinimumWithdrawalAmount");
l1FeeVaultRecipient = stdJson.readAddress(_json, "$.l1FeeVaultRecipient"); l1FeeVaultRecipient = stdJson.readAddress(_json, "$.l1FeeVaultRecipient");
l1FeeVaultMinimumWithdrawalAmount = stdJson.readUint(_json, "$.l1FeeVaultMinimumWithdrawalAmount");
sequencerFeeVaultRecipient = stdJson.readAddress(_json, "$.sequencerFeeVaultRecipient"); sequencerFeeVaultRecipient = stdJson.readAddress(_json, "$.sequencerFeeVaultRecipient");
sequencerFeeVaultMinimumWithdrawalAmount = stdJson.readUint(_json, "$.sequencerFeeVaultMinimumWithdrawalAmount");
governanceTokenName = stdJson.readString(_json, "$.governanceTokenName"); governanceTokenName = stdJson.readString(_json, "$.governanceTokenName");
governanceTokenSymbol = stdJson.readString(_json, "$.governanceTokenSymbol"); governanceTokenSymbol = stdJson.readString(_json, "$.governanceTokenSymbol");
governanceTokenOwner = stdJson.readAddress(_json, "$.governanceTokenOwner"); governanceTokenOwner = stdJson.readAddress(_json, "$.governanceTokenOwner");
......
...@@ -12,7 +12,6 @@ import { Proxy } from "src/universal/Proxy.sol"; ...@@ -12,7 +12,6 @@ import { Proxy } from "src/universal/Proxy.sol";
import { Faucet } from "src/periphery/faucet/Faucet.sol"; import { Faucet } from "src/periphery/faucet/Faucet.sol";
import { Drippie } from "src/periphery/drippie/Drippie.sol"; import { Drippie } from "src/periphery/drippie/Drippie.sol";
import { CheckGelatoLow } from "src/periphery/drippie/dripchecks/CheckGelatoLow.sol"; import { CheckGelatoLow } from "src/periphery/drippie/dripchecks/CheckGelatoLow.sol";
import { CheckBalanceHigh } from "src/periphery/drippie/dripchecks/CheckBalanceHigh.sol";
import { CheckBalanceLow } from "src/periphery/drippie/dripchecks/CheckBalanceLow.sol"; import { CheckBalanceLow } from "src/periphery/drippie/dripchecks/CheckBalanceLow.sol";
import { CheckTrue } from "src/periphery/drippie/dripchecks/CheckTrue.sol"; import { CheckTrue } from "src/periphery/drippie/dripchecks/CheckTrue.sol";
import { AdminFaucetAuthModule } from "src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol"; import { AdminFaucetAuthModule } from "src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol";
...@@ -62,7 +61,6 @@ contract DeployPeriphery is Deployer { ...@@ -62,7 +61,6 @@ contract DeployPeriphery is Deployer {
deployFaucetDrippie(); deployFaucetDrippie();
deployCheckTrue(); deployCheckTrue();
deployCheckBalanceLow(); deployCheckBalanceLow();
deployCheckBalanceHigh();
deployCheckGelatoLow(); deployCheckGelatoLow();
deployOnChainAuthModule(); deployOnChainAuthModule();
deployOffChainAuthModule(); deployOffChainAuthModule();
...@@ -199,25 +197,6 @@ contract DeployPeriphery is Deployer { ...@@ -199,25 +197,6 @@ contract DeployPeriphery is Deployer {
} }
} }
/// @notice Deploy CheckBalanceHigh contract.
function deployCheckBalanceHigh() public broadcast returns (address addr_) {
bytes32 salt = keccak256(bytes("CheckBalanceHigh"));
bytes32 initCodeHash = keccak256(abi.encodePacked(type(CheckBalanceHigh).creationCode));
address preComputedAddress = computeCreate2Address(salt, initCodeHash);
if (preComputedAddress.code.length > 0) {
console.log("CheckBalanceHigh already deployed at %s", preComputedAddress);
save("CheckBalanceHigh", preComputedAddress);
addr_ = preComputedAddress;
} else {
CheckBalanceHigh checkBalanceHigh = new CheckBalanceHigh{ salt: salt }();
save("CheckBalanceHigh", address(checkBalanceHigh));
console.log("CheckBalanceHigh deployed at %s", address(checkBalanceHigh));
addr_ = address(checkBalanceHigh);
}
}
/// @notice Deploy CheckGelatoLow contract. /// @notice Deploy CheckGelatoLow contract.
function deployCheckGelatoLow() public broadcast returns (address addr_) { function deployCheckGelatoLow() public broadcast returns (address addr_) {
bytes32 salt = keccak256(bytes("CheckGelatoLow")); bytes32 salt = keccak256(bytes("CheckGelatoLow"));
...@@ -468,7 +447,7 @@ contract DeployPeriphery is Deployer { ...@@ -468,7 +447,7 @@ contract DeployPeriphery is Deployer {
/// @notice installs the OnChain AuthModule on the Faucet contract. /// @notice installs the OnChain AuthModule on the Faucet contract.
function installOnChainAuthModule() public broadcast { function installOnChainAuthModule() public broadcast {
string memory moduleName = "OnChainAuthModule"; string memory moduleName = "OnChainAuthModule";
Faucet faucet = Faucet(mustGetAddress("Faucet")); Faucet faucet = Faucet(mustGetAddress("FaucetProxy"));
AdminFaucetAuthModule onChainAuthModule = AdminFaucetAuthModule(mustGetAddress(moduleName)); AdminFaucetAuthModule onChainAuthModule = AdminFaucetAuthModule(mustGetAddress(moduleName));
if (faucet.isModuleEnabled(onChainAuthModule)) { if (faucet.isModuleEnabled(onChainAuthModule)) {
console.log("%s already installed.", moduleName); console.log("%s already installed.", moduleName);
...@@ -488,7 +467,7 @@ contract DeployPeriphery is Deployer { ...@@ -488,7 +467,7 @@ contract DeployPeriphery is Deployer {
/// @notice installs the OffChain AuthModule on the Faucet contract. /// @notice installs the OffChain AuthModule on the Faucet contract.
function installOffChainAuthModule() public broadcast { function installOffChainAuthModule() public broadcast {
string memory moduleName = "OffChainAuthModule"; string memory moduleName = "OffChainAuthModule";
Faucet faucet = Faucet(mustGetAddress("Faucet")); Faucet faucet = Faucet(mustGetAddress("FaucetProxy"));
AdminFaucetAuthModule offChainAuthModule = AdminFaucetAuthModule(mustGetAddress(moduleName)); AdminFaucetAuthModule offChainAuthModule = AdminFaucetAuthModule(mustGetAddress(moduleName));
if (faucet.isModuleEnabled(offChainAuthModule)) { if (faucet.isModuleEnabled(offChainAuthModule)) {
console.log("%s already installed.", moduleName); console.log("%s already installed.", moduleName);
...@@ -507,7 +486,7 @@ contract DeployPeriphery is Deployer { ...@@ -507,7 +486,7 @@ contract DeployPeriphery is Deployer {
/// @notice installs all of the auth module in the faucet contract. /// @notice installs all of the auth module in the faucet contract.
function installFaucetAuthModulesConfigs() public { function installFaucetAuthModulesConfigs() public {
Faucet faucet = Faucet(mustGetAddress("Faucet")); Faucet faucet = Faucet(mustGetAddress("FaucetProxy"));
console.log("Installing auth modules at %s", address(faucet)); console.log("Installing auth modules at %s", address(faucet));
installOnChainAuthModule(); installOnChainAuthModule();
installOffChainAuthModule(); installOffChainAuthModule();
......
...@@ -163,6 +163,8 @@ abstract contract Deployer is Script { ...@@ -163,6 +163,8 @@ abstract contract Deployer is Script {
/// @notice Returns the name of the deployment script. Children contracts /// @notice Returns the name of the deployment script. Children contracts
/// must implement this to ensure that the deploy artifacts can be found. /// must implement this to ensure that the deploy artifacts can be found.
/// This should be the same as the name of the script and is used as the file
/// name inside of the `broadcast` directory when looking up deployment artifacts.
function name() public pure virtual returns (string memory); function name() public pure virtual returns (string memory);
/// @notice Returns all of the deployments done in the current context. /// @notice Returns all of the deployments done in the current context.
......
...@@ -3,16 +3,16 @@ pragma solidity 0.8.15; ...@@ -3,16 +3,16 @@ pragma solidity 0.8.15;
import { console } from "forge-std/console.sol"; import { console } from "forge-std/console.sol";
import { Script } from "forge-std/Script.sol"; import { Script } from "forge-std/Script.sol";
import { Semver } from "../../src/universal/Semver.sol"; import { ISemver } from "../../src/universal/ISemver.sol";
/// @title EnhancedScript /// @title EnhancedScript
/// @notice Enhances forge-std' Script.sol with some additional application-specific functionality. /// @notice Enhances forge-std' Script.sol with some additional application-specific functionality.
/// Logs simulation links using Tenderly. /// Logs simulation links using Tenderly.
abstract contract EnhancedScript is Script { abstract contract EnhancedScript is Script {
/// @notice Helper function used to compute the hash of Semver's version string to be used in a /// @notice Helper function used to compute the hash of ISemver's version string to be used in a
/// comparison. /// comparison.
function _versionHash(address _addr) internal view returns (bytes32) { function _versionHash(address _addr) internal view returns (bytes32) {
return keccak256(bytes(Semver(_addr).version())); return keccak256(bytes(ISemver(_addr).version()));
} }
/// @notice Log a tenderly simulation link. The TENDERLY_USERNAME and TENDERLY_PROJECT /// @notice Log a tenderly simulation link. The TENDERLY_USERNAME and TENDERLY_PROJECT
......
...@@ -10,7 +10,6 @@ import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; ...@@ -10,7 +10,6 @@ import { ProxyAdmin } from "src/universal/ProxyAdmin.sol";
import { Constants } from "src/libraries/Constants.sol"; import { Constants } from "src/libraries/Constants.sol";
import { SystemConfig } from "src/L1/SystemConfig.sol"; import { SystemConfig } from "src/L1/SystemConfig.sol";
import { ResourceMetering } from "src/L1/ResourceMetering.sol"; import { ResourceMetering } from "src/L1/ResourceMetering.sol";
import { Semver } from "src/universal/Semver.sol";
import { DeployConfig } from "scripts/DeployConfig.s.sol"; import { DeployConfig } from "scripts/DeployConfig.s.sol";
import { SystemConfig } from "src/L1/SystemConfig.sol"; import { SystemConfig } from "src/L1/SystemConfig.sol";
......
{ {
"src/EAS/EAS.sol": "0xea04b7a9ec170ce9cbd466ede87650f9e9ffe3d725d7951cef5777a98a840173", "src/EAS/EAS.sol": "0xe5e9700b94a88a2e1baabe4bfa66d5c1c94e811bcc5d2c64526afc649df59118",
"src/EAS/SchemaRegistry.sol": "0x5ee1a0c3b2bf1eb5edb53fb0967cf13856be546f0f16fe7acdc3e4f286db6831", "src/EAS/SchemaRegistry.sol": "0x5ee1a0c3b2bf1eb5edb53fb0967cf13856be546f0f16fe7acdc3e4f286db6831",
"src/L1/DelayedVetoable.sol": "0x276c6276292095e6aa37a70008cf4e0d1cbcc020dbc9107459bbc72ab5ed744f", "src/L1/DelayedVetoable.sol": "0x276c6276292095e6aa37a70008cf4e0d1cbcc020dbc9107459bbc72ab5ed744f",
"src/L1/L1CrossDomainMessenger.sol": "0x2aa4e06827bc48484212eb2bdc30fd604ffd23b37e401b78ef428c12fa9b8385", "src/L1/L1CrossDomainMessenger.sol": "0x2aa4e06827bc48484212eb2bdc30fd604ffd23b37e401b78ef428c12fa9b8385",
...@@ -26,12 +26,12 @@ ...@@ -26,12 +26,12 @@
"src/legacy/DeployerWhitelist.sol": "0x0a6840074734c9d167321d3299be18ef911a415e4c471fa92af7d6cfaa8336d4", "src/legacy/DeployerWhitelist.sol": "0x0a6840074734c9d167321d3299be18ef911a415e4c471fa92af7d6cfaa8336d4",
"src/legacy/L1BlockNumber.sol": "0x20d83a636c5e2067fca8c0ed505b295174e6eddb25960d8705e6b6fea8e77fa6", "src/legacy/L1BlockNumber.sol": "0x20d83a636c5e2067fca8c0ed505b295174e6eddb25960d8705e6b6fea8e77fa6",
"src/legacy/LegacyMessagePasser.sol": "0x80f355c9710af586f58cf6a86d1925e0073d1e504d0b3d814284af1bafe4dece", "src/legacy/LegacyMessagePasser.sol": "0x80f355c9710af586f58cf6a86d1925e0073d1e504d0b3d814284af1bafe4dece",
"src/periphery/op-nft/AttestationStation.sol": "0x9cf6f2fd909f9bcff229a137198186749a839cfa3d11ddbb3021fe71c30a2fa9", "src/periphery/op-nft/AttestationStation.sol": "0x067b29fe24734c121469c1cb2e9b2602ddabb9e07792338b766cab341776cd78",
"src/periphery/op-nft/Optimist.sol": "0x38407f766aa9d394403e9da388dd0265b48901789f3e8a28af50014f9f5251d9", "src/periphery/op-nft/Optimist.sol": "0x128113cd97433987220f25b59d883d5ee7e70ff2214a536c0df3d892e13287fc",
"src/periphery/op-nft/OptimistAllowlist.sol": "0x53e9a9dfecbae036fd468e8f34c80c7d9c35bd8908c8a6483db44dbc5128ad69", "src/periphery/op-nft/OptimistAllowlist.sol": "0x12e5d0a79c8c05cfd41be8a1bcbd5c889652182a723aeb5eccb35e0ee2a5b6c0",
"src/periphery/op-nft/OptimistInviter.sol": "0xfdd5b9d45205ef9372ba37f7a6394724695e676d27a47cb154ee6e4148490013", "src/periphery/op-nft/OptimistInviter.sol": "0xe5353f882475396ca8c3c0f8905baf3450697612ee6ac1d7053a80f6e1ecdd3b",
"src/universal/OptimismMintableERC20.sol": "0x716db294648fce1bb41c5f95a20f92445f165a568886f21edd922d5c9b2cb0b5", "src/universal/OptimismMintableERC20.sol": "0x099bea9f5d2f0a827f87485a4e51b8055981f6d84a0e974d226ba6d8ed5ba73d",
"src/universal/OptimismMintableERC20Factory.sol": "0xfcf2eb56777478f47f3bf2f1111aa2e3769d5ed28a6f5fceff4517683447131a", "src/universal/OptimismMintableERC20Factory.sol": "0x8d4cbf4cc30a0bb72925b5d2e0386b8f91559f00933a9c7cf3dcc118e34fe61b",
"src/universal/OptimismMintableERC721.sol": "0x4c73bf8474fa7eb091796a4db7e57bc5f26d50a3d1cfcb78d5efa47ced5ced2b", "src/universal/OptimismMintableERC721.sol": "0x4c73bf8474fa7eb091796a4db7e57bc5f26d50a3d1cfcb78d5efa47ced5ced2b",
"src/universal/OptimismMintableERC721Factory.sol": "0x935fd97018b6ef10fa813d9d43ab7a77c80885f7a8d7feb430097645cb2abd2c", "src/universal/OptimismMintableERC721Factory.sol": "0x935fd97018b6ef10fa813d9d43ab7a77c80885f7a8d7feb430097645cb2abd2c",
"src/universal/StorageSetter.sol": "0x6372647d8a67d243bc2fb40d2c4bf5807022d94d52d9423cfed27a7d57918635" "src/universal/StorageSetter.sol": "0x6372647d8a67d243bc2fb40d2c4bf5807022d94d52d9423cfed27a7d57918635"
......
...@@ -16,7 +16,7 @@ import { ...@@ -16,7 +16,7 @@ import {
NotFound, NotFound,
NO_EXPIRATION_TIME, NO_EXPIRATION_TIME,
uncheckedInc uncheckedInc
} from "./Common.sol"; } from "src/EAS/Common.sol";
import { import {
Attestation, Attestation,
...@@ -31,9 +31,9 @@ import { ...@@ -31,9 +31,9 @@ import {
MultiRevocationRequest, MultiRevocationRequest,
RevocationRequest, RevocationRequest,
RevocationRequestData RevocationRequestData
} from "./IEAS.sol"; } from "src/EAS/IEAS.sol";
import { ISchemaRegistry, SchemaRecord } from "./ISchemaRegistry.sol"; import { ISchemaRegistry, SchemaRecord } from "src/EAS/ISchemaRegistry.sol";
struct AttestationsResult { struct AttestationsResult {
uint256 usedValue; // Total ETH amount that was sent to resolvers. uint256 usedValue; // Total ETH amount that was sent to resolvers.
......
...@@ -10,7 +10,7 @@ import { ...@@ -10,7 +10,7 @@ import {
DelegatedAttestationRequest, DelegatedAttestationRequest,
DelegatedRevocationRequest, DelegatedRevocationRequest,
RevocationRequestData RevocationRequestData
} from "../IEAS.sol"; } from "src/EAS/IEAS.sol";
import { import {
DeadlineExpired, DeadlineExpired,
...@@ -20,7 +20,7 @@ import { ...@@ -20,7 +20,7 @@ import {
MAX_GAP, MAX_GAP,
stringToBytes32, stringToBytes32,
bytes32ToString bytes32ToString
} from "../Common.sol"; } from "src/EAS/Common.sol";
/// @title EIP1271Verifier /// @title EIP1271Verifier
/// @notice EIP1271Verifier typed signatures verifier for EAS delegated attestations. /// @notice EIP1271Verifier typed signatures verifier for EAS delegated attestations.
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.19; pragma solidity 0.8.19;
import { Semver } from "../../universal/Semver.sol"; import { IEAS, Attestation } from "src/EAS/IEAS.sol";
import { AccessDenied, InvalidEAS, InvalidLength, uncheckedInc } from "src/EAS/Common.sol";
import { IEAS, Attestation } from "../IEAS.sol"; import { ISchemaResolver } from "src/EAS/resolver/ISchemaResolver.sol";
import { AccessDenied, InvalidEAS, InvalidLength, uncheckedInc } from "../Common.sol";
import { ISchemaResolver } from "./ISchemaResolver.sol";
/// @title SchemaResolver /// @title SchemaResolver
/// @notice The base schema resolver contract. /// @notice The base schema resolver contract.
abstract contract SchemaResolver is ISchemaResolver, Semver { abstract contract SchemaResolver is ISchemaResolver {
error InsufficientValue(); error InsufficientValue();
error NotPayable(); error NotPayable();
...@@ -19,7 +17,7 @@ abstract contract SchemaResolver is ISchemaResolver, Semver { ...@@ -19,7 +17,7 @@ abstract contract SchemaResolver is ISchemaResolver, Semver {
/// @dev Creates a new resolver. /// @dev Creates a new resolver.
/// @param eas The address of the global EAS contract. /// @param eas The address of the global EAS contract.
constructor(IEAS eas) Semver(1, 2, 0) { constructor(IEAS eas) {
if (address(eas) == address(0)) { if (address(eas) == address(0)) {
revert InvalidEAS(); revert InvalidEAS();
} }
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { IDripCheck } from "../IDripCheck.sol";
/// @title CheckBalanceHigh
/// @notice DripCheck for checking if an account's balance is above a given threshold.
contract CheckBalanceHigh is IDripCheck {
struct Params {
address target;
uint256 threshold;
}
/// @notice External event used to help client-side tooling encode parameters.
/// @param params Parameters to encode.
event _EventToExposeStructInABI__Params(Params params);
/// @inheritdoc IDripCheck
function check(bytes memory _params) external view returns (bool execute_) {
Params memory params = abi.decode(_params, (Params));
// Check target balance is above threshold.
execute_ = params.target.balance > params.threshold;
}
}
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Semver } from "../../universal/Semver.sol"; import { ISemver } from "src/universal/ISemver.sol";
/// @title AttestationStation /// @title AttestationStation
/// @author Optimism Collective /// @author Optimism Collective
/// @author Gitcoin /// @author Gitcoin
/// @notice Where attestations live. /// @notice Where attestations live.
contract AttestationStation is Semver { contract AttestationStation is ISemver {
/// @notice Struct representing data that is being attested. /// @notice Struct representing data that is being attested.
/// @custom:field about Address for which the attestation is about. /// @custom:field about Address for which the attestation is about.
/// @custom:field key A bytes32 key for the attestation. /// @custom:field key A bytes32 key for the attestation.
...@@ -28,8 +28,9 @@ contract AttestationStation is Semver { ...@@ -28,8 +28,9 @@ contract AttestationStation is Semver {
/// @param val Value of the attestation. /// @param val Value of the attestation.
event AttestationCreated(address indexed creator, address indexed about, bytes32 indexed key, bytes val); event AttestationCreated(address indexed creator, address indexed about, bytes32 indexed key, bytes val);
/// @custom:semver 1.1.2 /// @notice Semantic version.
constructor() Semver(1, 1, 2) { } /// @custom:semver 1.2.0
string public constant version = "1.2.0";
/// @notice Allows anyone to create an attestation. /// @notice Allows anyone to create an attestation.
/// @param _about Address that the attestation is about. /// @param _about Address that the attestation is about.
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Semver } from "../../universal/Semver.sol"; import { ISemver } from "src/universal/ISemver.sol";
import { ERC721BurnableUpgradeable } from import { ERC721BurnableUpgradeable } from
"@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol"; "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol";
import { AttestationStation } from "./AttestationStation.sol"; import { AttestationStation } from "src/periphery/op-nft/AttestationStation.sol";
import { OptimistAllowlist } from "./OptimistAllowlist.sol"; import { OptimistAllowlist } from "src/periphery/op-nft/OptimistAllowlist.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
/// @author Optimism Collective /// @author Optimism Collective
/// @author Gitcoin /// @author Gitcoin
/// @title Optimist /// @title Optimist
/// @notice A Soul Bound Token for real humans only(tm). /// @notice A Soul Bound Token for real humans only(tm).
contract Optimist is ERC721BurnableUpgradeable, Semver { contract Optimist is ERC721BurnableUpgradeable, ISemver {
/// @notice Attestation key used by the attestor to attest the baseURI. /// @notice Attestation key used by the attestor to attest the baseURI.
bytes32 public constant BASE_URI_ATTESTATION_KEY = bytes32("optimist.base-uri"); bytes32 public constant BASE_URI_ATTESTATION_KEY = bytes32("optimist.base-uri");
...@@ -25,7 +25,10 @@ contract Optimist is ERC721BurnableUpgradeable, Semver { ...@@ -25,7 +25,10 @@ contract Optimist is ERC721BurnableUpgradeable, Semver {
/// @notice Address of the OptimistAllowlist contract. /// @notice Address of the OptimistAllowlist contract.
OptimistAllowlist public immutable OPTIMIST_ALLOWLIST; OptimistAllowlist public immutable OPTIMIST_ALLOWLIST;
/// @custom:semver 2.0.2 /// @notice Semantic version.
/// @custom:semver 2.1.0
string public constant version = "2.1.0";
/// @param _name Token name. /// @param _name Token name.
/// @param _symbol Token symbol. /// @param _symbol Token symbol.
/// @param _baseURIAttestor Address of the baseURI attestor. /// @param _baseURIAttestor Address of the baseURI attestor.
...@@ -37,9 +40,7 @@ contract Optimist is ERC721BurnableUpgradeable, Semver { ...@@ -37,9 +40,7 @@ contract Optimist is ERC721BurnableUpgradeable, Semver {
address _baseURIAttestor, address _baseURIAttestor,
AttestationStation _attestationStation, AttestationStation _attestationStation,
OptimistAllowlist _optimistAllowlist OptimistAllowlist _optimistAllowlist
) ) {
Semver(2, 0, 2)
{
BASE_URI_ATTESTOR = _baseURIAttestor; BASE_URI_ATTESTOR = _baseURIAttestor;
ATTESTATION_STATION = _attestationStation; ATTESTATION_STATION = _attestationStation;
OPTIMIST_ALLOWLIST = _optimistAllowlist; OPTIMIST_ALLOWLIST = _optimistAllowlist;
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Semver } from "../../universal/Semver.sol"; import { ISemver } from "src/universal/ISemver.sol";
import { AttestationStation } from "./AttestationStation.sol"; import { AttestationStation } from "src/periphery/op-nft/AttestationStation.sol";
import { OptimistConstants } from "./libraries/OptimistConstants.sol"; import { OptimistConstants } from "src/periphery/op-nft/libraries/OptimistConstants.sol";
/// @title OptimistAllowlist /// @title OptimistAllowlist
/// @notice Source of truth for whether an address is able to mint an Optimist NFT. /// @notice Source of truth for whether an address is able to mint an Optimist NFT.
/// isAllowedToMint function checks various signals to return boolean value /// isAllowedToMint function checks various signals to return boolean value
/// for whether an address is eligible or not. /// for whether an address is eligible or not.
contract OptimistAllowlist is Semver { contract OptimistAllowlist is ISemver {
/// @notice Attestation key used by the AllowlistAttestor to manually add addresses to the /// @notice Attestation key used by the AllowlistAttestor to manually add addresses to the
/// allowlist. /// allowlist.
bytes32 public constant OPTIMIST_CAN_MINT_ATTESTATION_KEY = bytes32("optimist.can-mint"); bytes32 public constant OPTIMIST_CAN_MINT_ATTESTATION_KEY = bytes32("optimist.can-mint");
...@@ -30,7 +30,10 @@ contract OptimistAllowlist is Semver { ...@@ -30,7 +30,10 @@ contract OptimistAllowlist is Semver {
/// attestations. /// attestations.
address public immutable OPTIMIST_INVITER; address public immutable OPTIMIST_INVITER;
/// @custom:semver 1.0.2 /// @notice Semantic version.
/// @custom:semver 1.1.0
string public constant version = "1.1.0";
/// @param _attestationStation Address of the AttestationStation contract. /// @param _attestationStation Address of the AttestationStation contract.
/// @param _allowlistAttestor Address of the allowlist attestor. /// @param _allowlistAttestor Address of the allowlist attestor.
/// @param _coinbaseQuestAttestor Address of the Coinbase Quest attestor. /// @param _coinbaseQuestAttestor Address of the Coinbase Quest attestor.
...@@ -40,9 +43,7 @@ contract OptimistAllowlist is Semver { ...@@ -40,9 +43,7 @@ contract OptimistAllowlist is Semver {
address _allowlistAttestor, address _allowlistAttestor,
address _coinbaseQuestAttestor, address _coinbaseQuestAttestor,
address _optimistInviter address _optimistInviter
) ) {
Semver(1, 0, 2)
{
ATTESTATION_STATION = _attestationStation; ATTESTATION_STATION = _attestationStation;
ALLOWLIST_ATTESTOR = _allowlistAttestor; ALLOWLIST_ATTESTOR = _allowlistAttestor;
COINBASE_QUEST_ATTESTOR = _coinbaseQuestAttestor; COINBASE_QUEST_ATTESTOR = _coinbaseQuestAttestor;
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { OptimistConstants } from "./libraries/OptimistConstants.sol"; import { OptimistConstants } from "src/periphery/op-nft/libraries/OptimistConstants.sol";
import { Semver } from "../../universal/Semver.sol"; import { ISemver } from "src/universal/ISemver.sol";
import { AttestationStation } from "./AttestationStation.sol"; import { AttestationStation } from "src/periphery/op-nft/AttestationStation.sol";
import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import { EIP712Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/cryptography/draft-EIP712Upgradeable.sol"; import { EIP712Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/cryptography/draft-EIP712Upgradeable.sol";
...@@ -32,7 +32,7 @@ import { EIP712Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/cry ...@@ -32,7 +32,7 @@ import { EIP712Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/cry
/// 6) claimer waits for the MIN_COMMITMENT_PERIOD to pass. /// 6) claimer waits for the MIN_COMMITMENT_PERIOD to pass.
/// 7) claimer reveals the plaintext ClaimableInvite and the signature using the /// 7) claimer reveals the plaintext ClaimableInvite and the signature using the
/// claimInvite function, receiving the "optimist.can-mint-from-invite" attestation /// claimInvite function, receiving the "optimist.can-mint-from-invite" attestation
contract OptimistInviter is Semver, EIP712Upgradeable { contract OptimistInviter is ISemver, EIP712Upgradeable {
/// @notice Emitted when an invite is claimed. /// @notice Emitted when an invite is claimed.
/// @param issuer Address that issued the signature. /// @param issuer Address that issued the signature.
/// @param claimer Address that claimed the invite. /// @param claimer Address that claimed the invite.
...@@ -87,10 +87,13 @@ contract OptimistInviter is Semver, EIP712Upgradeable { ...@@ -87,10 +87,13 @@ contract OptimistInviter is Semver, EIP712Upgradeable {
/// @notice Maps from addresses to number of invites they have. /// @notice Maps from addresses to number of invites they have.
mapping(address => uint256) public inviteCounts; mapping(address => uint256) public inviteCounts;
/// @custom:semver 1.0.2 /// @notice Semantic version.
/// @custom:semver 1.1.0
string public constant version = "1.1.0";
/// @param _inviteGranter Address of the invite granter. /// @param _inviteGranter Address of the invite granter.
/// @param _attestationStation Address of the AttestationStation contract. /// @param _attestationStation Address of the AttestationStation contract.
constructor(address _inviteGranter, AttestationStation _attestationStation) Semver(1, 0, 2) { constructor(address _inviteGranter, AttestationStation _attestationStation) {
INVITE_GRANTER = _inviteGranter; INVITE_GRANTER = _inviteGranter;
ATTESTATION_STATION = _attestationStation; ATTESTATION_STATION = _attestationStation;
} }
......
...@@ -4,7 +4,7 @@ pragma solidity 0.8.15; ...@@ -4,7 +4,7 @@ pragma solidity 0.8.15;
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import { ILegacyMintableERC20, IOptimismMintableERC20 } from "src/universal/IOptimismMintableERC20.sol"; import { ILegacyMintableERC20, IOptimismMintableERC20 } from "src/universal/IOptimismMintableERC20.sol";
import { Semver } from "src/universal/Semver.sol"; import { ISemver } from "src/universal/ISemver.sol";
/// @title OptimismMintableERC20 /// @title OptimismMintableERC20
/// @notice OptimismMintableERC20 is a standard extension of the base ERC20 token contract designed /// @notice OptimismMintableERC20 is a standard extension of the base ERC20 token contract designed
...@@ -12,7 +12,7 @@ import { Semver } from "src/universal/Semver.sol"; ...@@ -12,7 +12,7 @@ import { Semver } from "src/universal/Semver.sol";
/// use an OptimismMintablERC20 as the L2 representation of an L1 token, or vice-versa. /// use an OptimismMintablERC20 as the L2 representation of an L1 token, or vice-versa.
/// Designed to be backwards compatible with the older StandardL2ERC20 token which was only /// Designed to be backwards compatible with the older StandardL2ERC20 token which was only
/// meant for use on L2. /// meant for use on L2.
contract OptimismMintableERC20 is IOptimismMintableERC20, ILegacyMintableERC20, ERC20, Semver { contract OptimismMintableERC20 is IOptimismMintableERC20, ILegacyMintableERC20, ERC20, ISemver {
/// @notice Address of the corresponding version of this token on the remote chain. /// @notice Address of the corresponding version of this token on the remote chain.
address public immutable REMOTE_TOKEN; address public immutable REMOTE_TOKEN;
...@@ -38,7 +38,10 @@ contract OptimismMintableERC20 is IOptimismMintableERC20, ILegacyMintableERC20, ...@@ -38,7 +38,10 @@ contract OptimismMintableERC20 is IOptimismMintableERC20, ILegacyMintableERC20,
_; _;
} }
/// @custom:semver 1.2.1 /// @notice Semantic version.
/// @custom:semver 1.3.0
string public constant version = "1.3.0";
/// @param _bridge Address of the L2 standard bridge. /// @param _bridge Address of the L2 standard bridge.
/// @param _remoteToken Address of the corresponding L1 token. /// @param _remoteToken Address of the corresponding L1 token.
/// @param _name ERC20 name. /// @param _name ERC20 name.
...@@ -51,7 +54,6 @@ contract OptimismMintableERC20 is IOptimismMintableERC20, ILegacyMintableERC20, ...@@ -51,7 +54,6 @@ contract OptimismMintableERC20 is IOptimismMintableERC20, ILegacyMintableERC20,
uint8 _decimals uint8 _decimals
) )
ERC20(_name, _symbol) ERC20(_name, _symbol)
Semver(1, 2, 1)
{ {
REMOTE_TOKEN = _remoteToken; REMOTE_TOKEN = _remoteToken;
BRIDGE = _bridge; BRIDGE = _bridge;
......
...@@ -32,8 +32,8 @@ contract OptimismMintableERC20Factory is ISemver, Initializable { ...@@ -32,8 +32,8 @@ contract OptimismMintableERC20Factory is ISemver, Initializable {
event OptimismMintableERC20Created(address indexed localToken, address indexed remoteToken, address deployer); event OptimismMintableERC20Created(address indexed localToken, address indexed remoteToken, address deployer);
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.6.1 /// @custom:semver 1.7.0
string public constant version = "1.6.1"; string public constant version = "1.7.0";
/// @notice The semver MUST be bumped any time that there is a change in /// @notice The semver MUST be bumped any time that there is a change in
/// the OptimismMintableERC20 token contract since this contract /// the OptimismMintableERC20 token contract since this contract
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
/// @title Semver
/// @notice Semver is a simple contract for managing contract versions.
contract Semver {
/// @notice Contract version number (major).
uint256 private immutable MAJOR_VERSION;
/// @notice Contract version number (minor).
uint256 private immutable MINOR_VERSION;
/// @notice Contract version number (patch).
uint256 private immutable PATCH_VERSION;
/// @param _major Version number (major).
/// @param _minor Version number (minor).
/// @param _patch Version number (patch).
constructor(uint256 _major, uint256 _minor, uint256 _patch) {
MAJOR_VERSION = _major;
MINOR_VERSION = _minor;
PATCH_VERSION = _patch;
}
/// @notice Returns the full semver contract version.
/// @return Semver contract version as a string.
function version() public view returns (string memory) {
return string(
abi.encodePacked(
Strings.toString(MAJOR_VERSION),
".",
Strings.toString(MINOR_VERSION),
".",
Strings.toString(PATCH_VERSION)
)
);
}
}
...@@ -4,7 +4,7 @@ pragma solidity 0.8.15; ...@@ -4,7 +4,7 @@ pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { AdminFaucetAuthModule } from "src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol"; import { AdminFaucetAuthModule } from "src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol";
import { Faucet } from "src/periphery/faucet/Faucet.sol"; import { Faucet } from "src/periphery/faucet/Faucet.sol";
import { FaucetHelper } from "test/Helpers.sol"; import { FaucetHelper } from "test/mocks/FaucetHelper.sol";
/// @title AdminFaucetAuthModuleTest /// @title AdminFaucetAuthModuleTest
/// @notice Tests the AdminFaucetAuthModule contract. /// @notice Tests the AdminFaucetAuthModule contract.
......
...@@ -3,8 +3,8 @@ pragma solidity 0.8.15; ...@@ -3,8 +3,8 @@ pragma solidity 0.8.15;
// Testing utilities // Testing utilities
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { TestERC20 } from "test/Helpers.sol"; import { TestERC20 } from "test/mocks/TestERC20.sol";
import { TestERC721 } from "test/Helpers.sol"; import { TestERC721 } from "test/mocks/TestERC721.sol";
import { AssetReceiver } from "src/periphery/AssetReceiver.sol"; import { AssetReceiver } from "src/periphery/AssetReceiver.sol";
contract AssetReceiver_Initializer is Test { contract AssetReceiver_Initializer is Test {
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol";
import { CheckBalanceHigh } from "src/periphery/drippie/dripchecks/CheckBalanceHigh.sol";
/// @title CheckBalanceHighTest
/// @notice Tests the CheckBalanceHigh contract via fuzzing both the success case
/// and the failure case.
contract CheckBalanceHighTest is Test {
/// @notice An instance of the CheckBalanceHigh contract.
CheckBalanceHigh c;
/// @notice Deploy the `CheckTrue` contract.
function setUp() external {
c = new CheckBalanceHigh();
}
/// @notice Fuzz the `check` function and assert that it always returns true
/// when the target's balance is larger than the threshold.
function testFuzz_check_succeeds(address _target, uint256 _threshold) external {
CheckBalanceHigh.Params memory p = CheckBalanceHigh.Params({ target: _target, threshold: _threshold });
// prevent overflows
vm.assume(_threshold != type(uint256).max);
vm.deal(_target, _threshold + 1);
assertEq(c.check(abi.encode(p)), true);
}
/// @notice Fuzz the `check` function and assert that it always returns false
/// when the target's balance is smaller than the threshold.
function testFuzz_check_lowBalance_fails(address _target, uint256 _threshold) external {
CheckBalanceHigh.Params memory p = CheckBalanceHigh.Params({ target: _target, threshold: _threshold });
vm.assume(_target.balance < _threshold);
assertEq(c.check(abi.encode(p)), false);
}
}
...@@ -20,7 +20,7 @@ contract MockGelatoTreasury is IGelatoTreasury { ...@@ -20,7 +20,7 @@ contract MockGelatoTreasury is IGelatoTreasury {
} }
/// @title CheckGelatoLowTest /// @title CheckGelatoLowTest
/// @notice Tests the CheckBalanceHigh contract via fuzzing both the success case /// @notice Tests the CheckGelatoLow contract via fuzzing both the success case
/// and the failure case. /// and the failure case.
contract CheckGelatoLowTest is Test { contract CheckGelatoLowTest is Test {
/// @notice An instance of the CheckGelatoLow contract. /// @notice An instance of the CheckGelatoLow contract.
......
...@@ -731,99 +731,3 @@ contract FFIInterface is Test { ...@@ -731,99 +731,3 @@ contract FFIInterface is Test {
return (memRoot, proof); return (memRoot, proof);
} }
} }
library EIP1967Helper {
Vm internal constant vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
function getAdmin(address _proxy) internal view returns (address) {
return address(uint160(uint256(vm.load(address(_proxy), Constants.PROXY_OWNER_ADDRESS))));
}
function getImplementation(address _proxy) internal view returns (address) {
return address(uint160(uint256(vm.load(address(_proxy), Constants.PROXY_IMPLEMENTATION_ADDRESS))));
}
}
// Used for testing a future upgrade beyond the current implementations.
// We include some variables so that we can sanity check accessing storage values after an upgrade.
contract NextImpl is Initializable {
// Initializable occupies the zero-th slot.
bytes32 slot1;
bytes32[19] __gap;
bytes32 slot21;
bytes32 public constant slot21Init = bytes32(hex"1337");
function initialize(uint8 _init) public reinitializer(_init) {
// Slot21 is unused by an of our upgradeable contracts.
// This is used to verify that we can access this value after an upgrade.
slot21 = slot21Init;
}
}
contract Reverter {
fallback() external {
revert();
}
}
// Useful for testing reentrancy guards
contract CallerCaller {
event WhatHappened(bool success, bytes returndata);
fallback() external {
(bool success, bytes memory returndata) = msg.sender.call(msg.data);
emit WhatHappened(success, returndata);
assembly {
switch success
case 0 { revert(add(returndata, 0x20), mload(returndata)) }
default { return(add(returndata, 0x20), mload(returndata)) }
}
}
}
// Used for testing the `CrossDomainMessenger`'s per-message reentrancy guard.
contract ConfigurableCaller {
bool doRevert = true;
address target;
bytes payload;
event WhatHappened(bool success, bytes returndata);
/// @notice Call the configured target with the configured payload OR revert.
function call() external {
if (doRevert) {
revert("ConfigurableCaller: revert");
} else {
(bool success, bytes memory returndata) = address(target).call(payload);
emit WhatHappened(success, returndata);
assembly {
switch success
case 0 { revert(add(returndata, 0x20), mload(returndata)) }
default { return(add(returndata, 0x20), mload(returndata)) }
}
}
}
/// @notice Set whether or not to have `call` revert.
function setDoRevert(bool _doRevert) external {
doRevert = _doRevert;
}
/// @notice Set the target for the call made in `call`.
function setTarget(address _target) external {
target = _target;
}
/// @notice Set the payload for the call made in `call`.
function setPayload(bytes calldata _payload) external {
payload = _payload;
}
/// @notice Fallback function that reverts if `doRevert` is true.
/// Otherwise, it does nothing.
fallback() external {
if (doRevert) {
revert("ConfigurableCaller: revert");
}
}
}
...@@ -2,13 +2,16 @@ ...@@ -2,13 +2,16 @@
pragma solidity 0.8.15; pragma solidity 0.8.15;
// Testing utilities // Testing utilities
import { Messenger_Initializer, Reverter, CallerCaller, CommonTest } from "test/CommonTest.t.sol"; import { Test } from "forge-std/Test.sol";
import { Messenger_Initializer, Test } from "test/CommonTest.t.sol";
import { L1CrossDomainMessenger } from "src/L1/L1CrossDomainMessenger.sol"; import { L1CrossDomainMessenger } from "src/L1/L1CrossDomainMessenger.sol";
import { Reverter, CallerCaller } from "test/mocks/Callers.sol";
// Libraries // Libraries
import { Predeploys } from "../src/libraries/Predeploys.sol"; import { Predeploys } from "src/libraries/Predeploys.sol";
import { Hashing } from "../src/libraries/Hashing.sol"; import { Hashing } from "src/libraries/Hashing.sol";
import { Encoding } from "../src/libraries/Encoding.sol"; import { Encoding } from "src/libraries/Encoding.sol";
// CrossDomainMessenger_Test is for testing functionality which is common to both the L1 and L2 // CrossDomainMessenger_Test is for testing functionality which is common to both the L1 and L2
// CrossDomainMessenger contracts. For simplicity, we use the L1 Messenger as the test contract. // CrossDomainMessenger contracts. For simplicity, we use the L1 Messenger as the test contract.
...@@ -39,7 +42,7 @@ contract CrossDomainMessenger_BaseGas_Test is Messenger_Initializer { ...@@ -39,7 +42,7 @@ contract CrossDomainMessenger_BaseGas_Test is Messenger_Initializer {
/// @title ExternalRelay /// @title ExternalRelay
/// @notice A mock external contract called via the SafeCall inside /// @notice A mock external contract called via the SafeCall inside
/// the CrossDomainMessenger's `relayMessage` function. /// the CrossDomainMessenger's `relayMessage` function.
contract ExternalRelay is CommonTest { contract ExternalRelay is Test {
address internal op; address internal op;
address internal fuzzedSender; address internal fuzzedSender;
L1CrossDomainMessenger internal L1Messenger; L1CrossDomainMessenger internal L1Messenger;
......
...@@ -5,7 +5,7 @@ import { Test } from "forge-std/Test.sol"; ...@@ -5,7 +5,7 @@ import { Test } from "forge-std/Test.sol";
import { Drippie } from "src/periphery/drippie/Drippie.sol"; import { Drippie } from "src/periphery/drippie/Drippie.sol";
import { IDripCheck } from "src/periphery/drippie/IDripCheck.sol"; import { IDripCheck } from "src/periphery/drippie/IDripCheck.sol";
import { CheckTrue } from "src/periphery/drippie/dripchecks/CheckTrue.sol"; import { CheckTrue } from "src/periphery/drippie/dripchecks/CheckTrue.sol";
import { SimpleStorage } from "test/Helpers.sol"; import { SimpleStorage } from "test/mocks/SimpleStorage.sol";
/// @title TestDrippie /// @title TestDrippie
/// @notice This is a wrapper contract around Drippie used for testing. /// @notice This is a wrapper contract around Drippie used for testing.
......
...@@ -4,7 +4,7 @@ pragma solidity 0.8.15; ...@@ -4,7 +4,7 @@ pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { Faucet } from "src/periphery/faucet/Faucet.sol"; import { Faucet } from "src/periphery/faucet/Faucet.sol";
import { AdminFaucetAuthModule } from "src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol"; import { AdminFaucetAuthModule } from "src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol";
import { FaucetHelper } from "test/Helpers.sol"; import { FaucetHelper } from "test/mocks/FaucetHelper.sol";
contract Faucet_Initializer is Test { contract Faucet_Initializer is Test {
event Drip(string indexed authModule, bytes32 indexed userId, uint256 amount, address indexed recipient); event Drip(string indexed authModule, bytes32 indexed userId, uint256 amount, address indexed recipient);
......
...@@ -17,6 +17,7 @@ import { Types } from "src/libraries/Types.sol"; ...@@ -17,6 +17,7 @@ import { Types } from "src/libraries/Types.sol";
import { LibClock } from "src/dispute/lib/LibClock.sol"; import { LibClock } from "src/dispute/lib/LibClock.sol";
import { LibPosition } from "src/dispute/lib/LibPosition.sol"; import { LibPosition } from "src/dispute/lib/LibPosition.sol";
import { IBigStepper, IPreimageOracle } from "src/dispute/interfaces/IBigStepper.sol"; import { IBigStepper, IPreimageOracle } from "src/dispute/interfaces/IBigStepper.sol";
import { AlphabetVM } from "test/mocks/AlphabetVM.sol";
contract FaultDisputeGame_Init is DisputeGameFactory_Init { contract FaultDisputeGame_Init is DisputeGameFactory_Init {
/// @dev The type of the game being tested. /// @dev The type of the game being tested.
...@@ -1093,37 +1094,3 @@ contract VariableDivergentPlayer is GamePlayer { ...@@ -1093,37 +1094,3 @@ contract VariableDivergentPlayer is GamePlayer {
trace = _trace; trace = _trace;
} }
} }
////////////////////////////////////////////////////////////////
// MOCK VMS //
////////////////////////////////////////////////////////////////
contract AlphabetVM is IBigStepper {
Claim internal immutable ABSOLUTE_PRESTATE;
IPreimageOracle public oracle;
constructor(Claim _absolutePrestate) {
ABSOLUTE_PRESTATE = _absolutePrestate;
oracle = new PreimageOracle();
}
/// @inheritdoc IBigStepper
function step(bytes calldata _stateData, bytes calldata, uint256) external view returns (bytes32 postState_) {
uint256 traceIndex;
uint256 claim;
if ((keccak256(_stateData) << 8) == (Claim.unwrap(ABSOLUTE_PRESTATE) << 8)) {
// If the state data is empty, then the absolute prestate is the claim.
traceIndex = 0;
(claim) = abi.decode(_stateData, (uint256));
} else {
// Otherwise, decode the state data.
(traceIndex, claim) = abi.decode(_stateData, (uint256, uint256));
traceIndex++;
}
// STF: n -> n + 1
postState_ = keccak256(abi.encode(traceIndex, claim + 1));
assembly {
postState_ := or(and(postState_, not(shl(248, 0xFF))), shl(248, 1))
}
}
}
...@@ -2,8 +2,9 @@ ...@@ -2,8 +2,9 @@
pragma solidity 0.8.15; pragma solidity 0.8.15;
// Testing utilities // Testing utilities
import { Messenger_Initializer, Reverter, ConfigurableCaller } from "test/CommonTest.t.sol"; import { Messenger_Initializer } from "test/CommonTest.t.sol";
import { L2OutputOracle_Initializer } from "test/L2OutputOracle.t.sol"; import { L2OutputOracle_Initializer } from "test/L2OutputOracle.t.sol";
import { Reverter, ConfigurableCaller } from "test/mocks/Callers.sol";
// Libraries // Libraries
import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol";
...@@ -12,12 +13,8 @@ import { Hashing } from "src/libraries/Hashing.sol"; ...@@ -12,12 +13,8 @@ import { Hashing } from "src/libraries/Hashing.sol";
import { Encoding } from "src/libraries/Encoding.sol"; import { Encoding } from "src/libraries/Encoding.sol";
// Target contract dependencies // Target contract dependencies
import { L2OutputOracle } from "src/L1/L2OutputOracle.sol";
import { OptimismPortal } from "src/L1/OptimismPortal.sol"; import { OptimismPortal } from "src/L1/OptimismPortal.sol";
// Target contract
import { L1CrossDomainMessenger } from "src/L1/L1CrossDomainMessenger.sol";
contract L1CrossDomainMessenger_Test is Messenger_Initializer { contract L1CrossDomainMessenger_Test is Messenger_Initializer {
/// @dev The receiver address /// @dev The receiver address
address recipient = address(0xabbaacdc); address recipient = address(0xabbaacdc);
......
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
pragma solidity 0.8.15; pragma solidity 0.8.15;
// Testing utilities // Testing utilities
import { Messenger_Initializer, Reverter, ConfigurableCaller } from "test/CommonTest.t.sol"; import { Messenger_Initializer } from "test/CommonTest.t.sol";
import { Reverter, ConfigurableCaller } from "test/mocks/Callers.sol";
// Libraries // Libraries
import { Hashing } from "src/libraries/Hashing.sol"; import { Hashing } from "src/libraries/Hashing.sol";
...@@ -12,10 +13,6 @@ import { Types } from "src/libraries/Types.sol"; ...@@ -12,10 +13,6 @@ import { Types } from "src/libraries/Types.sol";
// Target contract dependencies // Target contract dependencies
import { L2ToL1MessagePasser } from "src/L2/L2ToL1MessagePasser.sol"; import { L2ToL1MessagePasser } from "src/L2/L2ToL1MessagePasser.sol";
import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol";
import { L1CrossDomainMessenger } from "src/L1/L1CrossDomainMessenger.sol";
// Target contract
import { L2CrossDomainMessenger } from "src/L2/L2CrossDomainMessenger.sol";
contract L2CrossDomainMessenger_Test is Messenger_Initializer { contract L2CrossDomainMessenger_Test is Messenger_Initializer {
/// @dev Receiver address for testing /// @dev Receiver address for testing
......
...@@ -3,7 +3,9 @@ pragma solidity 0.8.15; ...@@ -3,7 +3,9 @@ pragma solidity 0.8.15;
// Testing utilities // Testing utilities
import { stdError } from "forge-std/Test.sol"; import { stdError } from "forge-std/Test.sol";
import { L2OutputOracle_Initializer, NextImpl, EIP1967Helper } from "test/CommonTest.t.sol"; import { L2OutputOracle_Initializer } from "test/CommonTest.t.sol";
import { NextImpl } from "test/mocks/NextImpl.sol";
import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol";
// Libraries // Libraries
import { Types } from "src/libraries/Types.sol"; import { Types } from "src/libraries/Types.sol";
......
...@@ -72,37 +72,56 @@ contract L2ToL1MessagePasserTest is CommonTest { ...@@ -72,37 +72,56 @@ contract L2ToL1MessagePasserTest is CommonTest {
/// @dev Tests that `initiateWithdrawal` succeeds and emits the correct MessagePassed /// @dev Tests that `initiateWithdrawal` succeeds and emits the correct MessagePassed
/// log when called by a contract. /// log when called by a contract.
function test_initiateWithdrawal_fromContract_succeeds() external { function testFuzz_initiateWithdrawal_fromContract_succeeds(
address _target,
uint256 _gasLimit,
uint256 _value,
bytes memory _data
)
external
{
bytes32 withdrawalHash = Hashing.hashWithdrawal( bytes32 withdrawalHash = Hashing.hashWithdrawal(
Types.WithdrawalTransaction(messagePasser.messageNonce(), address(this), address(4), 100, 64000, hex"") Types.WithdrawalTransaction({
nonce: messagePasser.messageNonce(),
sender: address(this),
target: _target,
value: _value,
gasLimit: _gasLimit,
data: _data
})
); );
vm.expectEmit(true, true, true, true); vm.expectEmit(address(messagePasser));
emit MessagePassed(messagePasser.messageNonce(), address(this), address(4), 100, 64000, hex"", withdrawalHash); emit MessagePassed(
messagePasser.messageNonce(), address(this), _target, _value, _gasLimit, _data, withdrawalHash
);
vm.deal(address(this), 2 ** 64); vm.deal(address(this), _value);
messagePasser.initiateWithdrawal{ value: 100 }(address(4), 64000, hex""); messagePasser.initiateWithdrawal{ value: _value }(_target, _gasLimit, _data);
} }
/// @dev Tests that `initiateWithdrawal` succeeds and emits the correct MessagePassed /// @dev Tests that `initiateWithdrawal` succeeds and emits the correct MessagePassed
/// log when called by an EOA. /// log when called by an EOA.
function test_initiateWithdrawal_fromEOA_succeeds() external { function testFuzz_initiateWithdrawal_fromEOA_succeeds(
uint256 gasLimit = 64000; uint256 _gasLimit,
address target = address(4); address _target,
uint256 value = 100; uint256 _value,
bytes memory data = hex"ff"; bytes memory _data
)
external
{
uint256 nonce = messagePasser.messageNonce(); uint256 nonce = messagePasser.messageNonce();
// EOA emulation // EOA emulation
vm.prank(alice, alice); vm.prank(alice, alice);
vm.deal(alice, 2 ** 64); vm.deal(alice, _value);
bytes32 withdrawalHash = bytes32 withdrawalHash =
Hashing.hashWithdrawal(Types.WithdrawalTransaction(nonce, alice, target, value, gasLimit, data)); Hashing.hashWithdrawal(Types.WithdrawalTransaction(nonce, alice, _target, _value, _gasLimit, _data));
vm.expectEmit(true, true, true, true); vm.expectEmit(address(messagePasser));
emit MessagePassed(nonce, alice, target, value, gasLimit, data, withdrawalHash); emit MessagePassed(nonce, alice, _target, _value, _gasLimit, _data, withdrawalHash);
messagePasser.initiateWithdrawal{ value: value }(target, gasLimit, data); messagePasser.initiateWithdrawal{ value: _value }({ _target: _target, _gasLimit: _gasLimit, _data: _data });
// the sent messages mapping is filled // the sent messages mapping is filled
assertEq(messagePasser.sentMessages(withdrawalHash), true); assertEq(messagePasser.sentMessages(withdrawalHash), true);
...@@ -111,12 +130,14 @@ contract L2ToL1MessagePasserTest is CommonTest { ...@@ -111,12 +130,14 @@ contract L2ToL1MessagePasserTest is CommonTest {
} }
/// @dev Tests that `burn` succeeds and destroys the ETH held in the contract. /// @dev Tests that `burn` succeeds and destroys the ETH held in the contract.
function test_burn_succeeds() external { function testFuzz_burn_succeeds(uint256 _value, address _target, uint256 _gasLimit, bytes memory _data) external {
messagePasser.initiateWithdrawal{ value: NON_ZERO_VALUE }(NON_ZERO_ADDRESS, NON_ZERO_GASLIMIT, NON_ZERO_DATA); vm.deal(address(this), _value);
messagePasser.initiateWithdrawal{ value: _value }({ _target: _target, _gasLimit: _gasLimit, _data: _data });
assertEq(address(messagePasser).balance, NON_ZERO_VALUE); assertEq(address(messagePasser).balance, _value);
vm.expectEmit(true, false, false, false); vm.expectEmit(true, false, false, false);
emit WithdrawerBalanceBurnt(NON_ZERO_VALUE); emit WithdrawerBalanceBurnt(_value);
messagePasser.burn(); messagePasser.burn();
// The Withdrawer should have no balance // The Withdrawer should have no balance
......
...@@ -47,7 +47,7 @@ contract LivenessGuard_Constructor_Test is LivenessGuard_TestInit { ...@@ -47,7 +47,7 @@ contract LivenessGuard_Constructor_Test is LivenessGuard_TestInit {
function test_constructor_works() external { function test_constructor_works() external {
address[] memory owners = safeInstance.owners; address[] memory owners = safeInstance.owners;
livenessGuard = new WrappedGuard(safeInstance.safe); livenessGuard = new WrappedGuard(safeInstance.safe);
for (uint256 i = 0; i < owners.length; i++) { for (uint256 i; i < owners.length; i++) {
assertEq(livenessGuard.lastLive(owners[i]), initTime); assertEq(livenessGuard.lastLive(owners[i]), initTime);
} }
} }
...@@ -242,7 +242,7 @@ contract LivenessGuard_FuzzOwnerManagement_Test is StdCheats, StdUtils, Liveness ...@@ -242,7 +242,7 @@ contract LivenessGuard_FuzzOwnerManagement_Test is StdCheats, StdUtils, Liveness
(address[] memory ownerAddrs, uint256[] memory ownerkeys) = (address[] memory ownerAddrs, uint256[] memory ownerkeys) =
SafeTestLib.makeAddrsAndKeys("safeTest", initialOwners); SafeTestLib.makeAddrsAndKeys("safeTest", initialOwners);
// record the private keys for later use // record the private keys for later use
for (uint256 i = 0; i < ownerAddrs.length; i++) { for (uint256 i; i < ownerAddrs.length; i++) {
privateKeys[ownerAddrs[i]] = ownerkeys[i]; privateKeys[ownerAddrs[i]] = ownerkeys[i];
} }
...@@ -251,7 +251,7 @@ contract LivenessGuard_FuzzOwnerManagement_Test is StdCheats, StdUtils, Liveness ...@@ -251,7 +251,7 @@ contract LivenessGuard_FuzzOwnerManagement_Test is StdCheats, StdUtils, Liveness
livenessGuard = new WrappedGuard(safeInstance.safe); livenessGuard = new WrappedGuard(safeInstance.safe);
safeInstance.setGuard(address(livenessGuard)); safeInstance.setGuard(address(livenessGuard));
for (uint256 i = 0; i < changes.length; i++) { for (uint256 i; i < changes.length; i++) {
vm.warp(block.timestamp + changes[i].timeDelta); vm.warp(block.timestamp + changes[i].timeDelta);
OwnerChange memory change = changes[i]; OwnerChange memory change = changes[i];
address[] memory currentOwners = safeInstance.safe.getOwners(); address[] memory currentOwners = safeInstance.safe.getOwners();
...@@ -312,16 +312,16 @@ contract LivenessGuard_FuzzOwnerManagement_Test is StdCheats, StdUtils, Liveness ...@@ -312,16 +312,16 @@ contract LivenessGuard_FuzzOwnerManagement_Test is StdCheats, StdUtils, Liveness
// Looks up the private key for each owner // Looks up the private key for each owner
uint256[] memory unsortedOwnerPKs = new uint256[](instance.owners.length); uint256[] memory unsortedOwnerPKs = new uint256[](instance.owners.length);
for (uint256 j = 0; j < instance.owners.length; j++) { for (uint256 i; i < instance.owners.length; i++) {
unsortedOwnerPKs[j] = privateKeys[instance.owners[j]]; unsortedOwnerPKs[i] = privateKeys[instance.owners[i]];
} }
// Sort the keys by address and store them in the SafeInstance // Sort the keys by address and store them in the SafeInstance
instance.ownerPKs = SafeTestLib.sortPKsByComputedAddress(unsortedOwnerPKs); instance.ownerPKs = SafeTestLib.sortPKsByComputedAddress(unsortedOwnerPKs);
// Overwrite the SafeInstances owners array with the computed addresses from the ownerPKs array // Overwrite the SafeInstances owners array with the computed addresses from the ownerPKs array
for (uint256 k; k < instance.owners.length; k++) { for (uint256 i; i < instance.owners.length; i++) {
instance.owners[k] = SafeTestLib.getAddr(instance.ownerPKs[k]); instance.owners[i] = SafeTestLib.getAddr(instance.ownerPKs[i]);
} }
} }
} }
...@@ -199,7 +199,7 @@ contract LivenessModule_RemoveOwners_TestFail is LivenessModule_TestInit { ...@@ -199,7 +199,7 @@ contract LivenessModule_RemoveOwners_TestFail is LivenessModule_TestInit {
uint256 numOwners = safeInstance.owners.length; uint256 numOwners = safeInstance.owners.length;
address[] memory ownersToRemove = new address[](numOwners); address[] memory ownersToRemove = new address[](numOwners);
for (uint256 i = 0; i < numOwners; i++) { for (uint256 i; i < numOwners; i++) {
ownersToRemove[i] = safeInstance.owners[i]; ownersToRemove[i] = safeInstance.owners[i];
} }
address[] memory prevOwners = safeInstance.getPrevOwners(ownersToRemove); address[] memory prevOwners = safeInstance.getPrevOwners(ownersToRemove);
...@@ -218,7 +218,7 @@ contract LivenessModule_RemoveOwners_TestFail is LivenessModule_TestInit { ...@@ -218,7 +218,7 @@ contract LivenessModule_RemoveOwners_TestFail is LivenessModule_TestInit {
uint256 numOwners = safeInstance.owners.length - 2; uint256 numOwners = safeInstance.owners.length - 2;
address[] memory ownersToRemove = new address[](numOwners); address[] memory ownersToRemove = new address[](numOwners);
for (uint256 i = 0; i < numOwners; i++) { for (uint256 i; i < numOwners; i++) {
ownersToRemove[i] = safeInstance.owners[i]; ownersToRemove[i] = safeInstance.owners[i];
} }
address[] memory prevOwners = safeInstance.getPrevOwners(ownersToRemove); address[] memory prevOwners = safeInstance.getPrevOwners(ownersToRemove);
...@@ -236,7 +236,7 @@ contract LivenessModule_RemoveOwners_TestFail is LivenessModule_TestInit { ...@@ -236,7 +236,7 @@ contract LivenessModule_RemoveOwners_TestFail is LivenessModule_TestInit {
uint256 numOwners = safeInstance.owners.length - 1; uint256 numOwners = safeInstance.owners.length - 1;
address[] memory ownersToRemove = new address[](numOwners); address[] memory ownersToRemove = new address[](numOwners);
for (uint256 i = 0; i < numOwners; i++) { for (uint256 i; i < numOwners; i++) {
ownersToRemove[i] = safeInstance.owners[i]; ownersToRemove[i] = safeInstance.owners[i];
} }
address[] memory prevOwners = safeInstance.getPrevOwners(ownersToRemove); address[] memory prevOwners = safeInstance.getPrevOwners(ownersToRemove);
...@@ -297,7 +297,7 @@ contract LivenessModule_RemoveOwners_Test is LivenessModule_TestInit { ...@@ -297,7 +297,7 @@ contract LivenessModule_RemoveOwners_Test is LivenessModule_TestInit {
uint256 numOwners = safeInstance.owners.length; uint256 numOwners = safeInstance.owners.length;
address[] memory ownersToRemove = new address[](numOwners); address[] memory ownersToRemove = new address[](numOwners);
for (uint256 i = 0; i < numOwners; i++) { for (uint256 i; i < numOwners; i++) {
ownersToRemove[i] = safeInstance.owners[i]; ownersToRemove[i] = safeInstance.owners[i];
} }
address[] memory prevOwners = safeInstance.getPrevOwners(ownersToRemove); address[] memory prevOwners = safeInstance.getPrevOwners(ownersToRemove);
...@@ -348,15 +348,12 @@ contract LivenessModule_RemoveOwnersFuzz_Test is LivenessModule_TestInit { ...@@ -348,15 +348,12 @@ contract LivenessModule_RemoveOwnersFuzz_Test is LivenessModule_TestInit {
// //
// _numOwners must be at least 4, so that _minOwners can be set to at least 3 by the following bound() call. // _numOwners must be at least 4, so that _minOwners can be set to at least 3 by the following bound() call.
// Limiting the owner set to 20 helps to keep the runtime of the test reasonable. // Limiting the owner set to 20 helps to keep the runtime of the test reasonable.
console.log("bounding numOwners");
numOwners_ = bound(_numOwners, 4, 20); numOwners_ = bound(_numOwners, 4, 20);
// _minOwners must be at least 3, otherwise we don't have any range below _minOwners in which to test all of the // _minOwners must be at least 3, otherwise we don't have any range below _minOwners in which to test all of the
// ShutDownBehavior options. // ShutDownBehavior options.
console.log("bounding minOwners");
minOwners_ = bound(_minOwners, 3, numOwners_ - 1); minOwners_ = bound(_minOwners, 3, numOwners_ - 1);
// Ensure that _numLiveOwners is less than _numOwners so that we can remove at least one owner. // Ensure that _numLiveOwners is less than _numOwners so that we can remove at least one owner.
console.log("bounding numLiveOwners");
numLiveOwners_ = bound(_numLiveOwners, 0, numOwners_ - 1); numLiveOwners_ = bound(_numLiveOwners, 0, numOwners_ - 1);
// The above bounds are a bit tricky, so we assert that the resulting parameters enable us to test all possible // The above bounds are a bit tricky, so we assert that the resulting parameters enable us to test all possible
...@@ -402,20 +399,19 @@ contract LivenessModule_RemoveOwnersFuzz_Test is LivenessModule_TestInit { ...@@ -402,20 +399,19 @@ contract LivenessModule_RemoveOwnersFuzz_Test is LivenessModule_TestInit {
// Create an array of live owners, and call showLiveness for each of them // Create an array of live owners, and call showLiveness for each of them
address[] memory liveOwners = new address[](numLiveOwners); address[] memory liveOwners = new address[](numLiveOwners);
for (uint256 i = 0; i < numLiveOwners; i++) { for (uint256 i; i < numLiveOwners; i++) {
liveOwners[i] = safeInstance.owners[i]; liveOwners[i] = safeInstance.owners[i];
vm.prank(safeInstance.owners[i]); vm.prank(safeInstance.owners[i]);
livenessGuard.showLiveness(); livenessGuard.showLiveness();
} }
address[] memory nonLiveOwners = new address[](numOwners - numLiveOwners); address[] memory nonLiveOwners = new address[](numOwners - numLiveOwners);
for (uint256 i = 0; i < numOwners - numLiveOwners; i++) { for (uint256 i; i < numOwners - numLiveOwners; i++) {
nonLiveOwners[i] = safeInstance.owners[i + numLiveOwners]; nonLiveOwners[i] = safeInstance.owners[i + numLiveOwners];
} }
address[] memory prevOwners; address[] memory prevOwners;
if (numLiveOwners >= minOwners) { if (numLiveOwners >= minOwners) {
console.log("No shutdown");
// The safe will remain above the minimum number of owners, so we can remove only those owners which are not // The safe will remain above the minimum number of owners, so we can remove only those owners which are not
// live. // live.
prevOwners = safeInstance.getPrevOwners(nonLiveOwners); prevOwners = safeInstance.getPrevOwners(nonLiveOwners);
...@@ -424,10 +420,10 @@ contract LivenessModule_RemoveOwnersFuzz_Test is LivenessModule_TestInit { ...@@ -424,10 +420,10 @@ contract LivenessModule_RemoveOwnersFuzz_Test is LivenessModule_TestInit {
// Validate the resulting state of the Safe // Validate the resulting state of the Safe
assertEq(safeInstance.safe.getOwners().length, numLiveOwners); assertEq(safeInstance.safe.getOwners().length, numLiveOwners);
assertEq(safeInstance.safe.getThreshold(), get75PercentThreshold(numLiveOwners)); assertEq(safeInstance.safe.getThreshold(), get75PercentThreshold(numLiveOwners));
for (uint256 i = 0; i < numLiveOwners; i++) { for (uint256 i; i < numLiveOwners; i++) {
assertTrue(safeInstance.safe.isOwner(liveOwners[i])); assertTrue(safeInstance.safe.isOwner(liveOwners[i]));
} }
for (uint256 i = 0; i < nonLiveOwners.length; i++) { for (uint256 i; i < nonLiveOwners.length; i++) {
assertFalse(safeInstance.safe.isOwner(nonLiveOwners[i])); assertFalse(safeInstance.safe.isOwner(nonLiveOwners[i]));
} }
} else { } else {
...@@ -439,14 +435,10 @@ contract LivenessModule_RemoveOwnersFuzz_Test is LivenessModule_TestInit { ...@@ -439,14 +435,10 @@ contract LivenessModule_RemoveOwnersFuzz_Test is LivenessModule_TestInit {
// The safe is below the minimum number of owners. // The safe is below the minimum number of owners.
// The ShutDownBehavior enum determines how we handle this case. // The ShutDownBehavior enum determines how we handle this case.
if (shutDownBehavior == ShutDownBehavior.Correct) { if (shutDownBehavior == ShutDownBehavior.Correct) {
console.log("Correct Shutdown");
// We remove all owners, and transfer ownership to the shutDown owner. // We remove all owners, and transfer ownership to the shutDown owner.
// but we need to do remove the non-live owners first, so we reverse the owners array, since // but we need to do remove the non-live owners first, so we reverse the owners array, since
// the first owners in the array were the ones to call showLiveness. // the first owners in the array were the ones to call showLiveness.
// ownersToRemove = new address[](numOwners); for (uint256 i; i < numOwners; i++) {
for (uint256 i = 0; i < numOwners; i++) {
// ownersToRemove[numOwners - i - 1] = safeInstance.owners[i];
// ownersToRemove[i] = safeInstance.owners[numOwners - i - 1];
ownersToRemove.push(safeInstance.owners[numOwners - i - 1]); ownersToRemove.push(safeInstance.owners[numOwners - i - 1]);
} }
prevOwners = safeInstance.getPrevOwners(ownersToRemove); prevOwners = safeInstance.getPrevOwners(ownersToRemove);
...@@ -461,14 +453,11 @@ contract LivenessModule_RemoveOwnersFuzz_Test is LivenessModule_TestInit { ...@@ -461,14 +453,11 @@ contract LivenessModule_RemoveOwnersFuzz_Test is LivenessModule_TestInit {
// trigger that behavior. We initialize that value here then set it in the if statements below. // trigger that behavior. We initialize that value here then set it in the if statements below.
uint256 numOwnersToRemoveinShutDown; uint256 numOwnersToRemoveinShutDown;
if (shutDownBehavior == ShutDownBehavior.DoesNotRemoveAllOwners) { if (shutDownBehavior == ShutDownBehavior.DoesNotRemoveAllOwners) {
console.log("Shutdown DoesNotRemoveAllOwners");
// In the DoesNotRemoveAllOwners case, we should have more than 1 of the pre-existing owners // In the DoesNotRemoveAllOwners case, we should have more than 1 of the pre-existing owners
// remaining // remaining
console.log("bounding numOwnersToRemoveinShutDown");
numOwnersToRemoveinShutDown = numOwnersToRemoveinShutDown =
bound(_numOwnersToRemoveinShutDown, numOwners - minOwners + 1, numOwners - 2); bound(_numOwnersToRemoveinShutDown, numOwners - minOwners + 1, numOwners - 2);
uint256 i = 0; for (uint256 i; i < numOwnersToRemoveinShutDown; i++) {
for (i; i < numOwnersToRemoveinShutDown; i++) {
// Add non-live owners to remove first // Add non-live owners to remove first
if (i < nonLiveOwners.length) { if (i < nonLiveOwners.length) {
ownersToRemove.push(nonLiveOwners[i]); ownersToRemove.push(nonLiveOwners[i]);
...@@ -483,11 +472,9 @@ contract LivenessModule_RemoveOwnersFuzz_Test is LivenessModule_TestInit { ...@@ -483,11 +472,9 @@ contract LivenessModule_RemoveOwnersFuzz_Test is LivenessModule_TestInit {
); );
livenessModule.removeOwners(prevOwners, ownersToRemove); livenessModule.removeOwners(prevOwners, ownersToRemove);
} else if (shutDownBehavior == ShutDownBehavior.DoesNotTransferToFallbackOwner) { } else if (shutDownBehavior == ShutDownBehavior.DoesNotTransferToFallbackOwner) {
console.log("Shutdown DoesNotTransferToFallbackOwner");
// In the DoesNotRemoveAllOwners case, we should have exactly 1 pre-existing owners remaining // In the DoesNotRemoveAllOwners case, we should have exactly 1 pre-existing owners remaining
numOwnersToRemoveinShutDown = numOwners - 1; numOwnersToRemoveinShutDown = numOwners - 1;
uint256 i = 0; for (uint256 i; i < numOwnersToRemoveinShutDown; i++) {
for (i; i < numOwnersToRemoveinShutDown; i++) {
// Add non-live owners to remove first // Add non-live owners to remove first
if (i < nonLiveOwners.length) { if (i < nonLiveOwners.length) {
ownersToRemove.push(nonLiveOwners[i]); ownersToRemove.push(nonLiveOwners[i]);
...@@ -503,8 +490,8 @@ contract LivenessModule_RemoveOwnersFuzz_Test is LivenessModule_TestInit { ...@@ -503,8 +490,8 @@ contract LivenessModule_RemoveOwnersFuzz_Test is LivenessModule_TestInit {
// For both of the incorrect behaviors, verify no change to the Safe state // For both of the incorrect behaviors, verify no change to the Safe state
assertEq(safeInstance.safe.getOwners().length, numOwners); assertEq(safeInstance.safe.getOwners().length, numOwners);
assertEq(safeInstance.safe.getThreshold(), get75PercentThreshold(numOwners)); assertEq(safeInstance.safe.getThreshold(), get75PercentThreshold(numOwners));
for (uint256 j = 0; j < numOwners; j++) { for (uint256 i; i < numOwners; i++) {
assertTrue(safeInstance.safe.isOwner(safeInstance.owners[j])); assertTrue(safeInstance.safe.isOwner(safeInstance.owners[i]));
} }
} }
} }
......
...@@ -3,7 +3,8 @@ pragma solidity 0.8.15; ...@@ -3,7 +3,8 @@ pragma solidity 0.8.15;
// Testing utilities // Testing utilities
import { stdError } from "forge-std/Test.sol"; import { stdError } from "forge-std/Test.sol";
import { Portal_Initializer, CommonTest, NextImpl } from "test/CommonTest.t.sol"; import { Portal_Initializer } from "test/CommonTest.t.sol";
import { NextImpl } from "test/mocks/NextImpl.sol";
// Libraries // Libraries
import { Types } from "src/libraries/Types.sol"; import { Types } from "src/libraries/Types.sol";
......
...@@ -7,7 +7,7 @@ import { AttestationStation } from "src/periphery/op-nft/AttestationStation.sol" ...@@ -7,7 +7,7 @@ import { AttestationStation } from "src/periphery/op-nft/AttestationStation.sol"
import { Optimist } from "src/periphery/op-nft/Optimist.sol"; import { Optimist } from "src/periphery/op-nft/Optimist.sol";
import { OptimistAllowlist } from "src/periphery/op-nft/OptimistAllowlist.sol"; import { OptimistAllowlist } from "src/periphery/op-nft/OptimistAllowlist.sol";
import { OptimistInviter } from "src/periphery/op-nft/OptimistInviter.sol"; import { OptimistInviter } from "src/periphery/op-nft/OptimistInviter.sol";
import { OptimistInviterHelper } from "test/Helpers.sol"; import { OptimistInviterHelper } from "test/mocks/OptimistInviterHelper.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
......
...@@ -6,7 +6,7 @@ import { Test } from "forge-std/Test.sol"; ...@@ -6,7 +6,7 @@ import { Test } from "forge-std/Test.sol";
import { AttestationStation } from "src/periphery/op-nft/AttestationStation.sol"; import { AttestationStation } from "src/periphery/op-nft/AttestationStation.sol";
import { OptimistAllowlist } from "src/periphery/op-nft/OptimistAllowlist.sol"; import { OptimistAllowlist } from "src/periphery/op-nft/OptimistAllowlist.sol";
import { OptimistInviter } from "src/periphery/op-nft/OptimistInviter.sol"; import { OptimistInviter } from "src/periphery/op-nft/OptimistInviter.sol";
import { OptimistInviterHelper } from "test/Helpers.sol"; import { OptimistInviterHelper } from "test/mocks/OptimistInviterHelper.sol";
import { OptimistConstants } from "src/periphery/op-nft/libraries/OptimistConstants.sol"; import { OptimistConstants } from "src/periphery/op-nft/libraries/OptimistConstants.sol";
contract OptimistAllowlist_Initializer is Test { contract OptimistAllowlist_Initializer is Test {
......
...@@ -7,8 +7,8 @@ import { AttestationStation } from "src/periphery/op-nft/AttestationStation.sol" ...@@ -7,8 +7,8 @@ import { AttestationStation } from "src/periphery/op-nft/AttestationStation.sol"
import { OptimistInviter } from "src/periphery/op-nft/OptimistInviter.sol"; import { OptimistInviter } from "src/periphery/op-nft/OptimistInviter.sol";
import { Optimist } from "src/periphery/op-nft/Optimist.sol"; import { Optimist } from "src/periphery/op-nft/Optimist.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { TestERC1271Wallet } from "test/Helpers.sol"; import { TestERC1271Wallet } from "test/mocks/TestERC1271Wallet.sol";
import { OptimistInviterHelper } from "test/Helpers.sol"; import { OptimistInviterHelper } from "test/mocks/OptimistInviterHelper.sol";
import { OptimistConstants } from "src/periphery/op-nft/libraries/OptimistConstants.sol"; import { OptimistConstants } from "src/periphery/op-nft/libraries/OptimistConstants.sol";
contract OptimistInviter_Initializer is Test { contract OptimistInviter_Initializer is Test {
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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