From 0bfb03cc36461e17b286338e9a5bea2a68548da2 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 2 Jan 2025 14:01:14 +0800 Subject: [PATCH 1/6] feat(taiko-client): introduce `TxBuilderWithFallback` --- packages/taiko-client/cmd/flags/proposer.go | 8 + packages/taiko-client/pkg/rpc/methods.go | 2 +- packages/taiko-client/proposer/config.go | 2 + packages/taiko-client/proposer/proposer.go | 44 ++--- .../proposer/transaction_builder/fallback.go | 164 ++++++++++++++++++ 5 files changed, 190 insertions(+), 30 deletions(-) create mode 100644 packages/taiko-client/proposer/transaction_builder/fallback.go diff --git a/packages/taiko-client/cmd/flags/proposer.go b/packages/taiko-client/cmd/flags/proposer.go index fef22d7a1fb..63b0934cb00 100644 --- a/packages/taiko-client/cmd/flags/proposer.go +++ b/packages/taiko-client/cmd/flags/proposer.go @@ -105,6 +105,13 @@ var ( Value: false, EnvVars: []string{"L1_BLOB_ALLOWED"}, } + FallbackToCalldata = &cli.BoolFlag{ + Name: "l1.fallbackToCalldata", + Usage: "If set to true, proposer will use calldata as DA when blob fee is more expensive than using calldata", + Value: false, + Category: proposerCategory, + EnvVars: []string{"L1_FALLBACK_TO_CALLDATA"}, + } RevertProtectionEnabled = &cli.BoolFlag{ Name: "revertProtection", Usage: "Enable revert protection with the support of endpoint and contract", @@ -133,5 +140,6 @@ var ProposerFlags = MergeFlags(CommonFlags, []cli.Flag{ AllowZeroInterval, MaxProposedTxListsPerEpoch, BlobAllowed, + FallbackToCalldata, RevertProtectionEnabled, }, TxmgrFlags) diff --git a/packages/taiko-client/pkg/rpc/methods.go b/packages/taiko-client/pkg/rpc/methods.go index cc27518fef6..b9a5e04b547 100644 --- a/packages/taiko-client/pkg/rpc/methods.go +++ b/packages/taiko-client/pkg/rpc/methods.go @@ -642,7 +642,7 @@ func (c *Client) checkSyncedL1SnippetFromAnchor( blockID *big.Int, l1Height uint64, ) (bool, error) { - log.Info("Check synced L1 snippet from anchor", "blockID", blockID, "l1Height", l1Height) + log.Debug("Check synced L1 snippet from anchor", "blockID", blockID, "l1Height", l1Height) block, err := c.L2.BlockByNumber(ctx, blockID) if err != nil { return false, err diff --git a/packages/taiko-client/proposer/config.go b/packages/taiko-client/proposer/config.go index 72b755fe4be..74f77151f45 100644 --- a/packages/taiko-client/proposer/config.go +++ b/packages/taiko-client/proposer/config.go @@ -35,6 +35,7 @@ type Config struct { MaxProposedTxListsPerEpoch uint64 ProposeBlockTxGasLimit uint64 BlobAllowed bool + FallbackToCalldata bool RevertProtectionEnabled bool TxmgrConfigs *txmgr.CLIConfig PrivateTxmgrConfigs *txmgr.CLIConfig @@ -104,6 +105,7 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) { AllowZeroInterval: c.Uint64(flags.AllowZeroInterval.Name), ProposeBlockTxGasLimit: c.Uint64(flags.TxGasLimit.Name), BlobAllowed: c.Bool(flags.BlobAllowed.Name), + FallbackToCalldata: c.Bool(flags.FallbackToCalldata.Name), RevertProtectionEnabled: c.Bool(flags.RevertProtectionEnabled.Name), TxmgrConfigs: pkgFlags.InitTxmgrConfigsFromCli( c.String(flags.L1WSEndpoint.Name), diff --git a/packages/taiko-client/proposer/proposer.go b/packages/taiko-client/proposer/proposer.go index 35c2dfc5a3b..db2914cd5e9 100644 --- a/packages/taiko-client/proposer/proposer.go +++ b/packages/taiko-client/proposer/proposer.go @@ -114,35 +114,21 @@ func (p *Proposer) InitFromConfig( } p.txmgrSelector = utils.NewTxMgrSelector(txMgr, privateTxMgr, nil) - - chainConfig := config.NewChainConfig(p.protocolConfigs) - p.chainConfig = chainConfig - - if cfg.BlobAllowed { - p.txBuilder = builder.NewBlobTransactionBuilder( - p.rpc, - p.L1ProposerPrivKey, - cfg.TaikoL1Address, - cfg.ProverSetAddress, - cfg.L2SuggestedFeeRecipient, - cfg.ProposeBlockTxGasLimit, - cfg.ExtraData, - chainConfig, - cfg.RevertProtectionEnabled, - ) - } else { - p.txBuilder = builder.NewCalldataTransactionBuilder( - p.rpc, - p.L1ProposerPrivKey, - cfg.L2SuggestedFeeRecipient, - cfg.TaikoL1Address, - cfg.ProverSetAddress, - cfg.ProposeBlockTxGasLimit, - cfg.ExtraData, - chainConfig, - cfg.RevertProtectionEnabled, - ) - } + p.chainConfig = config.NewChainConfig(p.protocolConfigs) + p.txBuilder = builder.NewBuilderWithFallback( + p.rpc, + p.L1ProposerPrivKey, + cfg.L2SuggestedFeeRecipient, + cfg.TaikoL1Address, + cfg.ProverSetAddress, + cfg.ProposeBlockTxGasLimit, + cfg.ExtraData, + p.chainConfig, + p.txmgrSelector, + cfg.RevertProtectionEnabled, + cfg.BlobAllowed, + cfg.FallbackToCalldata, + ) return nil } diff --git a/packages/taiko-client/proposer/transaction_builder/fallback.go b/packages/taiko-client/proposer/transaction_builder/fallback.go new file mode 100644 index 00000000000..a454f607084 --- /dev/null +++ b/packages/taiko-client/proposer/transaction_builder/fallback.go @@ -0,0 +1,164 @@ +package builder + +import ( + "context" + "crypto/ecdsa" + "fmt" + "math/big" + + "github.com/ethereum-optimism/optimism/op-service/txmgr" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/config" + "github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/rpc" + "github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/utils" +) + +// TxBuilderWithFallback builds type-2 or type-3 transactions based on the +// the realtime onchain cost, if the fallback feature is enabled. +type TxBuilderWithFallback struct { + rpc *rpc.Client + blobTransactionBuilder *BlobTransactionBuilder + calldataTransactionBuilder *CalldataTransactionBuilder + txmgrSelector *utils.TxMgrSelector + fallback bool +} + +// NewBuilderWithFallback creates a new TxBuilderWithFallback instance. +func NewBuilderWithFallback( + rpc *rpc.Client, + proposerPrivateKey *ecdsa.PrivateKey, + l2SuggestedFeeRecipient common.Address, + taikoL1Address common.Address, + proverSetAddress common.Address, + gasLimit uint64, + extraData string, + chainConfig *config.ChainConfig, + txmgrSelector *utils.TxMgrSelector, + revertProtectionEnabled bool, + blobAllowed bool, + fallback bool, +) *TxBuilderWithFallback { + builder := &TxBuilderWithFallback{ + rpc: rpc, + fallback: fallback, + txmgrSelector: txmgrSelector, + } + + if blobAllowed { + builder.blobTransactionBuilder = NewBlobTransactionBuilder( + rpc, + proposerPrivateKey, + taikoL1Address, + proverSetAddress, + l2SuggestedFeeRecipient, + gasLimit, + extraData, + chainConfig, + revertProtectionEnabled, + ) + } + + builder.calldataTransactionBuilder = NewCalldataTransactionBuilder( + rpc, + proposerPrivateKey, + l2SuggestedFeeRecipient, + taikoL1Address, + proverSetAddress, + gasLimit, + extraData, + chainConfig, + revertProtectionEnabled, + ) + + return builder +} + +// BuildOntake builds a type-2 or type-3 transaction based on the +// the realtime onchain cost, if the fallback feature is enabled. +func (b *TxBuilderWithFallback) BuildOntake( + ctx context.Context, + txListBytesArray [][]byte, +) (*txmgr.TxCandidate, error) { + // If calldata is the only option, just use it. + if b.blobTransactionBuilder == nil { + return b.calldataTransactionBuilder.BuildOntake(ctx, txListBytesArray) + } + // If blob is enabled, and fallback is not enabled, just build a blob transaction. + if !b.fallback { + return b.blobTransactionBuilder.BuildOntake(ctx, txListBytesArray) + } + // Otherwise, compare the cost, and choose the cheaper option. + txWithCalldata, err := b.calldataTransactionBuilder.BuildOntake(ctx, txListBytesArray) + if err != nil { + return nil, err + } + txWithBlob, err := b.blobTransactionBuilder.BuildOntake(ctx, txListBytesArray) + if err != nil { + return nil, err + } + + costCalldata, err := b.estimateCandidateCost(ctx, txWithCalldata) + if err != nil { + return nil, err + } + costBlob, err := b.estimateCandidateCost(ctx, txWithBlob) + if err != nil { + return nil, err + } + + if costCalldata.Cmp(costBlob) < 1 { + log.Info("Building a type-2 transaction", "costCalldata", costCalldata, "costBlob", costBlob) + return txWithCalldata, nil + } + + log.Info("Building a type-3 transaction", "costCalldata", costCalldata, "costBlob", costBlob) + return txWithBlob, nil +} + +// estimateCandidateCost estimates the realtime onchain cost of the given transaction. +func (b *TxBuilderWithFallback) estimateCandidateCost( + ctx context.Context, + candidate *txmgr.TxCandidate, +) (*big.Int, error) { + txmgr, _ := b.txmgrSelector.Select() + gasTipCap, baseFee, blobBaseFee, err := txmgr.SuggestGasPriceCaps(ctx) + if err != nil { + return nil, err + } + log.Info("Suggested gas price", "gasTipCap", gasTipCap, "baseFee", baseFee, "blobBaseFee", blobBaseFee) + + gasPrice := new(big.Int).Add(baseFee, gasTipCap) + gasUsed, err := b.rpc.L1.EstimateGas(ctx, ethereum.CallMsg{ + From: txmgr.From(), + To: candidate.To, + Gas: candidate.GasLimit, + GasPrice: gasPrice, + GasFeeCap: gasPrice, + GasTipCap: gasTipCap, + Value: candidate.Value, + Data: candidate.TxData, + }) + if err != nil { + return nil, fmt.Errorf("failed to estimate gas used: %w", err) + } + + feeWithoutBlob := new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(gasUsed)) + + // If its a type-2 transaction, we won't calculate blob fee. + if len(candidate.Blobs) == 0 { + return feeWithoutBlob, nil + } + + // Otherwise, we add blob fee to the cost. + return new(big.Int).Add( + feeWithoutBlob, + new(big.Int).Mul(new(big.Int).SetUint64(uint64(len(candidate.Blobs))), blobBaseFee), + ), nil +} + +// TxBuilderWithFallback returns whether the blob transactions is enabled. +func (b *TxBuilderWithFallback) BlobAllow() bool { + return b.blobTransactionBuilder != nil +} From f3584a7575cbf97a37c3cdd4e1469cfda5d0a5e1 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 2 Jan 2025 14:11:47 +0800 Subject: [PATCH 2/6] feat: use errgroup --- .../proposer/transaction_builder/fallback.go | 46 +++++++++++++------ 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/packages/taiko-client/proposer/transaction_builder/fallback.go b/packages/taiko-client/proposer/transaction_builder/fallback.go index a454f607084..68b2bad3685 100644 --- a/packages/taiko-client/proposer/transaction_builder/fallback.go +++ b/packages/taiko-client/proposer/transaction_builder/fallback.go @@ -13,6 +13,7 @@ import ( "github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/config" "github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/rpc" "github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/utils" + "golang.org/x/sync/errgroup" ) // TxBuilderWithFallback builds type-2 or type-3 transactions based on the @@ -89,22 +90,37 @@ func (b *TxBuilderWithFallback) BuildOntake( if !b.fallback { return b.blobTransactionBuilder.BuildOntake(ctx, txListBytesArray) } + // Otherwise, compare the cost, and choose the cheaper option. - txWithCalldata, err := b.calldataTransactionBuilder.BuildOntake(ctx, txListBytesArray) - if err != nil { - return nil, err - } - txWithBlob, err := b.blobTransactionBuilder.BuildOntake(ctx, txListBytesArray) - if err != nil { - return nil, err - } + var ( + g = new(errgroup.Group) + txWithCalldata *txmgr.TxCandidate + txWithBlob *txmgr.TxCandidate + costCalldata *big.Int + costBlob *big.Int + err error + ) - costCalldata, err := b.estimateCandidateCost(ctx, txWithCalldata) - if err != nil { - return nil, err - } - costBlob, err := b.estimateCandidateCost(ctx, txWithBlob) - if err != nil { + g.Go(func() error { + if txWithCalldata, err = b.calldataTransactionBuilder.BuildOntake(ctx, txListBytesArray); err != nil { + return err + } + if costCalldata, err = b.estimateCandidateCost(ctx, txWithCalldata); err != nil { + return err + } + return nil + }) + g.Go(func() error { + if txWithBlob, err = b.blobTransactionBuilder.BuildOntake(ctx, txListBytesArray); err != nil { + return err + } + if costBlob, err = b.estimateCandidateCost(ctx, txWithBlob); err != nil { + return err + } + return nil + }) + + if err = g.Wait(); err != nil { return nil, err } @@ -127,7 +143,7 @@ func (b *TxBuilderWithFallback) estimateCandidateCost( if err != nil { return nil, err } - log.Info("Suggested gas price", "gasTipCap", gasTipCap, "baseFee", baseFee, "blobBaseFee", blobBaseFee) + log.Debug("Suggested gas price", "gasTipCap", gasTipCap, "baseFee", baseFee, "blobBaseFee", blobBaseFee) gasPrice := new(big.Int).Add(baseFee, gasTipCap) gasUsed, err := b.rpc.L1.EstimateGas(ctx, ethereum.CallMsg{ From 8b6a1c59a3c96adcf4bd0e1d0de652f9d50aa32d Mon Sep 17 00:00:00 2001 From: David Date: Thu, 2 Jan 2025 14:40:32 +0800 Subject: [PATCH 3/6] test: more tests --- .../taiko-client/proposer/proposer_test.go | 1 + .../transaction_builder/fallback_test.go | 80 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 packages/taiko-client/proposer/transaction_builder/fallback_test.go diff --git a/packages/taiko-client/proposer/proposer_test.go b/packages/taiko-client/proposer/proposer_test.go index c81bfa2415a..c6f80a2dc65 100644 --- a/packages/taiko-client/proposer/proposer_test.go +++ b/packages/taiko-client/proposer/proposer_test.go @@ -81,6 +81,7 @@ func (s *ProposerTestSuite) SetupTest() { MaxProposedTxListsPerEpoch: 1, ExtraData: "test", ProposeBlockTxGasLimit: 10_000_000, + FallbackToCalldata: true, TxmgrConfigs: &txmgr.CLIConfig{ L1RPCURL: os.Getenv("L1_WS"), NumConfirmations: 0, diff --git a/packages/taiko-client/proposer/transaction_builder/fallback_test.go b/packages/taiko-client/proposer/transaction_builder/fallback_test.go new file mode 100644 index 00000000000..1a57b854b45 --- /dev/null +++ b/packages/taiko-client/proposer/transaction_builder/fallback_test.go @@ -0,0 +1,80 @@ +package builder + +import ( + "context" + "os" + "time" + + "github.com/ethereum-optimism/optimism/op-service/txmgr" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" + "github.com/taikoxyz/taiko-mono/packages/taiko-client/internal/metrics" + "github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/config" + "github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/rpc" + "github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/utils" +) + +func (s *TransactionBuilderTestSuite) TestBuildCalldataOnly() { + builder := s.newTestBuilderWithFallback(false, false) + candidate, err := builder.BuildOntake(context.Background(), [][]byte{{1}, {2}}) + s.Nil(err) + s.Zero(len(candidate.Blobs)) +} + +func (s *TransactionBuilderTestSuite) TestBuildCalldataWithBlobAllowed() { + builder := s.newTestBuilderWithFallback(true, false) + candidate, err := builder.BuildOntake(context.Background(), [][]byte{{1}, {2}}) + s.Nil(err) + s.NotZero(len(candidate.Blobs)) +} + +func (s *TransactionBuilderTestSuite) newTestBuilderWithFallback(blobAllowed, fallback bool) *TxBuilderWithFallback { + l1ProposerPrivKey, err := crypto.ToECDSA(common.FromHex(os.Getenv("L1_PROPOSER_PRIVATE_KEY"))) + s.Nil(err) + + protocolConfigs, err := rpc.GetProtocolConfigs(s.RPCClient.TaikoL1, nil) + s.Nil(err) + + chainConfig := config.NewChainConfig(&protocolConfigs) + + txMgr, err := txmgr.NewSimpleTxManager( + "tx_builder_test", + log.Root(), + &metrics.TxMgrMetrics, + txmgr.CLIConfig{ + L1RPCURL: os.Getenv("L1_WS"), + NumConfirmations: 0, + SafeAbortNonceTooLowCount: txmgr.DefaultBatcherFlagValues.SafeAbortNonceTooLowCount, + PrivateKey: common.Bytes2Hex(crypto.FromECDSA(l1ProposerPrivKey)), + FeeLimitMultiplier: txmgr.DefaultBatcherFlagValues.FeeLimitMultiplier, + FeeLimitThresholdGwei: txmgr.DefaultBatcherFlagValues.FeeLimitThresholdGwei, + MinBaseFeeGwei: txmgr.DefaultBatcherFlagValues.MinBaseFeeGwei, + MinTipCapGwei: txmgr.DefaultBatcherFlagValues.MinTipCapGwei, + ResubmissionTimeout: txmgr.DefaultBatcherFlagValues.ResubmissionTimeout, + ReceiptQueryInterval: 1 * time.Second, + NetworkTimeout: txmgr.DefaultBatcherFlagValues.NetworkTimeout, + TxSendTimeout: txmgr.DefaultBatcherFlagValues.TxSendTimeout, + TxNotInMempoolTimeout: txmgr.DefaultBatcherFlagValues.TxNotInMempoolTimeout, + }, + ) + + s.Nil(err) + + txmgrSelector := utils.NewTxMgrSelector(txMgr, nil, nil) + + return NewBuilderWithFallback( + s.RPCClient, + l1ProposerPrivKey, + common.HexToAddress(os.Getenv("TAIKO_L2")), + common.HexToAddress(os.Getenv("TAIKO_L1")), + common.Address{}, + 10_000_000, + "test_fallback_builder", + chainConfig, + txmgrSelector, + true, + blobAllowed, + fallback, + ) +} From 2344899e2aa6687046c526414d1fe371c403c178 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 2 Jan 2025 14:49:13 +0800 Subject: [PATCH 4/6] fix: fix an issue --- packages/taiko-client/proposer/transaction_builder/fallback.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/taiko-client/proposer/transaction_builder/fallback.go b/packages/taiko-client/proposer/transaction_builder/fallback.go index 68b2bad3685..49cc0d700e4 100644 --- a/packages/taiko-client/proposer/transaction_builder/fallback.go +++ b/packages/taiko-client/proposer/transaction_builder/fallback.go @@ -124,7 +124,7 @@ func (b *TxBuilderWithFallback) BuildOntake( return nil, err } - if costCalldata.Cmp(costBlob) < 1 { + if costCalldata.Cmp(costBlob) < 0 { log.Info("Building a type-2 transaction", "costCalldata", costCalldata, "costBlob", costBlob) return txWithCalldata, nil } From dcb7aedc43774dcf9cf4228c2c3632e1b4ae3ea9 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 2 Jan 2025 14:56:16 +0800 Subject: [PATCH 5/6] feat: add metrics --- packages/taiko-client/internal/metrics/metrics.go | 2 ++ .../taiko-client/proposer/transaction_builder/fallback.go | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/taiko-client/internal/metrics/metrics.go b/packages/taiko-client/internal/metrics/metrics.go index 27f4da07724..0b2fdced5ad 100644 --- a/packages/taiko-client/internal/metrics/metrics.go +++ b/packages/taiko-client/internal/metrics/metrics.go @@ -30,6 +30,8 @@ var ( ProposerProposedTxListsCounter = factory.NewCounter(prometheus.CounterOpts{Name: "proposer_proposed_txLists"}) ProposerProposedTxsCounter = factory.NewCounter(prometheus.CounterOpts{Name: "proposer_proposed_txs"}) ProposerPoolContentFetchTime = factory.NewGauge(prometheus.GaugeOpts{Name: "proposer_pool_content_fetch_time"}) + ProposerEstimatedCostCalldata = factory.NewGauge(prometheus.GaugeOpts{Name: "proposer_estimated_cost_calldata"}) + ProposerEstimatedCostBlob = factory.NewGauge(prometheus.GaugeOpts{Name: "proposer_estimated_cost_blob"}) // Prover ProverLatestVerifiedIDGauge = factory.NewGauge(prometheus.GaugeOpts{Name: "prover_latestVerified_id"}) diff --git a/packages/taiko-client/proposer/transaction_builder/fallback.go b/packages/taiko-client/proposer/transaction_builder/fallback.go index 49cc0d700e4..a97970f431e 100644 --- a/packages/taiko-client/proposer/transaction_builder/fallback.go +++ b/packages/taiko-client/proposer/transaction_builder/fallback.go @@ -10,10 +10,12 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" + "golang.org/x/sync/errgroup" + + "github.com/taikoxyz/taiko-mono/packages/taiko-client/internal/metrics" "github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/config" "github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/rpc" "github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/utils" - "golang.org/x/sync/errgroup" ) // TxBuilderWithFallback builds type-2 or type-3 transactions based on the @@ -124,6 +126,9 @@ func (b *TxBuilderWithFallback) BuildOntake( return nil, err } + metrics.ProposerEstimatedCostCalldata.Set(float64(costCalldata.Uint64())) + metrics.ProposerEstimatedCostBlob.Set(float64(costBlob.Uint64())) + if costCalldata.Cmp(costBlob) < 0 { log.Info("Building a type-2 transaction", "costCalldata", costCalldata, "costBlob", costBlob) return txWithCalldata, nil From d1b5582badd71144483b753781944f471bbebd25 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 2 Jan 2025 22:44:00 +0800 Subject: [PATCH 6/6] fix: fix lint --- .../taiko-client/proposer/transaction_builder/fallback_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/taiko-client/proposer/transaction_builder/fallback_test.go b/packages/taiko-client/proposer/transaction_builder/fallback_test.go index 1a57b854b45..3fcd0eb63c1 100644 --- a/packages/taiko-client/proposer/transaction_builder/fallback_test.go +++ b/packages/taiko-client/proposer/transaction_builder/fallback_test.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" + "github.com/taikoxyz/taiko-mono/packages/taiko-client/internal/metrics" "github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/config" "github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/rpc"