github.com/status-im/status-go@v1.1.0/services/wallet/router/pathprocessor/processor_bridge_hop.go (about)

     1  package pathprocessor
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"math/big"
     8  	netUrl "net/url"
     9  	"strings"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/ethereum/go-ethereum"
    14  	"github.com/ethereum/go-ethereum/accounts/abi"
    15  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    16  	"github.com/ethereum/go-ethereum/common"
    17  	"github.com/ethereum/go-ethereum/common/hexutil"
    18  	ethTypes "github.com/ethereum/go-ethereum/core/types"
    19  	"github.com/status-im/status-go/account"
    20  	"github.com/status-im/status-go/contracts"
    21  	"github.com/status-im/status-go/contracts/hop"
    22  	hopL1CctpImplementation "github.com/status-im/status-go/contracts/hop/l1Contracts/l1CctpImplementation"
    23  	hopL1Erc20Bridge "github.com/status-im/status-go/contracts/hop/l1Contracts/l1Erc20Bridge"
    24  	hopL1EthBridge "github.com/status-im/status-go/contracts/hop/l1Contracts/l1EthBridge"
    25  	hopL1HopBridge "github.com/status-im/status-go/contracts/hop/l1Contracts/l1HopBridge"
    26  	hopL2AmmWrapper "github.com/status-im/status-go/contracts/hop/l2Contracts/l2AmmWrapper"
    27  	hopL2ArbitrumBridge "github.com/status-im/status-go/contracts/hop/l2Contracts/l2ArbitrumBridge"
    28  	hopL2CctpImplementation "github.com/status-im/status-go/contracts/hop/l2Contracts/l2CctpImplementation"
    29  	hopL2OptimismBridge "github.com/status-im/status-go/contracts/hop/l2Contracts/l2OptimismBridge"
    30  	"github.com/status-im/status-go/eth-node/types"
    31  	"github.com/status-im/status-go/rpc"
    32  	"github.com/status-im/status-go/rpc/chain"
    33  	"github.com/status-im/status-go/rpc/network"
    34  	"github.com/status-im/status-go/services/wallet/bigint"
    35  	walletCommon "github.com/status-im/status-go/services/wallet/common"
    36  	"github.com/status-im/status-go/services/wallet/thirdparty"
    37  	"github.com/status-im/status-go/services/wallet/token"
    38  	"github.com/status-im/status-go/transactions"
    39  )
    40  
    41  type HopBridgeTxArgs struct {
    42  	transactions.SendTxArgs
    43  	ChainID   uint64         `json:"chainId"`
    44  	ChainIDTo uint64         `json:"chainIdTo"`
    45  	Symbol    string         `json:"symbol"`
    46  	Recipient common.Address `json:"recipient"`
    47  	Amount    *hexutil.Big   `json:"amount"`
    48  	BonderFee *hexutil.Big   `json:"bonderFee"`
    49  }
    50  
    51  type BonderFee struct {
    52  	AmountIn                *bigint.BigInt `json:"amountIn"`
    53  	Slippage                float32        `json:"slippage"`
    54  	AmountOutMin            *bigint.BigInt `json:"amountOutMin"`
    55  	DestinationAmountOutMin *bigint.BigInt `json:"destinationAmountOutMin"`
    56  	BonderFee               *bigint.BigInt `json:"bonderFee"`
    57  	EstimatedRecieved       *bigint.BigInt `json:"estimatedRecieved"`
    58  	Deadline                int64          `json:"deadline"`
    59  	DestinationDeadline     int64          `json:"destinationDeadline"`
    60  }
    61  
    62  func (bf *BonderFee) UnmarshalJSON(data []byte) error {
    63  	type Alias BonderFee
    64  	aux := &struct {
    65  		AmountIn                string  `json:"amountIn"`
    66  		Slippage                float32 `json:"slippage"`
    67  		AmountOutMin            string  `json:"amountOutMin"`
    68  		DestinationAmountOutMin string  `json:"destinationAmountOutMin"`
    69  		BonderFee               string  `json:"bonderFee"`
    70  		EstimatedRecieved       string  `json:"estimatedRecieved"`
    71  		Deadline                int64   `json:"deadline"`
    72  		DestinationDeadline     *int64  `json:"destinationDeadline"`
    73  		*Alias
    74  	}{
    75  		Alias: (*Alias)(bf),
    76  	}
    77  
    78  	if err := json.Unmarshal(data, aux); err != nil {
    79  		return createBridgeHopErrorResponse(err)
    80  	}
    81  
    82  	bf.AmountIn = &bigint.BigInt{Int: new(big.Int)}
    83  	bf.AmountIn.SetString(aux.AmountIn, 10)
    84  
    85  	bf.Slippage = aux.Slippage
    86  
    87  	bf.AmountOutMin = &bigint.BigInt{Int: new(big.Int)}
    88  	bf.AmountOutMin.SetString(aux.AmountOutMin, 10)
    89  
    90  	bf.DestinationAmountOutMin = &bigint.BigInt{Int: new(big.Int)}
    91  	bf.DestinationAmountOutMin.SetString(aux.DestinationAmountOutMin, 10)
    92  
    93  	bf.BonderFee = &bigint.BigInt{Int: new(big.Int)}
    94  	bf.BonderFee.SetString(aux.BonderFee, 10)
    95  
    96  	bf.EstimatedRecieved = &bigint.BigInt{Int: new(big.Int)}
    97  	bf.EstimatedRecieved.SetString(aux.EstimatedRecieved, 10)
    98  
    99  	bf.Deadline = aux.Deadline
   100  
   101  	if aux.DestinationDeadline != nil {
   102  		bf.DestinationDeadline = *aux.DestinationDeadline
   103  	}
   104  
   105  	return nil
   106  }
   107  
   108  type HopBridgeProcessor struct {
   109  	transactor     transactions.TransactorIface
   110  	httpClient     *thirdparty.HTTPClient
   111  	tokenManager   *token.Manager
   112  	contractMaker  *contracts.ContractMaker
   113  	networkManager network.ManagerInterface
   114  	bonderFee      *sync.Map // [fromChainName-toChainName]BonderFee
   115  }
   116  
   117  func NewHopBridgeProcessor(rpcClient rpc.ClientInterface, transactor transactions.TransactorIface, tokenManager *token.Manager, networkManager network.ManagerInterface) *HopBridgeProcessor {
   118  	return &HopBridgeProcessor{
   119  		contractMaker:  &contracts.ContractMaker{RPCClient: rpcClient},
   120  		httpClient:     thirdparty.NewHTTPClient(),
   121  		transactor:     transactor,
   122  		tokenManager:   tokenManager,
   123  		networkManager: networkManager,
   124  		bonderFee:      &sync.Map{},
   125  	}
   126  }
   127  
   128  func createBridgeHopErrorResponse(err error) error {
   129  	return createErrorResponse(ProcessorBridgeHopName, err)
   130  }
   131  
   132  func (h *HopBridgeProcessor) Name() string {
   133  	return ProcessorBridgeHopName
   134  }
   135  
   136  func (h *HopBridgeProcessor) Clear() {
   137  	h.bonderFee = &sync.Map{}
   138  }
   139  
   140  func (h *HopBridgeProcessor) AvailableFor(params ProcessorInputParams) (bool, error) {
   141  	if params.FromChain == nil || params.ToChain == nil {
   142  		return false, ErrNoChainSet
   143  	}
   144  	if params.FromToken == nil {
   145  		return false, ErrNoTokenSet
   146  	}
   147  	if params.ToToken != nil {
   148  		return false, ErrToTokenShouldNotBeSet
   149  	}
   150  	if params.FromChain.ChainID == params.ToChain.ChainID {
   151  		return false, ErrFromAndToChainsMustBeDifferent
   152  	}
   153  	// We chcek if the contract is available on the network for the token
   154  	_, err := h.GetContractAddress(params)
   155  	// toToken is not nil only if the send type is Swap
   156  	return err == nil, err
   157  }
   158  
   159  func (c *HopBridgeProcessor) getAppropriateABI(contractType string, chainID uint64, token *token.Token) (abi.ABI, error) {
   160  	switch contractType {
   161  	case hop.CctpL1Bridge:
   162  		return abi.JSON(strings.NewReader(hopL1CctpImplementation.HopL1CctpImplementationABI))
   163  	case hop.L1Bridge:
   164  		if token.IsNative() {
   165  			return abi.JSON(strings.NewReader(hopL1EthBridge.HopL1EthBridgeABI))
   166  		}
   167  		if token.Symbol == HopSymbol {
   168  			return abi.JSON(strings.NewReader(hopL1HopBridge.HopL1HopBridgeABI))
   169  		}
   170  		return abi.JSON(strings.NewReader(hopL1Erc20Bridge.HopL1Erc20BridgeABI))
   171  	case hop.L2AmmWrapper:
   172  		return abi.JSON(strings.NewReader(hopL2AmmWrapper.HopL2AmmWrapperABI))
   173  	case hop.CctpL2Bridge:
   174  		return abi.JSON(strings.NewReader(hopL2CctpImplementation.HopL2CctpImplementationABI))
   175  	case hop.L2Bridge:
   176  		if chainID == walletCommon.OptimismMainnet ||
   177  			chainID == walletCommon.OptimismSepolia {
   178  			return abi.JSON(strings.NewReader(hopL2OptimismBridge.HopL2OptimismBridgeABI))
   179  		}
   180  		if chainID == walletCommon.ArbitrumMainnet ||
   181  			chainID == walletCommon.ArbitrumSepolia {
   182  			return abi.JSON(strings.NewReader(hopL2ArbitrumBridge.HopL2ArbitrumBridgeABI))
   183  		}
   184  	}
   185  
   186  	return abi.ABI{}, ErrNotAvailableForContractType
   187  }
   188  
   189  func (h *HopBridgeProcessor) PackTxInputData(params ProcessorInputParams) ([]byte, error) {
   190  	_, contractType, err := hop.GetContractAddress(params.FromChain.ChainID, params.FromToken.Symbol)
   191  	if err != nil {
   192  		return []byte{}, createBridgeHopErrorResponse(err)
   193  	}
   194  
   195  	return h.packTxInputDataInternally(params, contractType)
   196  }
   197  
   198  func (h *HopBridgeProcessor) packTxInputDataInternally(params ProcessorInputParams, contractType string) ([]byte, error) {
   199  	abi, err := h.getAppropriateABI(contractType, params.FromChain.ChainID, params.FromToken)
   200  	if err != nil {
   201  		return []byte{}, createBridgeHopErrorResponse(err)
   202  	}
   203  
   204  	bonderKey := makeKey(params.FromChain.ChainID, params.ToChain.ChainID, "", "")
   205  	bonderFeeIns, ok := h.bonderFee.Load(bonderKey)
   206  	if !ok {
   207  		return nil, ErrNoBonderFeeFound
   208  	}
   209  	bonderFee := bonderFeeIns.(*BonderFee)
   210  
   211  	switch contractType {
   212  	case hop.CctpL1Bridge:
   213  		return h.packCctpL1BridgeTx(abi, params.ToChain.ChainID, params.ToAddr, bonderFee)
   214  	case hop.L1Bridge:
   215  		return h.packL1BridgeTx(abi, params.ToChain.ChainID, params.ToAddr, bonderFee)
   216  	case hop.L2AmmWrapper:
   217  		return h.packL2AmmWrapperTx(abi, params.ToChain.ChainID, params.ToAddr, bonderFee)
   218  	case hop.CctpL2Bridge:
   219  		return h.packCctpL2BridgeTx(abi, params.ToChain.ChainID, params.ToAddr, bonderFee)
   220  	case hop.L2Bridge:
   221  		return h.packL2BridgeTx(abi, params.ToChain.ChainID, params.ToAddr, bonderFee)
   222  	}
   223  
   224  	return []byte{}, ErrContractTypeNotSupported
   225  }
   226  
   227  func (h *HopBridgeProcessor) EstimateGas(params ProcessorInputParams) (uint64, error) {
   228  	if params.TestsMode {
   229  		if params.TestEstimationMap != nil {
   230  			if val, ok := params.TestEstimationMap[h.Name()]; ok {
   231  				return val.Value, val.Err
   232  			}
   233  		}
   234  		return 0, ErrNoEstimationFound
   235  	}
   236  
   237  	value := big.NewInt(0)
   238  	if params.FromToken.IsNative() {
   239  		value = params.AmountIn
   240  	}
   241  
   242  	contractAddress, contractType, err := hop.GetContractAddress(params.FromChain.ChainID, params.FromToken.Symbol)
   243  	if err != nil {
   244  		return 0, createBridgeHopErrorResponse(err)
   245  	}
   246  
   247  	input, err := h.packTxInputDataInternally(params, contractType)
   248  	if err != nil {
   249  		return 0, createBridgeHopErrorResponse(err)
   250  	}
   251  
   252  	ethClient, err := h.contractMaker.RPCClient.EthClient(params.FromChain.ChainID)
   253  	if err != nil {
   254  		return 0, createBridgeHopErrorResponse(err)
   255  	}
   256  
   257  	msg := ethereum.CallMsg{
   258  		From:  params.FromAddr,
   259  		To:    &contractAddress,
   260  		Value: value,
   261  		Data:  input,
   262  	}
   263  
   264  	estimation, err := ethClient.EstimateGas(context.Background(), msg)
   265  	if err != nil {
   266  		if !params.FromToken.IsNative() {
   267  			// TODO: this is a temporary solution until we find a better way to estimate the gas
   268  			// hardcoding the estimation for other than ETH, cause we cannot get a proper estimation without having an approval placed first
   269  			// this is an error we're facing otherwise: `execution reverted: ERC20: transfer amount exceeds allowance`
   270  			estimation = 350000
   271  		} else {
   272  			return 0, createBridgeHopErrorResponse(err)
   273  		}
   274  	}
   275  
   276  	increasedEstimation := float64(estimation) * IncreaseEstimatedGasFactor
   277  	return uint64(increasedEstimation), nil
   278  }
   279  
   280  func (h *HopBridgeProcessor) GetContractAddress(params ProcessorInputParams) (common.Address, error) {
   281  	address, _, err := hop.GetContractAddress(params.FromChain.ChainID, params.FromToken.Symbol)
   282  	return address, createBridgeHopErrorResponse(err)
   283  }
   284  
   285  func (h *HopBridgeProcessor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, signerFn bind.SignerFn, lastUsedNonce int64) (tx *ethTypes.Transaction, err error) {
   286  	fromChain := h.networkManager.Find(sendArgs.HopTx.ChainID)
   287  	if fromChain == nil {
   288  		return tx, fmt.Errorf("ChainID not supported %d", sendArgs.HopTx.ChainID)
   289  	}
   290  
   291  	token := h.tokenManager.FindToken(fromChain, sendArgs.HopTx.Symbol)
   292  
   293  	var nonce uint64
   294  	if lastUsedNonce < 0 {
   295  		nonce, err = h.transactor.NextNonce(h.contractMaker.RPCClient, fromChain.ChainID, sendArgs.HopTx.From)
   296  		if err != nil {
   297  			return tx, createBridgeHopErrorResponse(err)
   298  		}
   299  	} else {
   300  		nonce = uint64(lastUsedNonce) + 1
   301  	}
   302  
   303  	argNonce := hexutil.Uint64(nonce)
   304  	sendArgs.HopTx.Nonce = &argNonce
   305  
   306  	txOpts := sendArgs.HopTx.ToTransactOpts(signerFn)
   307  	if token.IsNative() {
   308  		txOpts.Value = (*big.Int)(sendArgs.HopTx.Amount)
   309  	}
   310  
   311  	ethClient, err := h.contractMaker.RPCClient.EthClient(fromChain.ChainID)
   312  	if err != nil {
   313  		return tx, createBridgeHopErrorResponse(err)
   314  	}
   315  
   316  	contractAddress, contractType, err := hop.GetContractAddress(fromChain.ChainID, sendArgs.HopTx.Symbol)
   317  	if err != nil {
   318  		return tx, createBridgeHopErrorResponse(err)
   319  	}
   320  
   321  	bonderKey := makeKey(sendArgs.HopTx.ChainID, sendArgs.HopTx.ChainIDTo, "", "")
   322  	bonderFeeIns, ok := h.bonderFee.Load(bonderKey)
   323  	if !ok {
   324  		return nil, ErrNoBonderFeeFound
   325  	}
   326  	bonderFee := bonderFeeIns.(*BonderFee)
   327  
   328  	switch contractType {
   329  	case hop.CctpL1Bridge:
   330  		tx, err = h.sendCctpL1BridgeTx(contractAddress, ethClient, sendArgs.HopTx.ChainIDTo, sendArgs.HopTx.Recipient, txOpts, bonderFee)
   331  	case hop.L1Bridge:
   332  		tx, err = h.sendL1BridgeTx(contractAddress, ethClient, sendArgs.HopTx.ChainIDTo, sendArgs.HopTx.Recipient, txOpts, token, bonderFee)
   333  	case hop.L2AmmWrapper:
   334  		tx, err = h.sendL2AmmWrapperTx(contractAddress, ethClient, sendArgs.HopTx.ChainIDTo, sendArgs.HopTx.Recipient, txOpts, bonderFee)
   335  	case hop.CctpL2Bridge:
   336  		tx, err = h.sendCctpL2BridgeTx(contractAddress, ethClient, sendArgs.HopTx.ChainIDTo, sendArgs.HopTx.Recipient, txOpts, bonderFee)
   337  	case hop.L2Bridge:
   338  		tx, err = h.sendL2BridgeTx(contractAddress, ethClient, sendArgs.HopTx.ChainIDTo, sendArgs.HopTx.Recipient, txOpts, bonderFee)
   339  	default:
   340  		return tx, ErrContractTypeNotSupported
   341  	}
   342  	if err != nil {
   343  		return tx, createBridgeHopErrorResponse(err)
   344  	}
   345  	err = h.transactor.StoreAndTrackPendingTx(txOpts.From, sendArgs.HopTx.Symbol, sendArgs.HopTx.ChainID, sendArgs.HopTx.MultiTransactionID, tx)
   346  	if err != nil {
   347  		return tx, createBridgeHopErrorResponse(err)
   348  	}
   349  	return tx, nil
   350  }
   351  
   352  func (h *HopBridgeProcessor) Send(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (hash types.Hash, nonce uint64, err error) {
   353  	tx, err := h.sendOrBuild(sendArgs, getSigner(sendArgs.HopTx.ChainID, sendArgs.HopTx.From, verifiedAccount), lastUsedNonce)
   354  	if err != nil {
   355  		return types.Hash{}, 0, createBridgeHopErrorResponse(err)
   356  	}
   357  	return types.Hash(tx.Hash()), tx.Nonce(), nil
   358  }
   359  
   360  func (h *HopBridgeProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) {
   361  	tx, err := h.sendOrBuild(sendArgs, nil, lastUsedNonce)
   362  	return tx, tx.Nonce(), createBridgeHopErrorResponse(err)
   363  }
   364  
   365  func (h *HopBridgeProcessor) CalculateFees(params ProcessorInputParams) (*big.Int, *big.Int, error) {
   366  	bonderKey := makeKey(params.FromChain.ChainID, params.ToChain.ChainID, "", "")
   367  	if params.TestsMode {
   368  		if val, ok := params.TestBonderFeeMap[params.FromToken.Symbol]; ok {
   369  			res := new(big.Int).Sub(params.AmountIn, val)
   370  			bonderFee := &BonderFee{
   371  				AmountIn:                &bigint.BigInt{Int: params.AmountIn},
   372  				Slippage:                5,
   373  				AmountOutMin:            &bigint.BigInt{Int: res},
   374  				DestinationAmountOutMin: &bigint.BigInt{Int: res},
   375  				BonderFee:               &bigint.BigInt{Int: val},
   376  				EstimatedRecieved:       &bigint.BigInt{Int: res},
   377  				Deadline:                time.Now().Add(SevenDaysInSeconds).Unix(),
   378  			}
   379  			h.bonderFee.Store(bonderKey, bonderFee)
   380  			return val, ZeroBigIntValue, nil
   381  		}
   382  		return nil, nil, ErrNoBonderFeeFound
   383  	}
   384  
   385  	hopChainsMap := map[uint64]string{
   386  		walletCommon.EthereumMainnet: "ethereum",
   387  		walletCommon.OptimismMainnet: "optimism",
   388  		walletCommon.ArbitrumMainnet: "arbitrum",
   389  	}
   390  
   391  	fromChainName, ok := hopChainsMap[params.FromChain.ChainID]
   392  	if !ok {
   393  		return nil, nil, ErrFromChainNotSupported
   394  	}
   395  
   396  	toChainName, ok := hopChainsMap[params.ToChain.ChainID]
   397  	if !ok {
   398  		return nil, nil, ErrToChainNotSupported
   399  	}
   400  
   401  	reqParams := netUrl.Values{}
   402  	reqParams.Add("amount", params.AmountIn.String())
   403  	reqParams.Add("token", params.FromToken.Symbol)
   404  	reqParams.Add("fromChain", fromChainName)
   405  	reqParams.Add("toChain", toChainName)
   406  	reqParams.Add("slippage", "0.5") // menas 0.5%
   407  
   408  	url := "https://api.hop.exchange/v1/quote"
   409  	response, err := h.httpClient.DoGetRequest(context.Background(), url, reqParams, nil)
   410  	if err != nil {
   411  		return nil, nil, err
   412  	}
   413  
   414  	bonderFee := &BonderFee{}
   415  	err = json.Unmarshal(response, bonderFee)
   416  	if err != nil {
   417  		return nil, nil, createBridgeHopErrorResponse(err)
   418  	}
   419  
   420  	h.bonderFee.Store(bonderKey, bonderFee)
   421  
   422  	// Remove token fee from bonder fee as said here:
   423  	// https://docs.hop.exchange/v/developer-docs/api/api#get-v1-quote
   424  	// `bonderFee` - The suggested bonder fee for the amount in. The bonder fee also includes the cost of the destination transaction fee.
   425  	tokenFee := new(big.Int).Sub(bonderFee.AmountIn.Int, bonderFee.EstimatedRecieved.Int)
   426  
   427  	return bonderFee.BonderFee.Int, tokenFee, nil
   428  }
   429  
   430  func (h *HopBridgeProcessor) CalculateAmountOut(params ProcessorInputParams) (*big.Int, error) {
   431  	bonderKey := makeKey(params.FromChain.ChainID, params.ToChain.ChainID, "", "")
   432  	bonderFeeIns, ok := h.bonderFee.Load(bonderKey)
   433  	if !ok {
   434  		return nil, ErrNoBonderFeeFound
   435  	}
   436  	bonderFee := bonderFeeIns.(*BonderFee)
   437  	return bonderFee.AmountOutMin.Int, nil
   438  }
   439  
   440  func (h *HopBridgeProcessor) packCctpL1BridgeTx(abi abi.ABI, toChainID uint64, to common.Address, bonderFee *BonderFee) ([]byte, error) {
   441  	return abi.Pack("send",
   442  		big.NewInt(int64(toChainID)),
   443  		to,
   444  		bonderFee.AmountIn.Int,
   445  		bonderFee.BonderFee.Int)
   446  }
   447  
   448  func (h *HopBridgeProcessor) sendCctpL1BridgeTx(contractAddress common.Address, ethClient chain.ClientInterface, toChainID uint64,
   449  	to common.Address, txOpts *bind.TransactOpts, bonderFee *BonderFee) (tx *ethTypes.Transaction, err error) {
   450  	contractInstance, err := hopL1CctpImplementation.NewHopL1CctpImplementation(
   451  		contractAddress,
   452  		ethClient,
   453  	)
   454  	if err != nil {
   455  		return tx, createBridgeHopErrorResponse(err)
   456  	}
   457  
   458  	return contractInstance.Send(
   459  		txOpts,
   460  		big.NewInt(int64(toChainID)),
   461  		to,
   462  		bonderFee.AmountIn.Int,
   463  		bonderFee.BonderFee.Int)
   464  }
   465  
   466  func (h *HopBridgeProcessor) packL1BridgeTx(abi abi.ABI, toChainID uint64, to common.Address, bonderFee *BonderFee) ([]byte, error) {
   467  	return abi.Pack("sendToL2",
   468  		big.NewInt(int64(toChainID)),
   469  		to,
   470  		bonderFee.AmountIn.Int,
   471  		bonderFee.AmountOutMin.Int,
   472  		big.NewInt(bonderFee.Deadline),
   473  		common.Address{},
   474  		ZeroBigIntValue)
   475  }
   476  
   477  func (h *HopBridgeProcessor) sendL1BridgeTx(contractAddress common.Address, ethClient chain.ClientInterface, toChainID uint64,
   478  	to common.Address, txOpts *bind.TransactOpts, token *token.Token, bonderFee *BonderFee) (tx *ethTypes.Transaction, err error) {
   479  	if token.IsNative() {
   480  		contractInstance, err := hopL1EthBridge.NewHopL1EthBridge(
   481  			contractAddress,
   482  			ethClient,
   483  		)
   484  		if err != nil {
   485  			return tx, createBridgeHopErrorResponse(err)
   486  		}
   487  
   488  		return contractInstance.SendToL2(
   489  			txOpts,
   490  			big.NewInt(int64(toChainID)),
   491  			to,
   492  			bonderFee.AmountIn.Int,
   493  			bonderFee.AmountOutMin.Int,
   494  			big.NewInt(bonderFee.Deadline),
   495  			common.Address{},
   496  			ZeroBigIntValue)
   497  	}
   498  
   499  	if token.Symbol == HopSymbol {
   500  		contractInstance, err := hopL1HopBridge.NewHopL1HopBridge(
   501  			contractAddress,
   502  			ethClient,
   503  		)
   504  		if err != nil {
   505  			return tx, createBridgeHopErrorResponse(err)
   506  		}
   507  
   508  		return contractInstance.SendToL2(
   509  			txOpts,
   510  			big.NewInt(int64(toChainID)),
   511  			to,
   512  			bonderFee.AmountIn.Int,
   513  			bonderFee.AmountOutMin.Int,
   514  			big.NewInt(bonderFee.Deadline),
   515  			common.Address{},
   516  			ZeroBigIntValue)
   517  	}
   518  
   519  	contractInstance, err := hopL1Erc20Bridge.NewHopL1Erc20Bridge(
   520  		contractAddress,
   521  		ethClient,
   522  	)
   523  	if err != nil {
   524  		return tx, createBridgeHopErrorResponse(err)
   525  	}
   526  
   527  	return contractInstance.SendToL2(
   528  		txOpts,
   529  		big.NewInt(int64(toChainID)),
   530  		to,
   531  		bonderFee.AmountIn.Int,
   532  		bonderFee.AmountOutMin.Int,
   533  		big.NewInt(bonderFee.Deadline),
   534  		common.Address{},
   535  		ZeroBigIntValue)
   536  
   537  }
   538  
   539  func (h *HopBridgeProcessor) packCctpL2BridgeTx(abi abi.ABI, toChainID uint64, to common.Address, bonderFee *BonderFee) ([]byte, error) {
   540  	return abi.Pack("send",
   541  		big.NewInt(int64(toChainID)),
   542  		to,
   543  		bonderFee.AmountIn.Int,
   544  		bonderFee.BonderFee.Int)
   545  }
   546  
   547  func (h *HopBridgeProcessor) sendCctpL2BridgeTx(contractAddress common.Address, ethClient chain.ClientInterface, toChainID uint64,
   548  	to common.Address, txOpts *bind.TransactOpts, bonderFee *BonderFee) (tx *ethTypes.Transaction, err error) {
   549  	contractInstance, err := hopL2CctpImplementation.NewHopL2CctpImplementation(
   550  		contractAddress,
   551  		ethClient,
   552  	)
   553  	if err != nil {
   554  		return tx, createBridgeHopErrorResponse(err)
   555  	}
   556  
   557  	return contractInstance.Send(
   558  		txOpts,
   559  		big.NewInt(int64(toChainID)),
   560  		to,
   561  		bonderFee.AmountIn.Int,
   562  		bonderFee.BonderFee.Int,
   563  	)
   564  }
   565  
   566  func (h *HopBridgeProcessor) packL2AmmWrapperTx(abi abi.ABI, toChainID uint64, to common.Address, bonderFee *BonderFee) ([]byte, error) {
   567  	return abi.Pack("swapAndSend",
   568  		big.NewInt(int64(toChainID)),
   569  		to,
   570  		bonderFee.AmountIn.Int,
   571  		bonderFee.BonderFee.Int,
   572  		bonderFee.AmountOutMin.Int,
   573  		big.NewInt(bonderFee.Deadline),
   574  		bonderFee.DestinationAmountOutMin.Int,
   575  		big.NewInt(bonderFee.DestinationDeadline))
   576  }
   577  
   578  func (h *HopBridgeProcessor) sendL2AmmWrapperTx(contractAddress common.Address, ethClient chain.ClientInterface, toChainID uint64,
   579  	to common.Address, txOpts *bind.TransactOpts, bonderFee *BonderFee) (tx *ethTypes.Transaction, err error) {
   580  	contractInstance, err := hopL2AmmWrapper.NewHopL2AmmWrapper(
   581  		contractAddress,
   582  		ethClient,
   583  	)
   584  	if err != nil {
   585  		return tx, createBridgeHopErrorResponse(err)
   586  	}
   587  
   588  	return contractInstance.SwapAndSend(
   589  		txOpts,
   590  		big.NewInt(int64(toChainID)),
   591  		to,
   592  		bonderFee.AmountIn.Int,
   593  		bonderFee.BonderFee.Int,
   594  		bonderFee.AmountOutMin.Int,
   595  		big.NewInt(bonderFee.Deadline),
   596  		bonderFee.DestinationAmountOutMin.Int,
   597  		big.NewInt(bonderFee.DestinationDeadline))
   598  }
   599  
   600  func (h *HopBridgeProcessor) packL2BridgeTx(abi abi.ABI, toChainID uint64, to common.Address, bonderFee *BonderFee) ([]byte, error) {
   601  	return abi.Pack("send",
   602  		big.NewInt(int64(toChainID)),
   603  		to,
   604  		bonderFee.AmountIn.Int,
   605  		bonderFee.BonderFee.Int,
   606  		bonderFee.AmountOutMin.Int,
   607  		big.NewInt(bonderFee.Deadline))
   608  }
   609  
   610  func (h *HopBridgeProcessor) sendL2BridgeTx(contractAddress common.Address, ethClient chain.ClientInterface, toChainID uint64,
   611  	to common.Address, txOpts *bind.TransactOpts, bonderFee *BonderFee) (tx *ethTypes.Transaction, err error) {
   612  	fromChainID := ethClient.NetworkID()
   613  	if fromChainID == walletCommon.OptimismMainnet ||
   614  		fromChainID == walletCommon.OptimismSepolia {
   615  		contractInstance, err := hopL2OptimismBridge.NewHopL2OptimismBridge(
   616  			contractAddress,
   617  			ethClient,
   618  		)
   619  		if err != nil {
   620  			return tx, createBridgeHopErrorResponse(err)
   621  		}
   622  
   623  		return contractInstance.Send(
   624  			txOpts,
   625  			big.NewInt(int64(toChainID)),
   626  			to,
   627  			bonderFee.AmountIn.Int,
   628  			bonderFee.BonderFee.Int,
   629  			bonderFee.AmountOutMin.Int,
   630  			big.NewInt(bonderFee.Deadline))
   631  	}
   632  	if fromChainID == walletCommon.ArbitrumMainnet ||
   633  		fromChainID == walletCommon.ArbitrumSepolia {
   634  		contractInstance, err := hopL2ArbitrumBridge.NewHopL2ArbitrumBridge(
   635  			contractAddress,
   636  			ethClient,
   637  		)
   638  		if err != nil {
   639  			return tx, createBridgeHopErrorResponse(err)
   640  		}
   641  
   642  		return contractInstance.Send(
   643  			txOpts,
   644  			big.NewInt(int64(toChainID)),
   645  			to,
   646  			bonderFee.AmountIn.Int,
   647  			bonderFee.BonderFee.Int,
   648  			bonderFee.AmountOutMin.Int,
   649  			big.NewInt(bonderFee.Deadline))
   650  	}
   651  
   652  	return tx, ErrTxForChainNotSupported
   653  }