Skip to content

Commit

Permalink
fix(relayer): cost estimation (#16707)
Browse files Browse the repository at this point in the history
  • Loading branch information
cyberhorsey authored Apr 11, 2024
1 parent 68bd435 commit 6baf7fd
Show file tree
Hide file tree
Showing 9 changed files with 273 additions and 54 deletions.
18 changes: 13 additions & 5 deletions packages/relayer/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/ethereum/go-ethereum/ethclient"
"github.com/labstack/echo/v4"
"github.com/taikoxyz/taiko-mono/packages/relayer/bindings/taikol2"
"github.com/taikoxyz/taiko-mono/packages/relayer/pkg/http"
"github.com/taikoxyz/taiko-mono/packages/relayer/pkg/repo"
"github.com/urfave/cli/v2"
Expand Down Expand Up @@ -55,12 +56,19 @@ func InitFromConfig(ctx context.Context, api *API, cfg *Config) (err error) {
return err
}

taikoL2, err := taikol2.NewTaikoL2(cfg.DestTaikoAddress, destEthClient)
if err != nil {
return err
}

srv, err := http.NewServer(http.NewServerOpts{
EventRepo: eventRepository,
Echo: echo.New(),
CorsOrigins: cfg.CORSOrigins,
SrcEthClient: srcEthClient,
DestEthClient: destEthClient,
EventRepo: eventRepository,
Echo: echo.New(),
CorsOrigins: cfg.CORSOrigins,
SrcEthClient: srcEthClient,
DestEthClient: destEthClient,
TaikoL2: taikoL2,
ProcessingFeeMultiplier: cfg.ProcessingFeeMultiplier,
})
if err != nil {
return err
Expand Down
13 changes: 9 additions & 4 deletions packages/relayer/api/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package api
import (
"strings"

"github.com/ethereum/go-ethereum/common"
"github.com/taikoxyz/taiko-mono/packages/relayer/cmd/flags"
"github.com/taikoxyz/taiko-mono/packages/relayer/pkg/db"
"github.com/urfave/cli/v2"
Expand All @@ -22,10 +23,12 @@ type Config struct {
DatabaseMaxConnLifetime uint64
CORSOrigins []string
// rpc configs
SrcRPCUrl string
DestRPCUrl string
HTTPPort uint64
OpenDBFunc func() (DB, error)
SrcRPCUrl string
DestRPCUrl string
ProcessingFeeMultiplier float64
DestTaikoAddress common.Address
HTTPPort uint64
OpenDBFunc func() (DB, error)
}

// NewConfigFromCliContext creates a new config instance from command line flags.
Expand All @@ -42,6 +45,8 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) {
HTTPPort: c.Uint64(flags.HTTPPort.Name),
SrcRPCUrl: c.String(flags.SrcRPCUrl.Name),
DestRPCUrl: c.String(flags.DestRPCUrl.Name),
ProcessingFeeMultiplier: c.Float64(flags.ProcessingFeeMultiplier.Name),
DestTaikoAddress: common.HexToAddress(c.String(flags.DestTaikoAddress.Name)),
OpenDBFunc: func() (DB, error) {
return db.OpenDBConnection(db.DBConnectionOpts{
Name: c.String(flags.DatabaseUsername.Name),
Expand Down
3 changes: 3 additions & 0 deletions packages/relayer/api/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ var (
databaseMaxOpenConns = "10"
databaseMaxConnLifetime = "30"
HTTPPort = "1000"
destTaikoAddress = "0x63FaC9201494f0bd17B9892B9fae4d52fe3BD377"
)

func setupApp() *cli.App {
Expand Down Expand Up @@ -44,6 +45,7 @@ func TestNewConfigFromCliContext(t *testing.T) {
assert.Equal(t, uint64(1000), c.HTTPPort)
assert.Equal(t, "srcRpcUrl", c.SrcRPCUrl)
assert.Equal(t, "destRpcUrl", c.DestRPCUrl)
assert.Equal(t, destTaikoAddress, c.DestTaikoAddress.Hex())

c.OpenDBFunc = func() (DB, error) {
return &mock.DB{}, nil
Expand All @@ -67,5 +69,6 @@ func TestNewConfigFromCliContext(t *testing.T) {
"--" + flags.HTTPPort.Name, HTTPPort,
"--" + flags.SrcRPCUrl.Name, "srcRpcUrl",
"--" + flags.DestRPCUrl.Name, "destRpcUrl",
"--" + flags.DestTaikoAddress.Name, destTaikoAddress,
}))
}
9 changes: 9 additions & 0 deletions packages/relayer/cmd/flags/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,19 @@ var (
Value: "*",
EnvVars: []string{"HTTP_CORS_ORIGINS"},
}
ProcessingFeeMultiplier = &cli.Float64Flag{
Name: "processingFeeMultiplier",
Usage: "Processing fee multiplier",
Category: indexerCategory,
Value: 2.5,
EnvVars: []string{"PROCESSING_FEE_MULTIPLIER"},
}
)

var APIFlags = MergeFlags(CommonFlags, []cli.Flag{
// optional
HTTPPort,
CORSOrigins,
ProcessingFeeMultiplier,
DestTaikoAddress,
})
193 changes: 193 additions & 0 deletions packages/relayer/pkg/http/get_recommended_processing_fees.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package http

import (
"context"
"math/big"
"net/http"
"time"

"github.com/cyberhorsey/webutils"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/params"
"github.com/labstack/echo/v4"
)

type getRecommendedProcessingFeesResponse struct {
Fees []fee `json:"fees"`
}

type fee struct {
Type string `json:"type"`
Amount string `json:"amount"`
DestChainID uint64 `json:"destChainID"`
}

type FeeType uint64

// gas limits
var (
Eth FeeType = 900000
ERC20NotDeployed FeeType = 1650000
ERC20Deployed FeeType = 1000000
ERC721NotDeployed FeeType = 2500000
ERC721Deployed FeeType = 1500000
ERC1155NotDeployed FeeType = 2650000
ERC1155Deployed FeeType = 1650000
)

var (
feeTypes = []FeeType{
Eth,
ERC20Deployed,
ERC20NotDeployed,
ERC721Deployed,
ERC721NotDeployed,
ERC1155Deployed,
ERC1155NotDeployed}
)

func (f FeeType) String() string {
switch f {
case Eth:
return "eth"
case ERC20NotDeployed:
return "erc20NotDeployed"
case ERC20Deployed:
return "erc20Deployed"
case ERC721Deployed:
return "erc721Deployed"
case ERC721NotDeployed:
return "erc721NotDeployed"
case ERC1155NotDeployed:
return "erc1155NotDeployed"
case ERC1155Deployed:
return "erc1155Deployed"
default:
return ""
}
}

type layer int

const (
Layer1 layer = iota
Layer2 layer = iota
)

// getBlockInfoResponse
//
// returns block info for the chains
//
// @Summary Get block info
// @ID get-block-info
// @Accept json
// @Produce json
// @Success 200 {object} getBlockInfoResponse
// @Router /blockInfo [get]
func (srv *Server) GetRecommendedProcessingFees(c echo.Context) error {
fees := make([]fee, 0)

srcChainID, err := srv.srcEthClient.ChainID(c.Request().Context())
if err != nil {
return webutils.LogAndRenderErrors(c, http.StatusUnprocessableEntity, err)
}

destChainID, err := srv.destEthClient.ChainID(c.Request().Context())
if err != nil {
return webutils.LogAndRenderErrors(c, http.StatusUnprocessableEntity, err)
}

srcGasTipCap, err := srv.srcEthClient.SuggestGasTipCap(c.Request().Context())
if err != nil {
return webutils.LogAndRenderErrors(c, http.StatusUnprocessableEntity, err)
}

srcBaseFee, err := srv.getDestChainBaseFee(c.Request().Context(), Layer1, srcChainID)
if err != nil {
return webutils.LogAndRenderErrors(c, http.StatusUnprocessableEntity, err)
}

destBaseFee, err := srv.getDestChainBaseFee(c.Request().Context(), Layer2, destChainID)
if err != nil {
return webutils.LogAndRenderErrors(c, http.StatusUnprocessableEntity, err)
}

destGasTipCap, err := srv.destEthClient.SuggestGasTipCap(c.Request().Context())
if err != nil {
return webutils.LogAndRenderErrors(c, http.StatusUnprocessableEntity, err)
}

for _, f := range feeTypes {
fees = append(fees, fee{
Type: f.String(),
Amount: srv.getCost(c.Request().Context(), uint64(f), srcGasTipCap, srcBaseFee, Layer1).String(),
DestChainID: srcChainID.Uint64(),
})

fees = append(fees, fee{
Type: f.String(),
Amount: srv.getCost(c.Request().Context(), uint64(f), destGasTipCap, destBaseFee, Layer2).String(),
DestChainID: destChainID.Uint64(),
})
}

resp := getRecommendedProcessingFeesResponse{
Fees: fees,
}

return c.JSON(http.StatusOK, resp)
}

func (srv *Server) getCost(
ctx context.Context,
gasLimit uint64,
gasTipCap *big.Int,
baseFee *big.Int,
destLayer layer,
) *big.Int {
cost := new(big.Int).Mul(
new(big.Int).SetUint64(gasLimit),
new(big.Int).Add(gasTipCap, baseFee))

if destLayer == Layer2 {
return cost
}

costRat := new(big.Rat).SetInt(cost)

multiplierRat := new(big.Rat).SetFloat64(srv.processingFeeMultiplier)

costRat.Mul(costRat, multiplierRat)

costAfterMultiplier := new(big.Int).Div(costRat.Num(), costRat.Denom())

return costAfterMultiplier
}

func (srv *Server) getDestChainBaseFee(ctx context.Context, l layer, chainID *big.Int) (*big.Int, error) {
blk, err := srv.destEthClient.BlockByNumber(ctx, nil)
if err != nil {
return nil, err
}

var baseFee *big.Int

// if layerL1 bridge, we need to calc basefee on Layer 2, since it's the dest chain
if l == Layer1 {
gasUsed := uint32(blk.GasUsed())
timeSince := uint64(time.Since(time.Unix(int64(blk.Time()), 0)))
bf, err := srv.taikoL2.GetBasefee(&bind.CallOpts{Context: ctx}, timeSince, gasUsed)

if err != nil {
return nil, err
}

baseFee = bf.Basefee
} else {
cfg := params.NetworkIDToChainConfigOrDefault(chainID)
baseFee = eip1559.CalcBaseFee(cfg, blk.Header())
}

return baseFee, nil
}
1 change: 1 addition & 0 deletions packages/relayer/pkg/http/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ func (srv *Server) configureRoutes() {
srv.echo.GET("/events", srv.GetEventsByAddress)
srv.echo.GET("/blockInfo", srv.GetBlockInfo)
srv.echo.GET("/suspendedTransactions", srv.GetSuspendedTransactions)
srv.echo.GET("/recommendedProcessingFees", srv.GetRecommendedProcessingFees)
}
42 changes: 26 additions & 16 deletions packages/relayer/pkg/http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ import (
"net/http"
"os"

"github.com/ethereum/go-ethereum/core/types"
"github.com/labstack/echo/v4/middleware"
"github.com/taikoxyz/taiko-mono/packages/relayer"
"github.com/taikoxyz/taiko-mono/packages/relayer/bindings/taikol2"

echo "github.com/labstack/echo/v4"
)

type ethClient interface {
BlockNumber(ctx context.Context) (uint64, error)
ChainID(ctx context.Context) (*big.Int, error)
SuggestGasTipCap(ctx context.Context) (*big.Int, error)
BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error)
}

// @title Taiko Relayer API
Expand All @@ -30,20 +34,24 @@ type ethClient interface {
// @host relayer.katla.taiko.xyz
// Server represents an relayer http server instance.
type Server struct {
echo *echo.Echo
eventRepo relayer.EventRepository
suspendedTxRepo relayer.SuspendedTransactionRepository
srcEthClient ethClient
destEthClient ethClient
echo *echo.Echo
eventRepo relayer.EventRepository
suspendedTxRepo relayer.SuspendedTransactionRepository
srcEthClient ethClient
destEthClient ethClient
processingFeeMultiplier float64
taikoL2 *taikol2.TaikoL2
}

type NewServerOpts struct {
Echo *echo.Echo
EventRepo relayer.EventRepository
SuspendedTxRepo relayer.SuspendedTransactionRepository
CorsOrigins []string
SrcEthClient ethClient
DestEthClient ethClient
Echo *echo.Echo
EventRepo relayer.EventRepository
SuspendedTxRepo relayer.SuspendedTransactionRepository
CorsOrigins []string
SrcEthClient ethClient
DestEthClient ethClient
ProcessingFeeMultiplier float64
TaikoL2 *taikol2.TaikoL2
}

func (opts NewServerOpts) Validate() error {
Expand Down Expand Up @@ -76,11 +84,13 @@ func NewServer(opts NewServerOpts) (*Server, error) {
}

srv := &Server{
echo: opts.Echo,
eventRepo: opts.EventRepo,
suspendedTxRepo: opts.SuspendedTxRepo,
srcEthClient: opts.SrcEthClient,
destEthClient: opts.DestEthClient,
echo: opts.Echo,
eventRepo: opts.EventRepo,
suspendedTxRepo: opts.SuspendedTxRepo,
srcEthClient: opts.SrcEthClient,
destEthClient: opts.DestEthClient,
processingFeeMultiplier: opts.ProcessingFeeMultiplier,
taikoL2: opts.TaikoL2,
}

corsOrigins := opts.CorsOrigins
Expand Down
Loading

0 comments on commit 6baf7fd

Please sign in to comment.