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

Merge branch 'develop' into refcell/rlp-style-fixes

parents 6cf9d616 a7bd0b11
...@@ -25,4 +25,4 @@ ...@@ -25,4 +25,4 @@
"eslint.format.enable": true, "eslint.format.enable": true,
"editorconfig.generateAuto": false, "editorconfig.generateAuto": false,
"files.trimTrailingWhitespace": true "files.trimTrailingWhitespace": true
} }
\ No newline at end of file
...@@ -3,7 +3,6 @@ package op_challenger ...@@ -3,7 +3,6 @@ package op_challenger
import ( import (
"context" "context"
"fmt" "fmt"
"time"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
...@@ -31,37 +30,20 @@ func Main(ctx context.Context, logger log.Logger, cfg *config.Config) error { ...@@ -31,37 +30,20 @@ func Main(ctx context.Context, logger log.Logger, cfg *config.Config) error {
return fmt.Errorf("failed to bind the fault dispute game contract: %w", err) return fmt.Errorf("failed to bind the fault dispute game contract: %w", err)
} }
loader := fault.NewLoader(logger, contract) gameLogger := logger.New("game", cfg.GameAddress)
responder, err := fault.NewFaultResponder(logger, txMgr, cfg.GameAddress) loader := fault.NewLoader(contract)
responder, err := fault.NewFaultResponder(gameLogger, txMgr, cfg.GameAddress)
if err != nil { if err != nil {
return fmt.Errorf("failed to create the responder: %w", err) return fmt.Errorf("failed to create the responder: %w", err)
} }
trace := fault.NewAlphabetProvider(cfg.AlphabetTrace, uint64(cfg.GameDepth)) trace := fault.NewAlphabetProvider(cfg.AlphabetTrace, uint64(cfg.GameDepth))
agent := fault.NewAgent(loader, cfg.GameDepth, trace, responder, cfg.AgreeWithProposedOutput, logger) agent := fault.NewAgent(loader, cfg.GameDepth, trace, responder, cfg.AgreeWithProposedOutput, gameLogger)
caller, err := fault.NewFaultCallerFromBindings(cfg.GameAddress, client, logger) caller, err := fault.NewFaultCallerFromBindings(cfg.GameAddress, client, gameLogger)
if err != nil { if err != nil {
return fmt.Errorf("failed to bind the fault contract: %w", err) return fmt.Errorf("failed to bind the fault contract: %w", err)
} }
logger.Info("Fault game started") return fault.MonitorGame(ctx, gameLogger, cfg.AgreeWithProposedOutput, agent, caller)
for {
logger.Info("Performing action")
_ = agent.Act(ctx)
status, _ := caller.GetGameStatus(ctx)
if status != 0 {
caller.LogGameStatus(ctx)
return nil
} else {
caller.LogGameInfo(ctx)
}
select {
case <-time.After(300 * time.Millisecond):
// Continue
case <-ctx.Done():
return ctx.Err()
}
}
} }
...@@ -17,8 +17,8 @@ type Agent struct { ...@@ -17,8 +17,8 @@ type Agent struct {
log log.Logger log log.Logger
} }
func NewAgent(loader Loader, maxDepth int, trace TraceProvider, responder Responder, agreeWithProposedOutput bool, log log.Logger) Agent { func NewAgent(loader Loader, maxDepth int, trace TraceProvider, responder Responder, agreeWithProposedOutput bool, log log.Logger) *Agent {
return Agent{ return &Agent{
solver: NewSolver(maxDepth, trace), solver: NewSolver(maxDepth, trace),
loader: loader, loader: loader,
responder: responder, responder: responder,
...@@ -35,12 +35,11 @@ func (a *Agent) Act(ctx context.Context) error { ...@@ -35,12 +35,11 @@ func (a *Agent) Act(ctx context.Context) error {
} }
game, err := a.newGameFromContracts(ctx) game, err := a.newGameFromContracts(ctx)
if err != nil { if err != nil {
a.log.Error("Failed to create new game", "err", err) return fmt.Errorf("create game from contracts: %w", err)
return err
} }
// Create counter claims // Create counter claims
for _, claim := range game.Claims() { for _, claim := range game.Claims() {
if err := a.move(ctx, claim, game); err != nil { if err := a.move(ctx, claim, game); err != nil && !errors.Is(err, ErrGameDepthReached) {
log.Error("Failed to move", "err", err) log.Error("Failed to move", "err", err)
} }
} }
...@@ -57,11 +56,13 @@ func (a *Agent) Act(ctx context.Context) error { ...@@ -57,11 +56,13 @@ func (a *Agent) Act(ctx context.Context) error {
// and returns true if the game resolves successfully. // and returns true if the game resolves successfully.
func (a *Agent) tryResolve(ctx context.Context) bool { func (a *Agent) tryResolve(ctx context.Context) bool {
if a.responder.CanResolve(ctx) { if a.responder.CanResolve(ctx) {
a.log.Info("Resolving game")
err := a.responder.Resolve(ctx) err := a.responder.Resolve(ctx)
if err != nil { if err != nil {
return true a.log.Error("Failed to resolve the game", "err", err)
return false
} }
a.log.Error("failed to resolve the game", "err", err) return true
} }
return false return false
} }
...@@ -86,8 +87,7 @@ func (a *Agent) newGameFromContracts(ctx context.Context) (Game, error) { ...@@ -86,8 +87,7 @@ func (a *Agent) newGameFromContracts(ctx context.Context) (Game, error) {
func (a *Agent) move(ctx context.Context, claim Claim, game Game) error { func (a *Agent) move(ctx context.Context, claim Claim, game Game) error {
nextMove, err := a.solver.NextMove(claim, game.AgreeWithClaimLevel(claim)) nextMove, err := a.solver.NextMove(claim, game.AgreeWithClaimLevel(claim))
if err != nil { if err != nil {
a.log.Warn("Failed to execute the next move", "err", err) return fmt.Errorf("execute next move: %w", err)
return err
} }
if nextMove == nil { if nextMove == nil {
a.log.Debug("No next move") a.log.Debug("No next move")
...@@ -98,7 +98,7 @@ func (a *Agent) move(ctx context.Context, claim Claim, game Game) error { ...@@ -98,7 +98,7 @@ func (a *Agent) move(ctx context.Context, claim Claim, game Game) error {
"value", move.Value, "trace_index", move.TraceIndex(a.maxDepth), "value", move.Value, "trace_index", move.TraceIndex(a.maxDepth),
"parent_value", claim.Value, "parent_trace_index", claim.TraceIndex(a.maxDepth)) "parent_value", claim.Value, "parent_trace_index", claim.TraceIndex(a.maxDepth))
if game.IsDuplicate(move) { if game.IsDuplicate(move) {
log.Debug("Duplicate move") log.Debug("Skipping duplicate move")
return nil return nil
} }
log.Info("Performing move") log.Info("Performing move")
...@@ -113,20 +113,19 @@ func (a *Agent) step(ctx context.Context, claim Claim, game Game) error { ...@@ -113,20 +113,19 @@ func (a *Agent) step(ctx context.Context, claim Claim, game Game) error {
agreeWithClaimLevel := game.AgreeWithClaimLevel(claim) agreeWithClaimLevel := game.AgreeWithClaimLevel(claim)
if agreeWithClaimLevel { if agreeWithClaimLevel {
a.log.Warn("Agree with leaf claim, skipping step", "claim_depth", claim.Depth(), "maxDepth", a.maxDepth) a.log.Debug("Agree with leaf claim, skipping step", "claim_depth", claim.Depth(), "maxDepth", a.maxDepth)
return nil return nil
} }
if claim.Countered { if claim.Countered {
a.log.Info("Claim already stepped on", "claim_depth", claim.Depth(), "maxDepth", a.maxDepth) a.log.Debug("Step already executed against claim", "depth", claim.Depth(), "index_at_depth", claim.IndexAtDepth(), "value", claim.Value)
return nil return nil
} }
a.log.Info("Attempting step", "claim_depth", claim.Depth(), "maxDepth", a.maxDepth) a.log.Info("Attempting step", "claim_depth", claim.Depth(), "maxDepth", a.maxDepth)
step, err := a.solver.AttemptStep(claim, agreeWithClaimLevel) step, err := a.solver.AttemptStep(claim, agreeWithClaimLevel)
if err != nil { if err != nil {
a.log.Warn("Failed to get a step", "err", err) return fmt.Errorf("attempt step: %w", err)
return err
} }
a.log.Info("Performing step", "is_attack", step.IsAttack, a.log.Info("Performing step", "is_attack", step.IsAttack,
......
...@@ -19,15 +19,13 @@ type FaultDisputeGameCaller interface { ...@@ -19,15 +19,13 @@ type FaultDisputeGameCaller interface {
type FaultCaller struct { type FaultCaller struct {
FaultDisputeGameCaller FaultDisputeGameCaller
log log.Logger log log.Logger
fdgAddr common.Address
} }
func NewFaultCaller(fdgAddr common.Address, caller FaultDisputeGameCaller, log log.Logger) *FaultCaller { func NewFaultCaller(caller FaultDisputeGameCaller, log log.Logger) *FaultCaller {
return &FaultCaller{ return &FaultCaller{
caller, caller,
log, log,
fdgAddr,
} }
} }
...@@ -39,7 +37,6 @@ func NewFaultCallerFromBindings(fdgAddr common.Address, client *ethclient.Client ...@@ -39,7 +37,6 @@ func NewFaultCallerFromBindings(fdgAddr common.Address, client *ethclient.Client
return &FaultCaller{ return &FaultCaller{
caller, caller,
log, log,
fdgAddr,
}, nil }, nil
} }
...@@ -55,24 +52,16 @@ func (fc *FaultCaller) LogGameInfo(ctx context.Context) { ...@@ -55,24 +52,16 @@ func (fc *FaultCaller) LogGameInfo(ctx context.Context) {
fc.log.Error("failed to get claim count", "err", err) fc.log.Error("failed to get claim count", "err", err)
return return
} }
fc.log.Info("Game info", "addr", fc.fdgAddr, "claims", claimLen, "status", GameStatusString(status)) fc.log.Info("Game info", "claims", claimLen, "status", GameStatusString(status))
} }
// GetGameStatus returns the current game status. // GetGameStatus returns the current game status.
// 0: In Progress // 0: In Progress
// 1: Challenger Won // 1: Challenger Won
// 2: Defender Won // 2: Defender Won
func (fc *FaultCaller) GetGameStatus(ctx context.Context) (uint8, error) { func (fc *FaultCaller) GetGameStatus(ctx context.Context) (GameStatus, error) {
return fc.Status(&bind.CallOpts{Context: ctx}) status, err := fc.Status(&bind.CallOpts{Context: ctx})
} return GameStatus(status), err
func (fc *FaultCaller) LogGameStatus(ctx context.Context) {
status, err := fc.GetGameStatus(ctx)
if err != nil {
fc.log.Error("failed to get game status", "err", err)
return
}
fc.log.Info("Game status", "status", GameStatusString(status))
} }
// GetClaimDataLength returns the number of claims in the game. // GetClaimDataLength returns the number of claims in the game.
...@@ -90,13 +79,13 @@ func (fc *FaultCaller) LogClaimDataLength(ctx context.Context) { ...@@ -90,13 +79,13 @@ func (fc *FaultCaller) LogClaimDataLength(ctx context.Context) {
} }
// GameStatusString returns the current game status as a string. // GameStatusString returns the current game status as a string.
func GameStatusString(status uint8) string { func GameStatusString(status GameStatus) string {
switch status { switch status {
case 0: case GameStatusInProgress:
return "In Progress" return "In Progress"
case 1: case GameStatusChallengerWon:
return "Challenger Won" return "Challenger Won"
case 2: case GameStatusDefenderWon:
return "Defender Won" return "Defender Won"
default: default:
return "Unknown" return "Unknown"
......
...@@ -7,13 +7,11 @@ import ( ...@@ -7,13 +7,11 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
var ( var (
testAddr = common.HexToAddress("0x1234567890123456789012345678901234567890") errMock = errors.New("mock error")
errMock = errors.New("mock error")
) )
type mockFaultDisputeGameCaller struct { type mockFaultDisputeGameCaller struct {
...@@ -42,7 +40,7 @@ func TestFaultCaller_GetGameStatus(t *testing.T) { ...@@ -42,7 +40,7 @@ func TestFaultCaller_GetGameStatus(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
caller FaultDisputeGameCaller caller FaultDisputeGameCaller
expectedStatus uint8 expectedStatus GameStatus
expectedErr error expectedErr error
}{ }{
{ {
...@@ -50,7 +48,7 @@ func TestFaultCaller_GetGameStatus(t *testing.T) { ...@@ -50,7 +48,7 @@ func TestFaultCaller_GetGameStatus(t *testing.T) {
caller: &mockFaultDisputeGameCaller{ caller: &mockFaultDisputeGameCaller{
status: 1, status: 1,
}, },
expectedStatus: 1, expectedStatus: GameStatusChallengerWon,
expectedErr: nil, expectedErr: nil,
}, },
{ {
...@@ -65,7 +63,7 @@ func TestFaultCaller_GetGameStatus(t *testing.T) { ...@@ -65,7 +63,7 @@ func TestFaultCaller_GetGameStatus(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
fc := NewFaultCaller(testAddr, test.caller, nil) fc := NewFaultCaller(test.caller, nil)
status, err := fc.GetGameStatus(context.Background()) status, err := fc.GetGameStatus(context.Background())
require.Equal(t, test.expectedStatus, status) require.Equal(t, test.expectedStatus, status)
require.Equal(t, test.expectedErr, err) require.Equal(t, test.expectedErr, err)
...@@ -100,7 +98,7 @@ func TestFaultCaller_GetClaimDataLength(t *testing.T) { ...@@ -100,7 +98,7 @@ func TestFaultCaller_GetClaimDataLength(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
fc := NewFaultCaller(testAddr, test.caller, nil) fc := NewFaultCaller(test.caller, nil)
claimDataLen, err := fc.GetClaimDataLength(context.Background()) claimDataLen, err := fc.GetClaimDataLength(context.Background())
require.Equal(t, test.expectedClaimDataLen, claimDataLen) require.Equal(t, test.expectedClaimDataLen, claimDataLen)
require.Equal(t, test.expectedErr, err) require.Equal(t, test.expectedErr, err)
......
...@@ -5,7 +5,6 @@ import ( ...@@ -5,7 +5,6 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/log"
) )
// ClaimFetcher is a minimal interface around [bindings.FaultDisputeGameCaller]. // ClaimFetcher is a minimal interface around [bindings.FaultDisputeGameCaller].
...@@ -28,14 +27,12 @@ type Loader interface { ...@@ -28,14 +27,12 @@ type Loader interface {
// loader pulls in fault dispute game claim data periodically and over subscriptions. // loader pulls in fault dispute game claim data periodically and over subscriptions.
type loader struct { type loader struct {
log log.Logger
claimFetcher ClaimFetcher claimFetcher ClaimFetcher
} }
// NewLoader creates a new [loader]. // NewLoader creates a new [loader].
func NewLoader(log log.Logger, claimFetcher ClaimFetcher) *loader { func NewLoader(claimFetcher ClaimFetcher) *loader {
return &loader{ return &loader{
log: log,
claimFetcher: claimFetcher, claimFetcher: claimFetcher,
} }
} }
......
...@@ -6,9 +6,7 @@ import ( ...@@ -6,9 +6,7 @@ import (
"math/big" "math/big"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -91,10 +89,9 @@ func (m *mockClaimFetcher) ClaimDataLen(opts *bind.CallOpts) (*big.Int, error) { ...@@ -91,10 +89,9 @@ func (m *mockClaimFetcher) ClaimDataLen(opts *bind.CallOpts) (*big.Int, error) {
// TestLoader_FetchClaims_Succeeds tests [loader.FetchClaims]. // TestLoader_FetchClaims_Succeeds tests [loader.FetchClaims].
func TestLoader_FetchClaims_Succeeds(t *testing.T) { func TestLoader_FetchClaims_Succeeds(t *testing.T) {
log := testlog.Logger(t, log.LvlError)
mockClaimFetcher := newMockClaimFetcher() mockClaimFetcher := newMockClaimFetcher()
expectedClaims := mockClaimFetcher.returnClaims expectedClaims := mockClaimFetcher.returnClaims
loader := NewLoader(log, mockClaimFetcher) loader := NewLoader(mockClaimFetcher)
claims, err := loader.FetchClaims(context.Background()) claims, err := loader.FetchClaims(context.Background())
require.NoError(t, err) require.NoError(t, err)
require.ElementsMatch(t, []Claim{ require.ElementsMatch(t, []Claim{
...@@ -143,10 +140,9 @@ func TestLoader_FetchClaims_Succeeds(t *testing.T) { ...@@ -143,10 +140,9 @@ func TestLoader_FetchClaims_Succeeds(t *testing.T) {
// TestLoader_FetchClaims_ClaimDataErrors tests [loader.FetchClaims] // TestLoader_FetchClaims_ClaimDataErrors tests [loader.FetchClaims]
// when the claim fetcher [ClaimData] function call errors. // when the claim fetcher [ClaimData] function call errors.
func TestLoader_FetchClaims_ClaimDataErrors(t *testing.T) { func TestLoader_FetchClaims_ClaimDataErrors(t *testing.T) {
log := testlog.Logger(t, log.LvlError)
mockClaimFetcher := newMockClaimFetcher() mockClaimFetcher := newMockClaimFetcher()
mockClaimFetcher.claimDataError = true mockClaimFetcher.claimDataError = true
loader := NewLoader(log, mockClaimFetcher) loader := NewLoader(mockClaimFetcher)
claims, err := loader.FetchClaims(context.Background()) claims, err := loader.FetchClaims(context.Background())
require.ErrorIs(t, err, mockClaimDataError) require.ErrorIs(t, err, mockClaimDataError)
require.Empty(t, claims) require.Empty(t, claims)
...@@ -155,10 +151,9 @@ func TestLoader_FetchClaims_ClaimDataErrors(t *testing.T) { ...@@ -155,10 +151,9 @@ func TestLoader_FetchClaims_ClaimDataErrors(t *testing.T) {
// TestLoader_FetchClaims_ClaimLenErrors tests [loader.FetchClaims] // TestLoader_FetchClaims_ClaimLenErrors tests [loader.FetchClaims]
// when the claim fetcher [ClaimDataLen] function call errors. // when the claim fetcher [ClaimDataLen] function call errors.
func TestLoader_FetchClaims_ClaimLenErrors(t *testing.T) { func TestLoader_FetchClaims_ClaimLenErrors(t *testing.T) {
log := testlog.Logger(t, log.LvlError)
mockClaimFetcher := newMockClaimFetcher() mockClaimFetcher := newMockClaimFetcher()
mockClaimFetcher.claimLenError = true mockClaimFetcher.claimLenError = true
loader := NewLoader(log, mockClaimFetcher) loader := NewLoader(mockClaimFetcher)
claims, err := loader.FetchClaims(context.Background()) claims, err := loader.FetchClaims(context.Background())
require.ErrorIs(t, err, mockClaimLenError) require.ErrorIs(t, err, mockClaimLenError)
require.Empty(t, claims) require.Empty(t, claims)
......
package fault
import (
"context"
"time"
"github.com/ethereum/go-ethereum/log"
)
type GameInfo interface {
GetGameStatus(context.Context) (GameStatus, error)
LogGameInfo(ctx context.Context)
}
type Actor interface {
Act(ctx context.Context) error
}
func MonitorGame(ctx context.Context, logger log.Logger, agreeWithProposedOutput bool, actor Actor, caller GameInfo) error {
logger.Info("Monitoring fault dispute game", "agreeWithOutput", agreeWithProposedOutput)
for {
done := progressGame(ctx, logger, agreeWithProposedOutput, actor, caller)
if done {
return nil
}
select {
case <-time.After(300 * time.Millisecond):
// Continue
case <-ctx.Done():
return ctx.Err()
}
}
}
// progressGame checks the current state of the game, and attempts to progress it by performing moves, steps or resolving
// Returns true if the game is complete or false if it needs to be monitored further
func progressGame(ctx context.Context, logger log.Logger, agreeWithProposedOutput bool, actor Actor, caller GameInfo) bool {
logger.Trace("Checking if actions are required")
if err := actor.Act(ctx); err != nil {
logger.Error("Error when acting on game", "err", err)
}
if status, err := caller.GetGameStatus(ctx); err != nil {
logger.Warn("Unable to retrieve game status", "err", err)
} else if status != 0 {
var expectedStatus GameStatus
if agreeWithProposedOutput {
expectedStatus = GameStatusChallengerWon
} else {
expectedStatus = GameStatusDefenderWon
}
if expectedStatus == status {
logger.Info("Game won", "status", GameStatusString(status))
} else {
logger.Error("Game lost", "status", GameStatusString(status))
}
return true
} else {
caller.LogGameInfo(ctx)
}
return false
}
package fault
import (
"context"
"errors"
"testing"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
func TestMonitorExitsWhenContextDone(t *testing.T) {
logger := testlog.Logger(t, log.LvlDebug)
actor := &stubActor{}
gameInfo := &stubGameInfo{}
ctx, cancel := context.WithCancel(context.Background())
cancel()
err := MonitorGame(ctx, logger, true, actor, gameInfo)
require.ErrorIs(t, err, context.Canceled)
}
func TestProgressGameAndLogState(t *testing.T) {
logger, _, actor, gameInfo := setupProgressGameTest(t)
done := progressGame(context.Background(), logger, true, actor, gameInfo)
require.False(t, done, "should not be done")
require.Equal(t, 1, actor.callCount, "should perform next actions")
require.Equal(t, 1, gameInfo.logCount, "should log latest game state")
}
func TestProgressGame_LogErrorFromAct(t *testing.T) {
logger, handler, actor, gameInfo := setupProgressGameTest(t)
actor.err = errors.New("Boom")
done := progressGame(context.Background(), logger, true, actor, gameInfo)
require.False(t, done, "should not be done")
require.Equal(t, 1, actor.callCount, "should perform next actions")
require.Equal(t, 1, gameInfo.logCount, "should log latest game state")
errLog := handler.FindLog(log.LvlError, "Error when acting on game")
require.NotNil(t, errLog, "should log error")
require.Equal(t, actor.err, errLog.GetContextValue("err"))
}
func TestProgressGame_LogErrorWhenGameLost(t *testing.T) {
tests := []struct {
name string
status GameStatus
agreeWithOutput bool
logLevel log.Lvl
logMsg string
statusText string
}{
{
name: "GameLostAsDefender",
status: GameStatusChallengerWon,
agreeWithOutput: false,
logLevel: log.LvlError,
logMsg: "Game lost",
statusText: "Challenger Won",
},
{
name: "GameLostAsChallenger",
status: GameStatusDefenderWon,
agreeWithOutput: true,
logLevel: log.LvlError,
logMsg: "Game lost",
statusText: "Defender Won",
},
{
name: "GameWonAsDefender",
status: GameStatusDefenderWon,
agreeWithOutput: false,
logLevel: log.LvlInfo,
logMsg: "Game won",
statusText: "Defender Won",
},
{
name: "GameWonAsChallenger",
status: GameStatusChallengerWon,
agreeWithOutput: true,
logLevel: log.LvlInfo,
logMsg: "Game won",
statusText: "Challenger Won",
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
logger, handler, actor, gameInfo := setupProgressGameTest(t)
gameInfo.status = test.status
done := progressGame(context.Background(), logger, test.agreeWithOutput, actor, gameInfo)
require.True(t, done, "should be done")
require.Equal(t, 0, gameInfo.logCount, "should not log latest game state")
errLog := handler.FindLog(test.logLevel, test.logMsg)
require.NotNil(t, errLog, "should log game result")
require.Equal(t, test.statusText, errLog.GetContextValue("status"))
})
}
}
func setupProgressGameTest(t *testing.T) (log.Logger, *testlog.CapturingHandler, *stubActor, *stubGameInfo) {
logger := testlog.Logger(t, log.LvlDebug)
handler := &testlog.CapturingHandler{
Delegate: logger.GetHandler(),
}
logger.SetHandler(handler)
actor := &stubActor{}
gameInfo := &stubGameInfo{}
return logger, handler, actor, gameInfo
}
type stubActor struct {
callCount int
err error
}
func (a *stubActor) Act(ctx context.Context) error {
a.callCount++
return a.err
}
type stubGameInfo struct {
status GameStatus
err error
logCount int
}
func (s *stubGameInfo) GetGameStatus(ctx context.Context) (GameStatus, error) {
return s.status, s.err
}
func (s *stubGameInfo) LogGameInfo(ctx context.Context) {
s.logCount++
}
...@@ -123,9 +123,9 @@ func (r *faultResponder) sendTxAndWait(ctx context.Context, txData []byte) error ...@@ -123,9 +123,9 @@ func (r *faultResponder) sendTxAndWait(ctx context.Context, txData []byte) error
return err return err
} }
if receipt.Status == types.ReceiptStatusFailed { if receipt.Status == types.ReceiptStatusFailed {
r.log.Error("responder tx successfully published but reverted", "tx_hash", receipt.TxHash) r.log.Error("Responder tx successfully published but reverted", "tx_hash", receipt.TxHash)
} else { } else {
r.log.Info("responder tx successfully published", "tx_hash", receipt.TxHash) r.log.Debug("Responder tx successfully published", "tx_hash", receipt.TxHash)
} }
return nil return nil
} }
......
...@@ -2,10 +2,15 @@ package fault ...@@ -2,10 +2,15 @@ package fault
import ( import (
"errors" "errors"
"fmt"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
var (
ErrGameDepthReached = errors.New("game depth reached")
)
// Solver uses a [TraceProvider] to determine the moves to make in a dispute game. // Solver uses a [TraceProvider] to determine the moves to make in a dispute game.
type Solver struct { type Solver struct {
TraceProvider TraceProvider
...@@ -54,7 +59,7 @@ func (s *Solver) handleMiddle(claim Claim) (*Claim, error) { ...@@ -54,7 +59,7 @@ func (s *Solver) handleMiddle(claim Claim) (*Claim, error) {
return nil, err return nil, err
} }
if claim.Depth() == s.gameDepth { if claim.Depth() == s.gameDepth {
return nil, errors.New("game depth reached") return nil, ErrGameDepthReached
} }
if claimCorrect { if claimCorrect {
return s.defend(claim) return s.defend(claim)
...@@ -113,7 +118,7 @@ func (s *Solver) attack(claim Claim) (*Claim, error) { ...@@ -113,7 +118,7 @@ func (s *Solver) attack(claim Claim) (*Claim, error) {
position := claim.Attack() position := claim.Attack()
value, err := s.traceAtPosition(position) value, err := s.traceAtPosition(position)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("attack claim: %w", err)
} }
return &Claim{ return &Claim{
ClaimData: ClaimData{Value: value, Position: position}, ClaimData: ClaimData{Value: value, Position: position},
...@@ -127,7 +132,7 @@ func (s *Solver) defend(claim Claim) (*Claim, error) { ...@@ -127,7 +132,7 @@ func (s *Solver) defend(claim Claim) (*Claim, error) {
position := claim.Defend() position := claim.Defend()
value, err := s.traceAtPosition(position) value, err := s.traceAtPosition(position)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("defend claim: %w", err)
} }
return &Claim{ return &Claim{
ClaimData: ClaimData{Value: value, Position: position}, ClaimData: ClaimData{Value: value, Position: position},
......
...@@ -11,6 +11,14 @@ var ( ...@@ -11,6 +11,14 @@ var (
ErrIndexTooLarge = errors.New("index is larger than the maximum index") ErrIndexTooLarge = errors.New("index is larger than the maximum index")
) )
type GameStatus uint8
const (
GameStatusInProgress GameStatus = iota
GameStatusChallengerWon
GameStatusDefenderWon
)
// StepCallData encapsulates the data needed to perform a step. // StepCallData encapsulates the data needed to perform a step.
type StepCallData struct { type StepCallData struct {
ClaimIndex uint64 ClaimIndex uint64
......
package testlog
import (
"github.com/ethereum/go-ethereum/log"
)
// CapturingHandler provides a log handler that captures all log records and optionally forwards them to a delegate.
// Note that it is not thread safe.
type CapturingHandler struct {
Delegate log.Handler
Logs []*log.Record
}
func (c *CapturingHandler) Log(r *log.Record) error {
c.Logs = append(c.Logs, r)
if c.Delegate != nil {
return c.Delegate.Log(r)
}
return nil
}
func (c *CapturingHandler) Clear() {
c.Logs = nil
}
func (c *CapturingHandler) FindLog(lvl log.Lvl, msg string) *HelperRecord {
for _, record := range c.Logs {
if record.Lvl == lvl && record.Msg == msg {
return &HelperRecord{record}
}
}
return nil
}
type HelperRecord struct {
*log.Record
}
func (h *HelperRecord) GetContextValue(name string) any {
for i := 0; i < len(h.Ctx); i += 2 {
if h.Ctx[i] == name {
return h.Ctx[i+1]
}
}
return nil
}
var _ log.Handler = (*CapturingHandler)(nil)
...@@ -209,7 +209,7 @@ func (m *SimpleTxManager) craftTx(ctx context.Context, candidate TxCandidate) (* ...@@ -209,7 +209,7 @@ func (m *SimpleTxManager) craftTx(ctx context.Context, candidate TxCandidate) (*
Data: candidate.TxData, Data: candidate.TxData,
} }
m.l.Info("creating tx", "to", rawTx.To, "from", m.cfg.From) m.l.Info("Creating tx", "to", rawTx.To, "from", m.cfg.From)
// If the gas limit is set, we can use that as the gas // If the gas limit is set, we can use that as the gas
if candidate.GasLimit != 0 { if candidate.GasLimit != 0 {
...@@ -333,7 +333,7 @@ func (m *SimpleTxManager) sendTx(ctx context.Context, tx *types.Transaction) (*t ...@@ -333,7 +333,7 @@ func (m *SimpleTxManager) sendTx(ctx context.Context, tx *types.Transaction) (*t
// for the transaction. // for the transaction.
func (m *SimpleTxManager) publishAndWaitForTx(ctx context.Context, tx *types.Transaction, sendState *SendState, receiptChan chan *types.Receipt) { func (m *SimpleTxManager) publishAndWaitForTx(ctx context.Context, tx *types.Transaction, sendState *SendState, receiptChan chan *types.Receipt) {
log := m.l.New("hash", tx.Hash(), "nonce", tx.Nonce(), "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap()) log := m.l.New("hash", tx.Hash(), "nonce", tx.Nonce(), "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap())
log.Info("publishing transaction") log.Info("Publishing transaction")
cCtx, cancel := context.WithTimeout(ctx, m.cfg.NetworkTimeout) cCtx, cancel := context.WithTimeout(ctx, m.cfg.NetworkTimeout)
defer cancel() defer cancel()
......
...@@ -103,6 +103,7 @@ export class WithdrawalMonitor extends BaseServiceV2<Options, Metrics, State> { ...@@ -103,6 +103,7 @@ export class WithdrawalMonitor extends BaseServiceV2<Options, Metrics, State> {
l2SignerOrProvider: this.options.l2RpcProvider, l2SignerOrProvider: this.options.l2RpcProvider,
l1ChainId: await getChainId(this.options.l1RpcProvider), l1ChainId: await getChainId(this.options.l1RpcProvider),
l2ChainId: await getChainId(this.options.l2RpcProvider), l2ChainId: await getChainId(this.options.l2RpcProvider),
bedrock: true,
}) })
// Not detected by default. // Not detected by default.
......
AdminFaucetAuthModuleTest:test_adminProof_verify_succeeds() (gas: 57577) AdminFaucetAuthModuleTest:test_adminProof_verify_succeeds() (gas: 57573)
AdminFaucetAuthModuleTest:test_nonAdminProof_verify_succeeds() (gas: 59050) AdminFaucetAuthModuleTest:test_nonAdminProof_verify_succeeds() (gas: 59050)
AdminFaucetAuthModuleTest:test_proofWithWrongId_verify_succeeds() (gas: 60673) AdminFaucetAuthModuleTest:test_proofWithWrongId_verify_succeeds() (gas: 60673)
AssetReceiverTest:test_constructor_succeeds() (gas: 9696) AssetReceiverTest:test_constructor_succeeds() (gas: 9696)
...@@ -70,14 +70,14 @@ Drippie_Test:test_status_unauthorized_reverts() (gas: 167344) ...@@ -70,14 +70,14 @@ Drippie_Test:test_status_unauthorized_reverts() (gas: 167344)
Drippie_Test:test_trigger_oneFunction_succeeds() (gas: 338143) Drippie_Test:test_trigger_oneFunction_succeeds() (gas: 338143)
Drippie_Test:test_trigger_twoFunctions_succeeds() (gas: 491870) Drippie_Test:test_trigger_twoFunctions_succeeds() (gas: 491870)
Drippie_Test:test_twice_inOneInterval_reverts() (gas: 303767) Drippie_Test:test_twice_inOneInterval_reverts() (gas: 303767)
FaucetTest:test_authAdmin_drip_succeeds() (gas: 366111) FaucetTest:test_authAdmin_drip_succeeds() (gas: 366107)
FaucetTest:test_drip_afterTimeout_succeeds() (gas: 447899) FaucetTest:test_drip_afterTimeout_succeeds() (gas: 447891)
FaucetTest:test_drip_beforeTimeout_reverts() (gas: 378888) FaucetTest:test_drip_beforeTimeout_reverts() (gas: 378884)
FaucetTest:test_drip_disabledModule_reverts() (gas: 352405) FaucetTest:test_drip_disabledModule_reverts() (gas: 352401)
FaucetTest:test_drip_emitsEvent_succeeds() (gas: 369165) FaucetTest:test_drip_emitsEvent_succeeds() (gas: 369161)
FaucetTest:test_drip_githubSendsCorrectAmount_succeeds() (gas: 366611) FaucetTest:test_drip_githubSendsCorrectAmount_succeeds() (gas: 366607)
FaucetTest:test_drip_optimistNftSendsCorrectAmount_succeeds() (gas: 366555) FaucetTest:test_drip_optimistNftSendsCorrectAmount_succeeds() (gas: 366551)
FaucetTest:test_drip_preventsReplayAttacks_succeeds() (gas: 369218) FaucetTest:test_drip_preventsReplayAttacks_succeeds() (gas: 369214)
FaucetTest:test_initialize_succeeds() (gas: 7626) FaucetTest:test_initialize_succeeds() (gas: 7626)
FaucetTest:test_nonAdmin_drip_fails() (gas: 262520) FaucetTest:test_nonAdmin_drip_fails() (gas: 262520)
FaucetTest:test_receive_succeeds() (gas: 17401) FaucetTest:test_receive_succeeds() (gas: 17401)
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
// A representation of an empty/uninitialized UID. // A representation of an empty/uninitialized UID.
...@@ -14,18 +13,14 @@ error InvalidLength(); ...@@ -14,18 +13,14 @@ error InvalidLength();
error InvalidSignature(); error InvalidSignature();
error NotFound(); error NotFound();
/** /// @dev A struct representing EIP712 signature data.
* @dev A struct representing EIP712 signature data.
*/
struct EIP712Signature { struct EIP712Signature {
uint8 v; // The recovery ID. uint8 v; // The recovery ID.
bytes32 r; // The x-coordinate of the nonce R. bytes32 r; // The x-coordinate of the nonce R.
bytes32 s; // The signature data. bytes32 s; // The signature data.
} }
/** /// @dev A struct representing a single attestation.
* @dev A struct representing a single attestation.
*/
struct Attestation { struct Attestation {
bytes32 uid; // A unique identifier of the attestation. bytes32 uid; // A unique identifier of the attestation.
bytes32 schema; // The unique identifier of the schema. bytes32 schema; // The unique identifier of the schema.
...@@ -42,26 +37,18 @@ struct Attestation { ...@@ -42,26 +37,18 @@ struct Attestation {
// Maximum upgrade forward-compatibility storage gap. // Maximum upgrade forward-compatibility storage gap.
uint32 constant MAX_GAP = 50; uint32 constant MAX_GAP = 50;
/** /// @dev A helper function to work with unchecked iterators in loops.
* @dev A helper function to work with unchecked iterators in loops. /// @param i The index to increment.
* /// @return j The incremented index.
* @param i The index to increment.
*
* @return j The incremented index.
*/
function uncheckedInc(uint256 i) pure returns (uint256 j) { function uncheckedInc(uint256 i) pure returns (uint256 j) {
unchecked { unchecked {
j = i + 1; j = i + 1;
} }
} }
/** /// @dev A helper function that converts a string to a bytes32.
* @dev A helper function that converts a string to a bytes32. /// @param str The string to convert.
* /// @return The converted bytes32.
* @param str The string to convert.
*
* @return The converted bytes32.
*/
function stringToBytes32(string memory str) pure returns (bytes32) { function stringToBytes32(string memory str) pure returns (bytes32) {
bytes32 result; bytes32 result;
...@@ -72,13 +59,9 @@ function stringToBytes32(string memory str) pure returns (bytes32) { ...@@ -72,13 +59,9 @@ function stringToBytes32(string memory str) pure returns (bytes32) {
return result; return result;
} }
/** /// @dev A helper function that converts a bytes32 to a string.
* @dev A helper function that converts a bytes32 to a string. /// @param data The bytes32 data to convert.
* /// @return The converted string.
* @param data The bytes32 data to convert.
*
* @return The converted string.
*/
function bytes32ToString(bytes32 data) pure returns (string memory) { function bytes32ToString(bytes32 data) pure returns (string memory) {
bytes memory byteArray = new bytes(32); bytes memory byteArray = new bytes(32);
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import { ISchemaResolver } from "./resolver/ISchemaResolver.sol"; import { ISchemaResolver } from "./resolver/ISchemaResolver.sol";
/** /// @title A struct representing a record for a submitted schema.
* @title A struct representing a record for a submitted schema.
*/
struct SchemaRecord { struct SchemaRecord {
bytes32 uid; // The unique identifier of the schema. bytes32 uid; // The unique identifier of the schema.
ISchemaResolver resolver; // Optional schema resolver. ISchemaResolver resolver; // Optional schema resolver.
...@@ -17,31 +14,20 @@ struct SchemaRecord { ...@@ -17,31 +14,20 @@ struct SchemaRecord {
/// @title ISchemaRegistry /// @title ISchemaRegistry
/// @notice The interface of global attestation schemas for the Ethereum Attestation Service protocol. /// @notice The interface of global attestation schemas for the Ethereum Attestation Service protocol.
interface ISchemaRegistry { interface ISchemaRegistry {
/** /// @dev Emitted when a new schema has been registered
* @dev Emitted when a new schema has been registered /// @param uid The schema UID.
* /// @param registerer The address of the account used to register the schema.
* @param uid The schema UID.
* @param registerer The address of the account used to register the schema.
*/
event Registered(bytes32 indexed uid, address registerer); event Registered(bytes32 indexed uid, address registerer);
/** /// @dev Submits and reserves a new schema
* @dev Submits and reserves a new schema /// @param schema The schema data schema.
* /// @param resolver An optional schema resolver.
* @param schema The schema data schema. /// @param revocable Whether the schema allows revocations explicitly.
* @param resolver An optional schema resolver. /// @return The UID of the new schema.
* @param revocable Whether the schema allows revocations explicitly.
*
* @return The UID of the new schema.
*/
function register(string calldata schema, ISchemaResolver resolver, bool revocable) external returns (bytes32); function register(string calldata schema, ISchemaResolver resolver, bool revocable) external returns (bytes32);
/** /// @dev Returns an existing schema by UID
* @dev Returns an existing schema by UID /// @param uid The UID of the schema to retrieve.
* /// @return The schema data members.
* @param uid The UID of the schema to retrieve.
*
* @return The schema data members.
*/
function getSchema(bytes32 uid) external view returns (SchemaRecord memory); function getSchema(bytes32 uid) external view returns (SchemaRecord memory);
} }
// 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 { Semver } from "../universal/Semver.sol";
...@@ -22,14 +21,10 @@ contract SchemaRegistry is ISchemaRegistry, Semver { ...@@ -22,14 +21,10 @@ contract SchemaRegistry is ISchemaRegistry, Semver {
// Upgrade forward-compatibility storage gap // Upgrade forward-compatibility storage gap
uint256[MAX_GAP - 1] private __gap; uint256[MAX_GAP - 1] private __gap;
/** /// @dev Creates a new SchemaRegistry instance.
* @dev Creates a new SchemaRegistry instance.
*/
constructor() Semver(1, 0, 0) {} constructor() Semver(1, 0, 0) {}
/** /// @inheritdoc ISchemaRegistry
* @inheritdoc ISchemaRegistry
*/
function register(string calldata schema, ISchemaResolver resolver, bool revocable) external returns (bytes32) { function register(string calldata schema, ISchemaResolver resolver, bool revocable) external returns (bytes32) {
SchemaRecord memory schemaRecord = SchemaRecord({ SchemaRecord memory schemaRecord = SchemaRecord({
uid: EMPTY_UID, uid: EMPTY_UID,
...@@ -51,20 +46,14 @@ contract SchemaRegistry is ISchemaRegistry, Semver { ...@@ -51,20 +46,14 @@ contract SchemaRegistry is ISchemaRegistry, Semver {
return uid; return uid;
} }
/** /// @inheritdoc ISchemaRegistry
* @inheritdoc ISchemaRegistry
*/
function getSchema(bytes32 uid) external view returns (SchemaRecord memory) { function getSchema(bytes32 uid) external view returns (SchemaRecord memory) {
return _registry[uid]; return _registry[uid];
} }
/** /// @dev Calculates a UID for a given schema.
* @dev Calculates a UID for a given schema. /// @param schemaRecord The input schema.
* /// @return schema UID.
* @param schemaRecord The input schema.
*
* @return schema UID.
*/
function _getUID(SchemaRecord memory schemaRecord) private pure returns (bytes32) { function _getUID(SchemaRecord memory schemaRecord) private pure returns (bytes32) {
return keccak256(abi.encodePacked(schemaRecord.schema, schemaRecord.resolver, schemaRecord.revocable)); return keccak256(abi.encodePacked(schemaRecord.schema, schemaRecord.resolver, schemaRecord.revocable));
} }
......
...@@ -36,59 +36,44 @@ abstract contract EIP712Verifier is EIP712 { ...@@ -36,59 +36,44 @@ abstract contract EIP712Verifier is EIP712 {
// Upgrade forward-compatibility storage gap // Upgrade forward-compatibility storage gap
uint256[MAX_GAP - 1] private __gap; uint256[MAX_GAP - 1] private __gap;
/** /// @dev Creates a new EIP712Verifier instance.
* @dev Creates a new EIP712Verifier instance. /// @param version The current major version of the signing domain
*
* @param version The current major version of the signing domain
*/
constructor(string memory name, string memory version) EIP712(name, version) { constructor(string memory name, string memory version) EIP712(name, version) {
_name = stringToBytes32(name); _name = stringToBytes32(name);
} }
/** /// @notice Returns the domain separator used in the encoding of the signatures for attest, and revoke.
* @dev Returns the domain separator used in the encoding of the signatures for attest, and revoke.
*/
function getDomainSeparator() external view returns (bytes32) { function getDomainSeparator() external view returns (bytes32) {
return _domainSeparatorV4(); return _domainSeparatorV4();
} }
/** /// @notice Returns the current nonce per-account.
* @dev Returns the current nonce per-account. /// @param account The requested account.
* /// @return The current nonce.
* @param account The requested account.
*
* @return The current nonce.
*/
function getNonce(address account) external view returns (uint256) { function getNonce(address account) external view returns (uint256) {
return _nonces[account]; return _nonces[account];
} }
/** /// @notice Returns the EIP712 type hash for the attest function.
* Returns the EIP712 type hash for the attest function. /// @return The EIP712 attest function type hash.
*/
function getAttestTypeHash() external pure returns (bytes32) { function getAttestTypeHash() external pure returns (bytes32) {
return ATTEST_TYPEHASH; return ATTEST_TYPEHASH;
} }
/** /// @notice Returns the EIP712 type hash for the revoke function.
* Returns the EIP712 type hash for the revoke function. /// @return hash_ The EIP712 revoke function type hash.
*/
function getRevokeTypeHash() external pure returns (bytes32) { function getRevokeTypeHash() external pure returns (bytes32) {
return REVOKE_TYPEHASH; return REVOKE_TYPEHASH;
} }
/** /// @notice Returns the EIP712 name.
* Returns the EIP712 name. /// @return The EIP712 name.
*/
function getName() external view returns (string memory) { function getName() external view returns (string memory) {
return bytes32ToString(_name); return bytes32ToString(_name);
} }
/** /// @notice Verifies delegated attestation request.
* @dev Verifies delegated attestation request. /// @param request The arguments of the delegated attestation request.
*
* @param request The arguments of the delegated attestation request.
*/
function _verifyAttest(DelegatedAttestationRequest memory request) internal { function _verifyAttest(DelegatedAttestationRequest memory request) internal {
AttestationRequestData memory data = request.data; AttestationRequestData memory data = request.data;
EIP712Signature memory signature = request.signature; EIP712Signature memory signature = request.signature;
...@@ -118,11 +103,8 @@ abstract contract EIP712Verifier is EIP712 { ...@@ -118,11 +103,8 @@ abstract contract EIP712Verifier is EIP712 {
} }
} }
/** /// @notice Verifies delegated revocation request.
* @dev Verifies delegated revocation request. /// @param request The arguments of the delegated revocation request.
*
* @param request The arguments of the delegated revocation request.
*/
function _verifyRevoke(DelegatedRevocationRequest memory request) internal { function _verifyRevoke(DelegatedRevocationRequest memory request) internal {
RevocationRequestData memory data = request.data; RevocationRequestData memory data = request.data;
EIP712Signature memory signature = request.signature; EIP712Signature memory signature = request.signature;
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import { Attestation } from "../Common.sol"; import { Attestation } from "../Common.sol";
...@@ -7,50 +6,33 @@ import { Attestation } from "../Common.sol"; ...@@ -7,50 +6,33 @@ import { Attestation } from "../Common.sol";
/// @title ISchemaResolver /// @title ISchemaResolver
/// @notice The interface of an optional schema resolver. /// @notice The interface of an optional schema resolver.
interface ISchemaResolver { interface ISchemaResolver {
/** /// @notice Checks if the resolve can be sent ETH.
* @dev Returns whether the resolver supports ETH transfers. /// @return Whether the resolver supports ETH transfers.
*/
function isPayable() external pure returns (bool); function isPayable() external pure returns (bool);
/** /// @notice Processes an attestation and verifies whether it's valid.
* @dev Processes an attestation and verifies whether it's valid. /// @param attestation The new attestation.
* /// @return Whether the attestation is valid.
* @param attestation The new attestation.
*
* @return Whether the attestation is valid.
*/
function attest(Attestation calldata attestation) external payable returns (bool); function attest(Attestation calldata attestation) external payable returns (bool);
/** /// @notice Processes multiple attestations and verifies whether they are valid.
* @dev Processes multiple attestations and verifies whether they are valid. /// @param attestations The new attestations.
* /// @param values Explicit ETH amounts which were sent with each attestation.
* @param attestations The new attestations. /// @return Whether all the attestations are valid.
* @param values Explicit ETH amounts which were sent with each attestation.
*
* @return Whether all the attestations are valid.
*/
function multiAttest( function multiAttest(
Attestation[] calldata attestations, Attestation[] calldata attestations,
uint256[] calldata values uint256[] calldata values
) external payable returns (bool); ) external payable returns (bool);
/** /// @notice Processes an attestation revocation and verifies if it can be revoked.
* @dev Processes an attestation revocation and verifies if it can be revoked. /// @param attestation The existing attestation to be revoked.
* /// @return Whether the attestation can be revoked.
* @param attestation The existing attestation to be revoked.
*
* @return Whether the attestation can be revoked.
*/
function revoke(Attestation calldata attestation) external payable returns (bool); function revoke(Attestation calldata attestation) external payable returns (bool);
/** /// @notice Processes revocation of multiple attestation and verifies they can be revoked.
* @dev Processes revocation of multiple attestation and verifies they can be revoked. /// @param attestations The existing attestations to be revoked.
* /// @param values Explicit ETH amounts which were sent with each revocation.
* @param attestations The existing attestations to be revoked. /// @return Whether the attestations can be revoked.
* @param values Explicit ETH amounts which were sent with each revocation.
*
* @return Whether the attestations can be revoked.
*/
function multiRevoke( function multiRevoke(
Attestation[] calldata attestations, Attestation[] calldata attestations,
uint256[] calldata values uint256[] calldata values
......
// 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 { Semver } from "../../universal/Semver.sol";
...@@ -19,11 +18,8 @@ abstract contract SchemaResolver is ISchemaResolver, Semver { ...@@ -19,11 +18,8 @@ abstract contract SchemaResolver is ISchemaResolver, Semver {
// The global EAS contract. // The global EAS contract.
IEAS internal immutable _eas; IEAS internal immutable _eas;
/** /// @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, 0, 0) { constructor(IEAS eas) Semver(1, 0, 0) {
if (address(eas) == address(0)) { if (address(eas) == address(0)) {
revert InvalidEAS(); revert InvalidEAS();
...@@ -32,41 +28,31 @@ abstract contract SchemaResolver is ISchemaResolver, Semver { ...@@ -32,41 +28,31 @@ abstract contract SchemaResolver is ISchemaResolver, Semver {
_eas = eas; _eas = eas;
} }
/** /// @dev Ensures that only the EAS contract can make this call.
* @dev Ensures that only the EAS contract can make this call.
*/
modifier onlyEAS() { modifier onlyEAS() {
_onlyEAS(); _onlyEAS();
_; _;
} }
/** /// @inheritdoc ISchemaResolver
* @inheritdoc ISchemaResolver
*/
function isPayable() public pure virtual returns (bool) { function isPayable() public pure virtual returns (bool) {
return false; return false;
} }
/** /// @dev ETH callback.
* @dev ETH callback.
*/
receive() external payable virtual { receive() external payable virtual {
if (!isPayable()) { if (!isPayable()) {
revert NotPayable(); revert NotPayable();
} }
} }
/** /// @inheritdoc ISchemaResolver
* @inheritdoc ISchemaResolver
*/
function attest(Attestation calldata attestation) external payable onlyEAS returns (bool) { function attest(Attestation calldata attestation) external payable onlyEAS returns (bool) {
return onAttest(attestation, msg.value); return onAttest(attestation, msg.value);
} }
/** /// @inheritdoc ISchemaResolver
* @inheritdoc ISchemaResolver
*/
function multiAttest( function multiAttest(
Attestation[] calldata attestations, Attestation[] calldata attestations,
uint256[] calldata values uint256[] calldata values
...@@ -100,16 +86,12 @@ abstract contract SchemaResolver is ISchemaResolver, Semver { ...@@ -100,16 +86,12 @@ abstract contract SchemaResolver is ISchemaResolver, Semver {
return true; return true;
} }
/** /// @inheritdoc ISchemaResolver
* @inheritdoc ISchemaResolver
*/
function revoke(Attestation calldata attestation) external payable onlyEAS returns (bool) { function revoke(Attestation calldata attestation) external payable onlyEAS returns (bool) {
return onRevoke(attestation, msg.value); return onRevoke(attestation, msg.value);
} }
/** /// @inheritdoc ISchemaResolver
* @inheritdoc ISchemaResolver
*/
function multiRevoke( function multiRevoke(
Attestation[] calldata attestations, Attestation[] calldata attestations,
uint256[] calldata values uint256[] calldata values
...@@ -143,35 +125,25 @@ abstract contract SchemaResolver is ISchemaResolver, Semver { ...@@ -143,35 +125,25 @@ abstract contract SchemaResolver is ISchemaResolver, Semver {
return true; return true;
} }
/** /// @notice A resolver callback that should be implemented by child contracts.
* @dev A resolver callback that should be implemented by child contracts. /// @param attestation The new attestation.
* /// @param value An explicit ETH amount that was sent to the resolver. Please note that this value is verified in
* @param attestation The new attestation. /// both attest() and multiAttest() callbacks EAS-only callbacks and that in case of multi attestations, it'll
* @param value An explicit ETH amount that was sent to the resolver. Please note that this value is verified in /// usually hold that msg.value != value, since msg.value aggregated the sent ETH amounts for all the attestations
* both attest() and multiAttest() callbacks EAS-only callbacks and that in case of multi attestations, it'll /// in the batch.
* usually hold that msg.value != value, since msg.value aggregated the sent ETH amounts for all the attestations /// @return Whether the attestation is valid.
* in the batch.
*
* @return Whether the attestation is valid.
*/
function onAttest(Attestation calldata attestation, uint256 value) internal virtual returns (bool); function onAttest(Attestation calldata attestation, uint256 value) internal virtual returns (bool);
/** /// @notice Processes an attestation revocation and verifies if it can be revoked.
* @dev Processes an attestation revocation and verifies if it can be revoked. /// @param attestation The existing attestation to be revoked.
* /// @param value An explicit ETH amount that was sent to the resolver. Please note that this value is verified in
* @param attestation The existing attestation to be revoked. /// both revoke() and multiRevoke() callbacks EAS-only callbacks and that in case of multi attestations, it'll
* @param value An explicit ETH amount that was sent to the resolver. Please note that this value is verified in /// usually hold that msg.value != value, since msg.value aggregated the sent ETH amounts for all the attestations
* both revoke() and multiRevoke() callbacks EAS-only callbacks and that in case of multi attestations, it'll /// in the batch.
* usually hold that msg.value != value, since msg.value aggregated the sent ETH amounts for all the attestations /// @return Whether the attestation can be revoked.
* in the batch.
*
* @return Whether the attestation can be revoked.
*/
function onRevoke(Attestation calldata attestation, uint256 value) internal virtual returns (bool); function onRevoke(Attestation calldata attestation, uint256 value) internal virtual returns (bool);
/** /// @notice Ensures that only the EAS contract can make this call.
* @dev Ensures that only the EAS contract can make this call.
*/
function _onlyEAS() private view { function _onlyEAS() private view {
if (msg.sender != address(_eas)) { if (msg.sender != address(_eas)) {
revert AccessDenied(); revert AccessDenied();
......
...@@ -7,12 +7,8 @@ interface IDripCheck { ...@@ -7,12 +7,8 @@ interface IDripCheck {
// possible to easily encode parameters on the client side. Solidity does not support generics // possible to easily encode parameters on the client side. Solidity does not support generics
// so it's not possible to do this with explicit typing. // so it's not possible to do this with explicit typing.
/** /// @notice Checks whether a drip should be executable.
* @notice Checks whether a drip should be executable. /// @param _params Encoded parameters for the drip check.
* /// @return execute_ Whether the drip should be executed.
* @param _params Encoded parameters for the drip check. function check(bytes memory _params) external view returns (bool execute_);
*
* @return Whether the drip should be executed.
*/
function check(bytes memory _params) external view returns (bool);
} }
...@@ -3,30 +3,23 @@ pragma solidity 0.8.15; ...@@ -3,30 +3,23 @@ pragma solidity 0.8.15;
import { IDripCheck } from "../IDripCheck.sol"; import { IDripCheck } from "../IDripCheck.sol";
/** /// @title CheckBalanceHigh
* @title CheckBalanceHigh /// @notice DripCheck for checking if an account's balance is above a given threshold.
* @notice DripCheck for checking if an account's balance is above a given threshold.
*/
contract CheckBalanceHigh is IDripCheck { contract CheckBalanceHigh is IDripCheck {
struct Params { struct Params {
address target; address target;
uint256 threshold; uint256 threshold;
} }
/** /// @notice External event used to help client-side tooling encode parameters.
* @notice External event used to help client-side tooling encode parameters. /// @param params Parameters to encode.
*
* @param params Parameters to encode.
*/
event _EventToExposeStructInABI__Params(Params params); event _EventToExposeStructInABI__Params(Params params);
/** /// @inheritdoc IDripCheck
* @inheritdoc IDripCheck function check(bytes memory _params) external view returns (bool execute_) {
*/
function check(bytes memory _params) external view returns (bool) {
Params memory params = abi.decode(_params, (Params)); Params memory params = abi.decode(_params, (Params));
// Check target balance is above threshold. // Check target balance is above threshold.
return params.target.balance > params.threshold; execute_ = params.target.balance > params.threshold;
} }
} }
...@@ -3,30 +3,23 @@ pragma solidity 0.8.15; ...@@ -3,30 +3,23 @@ pragma solidity 0.8.15;
import { IDripCheck } from "../IDripCheck.sol"; import { IDripCheck } from "../IDripCheck.sol";
/** /// @title CheckBalanceLow
* @title CheckBalanceLow /// @notice DripCheck for checking if an account's balance is below a given threshold.
* @notice DripCheck for checking if an account's balance is below a given threshold.
*/
contract CheckBalanceLow is IDripCheck { contract CheckBalanceLow is IDripCheck {
struct Params { struct Params {
address target; address target;
uint256 threshold; uint256 threshold;
} }
/** /// @notice External event used to help client-side tooling encode parameters.
* @notice External event used to help client-side tooling encode parameters. /// @param params Parameters to encode.
*
* @param params Parameters to encode.
*/
event _EventToExposeStructInABI__Params(Params params); event _EventToExposeStructInABI__Params(Params params);
/** /// @inheritdoc IDripCheck
* @inheritdoc IDripCheck function check(bytes memory _params) external view returns (bool execute_) {
*/
function check(bytes memory _params) external view returns (bool) {
Params memory params = abi.decode(_params, (Params)); Params memory params = abi.decode(_params, (Params));
// Check target ETH balance is below threshold. // Check target ETH balance is below threshold.
return params.target.balance < params.threshold; execute_ = params.target.balance < params.threshold;
} }
} }
...@@ -7,10 +7,8 @@ interface IGelatoTreasury { ...@@ -7,10 +7,8 @@ interface IGelatoTreasury {
function userTokenBalance(address _user, address _token) external view returns (uint256); function userTokenBalance(address _user, address _token) external view returns (uint256);
} }
/** /// @title CheckGelatoLow
* @title CheckGelatoLow /// @notice DripCheck for checking if an account's Gelato ETH balance is below some threshold.
* @notice DripCheck for checking if an account's Gelato ETH balance is below some threshold.
*/
contract CheckGelatoLow is IDripCheck { contract CheckGelatoLow is IDripCheck {
struct Params { struct Params {
address treasury; address treasury;
...@@ -18,25 +16,21 @@ contract CheckGelatoLow is IDripCheck { ...@@ -18,25 +16,21 @@ contract CheckGelatoLow is IDripCheck {
address recipient; address recipient;
} }
/** /// @notice External event used to help client-side tooling encode parameters.
* @notice External event used to help client-side tooling encode parameters. /// @param params Parameters to encode.
*
* @param params Parameters to encode.
*/
event _EventToExposeStructInABI__Params(Params params); event _EventToExposeStructInABI__Params(Params params);
/** /// @inheritdoc IDripCheck
* @inheritdoc IDripCheck function check(bytes memory _params) external view returns (bool execute_) {
*/
function check(bytes memory _params) external view returns (bool) {
Params memory params = abi.decode(_params, (Params)); Params memory params = abi.decode(_params, (Params));
// Check GelatoTreasury ETH balance is below threshold. // Check GelatoTreasury ETH balance is below threshold.
return execute_ =
IGelatoTreasury(params.treasury).userTokenBalance( IGelatoTreasury(params.treasury).userTokenBalance(
params.recipient, params.recipient,
// Gelato represents ETH as 0xeeeee....eeeee // Gelato represents ETH as 0xeeeee....eeeee
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE
) < params.threshold; ) <
params.threshold;
} }
} }
...@@ -3,15 +3,11 @@ pragma solidity 0.8.15; ...@@ -3,15 +3,11 @@ pragma solidity 0.8.15;
import { IDripCheck } from "../IDripCheck.sol"; import { IDripCheck } from "../IDripCheck.sol";
/** /// @title CheckTrue
* @title CheckTrue /// @notice DripCheck that always returns true.
* @notice DripCheck that always returns true.
*/
contract CheckTrue is IDripCheck { contract CheckTrue is IDripCheck {
/** /// @inheritdoc IDripCheck
* @inheritdoc IDripCheck function check(bytes memory) external pure returns (bool execute_) {
*/ execute_ = true;
function check(bytes memory) external pure returns (bool) {
return true;
} }
} }
...@@ -3,32 +3,23 @@ pragma solidity 0.8.15; ...@@ -3,32 +3,23 @@ pragma solidity 0.8.15;
import { IFaucetAuthModule } from "./authmodules/IFaucetAuthModule.sol"; import { IFaucetAuthModule } from "./authmodules/IFaucetAuthModule.sol";
/** /// @title SafeSend
* @title SafeSend /// @notice Sends ETH to a recipient account without triggering any code.
* @notice Sends ETH to a recipient account without triggering any code.
*/
contract SafeSend { contract SafeSend {
/** /// @param _recipient Account to send ETH to.
* @param _recipient Account to send ETH to.
*/
constructor(address payable _recipient) payable { constructor(address payable _recipient) payable {
selfdestruct(_recipient); selfdestruct(_recipient);
} }
} }
/** /// @title Faucet
* @title Faucet /// @notice Faucet contract that drips ETH to users.
* @notice Faucet contract that drips ETH to users.
*/
contract Faucet { contract Faucet {
/** /// @notice Emitted on each drip.
* @notice Emitted on each drip. /// @param authModule The type of authentication that was used for verifying the drip.
* /// @param userId The id of the user that requested the drip.
* @param authModule The type of authentication that was used for verifying the drip. /// @param amount The amount of funds sent.
* @param userId The id of the user that requested the drip. /// @param recipient The recipient of the drip.
* @param amount The amount of funds sent.
* @param recipient The recipient of the drip.
*/
event Drip( event Drip(
string indexed authModule, string indexed authModule,
bytes32 indexed userId, bytes32 indexed userId,
...@@ -36,26 +27,20 @@ contract Faucet { ...@@ -36,26 +27,20 @@ contract Faucet {
address indexed recipient address indexed recipient
); );
/** /// @notice Parameters for a drip.
* @notice Parameters for a drip.
*/
struct DripParameters { struct DripParameters {
address payable recipient; address payable recipient;
bytes32 nonce; bytes32 nonce;
} }
/** /// @notice Parameters for authentication.
* @notice Parameters for authentication.
*/
struct AuthParameters { struct AuthParameters {
IFaucetAuthModule module; IFaucetAuthModule module;
bytes32 id; bytes32 id;
bytes proof; bytes proof;
} }
/** /// @notice Configuration for an authentication module.
* @notice Configuration for an authentication module.
*/
struct ModuleConfig { struct ModuleConfig {
string name; string name;
bool enabled; bool enabled;
...@@ -63,74 +48,51 @@ contract Faucet { ...@@ -63,74 +48,51 @@ contract Faucet {
uint256 amount; uint256 amount;
} }
/** /// @notice Admin address that can configure the faucet.
* @notice Admin address that can configure the faucet.
*/
address public immutable ADMIN; address public immutable ADMIN;
/** /// @notice Mapping of authentication modules to their configurations.
* @notice Mapping of authentication modules to their configurations.
*/
mapping(IFaucetAuthModule => ModuleConfig) public modules; mapping(IFaucetAuthModule => ModuleConfig) public modules;
/** /// @notice Mapping of authentication IDs to the next timestamp at which they can be used.
* @notice Mapping of authentication IDs to the next timestamp at which they can be used.
*/
mapping(IFaucetAuthModule => mapping(bytes32 => uint256)) public timeouts; mapping(IFaucetAuthModule => mapping(bytes32 => uint256)) public timeouts;
/** /// @notice Maps from id to nonces to whether or not they have been used.
* @notice Maps from id to nonces to whether or not they have been used.
*/
mapping(bytes32 => mapping(bytes32 => bool)) public nonces; mapping(bytes32 => mapping(bytes32 => bool)) public nonces;
/** /// @notice Modifier that makes a function admin priviledged.
* @notice Modifier that makes a function admin priviledged.
*/
modifier priviledged() { modifier priviledged() {
require(msg.sender == ADMIN, "Faucet: function can only be called by admin"); require(msg.sender == ADMIN, "Faucet: function can only be called by admin");
_; _;
} }
/** /// @param _admin Admin address that can configure the faucet.
* @param _admin Admin address that can configure the faucet.
*/
constructor(address _admin) { constructor(address _admin) {
ADMIN = _admin; ADMIN = _admin;
} }
/** /// @notice Allows users to donate ETH to this contract.
* @notice Allows users to donate ETH to this contract.
*/
receive() external payable { receive() external payable {
// Thank you! // Thank you!
} }
/** /// @notice Allows the admin to withdraw funds.
* @notice Allows the admin to withdraw funds. /// @param _recipient Address to receive the funds.
* /// @param _amount Amount of ETH in wei to withdraw.
* @param _recipient Address to receive the funds.
* @param _amount Amount of ETH in wei to withdraw.
*/
function withdraw(address payable _recipient, uint256 _amount) public priviledged { function withdraw(address payable _recipient, uint256 _amount) public priviledged {
new SafeSend{ value: _amount }(_recipient); new SafeSend{ value: _amount }(_recipient);
} }
/** /// @notice Allows the admin to configure an authentication module.
* @notice Allows the admin to configure an authentication module. /// @param _module Authentication module to configure.
* /// @param _config Configuration to set for the module.
* @param _module Authentication module to configure.
* @param _config Configuration to set for the module.
*/
function configure(IFaucetAuthModule _module, ModuleConfig memory _config) public priviledged { function configure(IFaucetAuthModule _module, ModuleConfig memory _config) public priviledged {
modules[_module] = _config; modules[_module] = _config;
} }
/** /// @notice Drips ETH to a recipient account.
* @notice Drips ETH to a recipient account. /// @param _params Drip parameters.
* /// @param _auth Authentication parameters.
* @param _params Drip parameters.
* @param _auth Authentication parameters.
*/
function drip(DripParameters memory _params, AuthParameters memory _auth) public { function drip(DripParameters memory _params, AuthParameters memory _auth) public {
// Grab the module config once. // Grab the module config once.
ModuleConfig memory config = modules[_auth.module]; ModuleConfig memory config = modules[_auth.module];
......
...@@ -6,41 +6,30 @@ import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/Sig ...@@ -6,41 +6,30 @@ import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/Sig
import { IFaucetAuthModule } from "./IFaucetAuthModule.sol"; import { IFaucetAuthModule } from "./IFaucetAuthModule.sol";
import { Faucet } from "../Faucet.sol"; import { Faucet } from "../Faucet.sol";
/** /// @title AdminFaucetAuthModule
* @title AdminFaucetAuthModule /// @notice FaucetAuthModule that allows an admin to sign off on a given faucet drip. Takes an admin
* @notice FaucetAuthModule that allows an admin to sign off on a given faucet drip. Takes an admin /// as the constructor argument.
* as the constructor argument.
*/
contract AdminFaucetAuthModule is IFaucetAuthModule, EIP712 { contract AdminFaucetAuthModule is IFaucetAuthModule, EIP712 {
/** /// @notice Admin address that can sign off on drips.
* @notice Admin address that can sign off on drips.
*/
address public immutable ADMIN; address public immutable ADMIN;
/** /// @notice EIP712 typehash for the Proof type.
* @notice EIP712 typehash for the Proof type.
*/
bytes32 public constant PROOF_TYPEHASH = bytes32 public constant PROOF_TYPEHASH =
keccak256("Proof(address recipient,bytes32 nonce,bytes32 id)"); keccak256("Proof(address recipient,bytes32 nonce,bytes32 id)");
/** /// @notice Struct that represents a proof that verifies the admin.
* @notice Struct that represents a proof that verifies the admin. /// @custom:field recipient Address that will be receiving the faucet funds.
* /// @custom:field nonce Pseudorandom nonce to prevent replay attacks.
* @custom:field recipient Address that will be receiving the faucet funds. /// @custom:field id id for the user requesting the faucet funds.
* @custom:field nonce Pseudorandom nonce to prevent replay attacks.
* @custom:field id id for the user requesting the faucet funds.
*/
struct Proof { struct Proof {
address recipient; address recipient;
bytes32 nonce; bytes32 nonce;
bytes32 id; bytes32 id;
} }
/** /// @param _admin Admin address that can sign off on drips.
* @param _admin Admin address that can sign off on drips. /// @param _name Contract name.
* @param _name Contract name. /// @param _version The current major version of the signing domain.
* @param _version The current major version of the signing domain.
*/
constructor( constructor(
address _admin, address _admin,
string memory _name, string memory _name,
...@@ -49,22 +38,19 @@ contract AdminFaucetAuthModule is IFaucetAuthModule, EIP712 { ...@@ -49,22 +38,19 @@ contract AdminFaucetAuthModule is IFaucetAuthModule, EIP712 {
ADMIN = _admin; ADMIN = _admin;
} }
/** /// @inheritdoc IFaucetAuthModule
* @inheritdoc IFaucetAuthModule
*/
function verify( function verify(
Faucet.DripParameters memory _params, Faucet.DripParameters memory _params,
bytes32 _id, bytes32 _id,
bytes memory _proof bytes memory _proof
) external view returns (bool) { ) external view returns (bool valid_) {
// Generate a EIP712 typed data hash to compare against the proof. // Generate a EIP712 typed data hash to compare against the proof.
return valid_ = SignatureChecker.isValidSignatureNow(
SignatureChecker.isValidSignatureNow( ADMIN,
ADMIN, _hashTypedDataV4(
_hashTypedDataV4( keccak256(abi.encode(PROOF_TYPEHASH, _params.recipient, _params.nonce, _id))
keccak256(abi.encode(PROOF_TYPEHASH, _params.recipient, _params.nonce, _id)) ),
), _proof
_proof );
);
} }
} }
...@@ -3,18 +3,14 @@ pragma solidity 0.8.15; ...@@ -3,18 +3,14 @@ pragma solidity 0.8.15;
import { Faucet } from "../Faucet.sol"; import { Faucet } from "../Faucet.sol";
/** /// @title IFaucetAuthModule
* @title IFaucetAuthModule /// @notice Interface for faucet authentication modules.
* @notice Interface for faucet authentication modules.
*/
interface IFaucetAuthModule { interface IFaucetAuthModule {
/** /// @notice Verifies that the given drip parameters are valid.
* @notice Verifies that the given drip parameters are valid. /// @param _params Drip parameters to verify.
* /// @param _id Authentication ID to verify.
* @param _params Drip parameters to verify. /// @param _proof Authentication proof to verify.
* @param _id Authentication ID to verify. /// @return valid_ True if the drip parameters are valid.
* @param _proof Authentication proof to verify.
*/
function verify( function verify(
Faucet.DripParameters memory _params, Faucet.DripParameters memory _params,
bytes32 _id, bytes32 _id,
......
...@@ -3,39 +3,29 @@ pragma solidity 0.8.15; ...@@ -3,39 +3,29 @@ pragma solidity 0.8.15;
import { Semver } from "../../universal/Semver.sol"; import { Semver } from "../../universal/Semver.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 Semver {
/** /// @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 key A bytes32 key for the attestation.
* @custom:field about Address for which the attestation is about. /// @custom:field val The attestation as arbitrary bytes.
* @custom:field key A bytes32 key for the attestation.
* @custom:field val The attestation as arbitrary bytes.
*/
struct AttestationData { struct AttestationData {
address about; address about;
bytes32 key; bytes32 key;
bytes val; bytes val;
} }
/** /// @notice Maps addresses to attestations. Creator => About => Key => Value.
* @notice Maps addresses to attestations. Creator => About => Key => Value.
*/
mapping(address => mapping(address => mapping(bytes32 => bytes))) public attestations; mapping(address => mapping(address => mapping(bytes32 => bytes))) public attestations;
/** /// @notice Emitted when Attestation is created.
* @notice Emitted when Attestation is created. /// @param creator Address that made the attestation.
* /// @param about Address attestation is about.
* @param creator Address that made the attestation. /// @param key Key of the attestation.
* @param about Address attestation is about. /// @param val Value of the attestation.
* @param key Key of the attestation.
* @param val Value of the attestation.
*/
event AttestationCreated( event AttestationCreated(
address indexed creator, address indexed creator,
address indexed about, address indexed about,
...@@ -43,18 +33,13 @@ contract AttestationStation is Semver { ...@@ -43,18 +33,13 @@ contract AttestationStation is Semver {
bytes val bytes val
); );
/** /// @custom:semver 1.1.0
* @custom:semver 1.1.0
*/
constructor() Semver(1, 1, 0) {} constructor() Semver(1, 1, 0) {}
/** /// @notice Allows anyone to create an attestation.
* @notice Allows anyone to create an attestation. /// @param _about Address that the attestation is about.
* /// @param _key A key used to namespace the attestation.
* @param _about Address that the attestation is about. /// @param _val An arbitrary value stored as part of the attestation.
* @param _key A key used to namespace the attestation.
* @param _val An arbitrary value stored as part of the attestation.
*/
function attest( function attest(
address _about, address _about,
bytes32 _key, bytes32 _key,
...@@ -65,11 +50,8 @@ contract AttestationStation is Semver { ...@@ -65,11 +50,8 @@ contract AttestationStation is Semver {
emit AttestationCreated(msg.sender, _about, _key, _val); emit AttestationCreated(msg.sender, _about, _key, _val);
} }
/** /// @notice Allows anyone to create attestations.
* @notice Allows anyone to create attestations. /// @param _attestations An array of AttestationData structs.
*
* @param _attestations An array of AttestationData structs.
*/
function attest(AttestationData[] calldata _attestations) external { function attest(AttestationData[] calldata _attestations) external {
uint256 length = _attestations.length; uint256 length = _attestations.length;
for (uint256 i = 0; i < length; ) { for (uint256 i = 0; i < length; ) {
......
...@@ -9,41 +9,29 @@ import { AttestationStation } from "./AttestationStation.sol"; ...@@ -9,41 +9,29 @@ import { AttestationStation } from "./AttestationStation.sol";
import { OptimistAllowlist } from "./OptimistAllowlist.sol"; import { OptimistAllowlist } from "./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, Semver {
/** /// @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");
/** /// @notice Attestor who attests to baseURI.
* @notice Attestor who attests to baseURI.
*/
address public immutable BASE_URI_ATTESTOR; address public immutable BASE_URI_ATTESTOR;
/** /// @notice Address of the AttestationStation contract.
* @notice Address of the AttestationStation contract.
*/
AttestationStation public immutable ATTESTATION_STATION; AttestationStation public immutable ATTESTATION_STATION;
/** /// @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.0
* @custom:semver 2.0.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. /// @param _attestationStation Address of the AttestationStation contract.
* @param _attestationStation Address of the AttestationStation contract. /// @param _optimistAllowlist Address of the OptimistAllowlist contract
* @param _optimistAllowlist Address of the OptimistAllowlist contract
*/
constructor( constructor(
string memory _name, string memory _name,
string memory _symbol, string memory _symbol,
...@@ -57,109 +45,81 @@ contract Optimist is ERC721BurnableUpgradeable, Semver { ...@@ -57,109 +45,81 @@ contract Optimist is ERC721BurnableUpgradeable, Semver {
initialize(_name, _symbol); initialize(_name, _symbol);
} }
/** /// @notice Initializes the Optimist contract.
* @notice Initializes the Optimist contract. /// @param _name Token name.
* /// @param _symbol Token symbol.
* @param _name Token name.
* @param _symbol Token symbol.
*/
function initialize(string memory _name, string memory _symbol) public initializer { function initialize(string memory _name, string memory _symbol) public initializer {
__ERC721_init(_name, _symbol); __ERC721_init(_name, _symbol);
__ERC721Burnable_init(); __ERC721Burnable_init();
} }
/** /// @notice Allows an address to mint an Optimist NFT. Token ID is the uint256 representation
* @notice Allows an address to mint an Optimist NFT. Token ID is the uint256 representation /// of the recipient's address. Recipients must be permitted to mint, eventually anyone
* of the recipient's address. Recipients must be permitted to mint, eventually anyone /// will be able to mint. One token per address.
* will be able to mint. One token per address. /// @param _recipient Address of the token recipient.
*
* @param _recipient Address of the token recipient.
*/
function mint(address _recipient) public { function mint(address _recipient) public {
require(isOnAllowList(_recipient), "Optimist: address is not on allowList"); require(isOnAllowList(_recipient), "Optimist: address is not on allowList");
_safeMint(_recipient, tokenIdOfAddress(_recipient)); _safeMint(_recipient, tokenIdOfAddress(_recipient));
} }
/** /// @notice Returns the baseURI for all tokens.
* @notice Returns the baseURI for all tokens. /// @return uri_ BaseURI for all tokens.
* function baseURI() public view returns (string memory uri_) {
* @return BaseURI for all tokens. uri_ = string(
*/ abi.encodePacked(
function baseURI() public view returns (string memory) { ATTESTATION_STATION.attestations(
return BASE_URI_ATTESTOR,
string( address(this),
abi.encodePacked( bytes32("optimist.base-uri")
ATTESTATION_STATION.attestations(
BASE_URI_ATTESTOR,
address(this),
bytes32("optimist.base-uri")
)
) )
); )
);
} }
/** /// @notice Returns the token URI for a given token by ID
* @notice Returns the token URI for a given token by ID /// @param _tokenId Token ID to query.
* /// @return uri_ Token URI for the given token by ID.
* @param _tokenId Token ID to query. function tokenURI(uint256 _tokenId) public view virtual override returns (string memory uri_) {
uri_ = string(
* @return Token URI for the given token by ID. abi.encodePacked(
*/ baseURI(),
function tokenURI(uint256 _tokenId) public view virtual override returns (string memory) { "/",
return // Properly format the token ID as a 20 byte hex string (address).
string( Strings.toHexString(_tokenId, 20),
abi.encodePacked( ".json"
baseURI(), )
"/", );
// Properly format the token ID as a 20 byte hex string (address).
Strings.toHexString(_tokenId, 20),
".json"
)
);
} }
/** /// @notice Checks OptimistAllowlist to determine whether a given address is allowed to mint
* @notice Checks OptimistAllowlist to determine whether a given address is allowed to mint /// the Optimist NFT. Since the Optimist NFT will also be used as part of the
* the Optimist NFT. Since the Optimist NFT will also be used as part of the /// Citizens House, mints are currently restricted. Eventually anyone will be able
* Citizens House, mints are currently restricted. Eventually anyone will be able /// to mint.
* to mint. /// @return allowed_ Whether or not the address is allowed to mint yet.
* function isOnAllowList(address _recipient) public view returns (bool allowed_) {
* @return Whether or not the address is allowed to mint yet. allowed_ = OPTIMIST_ALLOWLIST.isAllowedToMint(_recipient);
*/
function isOnAllowList(address _recipient) public view returns (bool) {
return OPTIMIST_ALLOWLIST.isAllowedToMint(_recipient);
} }
/** /// @notice Returns the token ID for the token owned by a given address. This is the uint256
* @notice Returns the token ID for the token owned by a given address. This is the uint256 /// representation of the given address.
* representation of the given address. /// @return Token ID for the token owned by the given address.
*
* @return Token ID for the token owned by the given address.
*/
function tokenIdOfAddress(address _owner) public pure returns (uint256) { function tokenIdOfAddress(address _owner) public pure returns (uint256) {
return uint256(uint160(_owner)); return uint256(uint160(_owner));
} }
/** /// @notice Disabled for the Optimist NFT (Soul Bound Token).
* @notice Disabled for the Optimist NFT (Soul Bound Token).
*/
function approve(address, uint256) public pure override { function approve(address, uint256) public pure override {
revert("Optimist: soul bound token"); revert("Optimist: soul bound token");
} }
/** /// @notice Disabled for the Optimist NFT (Soul Bound Token).
* @notice Disabled for the Optimist NFT (Soul Bound Token).
*/
function setApprovalForAll(address, bool) public virtual override { function setApprovalForAll(address, bool) public virtual override {
revert("Optimist: soul bound token"); revert("Optimist: soul bound token");
} }
/** /// @notice Prevents transfers of the Optimist NFT (Soul Bound Token).
* @notice Prevents transfers of the Optimist NFT (Soul Bound Token). /// @param _from Address of the token sender.
* /// @param _to Address of the token recipient.
* @param _from Address of the token sender.
* @param _to Address of the token recipient.
*/
function _beforeTokenTransfer( function _beforeTokenTransfer(
address _from, address _from,
address _to, address _to,
......
...@@ -5,54 +5,37 @@ import { Semver } from "../../universal/Semver.sol"; ...@@ -5,54 +5,37 @@ import { Semver } from "../../universal/Semver.sol";
import { AttestationStation } from "./AttestationStation.sol"; import { AttestationStation } from "./AttestationStation.sol";
import { OptimistConstants } from "./libraries/OptimistConstants.sol"; import { OptimistConstants } from "./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 /// for whether an address is eligible or not.
address is eligible or not.
*/
contract OptimistAllowlist is Semver { contract OptimistAllowlist is Semver {
/** /// @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");
/** /// @notice Attestation key used by Coinbase to issue attestations for Quest participants.
* @notice Attestation key used by Coinbase to issue attestations for Quest participants.
*/
bytes32 public constant COINBASE_QUEST_ELIGIBLE_ATTESTATION_KEY = bytes32 public constant COINBASE_QUEST_ELIGIBLE_ATTESTATION_KEY =
bytes32("coinbase.quest-eligible"); bytes32("coinbase.quest-eligible");
/** /// @notice Address of the AttestationStation contract.
* @notice Address of the AttestationStation contract.
*/
AttestationStation public immutable ATTESTATION_STATION; AttestationStation public immutable ATTESTATION_STATION;
/** /// @notice Attestor that issues 'optimist.can-mint' attestations.
* @notice Attestor that issues 'optimist.can-mint' attestations.
*/
address public immutable ALLOWLIST_ATTESTOR; address public immutable ALLOWLIST_ATTESTOR;
/** /// @notice Attestor that issues 'coinbase.quest-eligible' attestations.
* @notice Attestor that issues 'coinbase.quest-eligible' attestations.
*/
address public immutable COINBASE_QUEST_ATTESTOR; address public immutable COINBASE_QUEST_ATTESTOR;
/** /// @notice Address of OptimistInviter contract that issues 'optimist.can-mint-from-invite'
* @notice Address of OptimistInviter contract that issues 'optimist.can-mint-from-invite' /// attestations.
* attestations.
*/
address public immutable OPTIMIST_INVITER; address public immutable OPTIMIST_INVITER;
/** /// @custom:semver 1.0.0
* @custom:semver 1.0.0 /// @param _attestationStation Address of the AttestationStation contract.
* /// @param _allowlistAttestor Address of the allowlist attestor.
* @param _attestationStation Address of the AttestationStation contract. /// @param _coinbaseQuestAttestor Address of the Coinbase Quest attestor.
* @param _allowlistAttestor Address of the allowlist attestor. /// @param _optimistInviter Address of the OptimistInviter contract.
* @param _coinbaseQuestAttestor Address of the Coinbase Quest attestor.
* @param _optimistInviter Address of the OptimistInviter contract.
*/
constructor( constructor(
AttestationStation _attestationStation, AttestationStation _attestationStation,
address _allowlistAttestor, address _allowlistAttestor,
...@@ -65,95 +48,83 @@ contract OptimistAllowlist is Semver { ...@@ -65,95 +48,83 @@ contract OptimistAllowlist is Semver {
OPTIMIST_INVITER = _optimistInviter; OPTIMIST_INVITER = _optimistInviter;
} }
/** /// @notice Checks whether a given address is allowed to mint the Optimist NFT yet. Since the
* @notice Checks whether a given address is allowed to mint the Optimist NFT yet. Since the /// Optimist NFT will also be used as part of the Citizens House, mints are currently
* Optimist NFT will also be used as part of the Citizens House, mints are currently /// restricted. Eventually anyone will be able to mint.
* restricted. Eventually anyone will be able to mint. /// Currently, address is allowed to mint if it satisfies any of the following:
* /// 1) Has a valid 'optimist.can-mint' attestation from the allowlist attestor.
* Currently, address is allowed to mint if it satisfies any of the following: /// 2) Has a valid 'coinbase.quest-eligible' attestation from Coinbase Quest attestor
* 1) Has a valid 'optimist.can-mint' attestation from the allowlist attestor. /// 3) Has a valid 'optimist.can-mint-from-invite' attestation from the OptimistInviter
* 2) Has a valid 'coinbase.quest-eligible' attestation from Coinbase Quest attestor /// contract.
* 3) Has a valid 'optimist.can-mint-from-invite' attestation from the OptimistInviter /// @param _claimer Address to check.
* contract. /// @return allowed_ Whether or not the address is allowed to mint yet.
* function isAllowedToMint(address _claimer) public view returns (bool allowed_) {
* @param _claimer Address to check. allowed_ =
*
* @return Whether or not the address is allowed to mint yet.
*/
function isAllowedToMint(address _claimer) public view returns (bool) {
return
_hasAttestationFromAllowlistAttestor(_claimer) || _hasAttestationFromAllowlistAttestor(_claimer) ||
_hasAttestationFromCoinbaseQuestAttestor(_claimer) || _hasAttestationFromCoinbaseQuestAttestor(_claimer) ||
_hasAttestationFromOptimistInviter(_claimer); _hasAttestationFromOptimistInviter(_claimer);
} }
/** /// @notice Checks whether an address has a valid 'optimist.can-mint' attestation from the
* @notice Checks whether an address has a valid 'optimist.can-mint' attestation from the /// allowlist attestor.
* allowlist attestor. /// @param _claimer Address to check.
* /// @return valid_ Whether or not the address has a valid attestation.
* @param _claimer Address to check. function _hasAttestationFromAllowlistAttestor(address _claimer)
* internal
* @return Whether or not the address has a valid attestation. view
*/ returns (bool valid_)
function _hasAttestationFromAllowlistAttestor(address _claimer) internal view returns (bool) { {
// Expected attestation value is bytes32("true") // Expected attestation value is bytes32("true")
return valid_ = _hasValidAttestation(
_hasValidAttestation(ALLOWLIST_ATTESTOR, _claimer, OPTIMIST_CAN_MINT_ATTESTATION_KEY); ALLOWLIST_ATTESTOR,
_claimer,
OPTIMIST_CAN_MINT_ATTESTATION_KEY
);
} }
/** /// @notice Checks whether an address has a valid attestation from the Coinbase attestor.
* @notice Checks whether an address has a valid attestation from the Coinbase attestor. /// @param _claimer Address to check.
* /// @return valid_ Whether or not the address has a valid attestation.
* @param _claimer Address to check.
*
* @return Whether or not the address has a valid attestation.
*/
function _hasAttestationFromCoinbaseQuestAttestor(address _claimer) function _hasAttestationFromCoinbaseQuestAttestor(address _claimer)
internal internal
view view
returns (bool) returns (bool valid_)
{ {
// Expected attestation value is bytes32("true") // Expected attestation value is bytes32("true")
return valid_ = _hasValidAttestation(
_hasValidAttestation( COINBASE_QUEST_ATTESTOR,
COINBASE_QUEST_ATTESTOR, _claimer,
_claimer, COINBASE_QUEST_ELIGIBLE_ATTESTATION_KEY
COINBASE_QUEST_ELIGIBLE_ATTESTATION_KEY );
);
} }
/** /// @notice Checks whether an address has a valid attestation from the OptimistInviter contract.
* @notice Checks whether an address has a valid attestation from the OptimistInviter contract. /// @param _claimer Address to check.
* /// @return valid_ Whether or not the address has a valid attestation.
* @param _claimer Address to check. function _hasAttestationFromOptimistInviter(address _claimer)
* internal
* @return Whether or not the address has a valid attestation. view
*/ returns (bool valid_)
function _hasAttestationFromOptimistInviter(address _claimer) internal view returns (bool) { {
// Expected attestation value is the inviter's address // Expected attestation value is the inviter's address
return valid_ = _hasValidAttestation(
_hasValidAttestation( OPTIMIST_INVITER,
OPTIMIST_INVITER, _claimer,
_claimer, OptimistConstants.OPTIMIST_CAN_MINT_FROM_INVITE_ATTESTATION_KEY
OptimistConstants.OPTIMIST_CAN_MINT_FROM_INVITE_ATTESTATION_KEY );
);
} }
/** /// @notice Checks whether an address has a valid truthy attestation.
* @notice Checks whether an address has a valid truthy attestation. /// Any attestation val other than bytes32("") is considered truthy.
* Any attestation val other than bytes32("") is considered truthy. /// @param _creator Address that made the attestation.
* /// @param _about Address attestation is about.
* @param _creator Address that made the attestation. /// @param _key Key of the attestation.
* @param _about Address attestation is about. /// @return valid_ Whether or not the address has a valid truthy attestation.
* @param _key Key of the attestation.
*
* @return Whether or not the address has a valid truthy attestation.
*/
function _hasValidAttestation( function _hasValidAttestation(
address _creator, address _creator,
address _about, address _about,
bytes32 _key bytes32 _key
) internal view returns (bool) { ) internal view returns (bool valid_) {
return ATTESTATION_STATION.attestations(_creator, _about, _key).length > 0; valid_ = ATTESTATION_STATION.attestations(_creator, _about, _key).length > 0;
} }
} }
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
/** /// @title OptimistConstants
* @title OptimistConstants /// @notice Library for storing Optimist related constants that are shared in multiple contracts.
* @notice Library for storing Optimist related constants that are shared in multiple contracts.
*/
library OptimistConstants { library OptimistConstants {
/** /// @notice Attestation key issued by OptimistInviter allowing the attested account to mint.
* @notice Attestation key issued by OptimistInviter allowing the attested account to mint.
*/
bytes32 internal constant OPTIMIST_CAN_MINT_FROM_INVITE_ATTESTATION_KEY = bytes32 internal constant OPTIMIST_CAN_MINT_FROM_INVITE_ATTESTATION_KEY =
bytes32("optimist.can-mint-from-invite"); bytes32("optimist.can-mint-from-invite");
} }
...@@ -5,9 +5,7 @@ import { Test } from "forge-std/Test.sol"; ...@@ -5,9 +5,7 @@ import { Test } from "forge-std/Test.sol";
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol"; import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol";
contract AddressAliasHelper_applyAndUndo_Test is Test { contract AddressAliasHelper_applyAndUndo_Test is Test {
/** /// @notice Tests that applying and then undoing an alias results in the original address.
* @notice Tests that applying and then undoing an alias results in the original address.
*/
function testFuzz_applyAndUndo_succeeds(address _address) external { function testFuzz_applyAndUndo_succeeds(address _address) external {
address aliased = AddressAliasHelper.applyL1ToL2Alias(_address); address aliased = AddressAliasHelper.applyL1ToL2Alias(_address);
address unaliased = AddressAliasHelper.undoL1ToL2Alias(aliased); address unaliased = AddressAliasHelper.undoL1ToL2Alias(aliased);
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
...@@ -6,41 +6,32 @@ import { AdminFaucetAuthModule } from "../periphery/faucet/authmodules/AdminFauc ...@@ -6,41 +6,32 @@ import { AdminFaucetAuthModule } from "../periphery/faucet/authmodules/AdminFauc
import { Faucet } from "../periphery/faucet/Faucet.sol"; import { Faucet } from "../periphery/faucet/Faucet.sol";
import { FaucetHelper } from "./Helpers.sol"; import { FaucetHelper } from "./Helpers.sol";
/** /// @title AdminFaucetAuthModuleTest
* @title AdminFaucetAuthModuleTest /// @notice Tests the AdminFaucetAuthModule contract.
* @notice Tests the AdminFaucetAuthModule contract.
*/
contract AdminFaucetAuthModuleTest is Test { contract AdminFaucetAuthModuleTest is Test {
/** /// @notice The admin of the `AdminFaucetAuthModule` contract.
* @notice The admin of the `AdminFaucetAuthModule` contract.
*/
address internal admin; address internal admin;
/**
* @notice Private key of the `admin`. /// @notice Private key of the `admin`.
*/
uint256 internal adminKey; uint256 internal adminKey;
/**
* @notice Not an admin of the `AdminFaucetAuthModule` contract. /// @notice Not an admin of the `AdminFaucetAuthModule` contract.
*/
address internal nonAdmin; address internal nonAdmin;
/**
* @notice Private key of the `nonAdmin`. /// @notice Private key of the `nonAdmin`.
*/
uint256 internal nonAdminKey; uint256 internal nonAdminKey;
/**
* @notice An instance of the `AdminFaucetAuthModule` contract. /// @notice An instance of the `AdminFaucetAuthModule` contract.
*/
AdminFaucetAuthModule internal adminFam; AdminFaucetAuthModule internal adminFam;
/**
* @notice An instance of the `FaucetHelper` contract. /// @notice An instance of the `FaucetHelper` contract.
*/
FaucetHelper internal faucetHelper; FaucetHelper internal faucetHelper;
string internal adminFamName = "AdminFAM"; string internal adminFamName = "AdminFAM";
string internal adminFamVersion = "1"; string internal adminFamVersion = "1";
/** /// @notice Deploy the `AdminFaucetAuthModule` contract.
* @notice Deploy the `AdminFaucetAuthModule` contract.
*/
function setUp() external { function setUp() external {
adminKey = 0xB0B0B0B0; adminKey = 0xB0B0B0B0;
admin = vm.addr(adminKey); admin = vm.addr(adminKey);
...@@ -53,10 +44,7 @@ contract AdminFaucetAuthModuleTest is Test { ...@@ -53,10 +44,7 @@ contract AdminFaucetAuthModuleTest is Test {
faucetHelper = new FaucetHelper(); faucetHelper = new FaucetHelper();
} }
/** /// @notice Get signature as a bytes blob.
* @notice Get signature as a bytes blob.
*
*/
function _getSignature(uint256 _signingPrivateKey, bytes32 _digest) function _getSignature(uint256 _signingPrivateKey, bytes32 _digest)
internal internal
pure pure
...@@ -68,11 +56,9 @@ contract AdminFaucetAuthModuleTest is Test { ...@@ -68,11 +56,9 @@ contract AdminFaucetAuthModuleTest is Test {
return signature; return signature;
} }
/** /// @notice Signs a proof with the given private key and returns the signature using
* @notice Signs a proof with the given private key and returns the signature using /// the given EIP712 domain separator. This assumes that the issuer's address is the
* the given EIP712 domain separator. This assumes that the issuer's address is the /// corresponding public key to _issuerPrivateKey.
* corresponding public key to _issuerPrivateKey.
*/
function issueProofWithEIP712Domain( function issueProofWithEIP712Domain(
uint256 _issuerPrivateKey, uint256 _issuerPrivateKey,
bytes memory _eip712Name, bytes memory _eip712Name,
...@@ -101,9 +87,7 @@ contract AdminFaucetAuthModuleTest is Test { ...@@ -101,9 +87,7 @@ contract AdminFaucetAuthModuleTest is Test {
); );
} }
/** /// @notice Assert that verify returns true for valid proofs signed by admins.
* @notice assert that verify returns true for valid proofs signed by admins.
*/
function test_adminProof_verify_succeeds() external { function test_adminProof_verify_succeeds() external {
bytes32 nonce = faucetHelper.consumeNonce(); bytes32 nonce = faucetHelper.consumeNonce();
address fundsReceiver = makeAddr("fundsReceiver"); address fundsReceiver = makeAddr("fundsReceiver");
...@@ -129,9 +113,7 @@ contract AdminFaucetAuthModuleTest is Test { ...@@ -129,9 +113,7 @@ contract AdminFaucetAuthModuleTest is Test {
); );
} }
/** /// @notice Assert that verify returns false for proofs signed by nonadmins.
* @notice assert that verify returns false for proofs signed by nonadmins.
*/
function test_nonAdminProof_verify_succeeds() external { function test_nonAdminProof_verify_succeeds() external {
bytes32 nonce = faucetHelper.consumeNonce(); bytes32 nonce = faucetHelper.consumeNonce();
address fundsReceiver = makeAddr("fundsReceiver"); address fundsReceiver = makeAddr("fundsReceiver");
...@@ -157,10 +139,8 @@ contract AdminFaucetAuthModuleTest is Test { ...@@ -157,10 +139,8 @@ contract AdminFaucetAuthModuleTest is Test {
); );
} }
/** /// @notice Assert that verify returns false for proofs where the id in the proof is different
* @notice assert that verify returns false for proofs where the id in the proof is different /// than the id in the call to verify.
* than the id in the call to verify.
*/
function test_proofWithWrongId_verify_succeeds() external { function test_proofWithWrongId_verify_succeeds() external {
bytes32 nonce = faucetHelper.consumeNonce(); bytes32 nonce = faucetHelper.consumeNonce();
address fundsReceiver = makeAddr("fundsReceiver"); address fundsReceiver = makeAddr("fundsReceiver");
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; 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 "./Helpers.sol"; import { TestERC20 } from "./Helpers.sol";
import { TestERC721 } from "./Helpers.sol"; import { TestERC721 } from "./Helpers.sol";
...@@ -53,12 +53,12 @@ contract AssetReceiver_Initializer is Test { ...@@ -53,12 +53,12 @@ contract AssetReceiver_Initializer is Test {
} }
contract AssetReceiverTest is AssetReceiver_Initializer { contract AssetReceiverTest is AssetReceiver_Initializer {
// Tests if the owner was set correctly during deploy /// @notice Tests if the owner was set correctly during deploy.
function test_constructor_succeeds() external { function test_constructor_succeeds() external {
assertEq(address(alice), assetReceiver.owner()); assertEq(address(alice), assetReceiver.owner());
} }
// Tests that receive works as inteded /// @notice Tests that receive works as inteded.
function test_receive_succeeds() external { function test_receive_succeeds() external {
// Check that contract balance is 0 initially // Check that contract balance is 0 initially
assertEq(address(assetReceiver).balance, 0); assertEq(address(assetReceiver).balance, 0);
...@@ -74,7 +74,8 @@ contract AssetReceiverTest is AssetReceiver_Initializer { ...@@ -74,7 +74,8 @@ contract AssetReceiverTest is AssetReceiver_Initializer {
assertEq(address(assetReceiver).balance, 100); assertEq(address(assetReceiver).balance, 100);
} }
// Tests withdrawETH function with only an address as argument, called by owner /// @notice Tests withdrawETH function with only an address
/// as an argument, called by owner.
function test_withdrawETH_succeeds() external { function test_withdrawETH_succeeds() external {
// Check contract initial balance // Check contract initial balance
assertEq(address(assetReceiver).balance, 0); assertEq(address(assetReceiver).balance, 0);
...@@ -96,14 +97,14 @@ contract AssetReceiverTest is AssetReceiver_Initializer { ...@@ -96,14 +97,14 @@ contract AssetReceiverTest is AssetReceiver_Initializer {
assertEq(address(alice).balance, 2 ether); assertEq(address(alice).balance, 2 ether);
} }
// withdrawETH should fail if called by non-owner /// @notice withdrawETH should fail if called by non-owner.
function test_withdrawETH_unauthorized_reverts() external { function test_withdrawETH_unauthorized_reverts() external {
vm.deal(address(assetReceiver), 1 ether); vm.deal(address(assetReceiver), 1 ether);
vm.expectRevert("UNAUTHORIZED"); vm.expectRevert("UNAUTHORIZED");
assetReceiver.withdrawETH(payable(alice)); assetReceiver.withdrawETH(payable(alice));
} }
// Similar as withdrawETH but specify amount to withdraw /// @notice Similar as withdrawETH but specify amount to withdraw.
function test_withdrawETHwithAmount_succeeds() external { function test_withdrawETHwithAmount_succeeds() external {
assertEq(address(assetReceiver).balance, 0); assertEq(address(assetReceiver).balance, 0);
...@@ -124,14 +125,14 @@ contract AssetReceiverTest is AssetReceiver_Initializer { ...@@ -124,14 +125,14 @@ contract AssetReceiverTest is AssetReceiver_Initializer {
assertEq(address(alice).balance, 1.5 ether); assertEq(address(alice).balance, 1.5 ether);
} }
// withdrawETH with address and amount as arguments called by non-owner /// @notice withdrawETH with address and amount as arguments called by non-owner.
function test_withdrawETHwithAmount_unauthorized_reverts() external { function test_withdrawETHwithAmount_unauthorized_reverts() external {
vm.deal(address(assetReceiver), 1 ether); vm.deal(address(assetReceiver), 1 ether);
vm.expectRevert("UNAUTHORIZED"); vm.expectRevert("UNAUTHORIZED");
assetReceiver.withdrawETH(payable(alice), 0.5 ether); assetReceiver.withdrawETH(payable(alice), 0.5 ether);
} }
// Test withdrawERC20 with token and address arguments, from owner /// @notice Test withdrawERC20 with token and address arguments, from owner.
function test_withdrawERC20_succeeds() external { function test_withdrawERC20_succeeds() external {
// check balances before the call // check balances before the call
assertEq(testERC20.balanceOf(address(assetReceiver)), 0); assertEq(testERC20.balanceOf(address(assetReceiver)), 0);
...@@ -152,14 +153,14 @@ contract AssetReceiverTest is AssetReceiver_Initializer { ...@@ -152,14 +153,14 @@ contract AssetReceiverTest is AssetReceiver_Initializer {
assertEq(testERC20.balanceOf(address(assetReceiver)), 0); assertEq(testERC20.balanceOf(address(assetReceiver)), 0);
} }
// Same as withdrawERC20 but call from non-owner /// @notice Same as withdrawERC20 but call from non-owner.
function test_withdrawERC20_unauthorized_reverts() external { function test_withdrawERC20_unauthorized_reverts() external {
deal(address(testERC20), address(assetReceiver), 100_000); deal(address(testERC20), address(assetReceiver), 100_000);
vm.expectRevert("UNAUTHORIZED"); vm.expectRevert("UNAUTHORIZED");
assetReceiver.withdrawERC20(testERC20, alice); assetReceiver.withdrawERC20(testERC20, alice);
} }
// Similar as withdrawERC20 but specify amount to withdraw /// @notice Similar as withdrawERC20 but specify amount to withdraw.
function test_withdrawERC20withAmount_succeeds() external { function test_withdrawERC20withAmount_succeeds() external {
// check balances before the call // check balances before the call
assertEq(testERC20.balanceOf(address(assetReceiver)), 0); assertEq(testERC20.balanceOf(address(assetReceiver)), 0);
...@@ -180,14 +181,14 @@ contract AssetReceiverTest is AssetReceiver_Initializer { ...@@ -180,14 +181,14 @@ contract AssetReceiverTest is AssetReceiver_Initializer {
assertEq(testERC20.balanceOf(address(assetReceiver)), 50_000); assertEq(testERC20.balanceOf(address(assetReceiver)), 50_000);
} }
// Similar as withdrawERC20 with amount but call from non-owner /// @notice Similar as withdrawERC20 with amount but call from non-owner.
function test_withdrawERC20withAmount_unauthorized_reverts() external { function test_withdrawERC20withAmount_unauthorized_reverts() external {
deal(address(testERC20), address(assetReceiver), 100_000); deal(address(testERC20), address(assetReceiver), 100_000);
vm.expectRevert("UNAUTHORIZED"); vm.expectRevert("UNAUTHORIZED");
assetReceiver.withdrawERC20(testERC20, alice, 50_000); assetReceiver.withdrawERC20(testERC20, alice, 50_000);
} }
// Test withdrawERC721 from owner /// @notice Test withdrawERC721 from owner.
function test_withdrawERC721_succeeds() external { function test_withdrawERC721_succeeds() external {
// Check owner of the token before calling withdrawERC721 // Check owner of the token before calling withdrawERC721
assertEq(testERC721.ownerOf(DEFAULT_TOKEN_ID), alice); assertEq(testERC721.ownerOf(DEFAULT_TOKEN_ID), alice);
...@@ -208,7 +209,7 @@ contract AssetReceiverTest is AssetReceiver_Initializer { ...@@ -208,7 +209,7 @@ contract AssetReceiverTest is AssetReceiver_Initializer {
assertEq(testERC721.ownerOf(DEFAULT_TOKEN_ID), alice); assertEq(testERC721.ownerOf(DEFAULT_TOKEN_ID), alice);
} }
// Similar as withdrawERC721 but call from non-owner /// @notice Similar as withdrawERC721 but call from non-owner.
function test_withdrawERC721_unauthorized_reverts() external { function test_withdrawERC721_unauthorized_reverts() external {
vm.prank(alice); vm.prank(alice);
testERC721.transferFrom(alice, address(assetReceiver), DEFAULT_TOKEN_ID); testERC721.transferFrom(alice, address(assetReceiver), DEFAULT_TOKEN_ID);
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
/* Testing utilities */ // Testing utilities
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { Vm } from "forge-std/Vm.sol"; import { Vm } from "forge-std/Vm.sol";
import "./CommonTest.t.sol"; import "./CommonTest.t.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
// Testing utilities // Testing utilities
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { CheckBalanceHigh } from "../periphery/drippie/dripchecks/CheckBalanceHigh.sol"; import { CheckBalanceHigh } from "../periphery/drippie/dripchecks/CheckBalanceHigh.sol";
/** /// @title CheckBalanceHighTest
* @title CheckBalanceHighTest /// @notice Tests the CheckBalanceHigh contract via fuzzing both the success case
* @notice Tests the CheckBalanceHigh contract via fuzzing both the success case /// and the failure case.
* and the failure case.
*/
contract CheckBalanceHighTest is Test { contract CheckBalanceHighTest is Test {
/** /// @notice An instance of the CheckBalanceHigh contract.
* @notice An instance of the CheckBalanceHigh contract.
*/
CheckBalanceHigh c; CheckBalanceHigh c;
/** /// @notice Deploy the `CheckTrue` contract.
* @notice Deploy the `CheckTrue` contract.
*/
function setUp() external { function setUp() external {
c = new CheckBalanceHigh(); c = new CheckBalanceHigh();
} }
/** /// @notice Fuzz the `check` function and assert that it always returns true
* @notice Fuzz the `check` function and assert that it always returns true /// when the target's balance is larger than the threshold.
* when the target's balance is larger than the threshold.
*/
function testFuzz_check_succeeds(address _target, uint256 _threshold) external { function testFuzz_check_succeeds(address _target, uint256 _threshold) external {
CheckBalanceHigh.Params memory p = CheckBalanceHigh.Params({ CheckBalanceHigh.Params memory p = CheckBalanceHigh.Params({
target: _target, target: _target,
...@@ -39,10 +31,8 @@ contract CheckBalanceHighTest is Test { ...@@ -39,10 +31,8 @@ contract CheckBalanceHighTest is Test {
assertEq(c.check(abi.encode(p)), true); assertEq(c.check(abi.encode(p)), true);
} }
/** /// @notice Fuzz the `check` function and assert that it always returns false
* @notice Fuzz the `check` function and assert that it always returns false /// when the target's balance is smaller than the threshold.
* when the target's balance is smaller than the threshold.
*/
function testFuzz_check_lowBalance_fails(address _target, uint256 _threshold) external { function testFuzz_check_lowBalance_fails(address _target, uint256 _threshold) external {
CheckBalanceHigh.Params memory p = CheckBalanceHigh.Params({ CheckBalanceHigh.Params memory p = CheckBalanceHigh.Params({
target: _target, target: _target,
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { CheckBalanceLow } from "../periphery/drippie/dripchecks/CheckBalanceLow.sol"; import { CheckBalanceLow } from "../periphery/drippie/dripchecks/CheckBalanceLow.sol";
/** /// @title CheckBalanceLowTest
* @title CheckBalanceLowTest /// @notice Tests the CheckBalanceLow contract via fuzzing both the success case
* @notice Tests the CheckBalanceLow contract via fuzzing both the success case /// and the failure case.
* and the failure case.
*/
contract CheckBalanceLowTest is Test { contract CheckBalanceLowTest is Test {
/** /// @notice An instance of the CheckBalanceLow contract.
* @notice An instance of the CheckBalanceLow contract.
*/
CheckBalanceLow c; CheckBalanceLow c;
/** /// @notice Deploy the `CheckBalanceLow` contract.
* @notice Deploy the `CheckBalanceLow` contract.
*/
function setUp() external { function setUp() external {
c = new CheckBalanceLow(); c = new CheckBalanceLow();
} }
/** /// @notice Fuzz the `check` function and assert that it always returns true
* @notice Fuzz the `check` function and assert that it always returns true /// when the target's balance is smaller than the threshold.
* when the target's balance is smaller than the threshold.
*/
function testFuzz_check_succeeds(address _target, uint256 _threshold) external { function testFuzz_check_succeeds(address _target, uint256 _threshold) external {
CheckBalanceLow.Params memory p = CheckBalanceLow.Params({ CheckBalanceLow.Params memory p = CheckBalanceLow.Params({
target: _target, target: _target,
...@@ -37,10 +29,8 @@ contract CheckBalanceLowTest is Test { ...@@ -37,10 +29,8 @@ contract CheckBalanceLowTest is Test {
assertEq(c.check(abi.encode(p)), true); assertEq(c.check(abi.encode(p)), true);
} }
/** /// @notice Fuzz the `check` function and assert that it always returns false
* @notice Fuzz the `check` function and assert that it always returns false /// when the target's balance is larger than the threshold.
* when the target's balance is larger than the threshold.
*/
function testFuzz_check_highBalance_fails(address _target, uint256 _threshold) external { function testFuzz_check_highBalance_fails(address _target, uint256 _threshold) external {
CheckBalanceLow.Params memory p = CheckBalanceLow.Params({ CheckBalanceLow.Params memory p = CheckBalanceLow.Params({
target: _target, target: _target,
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
...@@ -7,11 +7,9 @@ import { ...@@ -7,11 +7,9 @@ import {
IGelatoTreasury IGelatoTreasury
} from "../periphery/drippie/dripchecks/CheckGelatoLow.sol"; } from "../periphery/drippie/dripchecks/CheckGelatoLow.sol";
/** /// @title MockGelatoTreasury
* @title MockGelatoTreasury /// @notice Mocks the Gelato treasury for testing purposes. Allows arbitrary
* @notice Mocks the Gelato treasury for testing purposes. Allows arbitrary /// setting of user balances.
* setting of user balances.
*/
contract MockGelatoTreasury is IGelatoTreasury { contract MockGelatoTreasury is IGelatoTreasury {
mapping(address => mapping(address => uint256)) private tokenBalances; mapping(address => mapping(address => uint256)) private tokenBalances;
...@@ -28,39 +26,27 @@ contract MockGelatoTreasury is IGelatoTreasury { ...@@ -28,39 +26,27 @@ contract MockGelatoTreasury is IGelatoTreasury {
} }
} }
/** /// @title CheckGelatoLowTest
* @title CheckGelatoLowTest /// @notice Tests the CheckBalanceHigh contract via fuzzing both the success case
* @notice Tests the CheckBalanceHigh 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.
*/
CheckGelatoLow c; CheckGelatoLow c;
/** /// @notice An instance of the MockGelatoTreasury contract.
* @notice An instance of the MockGelatoTreasury contract.
*/
MockGelatoTreasury gelato; MockGelatoTreasury gelato;
/** /// @notice The account Gelato uses to represent ether
* @notice The account Gelato uses to represent ether
*/
address internal constant eth = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; address internal constant eth = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/** /// @notice Deploy the `CheckGelatoLow` and `MockGelatoTreasury` contracts.
* @notice Deploy the `CheckGelatoLow` and `MockGelatoTreasury` contracts.
*/
function setUp() external { function setUp() external {
c = new CheckGelatoLow(); c = new CheckGelatoLow();
gelato = new MockGelatoTreasury(); gelato = new MockGelatoTreasury();
} }
/** /// @notice Fuzz the `check` function and assert that it always returns true
* @notice Fuzz the `check` function and assert that it always returns true /// when the user's balance in the treasury is less than the threshold.
* when the user's balance in the treasury is less than the threshold.
*/
function testFuzz_check_succeeds(uint256 _threshold, address _recipient) external { function testFuzz_check_succeeds(uint256 _threshold, address _recipient) external {
CheckGelatoLow.Params memory p = CheckGelatoLow.Params({ CheckGelatoLow.Params memory p = CheckGelatoLow.Params({
treasury: address(gelato), treasury: address(gelato),
...@@ -73,11 +59,9 @@ contract CheckGelatoLowTest is Test { ...@@ -73,11 +59,9 @@ contract CheckGelatoLowTest is Test {
assertEq(c.check(abi.encode(p)), true); assertEq(c.check(abi.encode(p)), true);
} }
/** /// @notice Fuzz the `check` function and assert that it always returns false
* @notice Fuzz the `check` function and assert that it always returns false /// when the user's balance in the treasury is greater than or equal
* when the user's balance in the treasury is greater than or equal /// to the threshold.
* to the threshold.
*/
function testFuzz_check_highBalance_fails(uint256 _threshold, address _recipient) external { function testFuzz_check_highBalance_fails(uint256 _threshold, address _recipient) external {
CheckGelatoLow.Params memory p = CheckGelatoLow.Params({ CheckGelatoLow.Params memory p = CheckGelatoLow.Params({
treasury: address(gelato), treasury: address(gelato),
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { CheckTrue } from "../periphery/drippie/dripchecks/CheckTrue.sol"; import { CheckTrue } from "../periphery/drippie/dripchecks/CheckTrue.sol";
/** /// @title CheckTrueTest
* @title CheckTrueTest /// @notice Ensures that the CheckTrue DripCheck contract always returns true.
* @notice Ensures that the CheckTrue DripCheck contract always returns true.
*/
contract CheckTrueTest is Test { contract CheckTrueTest is Test {
/** /// @notice An instance of the CheckTrue contract.
* @notice An instance of the CheckTrue contract.
*/
CheckTrue c; CheckTrue c;
/** /// @notice Deploy the `CheckTrue` contract.
* @notice Deploy the `CheckTrue` contract.
*/
function setUp() external { function setUp() external {
c = new CheckTrue(); c = new CheckTrue();
} }
/** /// @notice Fuzz the `check` function and assert that it always returns true.
* @notice Fuzz the `check` function and assert that it always returns true.
*/
function testFuzz_always_true_succeeds(bytes memory input) external { function testFuzz_always_true_succeeds(bytes memory input) external {
assertEq(c.check(input), true); assertEq(c.check(input), true);
} }
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
/* Testing utilities */ // Testing utilities
import { Test, StdUtils } from "forge-std/Test.sol"; import { Test, StdUtils } from "forge-std/Test.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol"; import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol"; import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
...@@ -766,9 +766,7 @@ contract ConfigurableCaller { ...@@ -766,9 +766,7 @@ contract ConfigurableCaller {
event WhatHappened(bool success, bytes returndata); event WhatHappened(bool success, bytes returndata);
/** /// @notice Call the configured target with the configured payload OR revert.
* @notice Call the configured target with the configured payload OR revert.
*/
function call() external { function call() external {
if (doRevert) { if (doRevert) {
revert("ConfigurableCaller: revert"); revert("ConfigurableCaller: revert");
...@@ -787,31 +785,23 @@ contract ConfigurableCaller { ...@@ -787,31 +785,23 @@ contract ConfigurableCaller {
} }
} }
/** /// @notice Set whether or not to have `call` revert.
* @notice Set whether or not to have `call` revert.
*/
function setDoRevert(bool _doRevert) external { function setDoRevert(bool _doRevert) external {
doRevert = _doRevert; doRevert = _doRevert;
} }
/** /// @notice Set the target for the call made in `call`.
* @notice Set the target for the call made in `call`.
*/
function setTarget(address _target) external { function setTarget(address _target) external {
target = _target; target = _target;
} }
/** /// @notice Set the payload for the call made in `call`.
* @notice Set the payload for the call made in `call`.
*/
function setPayload(bytes calldata _payload) external { function setPayload(bytes calldata _payload) external {
payload = _payload; payload = _payload;
} }
/** /// @notice Fallback function that reverts if `doRevert` is true.
* @notice Fallback function that reverts if `doRevert` is true. /// Otherwise, it does nothing.
* Otherwise, it does nothing.
*/
fallback() external { fallback() external {
if (doRevert) { if (doRevert) {
revert("ConfigurableCaller: revert"); revert("ConfigurableCaller: revert");
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
...@@ -45,9 +45,7 @@ contract Faucet_Initializer is Test { ...@@ -45,9 +45,7 @@ contract Faucet_Initializer is Test {
_initializeContracts(); _initializeContracts();
} }
/** /// @notice Instantiates a Faucet.
* @notice Instantiates a Faucet.
*/
function _initializeContracts() internal { function _initializeContracts() internal {
faucet = new Faucet(faucetContractAdmin); faucet = new Faucet(faucetContractAdmin);
...@@ -76,10 +74,7 @@ contract Faucet_Initializer is Test { ...@@ -76,10 +74,7 @@ contract Faucet_Initializer is Test {
vm.stopPrank(); vm.stopPrank();
} }
/** /// @notice Get signature as a bytes blob.
* @notice Get signature as a bytes blob.
*
*/
function _getSignature(uint256 _signingPrivateKey, bytes32 _digest) function _getSignature(uint256 _signingPrivateKey, bytes32 _digest)
internal internal
pure pure
...@@ -91,11 +86,9 @@ contract Faucet_Initializer is Test { ...@@ -91,11 +86,9 @@ contract Faucet_Initializer is Test {
return signature; return signature;
} }
/** /// @notice Signs a proof with the given private key and returns the signature using
* @notice Signs a proof with the given private key and returns the signature using /// the given EIP712 domain separator. This assumes that the issuer's address is the
* the given EIP712 domain separator. This assumes that the issuer's address is the /// corresponding public key to _issuerPrivateKey.
* corresponding public key to _issuerPrivateKey.
*/
function issueProofWithEIP712Domain( function issueProofWithEIP712Domain(
uint256 _issuerPrivateKey, uint256 _issuerPrivateKey,
bytes memory _eip712Name, bytes memory _eip712Name,
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
/* Testing utilities */ // Testing utilities
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { AttestationStation } from "../periphery/op-nft/AttestationStation.sol"; import { AttestationStation } from "../periphery/op-nft/AttestationStation.sol";
import { OptimistAllowlist } from "../periphery/op-nft/OptimistAllowlist.sol"; import { OptimistAllowlist } from "../periphery/op-nft/OptimistAllowlist.sol";
...@@ -106,10 +106,7 @@ contract OptimistAllowlist_Initializer is Test { ...@@ -106,10 +106,7 @@ contract OptimistAllowlist_Initializer is Test {
optimistInviter.claimInvite(claimer, claimableInvite, signature); optimistInviter.claimInvite(claimer, claimableInvite, signature);
} }
/** /// @notice Get signature as a bytes blob, since SignatureChecker takes arbitrary signature blobs.
* @notice Get signature as a bytes blob, since SignatureChecker takes arbitrary signature blobs.
*
*/
function _getSignature(uint256 _signingPrivateKey, bytes32 _digest) function _getSignature(uint256 _signingPrivateKey, bytes32 _digest)
internal internal
pure pure
...@@ -149,42 +146,32 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer { ...@@ -149,42 +146,32 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer {
assertEq(optimistAllowlist.version(), "1.0.0"); assertEq(optimistAllowlist.version(), "1.0.0");
} }
/** /// @notice Base case, a account without any relevant attestations should not be able to mint.
* @notice Base case, a account without any relevant attestations should not be able to mint.
*/
function test_isAllowedToMint_withoutAnyAttestations_fails() external { function test_isAllowedToMint_withoutAnyAttestations_fails() external {
assertFalse(optimistAllowlist.isAllowedToMint(bob)); assertFalse(optimistAllowlist.isAllowedToMint(bob));
} }
/** /// @notice After receiving a valid allowlist attestation, the account should be able to mint.
* @notice After receiving a valid allowlist attestation, the account should be able to mint.
*/
function test_isAllowedToMint_fromAllowlistAttestor_succeeds() external { function test_isAllowedToMint_fromAllowlistAttestor_succeeds() external {
attestAllowlist(bob); attestAllowlist(bob);
assertTrue(optimistAllowlist.isAllowedToMint(bob)); assertTrue(optimistAllowlist.isAllowedToMint(bob));
} }
/** /// @notice After receiving a valid attestation from the Coinbase Quest attestor,
* @notice After receiving a valid attestation from the Coinbase Quest attestor, /// the account should be able to mint.
* the account should be able to mint.
*/
function test_isAllowedToMint_fromCoinbaseQuestAttestor_succeeds() external { function test_isAllowedToMint_fromCoinbaseQuestAttestor_succeeds() external {
attestCoinbaseQuest(bob); attestCoinbaseQuest(bob);
assertTrue(optimistAllowlist.isAllowedToMint(bob)); assertTrue(optimistAllowlist.isAllowedToMint(bob));
} }
/** /// @notice Account that received an attestation from the OptimistInviter contract by going
* @notice Account that received an attestation from the OptimistInviter contract by going /// through the claim invite flow should be able to mint.
* through the claim invite flow should be able to mint.
*/
function test_isAllowedToMint_fromInvite_succeeds() external { function test_isAllowedToMint_fromInvite_succeeds() external {
inviteAndClaim(bob); inviteAndClaim(bob);
assertTrue(optimistAllowlist.isAllowedToMint(bob)); assertTrue(optimistAllowlist.isAllowedToMint(bob));
} }
/** /// @notice Attestation from the wrong allowlist attestor should not allow minting.
* @notice Attestation from the wrong allowlist attestor should not allow minting.
*/
function test_isAllowedToMint_fromWrongAllowlistAttestor_fails() external { function test_isAllowedToMint_fromWrongAllowlistAttestor_fails() external {
// Ted is not the allowlist attestor // Ted is not the allowlist attestor
vm.prank(ted); vm.prank(ted);
...@@ -196,9 +183,7 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer { ...@@ -196,9 +183,7 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer {
assertFalse(optimistAllowlist.isAllowedToMint(bob)); assertFalse(optimistAllowlist.isAllowedToMint(bob));
} }
/** /// @notice Coinbase quest attestation from wrong attestor should not allow minting.
* @notice Coinbase quest attestation from wrong attestor should not allow minting.
*/
function test_isAllowedToMint_fromWrongCoinbaseQuestAttestor_fails() external { function test_isAllowedToMint_fromWrongCoinbaseQuestAttestor_fails() external {
// Ted is not the coinbase quest attestor // Ted is not the coinbase quest attestor
vm.prank(ted); vm.prank(ted);
...@@ -210,10 +195,8 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer { ...@@ -210,10 +195,8 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer {
assertFalse(optimistAllowlist.isAllowedToMint(bob)); assertFalse(optimistAllowlist.isAllowedToMint(bob));
} }
/** /// @notice Claiming an invite on the non-official OptimistInviter contract should not allow
* @notice Claiming an invite on the non-official OptimistInviter contract should not allow /// minting.
* minting.
*/
function test_isAllowedToMint_fromWrongOptimistInviter_fails() external { function test_isAllowedToMint_fromWrongOptimistInviter_fails() external {
vm.prank(ted); vm.prank(ted);
attestationStation.attest( attestationStation.attest(
...@@ -224,9 +207,7 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer { ...@@ -224,9 +207,7 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer {
assertFalse(optimistAllowlist.isAllowedToMint(bob)); assertFalse(optimistAllowlist.isAllowedToMint(bob));
} }
/** /// @notice Having multiple signals, even if one is invalid, should still allow minting.
* @notice Having multiple signals, even if one is invalid, should still allow minting.
*/
function test_isAllowedToMint_withMultipleAttestations_succeeds() external { function test_isAllowedToMint_withMultipleAttestations_succeeds() external {
attestAllowlist(bob); attestAllowlist(bob);
attestCoinbaseQuest(bob); attestCoinbaseQuest(bob);
...@@ -246,9 +227,7 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer { ...@@ -246,9 +227,7 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer {
assertTrue(optimistAllowlist.isAllowedToMint(bob)); assertTrue(optimistAllowlist.isAllowedToMint(bob));
} }
/** /// @notice Having falsy attestation value should not allow minting.
* @notice Having falsy attestation value should not allow minting.
*/
function test_isAllowedToMint_fromAllowlistAttestorWithFalsyValue_fails() external { function test_isAllowedToMint_fromAllowlistAttestorWithFalsyValue_fails() external {
// First sends correct attestation // First sends correct attestation
attestAllowlist(bob); attestAllowlist(bob);
...@@ -264,9 +243,7 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer { ...@@ -264,9 +243,7 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer {
assertFalse(optimistAllowlist.isAllowedToMint(bob)); assertFalse(optimistAllowlist.isAllowedToMint(bob));
} }
/** /// @notice Having falsy attestation value from Coinbase attestor should not allow minting.
* @notice Having falsy attestation value from Coinbase attestor should not allow minting.
*/
function test_isAllowedToMint_fromCoinbaseQuestAttestorWithFalsyValue_fails() external { function test_isAllowedToMint_fromCoinbaseQuestAttestorWithFalsyValue_fails() external {
// First sends correct attestation // First sends correct attestation
attestAllowlist(bob); attestAllowlist(bob);
......
...@@ -3,10 +3,8 @@ pragma solidity ^0.8.0; ...@@ -3,10 +3,8 @@ pragma solidity ^0.8.0;
import { Bytes32AddressLib } from "@rari-capital/solmate/src/utils/Bytes32AddressLib.sol"; import { Bytes32AddressLib } from "@rari-capital/solmate/src/utils/Bytes32AddressLib.sol";
/** /// @title LibRLP
* @title LibRLP /// @notice Via https://github.com/Rari-Capital/solmate/issues/207.
* @notice Via https://github.com/Rari-Capital/solmate/issues/207.
*/
library LibRLP { library LibRLP {
using Bytes32AddressLib for bytes32; using Bytes32AddressLib for bytes32;
......
# `AddressAliasHelper` Invariants # `AddressAliasHelper` Invariants
## Address aliases are always able to be undone. ## Address aliases are always able to be undone.
**Test:** [`AddressAliasHelper.t.sol#L48`](../contracts/test/invariants/AddressAliasHelper.t.sol#L48) **Test:** [`AddressAliasHelper.t.sol#L46`](../contracts/test/invariants/AddressAliasHelper.t.sol#L46)
Asserts that an address that has been aliased with `applyL1ToL2Alias` can always be unaliased with `undoL1ToL2Alias`. Asserts that an address that has been aliased with `applyL1ToL2Alias` can always be unaliased with `undoL1ToL2Alias`.
\ No newline at end of file
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