diff --git a/op-challenger/game/fault/agent.go b/op-challenger/game/fault/agent.go
index e5b9cc73542d7932ad27b132a1e6ca3630b104b1..f2ae73f489ea69a45d6778a1708d34a83975643e 100644
--- a/op-challenger/game/fault/agent.go
+++ b/op-challenger/game/fault/agent.go
@@ -7,6 +7,7 @@ import (
 
 	"github.com/ethereum-optimism/optimism/op-challenger/game/fault/solver"
 	"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
+	"github.com/ethereum-optimism/optimism/op-challenger/metrics"
 	"github.com/ethereum/go-ethereum/log"
 )
 
@@ -24,6 +25,7 @@ type ClaimLoader interface {
 }
 
 type Agent struct {
+	metrics                 metrics.Metricer
 	solver                  *solver.Solver
 	loader                  ClaimLoader
 	responder               Responder
@@ -33,8 +35,9 @@ type Agent struct {
 	log                     log.Logger
 }
 
-func NewAgent(loader ClaimLoader, maxDepth int, trace types.TraceProvider, responder Responder, updater types.OracleUpdater, agreeWithProposedOutput bool, log log.Logger) *Agent {
+func NewAgent(m metrics.Metricer, loader ClaimLoader, maxDepth int, trace types.TraceProvider, responder Responder, updater types.OracleUpdater, agreeWithProposedOutput bool, log log.Logger) *Agent {
 	return &Agent{
+		metrics:                 m,
 		solver:                  solver.NewSolver(maxDepth, trace),
 		loader:                  loader,
 		responder:               responder,
@@ -134,6 +137,7 @@ func (a *Agent) move(ctx context.Context, claim types.Claim, game types.Game) er
 		log.Debug("Skipping duplicate move")
 		return nil
 	}
+	a.metrics.RecordGameMove()
 	log.Info("Performing move")
 	return a.responder.Respond(ctx, move)
 }
@@ -170,6 +174,7 @@ func (a *Agent) step(ctx context.Context, claim types.Claim, game types.Game) er
 
 	a.log.Info("Performing step", "is_attack", step.IsAttack,
 		"depth", step.LeafClaim.Depth(), "index_at_depth", step.LeafClaim.IndexAtDepth(), "value", step.LeafClaim.Value)
+	a.metrics.RecordGameStep()
 	callData := types.StepCallData{
 		ClaimIndex: uint64(step.LeafClaim.ContractIndex),
 		IsAttack:   step.IsAttack,
diff --git a/op-challenger/game/fault/agent_test.go b/op-challenger/game/fault/agent_test.go
index dd18a9c7a2b7d840fdb2c6f75de5176c6810c368..117a3c7dc9b9fd0b72d274bc7413ba3d121957bd 100644
--- a/op-challenger/game/fault/agent_test.go
+++ b/op-challenger/game/fault/agent_test.go
@@ -5,6 +5,7 @@ import (
 	"testing"
 
 	"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
+	"github.com/ethereum-optimism/optimism/op-challenger/metrics"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/stretchr/testify/require"
 
@@ -16,14 +17,14 @@ func TestShouldResolve(t *testing.T) {
 	log := testlog.Logger(t, log.LvlCrit)
 
 	t.Run("AgreeWithProposedOutput", func(t *testing.T) {
-		agent := NewAgent(nil, 0, nil, nil, nil, true, log)
+		agent := NewAgent(metrics.NoopMetrics, nil, 0, nil, nil, nil, true, log)
 		require.False(t, agent.shouldResolve(context.Background(), types.GameStatusDefenderWon))
 		require.True(t, agent.shouldResolve(context.Background(), types.GameStatusChallengerWon))
 		require.False(t, agent.shouldResolve(context.Background(), types.GameStatusInProgress))
 	})
 
 	t.Run("DisagreeWithProposedOutput", func(t *testing.T) {
-		agent := NewAgent(nil, 0, nil, nil, nil, false, log)
+		agent := NewAgent(metrics.NoopMetrics, nil, 0, nil, nil, nil, false, log)
 		require.True(t, agent.shouldResolve(context.Background(), types.GameStatusDefenderWon))
 		require.False(t, agent.shouldResolve(context.Background(), types.GameStatusChallengerWon))
 		require.False(t, agent.shouldResolve(context.Background(), types.GameStatusInProgress))
diff --git a/op-challenger/game/fault/player.go b/op-challenger/game/fault/player.go
index b8904528bb108af60c9fe04bf4b28948ca545d73..d41100098296287a6b01c41b8a9feb8b54b38d60 100644
--- a/op-challenger/game/fault/player.go
+++ b/op-challenger/game/fault/player.go
@@ -11,6 +11,7 @@ import (
 	"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/alphabet"
 	"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/cannon"
 	"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
+	"github.com/ethereum-optimism/optimism/op-challenger/metrics"
 	"github.com/ethereum-optimism/optimism/op-service/txmgr"
 	"github.com/ethereum/go-ethereum/accounts/abi/bind"
 	"github.com/ethereum/go-ethereum/common"
@@ -37,6 +38,7 @@ type GamePlayer struct {
 func NewGamePlayer(
 	ctx context.Context,
 	logger log.Logger,
+	m metrics.Metricer,
 	cfg *config.Config,
 	dir string,
 	addr common.Address,
@@ -105,7 +107,7 @@ func NewGamePlayer(
 	}
 
 	return &GamePlayer{
-		act:                     NewAgent(loader, int(gameDepth), provider, responder, updater, cfg.AgreeWithProposedOutput, logger).Act,
+		act:                     NewAgent(m, loader, int(gameDepth), provider, responder, updater, cfg.AgreeWithProposedOutput, logger).Act,
 		agreeWithProposedOutput: cfg.AgreeWithProposedOutput,
 		loader:                  loader,
 		logger:                  logger,
diff --git a/op-challenger/game/monitor.go b/op-challenger/game/monitor.go
index 6f205fab39d74e86ddc4dd4961b83f4169a62daa..303b546080dbfc8153392a9f37faa957e361b006 100644
--- a/op-challenger/game/monitor.go
+++ b/op-challenger/game/monitor.go
@@ -8,6 +8,7 @@ import (
 	"time"
 
 	"github.com/ethereum-optimism/optimism/op-challenger/game/scheduler"
+	"github.com/ethereum-optimism/optimism/op-challenger/metrics"
 	"github.com/ethereum-optimism/optimism/op-service/clock"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/log"
@@ -26,6 +27,7 @@ type gameScheduler interface {
 
 type gameMonitor struct {
 	logger           log.Logger
+	metrics          metrics.Metricer
 	clock            clock.Clock
 	source           gameSource
 	scheduler        gameScheduler
@@ -36,6 +38,7 @@ type gameMonitor struct {
 
 func newGameMonitor(
 	logger log.Logger,
+	m metrics.Metricer,
 	cl clock.Clock,
 	source gameSource,
 	scheduler gameScheduler,
@@ -45,6 +48,7 @@ func newGameMonitor(
 ) *gameMonitor {
 	return &gameMonitor{
 		logger:           logger,
+		metrics:          m,
 		clock:            cl,
 		scheduler:        scheduler,
 		source:           source,
diff --git a/op-challenger/game/monitor_test.go b/op-challenger/game/monitor_test.go
index 732554c0b85f68660c6b6181408638e2128c21dc..105f8f73389e026c01002441cdaae4dfa80ccae3 100644
--- a/op-challenger/game/monitor_test.go
+++ b/op-challenger/game/monitor_test.go
@@ -6,6 +6,7 @@ import (
 	"testing"
 	"time"
 
+	"github.com/ethereum-optimism/optimism/op-challenger/metrics"
 	"github.com/ethereum-optimism/optimism/op-node/testlog"
 	"github.com/ethereum-optimism/optimism/op-service/clock"
 	"github.com/ethereum/go-ethereum/common"
@@ -100,7 +101,7 @@ func setupMonitorTest(t *testing.T, allowedGames []common.Address) (*gameMonitor
 		return i, nil
 	}
 	sched := &stubScheduler{}
-	monitor := newGameMonitor(logger, clock.SystemClock, source, sched, time.Duration(0), fetchBlockNum, allowedGames)
+	monitor := newGameMonitor(logger, metrics.NoopMetrics, clock.SystemClock, source, sched, time.Duration(0), fetchBlockNum, allowedGames)
 	return monitor, source, sched
 }
 
diff --git a/op-challenger/game/service.go b/op-challenger/game/service.go
index dc12e27dc33da8029b6f08d126527d8945dd8048..d176a559c7133ca42de1628dbf1b831e2813110a 100644
--- a/op-challenger/game/service.go
+++ b/op-challenger/game/service.go
@@ -72,10 +72,10 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*Se
 		disk,
 		cfg.MaxConcurrency,
 		func(addr common.Address, dir string) (scheduler.GamePlayer, error) {
-			return fault.NewGamePlayer(ctx, logger, cfg, dir, addr, txMgr, client)
+			return fault.NewGamePlayer(ctx, logger, m, cfg, dir, addr, txMgr, client)
 		})
 
-	monitor := newGameMonitor(logger, cl, loader, sched, cfg.GameWindow, client.BlockNumber, cfg.GameAllowlist)
+	monitor := newGameMonitor(logger, m, cl, loader, sched, cfg.GameWindow, client.BlockNumber, cfg.GameAllowlist)
 
 	m.RecordInfo(version.SimpleWithMeta)
 	m.RecordUp()
diff --git a/op-challenger/metrics/metrics.go b/op-challenger/metrics/metrics.go
index cefc488eab35a91c28ff84cfd678f3975d5ac33b..e4f6c922bebc2aa18e6f7991918d9d6eecb2880e 100644
--- a/op-challenger/metrics/metrics.go
+++ b/op-challenger/metrics/metrics.go
@@ -20,6 +20,9 @@ type Metricer interface {
 
 	// Record Tx metrics
 	txmetrics.TxMetricer
+
+	RecordGameStep()
+	RecordGameMove()
 }
 
 type Metrics struct {
@@ -31,6 +34,9 @@ type Metrics struct {
 
 	info prometheus.GaugeVec
 	up   prometheus.Gauge
+
+	moves prometheus.Counter
+	steps prometheus.Counter
 }
 
 var _ Metricer = (*Metrics)(nil)
@@ -58,6 +64,16 @@ func NewMetrics() *Metrics {
 			Name:      "up",
 			Help:      "1 if the op-challenger has finished starting up",
 		}),
+		moves: factory.NewCounter(prometheus.CounterOpts{
+			Namespace: Namespace,
+			Name:      "moves",
+			Help:      "Number of game moves made by the challenge agent",
+		}),
+		steps: factory.NewCounter(prometheus.CounterOpts{
+			Namespace: Namespace,
+			Name:      "steps",
+			Help:      "Number of game steps made by the challenge agent",
+		}),
 	}
 }
 
@@ -84,3 +100,11 @@ func (m *Metrics) RecordUp() {
 func (m *Metrics) Document() []opmetrics.DocumentedMetric {
 	return m.factory.Document()
 }
+
+func (m *Metrics) RecordGameMove() {
+	m.moves.Add(1)
+}
+
+func (m *Metrics) RecordGameStep() {
+	m.steps.Add(1)
+}
diff --git a/op-challenger/metrics/noop.go b/op-challenger/metrics/noop.go
index 052d288092e85d20e97fe0d3e8200ed39f5742d9..33ecdf3c8ed183b2b50a3d80b878248ad0bd4fc4 100644
--- a/op-challenger/metrics/noop.go
+++ b/op-challenger/metrics/noop.go
@@ -12,3 +12,5 @@ var NoopMetrics Metricer = new(noopMetrics)
 
 func (*noopMetrics) RecordInfo(version string) {}
 func (*noopMetrics) RecordUp()                 {}
+func (*noopMetrics) RecordGameMove()           {}
+func (*noopMetrics) RecordGameStep()           {}