github.com/0chain/gosdk@v1.17.11/zcnbridge/bridge.go (about)

     1  package zcnbridge
     2  
     3  import (
     4  	"context"
     5  	"encoding/hex"
     6  	"fmt"
     7  	"math/big"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/0chain/gosdk/zcnbridge/ethereum/uniswapnetwork"
    12  	"github.com/0chain/gosdk/zcnbridge/ethereum/uniswaprouter"
    13  
    14  	"github.com/ybbus/jsonrpc/v3"
    15  
    16  	"github.com/0chain/gosdk/zcnbridge/ethereum/zcntoken"
    17  	hdw "github.com/0chain/gosdk/zcncore/ethhdwallet"
    18  	"github.com/spf13/viper"
    19  
    20  	"gopkg.in/natefinch/lumberjack.v2"
    21  
    22  	"github.com/0chain/gosdk/core/logger"
    23  	"github.com/0chain/gosdk/zcnbridge/ethereum"
    24  	"github.com/0chain/gosdk/zcnbridge/ethereum/authorizers"
    25  	"github.com/0chain/gosdk/zcnbridge/ethereum/bridge"
    26  	"github.com/0chain/gosdk/zcnbridge/ethereum/nftconfig"
    27  	"github.com/0chain/gosdk/zcnbridge/log"
    28  	"github.com/0chain/gosdk/zcncore"
    29  
    30  	"github.com/0chain/gosdk/zcnbridge/transaction"
    31  	"github.com/0chain/gosdk/zcnbridge/wallet"
    32  	"github.com/0chain/gosdk/zcnbridge/zcnsc"
    33  	eth "github.com/ethereum/go-ethereum"
    34  	"github.com/ethereum/go-ethereum/accounts"
    35  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    36  	"github.com/ethereum/go-ethereum/common"
    37  	"github.com/ethereum/go-ethereum/core/types"
    38  	"github.com/ethereum/go-ethereum/crypto"
    39  	"github.com/pkg/errors"
    40  	"go.uber.org/zap"
    41  )
    42  
    43  var Logger logger.Logger
    44  var defaultLogLevel = logger.DEBUG
    45  
    46  func init() {
    47  	Logger.Init(defaultLogLevel, "zcnbridge-sdk")
    48  
    49  	Logger.SetLevel(logger.DEBUG)
    50  	ioWriter := &lumberjack.Logger{
    51  		Filename:   "bridge.log",
    52  		MaxSize:    100, // MB
    53  		MaxBackups: 5,   // number of backups
    54  		MaxAge:     28,  //days
    55  		LocalTime:  false,
    56  		Compress:   false, // disabled by default
    57  	}
    58  	Logger.SetLogFile(ioWriter, true)
    59  }
    60  
    61  var (
    62  	DefaultClientIDEncoder = func(id string) []byte {
    63  		result, err := hex.DecodeString(id)
    64  		if err != nil {
    65  			Logger.Fatal(err)
    66  		}
    67  		return result
    68  	}
    69  )
    70  
    71  // CreateSignedTransactionFromKeyStore creates signed transaction from key store
    72  // - client - Ethereum client
    73  // - gasLimitUnits - gas limit in units
    74  func (b *BridgeClient) CreateSignedTransactionFromKeyStore(client EthereumClient, gasLimitUnits uint64) *bind.TransactOpts {
    75  	var (
    76  		signerAddress = common.HexToAddress(b.EthereumAddress)
    77  		password      = b.Password
    78  	)
    79  
    80  	signer := accounts.Account{
    81  		Address: signerAddress,
    82  	}
    83  
    84  	signerAcc, err := b.keyStore.Find(signer)
    85  	if err != nil {
    86  		Logger.Fatal(errors.Wrapf(err, "signer: %s", signerAddress.Hex()))
    87  	}
    88  
    89  	chainID, err := client.ChainID(context.Background())
    90  	if err != nil {
    91  		Logger.Fatal(errors.Wrap(err, "failed to get chain ID"))
    92  	}
    93  
    94  	nonce, err := client.PendingNonceAt(context.Background(), signerAddress)
    95  	if err != nil {
    96  		Logger.Fatal(err)
    97  	}
    98  
    99  	gasPriceWei, err := client.SuggestGasPrice(context.Background())
   100  	if err != nil {
   101  		Logger.Fatal(err)
   102  	}
   103  
   104  	err = b.keyStore.TimedUnlock(signer, password, time.Second*2)
   105  	if err != nil {
   106  		Logger.Fatal(err)
   107  	}
   108  
   109  	opts, err := bind.NewKeyStoreTransactorWithChainID(b.keyStore.GetEthereumKeyStore(), signerAcc, chainID)
   110  	if err != nil {
   111  		Logger.Fatal(err)
   112  	}
   113  
   114  	opts.Nonce = big.NewInt(int64(nonce))
   115  	opts.GasLimit = gasLimitUnits // in units
   116  	opts.GasPrice = gasPriceWei   // wei
   117  
   118  	return opts
   119  }
   120  
   121  // AddEthereumAuthorizer Adds authorizer to Ethereum bridge. Only contract deployer can call this method
   122  //   - ctx go context instance to run the transaction
   123  //   - address Ethereum address of the authorizer
   124  func (b *BridgeClient) AddEthereumAuthorizer(ctx context.Context, address common.Address) (*types.Transaction, error) {
   125  	instance, transactOpts, err := b.prepareAuthorizers(ctx, "addAuthorizers", address)
   126  	if err != nil {
   127  		return nil, errors.Wrap(err, "failed to prepare bridge")
   128  	}
   129  
   130  	tran, err := instance.AddAuthorizers(transactOpts, address)
   131  	if err != nil {
   132  		msg := "failed to execute AddAuthorizers transaction to ClientID = %s with amount = %s"
   133  		return nil, errors.Wrapf(err, msg, zcncore.GetClientWalletID(), address.String())
   134  	}
   135  
   136  	return tran, err
   137  }
   138  
   139  // RemoveEthereumAuthorizer Removes authorizer from Ethereum bridge. Only contract deployer can call this method
   140  //   - ctx go context instance to run the transaction
   141  //   - address Ethereum address of the authorizer
   142  func (b *BridgeClient) RemoveEthereumAuthorizer(ctx context.Context, address common.Address) (*types.Transaction, error) {
   143  	instance, transactOpts, err := b.prepareAuthorizers(ctx, "removeAuthorizers", address)
   144  	if err != nil {
   145  		return nil, errors.Wrap(err, "failed to prepare bridge")
   146  	}
   147  
   148  	tran, err := instance.RemoveAuthorizers(transactOpts, address)
   149  	if err != nil {
   150  		msg := "failed to execute RemoveAuthorizers transaction to ClientID = %s with amount = %s"
   151  		return nil, errors.Wrapf(err, msg, zcncore.GetClientWalletID(), address.String())
   152  	}
   153  
   154  	return tran, err
   155  }
   156  
   157  // AddEthereumAuthorizers add bridge authorizers to the Ethereum authorizers contract
   158  // 		- configDir - configuration directory
   159  func (b *BridgeClient) AddEthereumAuthorizers(configDir string) {
   160  	cfg := viper.New()
   161  	cfg.AddConfigPath(configDir)
   162  	cfg.SetConfigName("authorizers")
   163  	if err := cfg.ReadInConfig(); err != nil {
   164  		fmt.Println(err)
   165  		return
   166  	}
   167  
   168  	mnemonics := cfg.GetStringSlice("authorizers")
   169  
   170  	for _, mnemonic := range mnemonics {
   171  		wallet, err := hdw.NewFromMnemonic(mnemonic)
   172  		if err != nil {
   173  			fmt.Printf("failed to read mnemonic: %v", err)
   174  			continue
   175  		}
   176  
   177  		pathD := hdw.MustParseDerivationPath("m/44'/60'/0'/0/0")
   178  		account, err := wallet.Derive(pathD, true)
   179  		if err != nil {
   180  			fmt.Println(err)
   181  			continue
   182  		}
   183  
   184  		transaction, err := b.AddEthereumAuthorizer(context.TODO(), account.Address)
   185  		if err != nil || transaction == nil {
   186  			fmt.Printf("AddAuthorizer error: %v, Address: %s", err, account.Address.Hex())
   187  			continue
   188  		}
   189  
   190  		status, err := ConfirmEthereumTransaction(transaction.Hash().String(), 100, time.Second*10)
   191  		if err != nil {
   192  			fmt.Println(err)
   193  		}
   194  
   195  		if status == 1 {
   196  			fmt.Printf("Authorizer has been added: %s\n", mnemonic)
   197  		} else {
   198  			fmt.Printf("Authorizer has failed to be added: %s\n", mnemonic)
   199  		}
   200  	}
   201  }
   202  
   203  func (b *BridgeClient) prepareNFTConfig(ctx context.Context, method string, params ...interface{}) (*nftconfig.NFTConfig, *bind.TransactOpts, error) {
   204  	// To (contract)
   205  	contractAddress := common.HexToAddress(b.NFTConfigAddress)
   206  
   207  	// Get ABI of the contract
   208  	abi, err := nftconfig.NFTConfigMetaData.GetAbi()
   209  	if err != nil {
   210  		return nil, nil, errors.Wrap(err, "failed to get nftconfig ABI")
   211  	}
   212  
   213  	// Pack the method argument
   214  	pack, err := abi.Pack(method, params...)
   215  	if err != nil {
   216  		return nil, nil, errors.Wrap(err, "failed to pack arguments")
   217  	}
   218  
   219  	from := common.HexToAddress(b.EthereumAddress)
   220  
   221  	// Gas limits in units
   222  	gasLimitUnits, err := b.ethereumClient.EstimateGas(ctx, eth.CallMsg{
   223  		To:   &contractAddress,
   224  		From: from,
   225  		Data: pack,
   226  	})
   227  	if err != nil {
   228  		return nil, nil, errors.Wrap(err, "failed to estimate gas")
   229  	}
   230  
   231  	// Update gas limits + 10%
   232  	gasLimitUnits = addPercents(gasLimitUnits, 10).Uint64()
   233  
   234  	transactOpts := b.CreateSignedTransactionFromKeyStore(b.ethereumClient, gasLimitUnits)
   235  
   236  	// NFTConfig instance
   237  	cfg, err := nftconfig.NewNFTConfig(contractAddress, b.ethereumClient)
   238  	if err != nil {
   239  		return nil, nil, errors.Wrap(err, "failed to create nftconfig instance")
   240  	}
   241  
   242  	return cfg, transactOpts, nil
   243  }
   244  
   245  // EncodePackInt do abi.encodedPack(string, int), it is used for setting plan id for royalty
   246  //   - key key for the plan
   247  //   - param plan id
   248  func EncodePackInt64(key string, param int64) common.Hash {
   249  	return crypto.Keccak256Hash(
   250  		[]byte(key),
   251  		common.LeftPadBytes(big.NewInt(param).Bytes(), 32),
   252  	)
   253  }
   254  
   255  // NFTConfigSetUint256  sets a uint256 field in the NFT config, given the key as a string
   256  //   - ctx go context instance to run the transaction
   257  //   - key key for this field
   258  //   - value value to set
   259  func (b *BridgeClient) NFTConfigSetUint256(ctx context.Context, key string, value int64) (*types.Transaction, error) {
   260  	kkey := crypto.Keccak256Hash([]byte(key))
   261  	return b.NFTConfigSetUint256Raw(ctx, kkey, value)
   262  }
   263  
   264  // NFTConfigSetUint256Raw sets a uint256 field in the NFT config, given the key as a Keccak256 hash
   265  //   - ctx go context instance to run the transaction
   266  //   - key key for this field (hased)
   267  //   - value value to set
   268  func (b *BridgeClient) NFTConfigSetUint256Raw(ctx context.Context, key common.Hash, value int64) (*types.Transaction, error) {
   269  	if value < 0 {
   270  		return nil, errors.New("value must be greater than zero")
   271  	}
   272  
   273  	v := big.NewInt(value)
   274  	Logger.Debug("NFT config setUint256", zap.String("key", key.String()), zap.Any("value", v))
   275  	instance, transactOpts, err := b.prepareNFTConfig(ctx, "setUint256", key, v)
   276  	if err != nil {
   277  		return nil, errors.Wrap(err, "failed to prepare bridge")
   278  	}
   279  
   280  	tran, err := instance.SetUint256(transactOpts, key, v)
   281  	if err != nil {
   282  		msg := "failed to execute setUint256 transaction to ClientID = %s with key = %s, value = %v"
   283  		return nil, errors.Wrapf(err, msg, zcncore.GetClientWalletID(), key, v)
   284  	}
   285  
   286  	return tran, err
   287  }
   288  
   289  // NFTConfigGetUint256 retrieves a uint256 field in the NFT config, given the key as a string
   290  //   - ctx go context instance to run the transaction
   291  //   - key key for this field
   292  //   - keyParam additional key parameter, only the first item is used
   293  func (b *BridgeClient) NFTConfigGetUint256(ctx context.Context, key string, keyParam ...int64) (string, int64, error) {
   294  	kkey := crypto.Keccak256Hash([]byte(key))
   295  	if len(keyParam) > 0 {
   296  		kkey = EncodePackInt64(key, keyParam[0])
   297  	}
   298  
   299  	contractAddress := common.HexToAddress(b.NFTConfigAddress)
   300  
   301  	cfg, err := nftconfig.NewNFTConfig(contractAddress, b.ethereumClient)
   302  	if err != nil {
   303  		return "", 0, errors.Wrap(err, "failed to create NFT config instance")
   304  	}
   305  
   306  	v, err := cfg.GetUint256(nil, kkey)
   307  	if err != nil {
   308  		Logger.Error("NFTConfig GetUint256 FAILED", zap.Error(err))
   309  		msg := "failed to execute getUint256 call, key = %s"
   310  		return "", 0, errors.Wrapf(err, msg, kkey)
   311  	}
   312  	return kkey.String(), v.Int64(), err
   313  }
   314  
   315  // NFTConfigSetAddress sets an address field in the NFT config, given the key as a string
   316  //   - ctx go context instance to run the transaction
   317  //   - key key for this field
   318  //   - address address to set
   319  func (b *BridgeClient) NFTConfigSetAddress(ctx context.Context, key, address string) (*types.Transaction, error) {
   320  	kkey := crypto.Keccak256Hash([]byte(key))
   321  	// return b.NFTConfigSetAddress(ctx, kkey, address)
   322  
   323  	Logger.Debug("NFT config setAddress",
   324  		zap.String("key", kkey.String()),
   325  		zap.String("address", address))
   326  
   327  	addr := common.HexToAddress(address)
   328  	instance, transactOpts, err := b.prepareNFTConfig(ctx, "setAddress", kkey, addr)
   329  	if err != nil {
   330  		return nil, errors.Wrap(err, "failed to prepare bridge")
   331  	}
   332  
   333  	tran, err := instance.SetAddress(transactOpts, kkey, addr)
   334  	if err != nil {
   335  		msg := "failed to execute setAddress transaction to ClientID = %s with key = %s, value = %v"
   336  		return nil, errors.Wrapf(err, msg, zcncore.GetClientWalletID(), key, address)
   337  	}
   338  
   339  	return tran, err
   340  }
   341  
   342  // NFTConfigGetAddress retrieves an address field in the NFT config, given the key as a string
   343  //   - ctx go context instance to run the transaction
   344  //   - key key for this field
   345  func (b *BridgeClient) NFTConfigGetAddress(ctx context.Context, key string) (string, string, error) {
   346  	kkey := crypto.Keccak256Hash([]byte(key))
   347  
   348  	contractAddress := common.HexToAddress(b.NFTConfigAddress)
   349  
   350  	cfg, err := nftconfig.NewNFTConfig(contractAddress, b.ethereumClient)
   351  	if err != nil {
   352  		return "", "", errors.Wrap(err, "failed to create NFT config instance")
   353  	}
   354  
   355  	v, err := cfg.GetAddress(nil, kkey)
   356  	if err != nil {
   357  		Logger.Error("NFTConfig GetAddress FAILED", zap.Error(err))
   358  		msg := "failed to execute getAddress call, key = %s"
   359  		return "", "", errors.Wrapf(err, msg, kkey)
   360  	}
   361  	return kkey.String(), v.String(), err
   362  }
   363  
   364  // IncreaseBurnerAllowance Increases allowance for bridge contract address to transfer
   365  // ERC-20 tokens on behalf of the zcntoken owner to the Burn TokenPool
   366  // During the burn the script transfers amount from zcntoken owner to the bridge burn zcntoken pool
   367  // Example: owner wants to burn some amount.
   368  // The contract will transfer some amount from owner address to the pool.
   369  // So the owner must call IncreaseAllowance of the WZCN zcntoken with 2 parameters:
   370  // spender address which is the bridge contract and amount to be burned (transferred)
   371  // Token signature: "increaseApproval(address,uint256)"
   372  //   - ctx go context instance to run the transaction
   373  //   - allowanceAmount amount to increase
   374  //
   375  //nolint:funlen
   376  func (b *BridgeClient) IncreaseBurnerAllowance(ctx context.Context, allowanceAmount uint64) (*types.Transaction, error) {
   377  	if allowanceAmount <= 0 {
   378  		return nil, errors.New("amount must be greater than zero")
   379  	}
   380  
   381  	// 1. Data Parameter (spender)
   382  	spenderAddress := common.HexToAddress(b.BridgeAddress)
   383  
   384  	// 2. Data Parameter (amount)
   385  	amount := big.NewInt(int64(allowanceAmount))
   386  
   387  	tokenAddress := common.HexToAddress(b.TokenAddress)
   388  
   389  	tokenInstance, transactOpts, err := b.prepareToken(ctx, "increaseApproval", tokenAddress, spenderAddress, amount)
   390  	if err != nil {
   391  		return nil, errors.Wrap(err, "failed to prepare zcntoken")
   392  	}
   393  
   394  	Logger.Info(
   395  		"Starting IncreaseApproval",
   396  		zap.String("zcntoken", tokenAddress.String()),
   397  		zap.String("spender", spenderAddress.String()),
   398  		zap.Int64("amount", amount.Int64()),
   399  	)
   400  
   401  	tran, err := tokenInstance.IncreaseApproval(transactOpts, spenderAddress, amount)
   402  	if err != nil {
   403  		Logger.Error(
   404  			"IncreaseApproval FAILED",
   405  			zap.String("zcntoken", tokenAddress.String()),
   406  			zap.String("spender", spenderAddress.String()),
   407  			zap.Int64("amount", amount.Int64()),
   408  			zap.Error(err))
   409  
   410  		return nil, errors.Wrapf(err, "failed to send `IncreaseApproval` transaction")
   411  	}
   412  
   413  	Logger.Info(
   414  		"Posted IncreaseApproval",
   415  		zap.String("hash", tran.Hash().String()),
   416  		zap.String("zcntoken", tokenAddress.String()),
   417  		zap.String("spender", spenderAddress.String()),
   418  		zap.Int64("amount", amount.Int64()),
   419  	)
   420  
   421  	return tran, nil
   422  }
   423  
   424  // GetTokenBalance returns balance of the current client for the zcntoken address
   425  func (b *BridgeClient) GetTokenBalance() (*big.Int, error) {
   426  	// 1. Token address parameter
   427  	of := common.HexToAddress(b.TokenAddress)
   428  
   429  	// 2. User's Ethereum wallet address parameter
   430  	from := common.HexToAddress(b.EthereumAddress)
   431  
   432  	tokenInstance, err := zcntoken.NewToken(of, b.ethereumClient)
   433  	if err != nil {
   434  		return nil, errors.Wrap(err, "failed to initialize zcntoken instance")
   435  	}
   436  
   437  	wei, err := tokenInstance.BalanceOf(&bind.CallOpts{}, from)
   438  	if err != nil {
   439  		return nil, errors.Wrapf(err, "failed to call `BalanceOf` for %s", b.EthereumAddress)
   440  	}
   441  
   442  	return wei, nil
   443  }
   444  
   445  // VerifyZCNTransaction verifies 0CHain transaction
   446  //   - ctx go context instance to run the transaction
   447  //   - hash transaction hash
   448  func (b *BridgeClient) VerifyZCNTransaction(ctx context.Context, hash string) (transaction.Transaction, error) {
   449  	return transaction.Verify(ctx, hash)
   450  }
   451  
   452  // SignWithEthereumChain signs the digest with Ethereum chain signer taking key from the current user key storage
   453  //   - message message to sign
   454  func (b *BridgeClient) SignWithEthereumChain(message string) ([]byte, error) {
   455  	hash := crypto.Keccak256Hash([]byte(message))
   456  
   457  	signer := accounts.Account{
   458  		Address: common.HexToAddress(b.EthereumAddress),
   459  	}
   460  
   461  	signerAcc, err := b.keyStore.Find(signer)
   462  	if err != nil {
   463  		Logger.Fatal(err)
   464  	}
   465  
   466  	signature, err := b.keyStore.SignHash(signerAcc, hash.Bytes())
   467  	if err != nil {
   468  		return nil, err
   469  	}
   470  
   471  	if err != nil {
   472  		return []byte{}, errors.Wrap(err, "failed to sign the message")
   473  	}
   474  
   475  	return signature, nil
   476  }
   477  
   478  // GetUserNonceMinted Returns nonce for a specified Ethereum address
   479  //   - ctx go context instance to run the transaction
   480  //   - rawEthereumAddress Ethereum address
   481  func (b *BridgeClient) GetUserNonceMinted(ctx context.Context, rawEthereumAddress string) (*big.Int, error) {
   482  	ethereumAddress := common.HexToAddress(rawEthereumAddress)
   483  
   484  	contractAddress := common.HexToAddress(b.BridgeAddress)
   485  
   486  	bridgeInstance, err := bridge.NewBridge(contractAddress, b.ethereumClient)
   487  	if err != nil {
   488  		return nil, errors.Wrap(err, "failed to create bridge instance")
   489  	}
   490  
   491  	var nonce *big.Int
   492  
   493  	nonce, err = bridgeInstance.GetUserNonceMinted(nil, ethereumAddress)
   494  	if err != nil {
   495  		Logger.Error("GetUserNonceMinted FAILED", zap.Error(err))
   496  		msg := "failed to execute GetUserNonceMinted call, ethereumAddress = %s"
   497  		return nil, errors.Wrapf(err, msg, rawEthereumAddress)
   498  	}
   499  
   500  	return nonce, err
   501  }
   502  
   503  // ResetUserNonceMinted Resets nonce for a specified Ethereum address
   504  //   - ctx go context instance to run the transaction
   505  func (b *BridgeClient) ResetUserNonceMinted(ctx context.Context) (*types.Transaction, error) {
   506  	bridgeInstance, transactOpts, err := b.prepareBridge(ctx, b.EthereumAddress, "resetUserNonceMinted")
   507  	if err != nil {
   508  		return nil, errors.Wrap(err, "failed to prepare bridge")
   509  	}
   510  
   511  	tran, err := bridgeInstance.ResetUserNonceMinted(transactOpts)
   512  	if err != nil {
   513  		Logger.Error("ResetUserNonceMinted FAILED", zap.Error(err))
   514  		msg := "failed to execute ResetUserNonceMinted call, ethereumAddress = %s"
   515  		return nil, errors.Wrapf(err, msg, b.EthereumAddress)
   516  	}
   517  
   518  	Logger.Info(
   519  		"Posted ResetUserMintedNonce",
   520  		zap.String("ethereumWallet", b.EthereumAddress),
   521  	)
   522  
   523  	return tran, err
   524  }
   525  
   526  // MintWZCN Mint ZCN tokens on behalf of the 0ZCN client
   527  //   - ctx go context instance to run the transaction
   528  //   - payload received from authorizers
   529  //
   530  // ERC20 signature: "mint(address,uint256,bytes,uint256,bytes[])"
   531  func (b *BridgeClient) MintWZCN(ctx context.Context, payload *ethereum.MintPayload) (*types.Transaction, error) {
   532  	if DefaultClientIDEncoder == nil {
   533  		return nil, errors.New("DefaultClientIDEncoder must be setup")
   534  	}
   535  
   536  	// 1. Burned amount parameter
   537  	amount := new(big.Int)
   538  	amount.SetInt64(payload.Amount) // wei
   539  
   540  	// 2. Transaction ID Parameter of burn operation (zcnTxd string as []byte)
   541  	zcnTxd := DefaultClientIDEncoder(payload.ZCNTxnID)
   542  
   543  	// 3. Nonce Parameter generated during burn operation
   544  	nonce := new(big.Int)
   545  	nonce.SetInt64(payload.Nonce)
   546  
   547  	// 4. Signature
   548  	// For requirements from ERC20 authorizer, the signature length must be 65
   549  	var sigs [][]byte
   550  	for _, signature := range payload.Signatures {
   551  		sigs = append(sigs, signature.Signature)
   552  	}
   553  
   554  	// 5. To Ethereum address
   555  
   556  	toAddress := common.HexToAddress(payload.To)
   557  
   558  	bridgeInstance, transactOpts, err := b.prepareBridge(ctx, payload.To, "mint", toAddress, amount, zcnTxd, nonce, sigs)
   559  	if err != nil {
   560  		return nil, errors.Wrap(err, "failed to prepare bridge")
   561  	}
   562  
   563  	Logger.Info(
   564  		"Staring Mint WZCN",
   565  		zap.Int64("amount", amount.Int64()),
   566  		zap.String("zcnTxd", string(zcnTxd)),
   567  		zap.String("nonce", nonce.String()))
   568  
   569  	var tran *types.Transaction
   570  	tran, err = bridgeInstance.Mint(transactOpts, toAddress, amount, zcnTxd, nonce, sigs)
   571  	if err != nil {
   572  		Logger.Error("Mint WZCN FAILED", zap.Error(err))
   573  		msg := "failed to execute MintWZCN transaction, amount = %s, ZCN TrxID = %s"
   574  		return nil, errors.Wrapf(err, msg, amount, zcnTxd)
   575  	}
   576  
   577  	Logger.Info(
   578  		"Posted Mint WZCN",
   579  		zap.String("hash", tran.Hash().String()),
   580  		zap.Int64("amount", amount.Int64()),
   581  		zap.String("zcnTxd", string(zcnTxd)),
   582  		zap.String("nonce", nonce.String()),
   583  	)
   584  
   585  	return tran, err
   586  }
   587  
   588  // BurnWZCN Burns WZCN tokens on behalf of the 0ZCN client
   589  //   - ctx go context instance to run the transaction
   590  //   - amountTokens amount of tokens to burn
   591  //
   592  // ERC20 signature: "burn(uint256,bytes)"
   593  func (b *BridgeClient) BurnWZCN(ctx context.Context, amountTokens uint64) (*types.Transaction, error) {
   594  	if DefaultClientIDEncoder == nil {
   595  		return nil, errors.New("DefaultClientIDEncoder must be setup")
   596  	}
   597  
   598  	// 1. Data Parameter (amount to burn)
   599  	clientID := DefaultClientIDEncoder(zcncore.GetClientWalletID())
   600  
   601  	// 2. Data Parameter (signature)
   602  	amount := new(big.Int)
   603  	amount.SetInt64(int64(amountTokens))
   604  
   605  	bridgeInstance, transactOpts, err := b.prepareBridge(ctx, b.EthereumAddress, "burn", amount, clientID)
   606  	if err != nil {
   607  		return nil, errors.Wrap(err, "failed to prepare bridge")
   608  	}
   609  
   610  	Logger.Info(
   611  		"Staring Burn WZCN",
   612  		zap.Int64("amount", amount.Int64()),
   613  	)
   614  
   615  	tran, err := bridgeInstance.Burn(transactOpts, amount, clientID)
   616  	if err != nil {
   617  		msg := "failed to execute Burn WZCN transaction to ClientID = %s with amount = %s"
   618  		return nil, errors.Wrapf(err, msg, zcncore.GetClientWalletID(), amount)
   619  	}
   620  
   621  	Logger.Info(
   622  		"Posted Burn WZCN",
   623  		zap.String("clientID", zcncore.GetClientWalletID()),
   624  		zap.Int64("amount", amount.Int64()),
   625  	)
   626  
   627  	return tran, err
   628  }
   629  
   630  // MintZCN mints ZCN tokens after receiving proof-of-burn of WZCN tokens
   631  //   - ctx go context instance to run the transaction
   632  //   - payload received from authorizers
   633  func (b *BridgeClient) MintZCN(ctx context.Context, payload *zcnsc.MintPayload) (string, error) {
   634  	trx, err := b.transactionProvider.NewTransactionEntity(0)
   635  	if err != nil {
   636  		log.Logger.Fatal("failed to create new transaction", zap.Error(err))
   637  	}
   638  
   639  	Logger.Info(
   640  		"Starting MINT smart contract",
   641  		zap.String("sc address", wallet.ZCNSCSmartContractAddress),
   642  		zap.String("function", wallet.MintFunc),
   643  		zap.Int64("mint amount", int64(payload.Amount)))
   644  
   645  	hash, err := trx.ExecuteSmartContract(
   646  		ctx,
   647  		wallet.ZCNSCSmartContractAddress,
   648  		wallet.MintFunc,
   649  		payload,
   650  		0)
   651  
   652  	if err != nil {
   653  		return "", errors.Wrap(err, fmt.Sprintf("failed to execute smart contract, hash = %s", hash))
   654  	}
   655  
   656  	Logger.Info(
   657  		"Mint ZCN transaction",
   658  		zap.String("hash", hash),
   659  		zap.Int64("mint amount", int64(payload.Amount)))
   660  
   661  	return hash, nil
   662  }
   663  
   664  // BurnZCN burns ZCN tokens before conversion from ZCN to WZCN as a first step
   665  //   - ctx go context instance to run the transaction
   666  //   - amount amount of tokens to burn
   667  //   - txnfee transaction fee
   668  func (b *BridgeClient) BurnZCN(ctx context.Context, amount, txnfee uint64) (transaction.Transaction, error) {
   669  	payload := zcnsc.BurnPayload{
   670  		EthereumAddress: b.EthereumAddress,
   671  	}
   672  
   673  	trx, err := b.transactionProvider.NewTransactionEntity(txnfee)
   674  	if err != nil {
   675  		log.Logger.Fatal("failed to create new transaction", zap.Error(err))
   676  	}
   677  
   678  	Logger.Info(
   679  		"Starting BURN smart contract",
   680  		zap.String("sc address", wallet.ZCNSCSmartContractAddress),
   681  		zap.String("function", wallet.BurnFunc),
   682  		zap.Uint64("burn amount", amount),
   683  	)
   684  
   685  	var hash string
   686  	hash, err = trx.ExecuteSmartContract(
   687  		ctx,
   688  		wallet.ZCNSCSmartContractAddress,
   689  		wallet.BurnFunc,
   690  		payload,
   691  		amount,
   692  	)
   693  
   694  	if err != nil {
   695  		Logger.Error("Burn ZCN transaction FAILED", zap.Error(err))
   696  		return trx, errors.Wrap(err, fmt.Sprintf("failed to execute smart contract, hash = %s", hash))
   697  	}
   698  
   699  	err = trx.Verify(context.Background())
   700  	if err != nil {
   701  		return trx, errors.Wrap(err, fmt.Sprintf("failed to verify smart contract, hash = %s", hash))
   702  	}
   703  
   704  	Logger.Info(
   705  		"Burn ZCN transaction",
   706  		zap.String("hash", hash),
   707  		zap.Uint64("burn amount", amount),
   708  		zap.Uint64("amount", amount),
   709  	)
   710  
   711  	return trx, nil
   712  }
   713  
   714  // ApproveUSDCSwap provides opportunity to approve swap operation for ERC20 tokens
   715  //   - ctx go context instance to run the transaction
   716  //   - source source amount
   717  func (b *BridgeClient) ApproveUSDCSwap(ctx context.Context, source uint64) (*types.Transaction, error) {
   718  	// 1. USDC token smart contract address
   719  	tokenAddress := common.HexToAddress(UsdcTokenAddress)
   720  
   721  	// 2. Swap source amount parameter.
   722  	sourceInt := big.NewInt(int64(source))
   723  
   724  	// 3. User's Ethereum wallet address parameter
   725  	spenderAddress := common.HexToAddress(b.UniswapAddress)
   726  
   727  	tokenInstance, transactOpts, err := b.prepareToken(ctx, "approve", tokenAddress, spenderAddress, sourceInt)
   728  	if err != nil {
   729  		return nil, errors.Wrap(err, "failed to prepare usdctoken")
   730  	}
   731  
   732  	Logger.Info(
   733  		"Starting ApproveUSDCSwap",
   734  		zap.String("usdctoken", tokenAddress.String()),
   735  		zap.String("spender", spenderAddress.String()),
   736  		zap.Int64("source", sourceInt.Int64()),
   737  	)
   738  
   739  	var tran *types.Transaction
   740  
   741  	tran, err = tokenInstance.Approve(transactOpts, spenderAddress, sourceInt)
   742  	if err != nil {
   743  		return nil, errors.Wrap(err, "failed to execute approve transaction")
   744  	}
   745  
   746  	return tran, nil
   747  }
   748  
   749  // GetETHSwapAmount retrieves ETH swap amount from the given source.
   750  //   - ctx go context instance to run the transaction
   751  //   - source source amount
   752  func (b *BridgeClient) GetETHSwapAmount(ctx context.Context, source uint64) (*big.Int, error) {
   753  	// 1. Uniswap smart contract address
   754  	contractAddress := common.HexToAddress(UniswapRouterAddress)
   755  
   756  	// 2. User's Ethereum wallet address parameter
   757  	from := common.HexToAddress(b.EthereumAddress)
   758  
   759  	// 3. Swap source amount parameter.
   760  	sourceInt := big.NewInt(int64(source))
   761  
   762  	// 3. Swap path parameter.
   763  	path := []common.Address{
   764  		common.HexToAddress(WethTokenAddress),
   765  		common.HexToAddress(b.TokenAddress)}
   766  
   767  	uniswapRouterInstance, err := uniswaprouter.NewUniswaprouter(contractAddress, b.ethereumClient)
   768  	if err != nil {
   769  		return nil, errors.Wrap(err, "failed to initialize uniswaprouter instance")
   770  	}
   771  
   772  	Logger.Info(
   773  		"Starting GetETHSwapAmount",
   774  		zap.Uint64("source", source))
   775  
   776  	var result []*big.Int
   777  
   778  	result, err = uniswapRouterInstance.GetAmountsIn(&bind.CallOpts{From: from}, sourceInt, path)
   779  	if err != nil {
   780  		Logger.Error("GetAmountsIn FAILED", zap.Error(err))
   781  		msg := "failed to execute GetAmountsIn call, ethereumAddress = %s"
   782  
   783  		return nil, errors.Wrapf(err, msg, from)
   784  	}
   785  
   786  	return result[0], nil
   787  }
   788  
   789  // SwapETH provides opportunity to perform zcn token swap operation using ETH as source token.
   790  //   - ctx go context instance to run the transaction
   791  //   - source source amount
   792  //   - target target amount
   793  func (b *BridgeClient) SwapETH(ctx context.Context, source uint64, target uint64) (*types.Transaction, error) {
   794  	// 1. Swap source amount parameter.
   795  	sourceInt := big.NewInt(int64(source))
   796  
   797  	// 2. Swap target amount parameter.
   798  	targetInt := big.NewInt(int64(target))
   799  
   800  	uniswapNetworkInstance, transactOpts, err := b.prepareUniswapNetwork(
   801  		ctx, sourceInt, "swapETHForZCNExactAmountOut", targetInt)
   802  	if err != nil {
   803  		return nil, errors.Wrap(err, "failed to prepare uniswapnetwork")
   804  	}
   805  
   806  	Logger.Info(
   807  		"Starting SwapETH",
   808  		zap.Uint64("source", source),
   809  		zap.Uint64("target", target))
   810  
   811  	var tran *types.Transaction
   812  
   813  	tran, err = uniswapNetworkInstance.SwapETHForZCNExactAmountOut(transactOpts, targetInt)
   814  	if err != nil {
   815  		return nil, errors.Wrap(err, "failed to execute swapETHForZCNExactAmountOut transaction")
   816  	}
   817  
   818  	return tran, nil
   819  }
   820  
   821  // SwapUSDC provides opportunity to perform zcn token swap operation using USDC as source token.
   822  //   - ctx go context instance to run the transaction
   823  //   - source source amount
   824  //   - target target amount
   825  func (b *BridgeClient) SwapUSDC(ctx context.Context, source uint64, target uint64) (*types.Transaction, error) {
   826  	// 1. Swap target amount parameter.
   827  	sourceInt := big.NewInt(int64(source))
   828  
   829  	// 2. Swap source amount parameter.
   830  	targetInt := big.NewInt(int64(target))
   831  
   832  	uniswapNetworkInstance, transactOpts, err := b.prepareUniswapNetwork(
   833  		ctx, big.NewInt(0), "swapUSDCForZCNExactAmountOut", targetInt, sourceInt)
   834  	if err != nil {
   835  		return nil, errors.Wrap(err, "failed to prepare uniswapnetwork")
   836  	}
   837  
   838  	Logger.Info(
   839  		"Starting SwapUSDC",
   840  		zap.Uint64("source", source),
   841  		zap.Uint64("target", target))
   842  
   843  	var tran *types.Transaction
   844  
   845  	tran, err = uniswapNetworkInstance.SwapUSDCForZCNExactAmountOut(transactOpts, targetInt, sourceInt)
   846  	if err != nil {
   847  		return nil, errors.Wrap(err, "failed to execute swapUSDCForZCNExactAmountOut transaction")
   848  	}
   849  
   850  	return tran, nil
   851  }
   852  
   853  // prepareUniswapNetwork performs uniswap network smart contract preparation actions.
   854  func (b *BridgeClient) prepareUniswapNetwork(ctx context.Context, value *big.Int, method string, params ...interface{}) (*uniswapnetwork.Uniswap, *bind.TransactOpts, error) {
   855  	// 1. Uniswap smart contract address
   856  	contractAddress := common.HexToAddress(b.UniswapAddress)
   857  
   858  	// 2. To address parameter.
   859  	to := common.HexToAddress(b.TokenAddress)
   860  
   861  	// 3. From address parameter.
   862  	from := common.HexToAddress(b.EthereumAddress)
   863  
   864  	abi, err := uniswapnetwork.UniswapMetaData.GetAbi()
   865  	if err != nil {
   866  		return nil, nil, errors.Wrap(err, "failed to get uniswaprouter abi")
   867  	}
   868  
   869  	var pack []byte
   870  
   871  	pack, err = abi.Pack(method, params...)
   872  	if err != nil {
   873  		return nil, nil, errors.Wrap(err, "failed to pack arguments")
   874  	}
   875  
   876  	opts := eth.CallMsg{
   877  		To:   &to,
   878  		From: from,
   879  		Data: pack,
   880  	}
   881  
   882  	if value.Int64() != 0 {
   883  		opts.Value = value
   884  	}
   885  
   886  	transactOpts := b.CreateSignedTransactionFromKeyStore(b.ethereumClient, 0)
   887  	if value.Int64() != 0 {
   888  		transactOpts.Value = value
   889  	}
   890  
   891  	var uniswapNetworkInstance *uniswapnetwork.Uniswap
   892  
   893  	uniswapNetworkInstance, err = uniswapnetwork.NewUniswap(contractAddress, b.ethereumClient)
   894  	if err != nil {
   895  		return nil, nil, errors.Wrap(err, "failed to initialize uniswapnetwork instance")
   896  	}
   897  
   898  	return uniswapNetworkInstance, transactOpts, nil
   899  }
   900  
   901  func (b *BridgeClient) prepareToken(ctx context.Context, method string, tokenAddress common.Address, params ...interface{}) (*zcntoken.Token, *bind.TransactOpts, error) {
   902  	abi, err := zcntoken.TokenMetaData.GetAbi()
   903  	if err != nil {
   904  		return nil, nil, errors.Wrap(err, "failed to get zcntoken abi")
   905  	}
   906  
   907  	pack, err := abi.Pack(method, params...)
   908  	if err != nil {
   909  		return nil, nil, errors.Wrap(err, "failed to pack arguments")
   910  	}
   911  
   912  	from := common.HexToAddress(b.EthereumAddress)
   913  
   914  	gasLimitUnits, err := b.ethereumClient.EstimateGas(ctx, eth.CallMsg{
   915  		To:   &tokenAddress,
   916  		From: from,
   917  		Data: pack,
   918  	})
   919  	if err != nil {
   920  		return nil, nil, errors.Wrap(err, "failed to estimate gas limit")
   921  	}
   922  
   923  	gasLimitUnits = addPercents(gasLimitUnits, 10).Uint64()
   924  
   925  	transactOpts := b.CreateSignedTransactionFromKeyStore(b.ethereumClient, gasLimitUnits)
   926  
   927  	var tokenInstance *zcntoken.Token
   928  
   929  	tokenInstance, err = zcntoken.NewToken(tokenAddress, b.ethereumClient)
   930  	if err != nil {
   931  		return nil, nil, errors.Wrap(err, "failed to initialize zcntoken instance")
   932  	}
   933  
   934  	return tokenInstance, transactOpts, nil
   935  }
   936  
   937  func (b *BridgeClient) prepareAuthorizers(ctx context.Context, method string, params ...interface{}) (*authorizers.Authorizers, *bind.TransactOpts, error) {
   938  	// To (contract)
   939  	contractAddress := common.HexToAddress(b.AuthorizersAddress)
   940  
   941  	// Get ABI of the contract
   942  	abi, err := authorizers.AuthorizersMetaData.GetAbi()
   943  	if err != nil {
   944  		return nil, nil, errors.Wrap(err, "failed to get ABI")
   945  	}
   946  
   947  	// Pack the method argument
   948  	pack, err := abi.Pack(method, params...)
   949  	if err != nil {
   950  		return nil, nil, errors.Wrap(err, "failed to pack arguments")
   951  	}
   952  
   953  	from := common.HexToAddress(b.EthereumAddress)
   954  
   955  	// Gas limits in units
   956  	gasLimitUnits, err := b.ethereumClient.EstimateGas(ctx, eth.CallMsg{
   957  		To:   &contractAddress,
   958  		From: from,
   959  		Data: pack,
   960  	})
   961  	if err != nil {
   962  		return nil, nil, errors.Wrap(err, "failed to estimate gas")
   963  	}
   964  
   965  	// Update gas limits + 10%
   966  	gasLimitUnits = addPercents(gasLimitUnits, 10).Uint64()
   967  
   968  	transactOpts := b.CreateSignedTransactionFromKeyStore(b.ethereumClient, gasLimitUnits)
   969  
   970  	// Authorizers instance
   971  	authorizersInstance, err := authorizers.NewAuthorizers(contractAddress, b.ethereumClient)
   972  	if err != nil {
   973  		return nil, nil, errors.Wrap(err, "failed to create authorizers instance")
   974  	}
   975  
   976  	return authorizersInstance, transactOpts, nil
   977  }
   978  
   979  func (b *BridgeClient) prepareBridge(ctx context.Context, ethereumAddress, method string, params ...interface{}) (*bridge.Bridge, *bind.TransactOpts, error) {
   980  	// To (contract)
   981  	contractAddress := common.HexToAddress(b.BridgeAddress)
   982  
   983  	//Get ABI of the contract
   984  	abi, err := bridge.BridgeMetaData.GetAbi()
   985  	if err != nil {
   986  		return nil, nil, errors.Wrap(err, "failed to get ABI")
   987  	}
   988  
   989  	//Pack the method argument
   990  	pack, err := abi.Pack(method, params...)
   991  	if err != nil {
   992  		return nil, nil, errors.Wrap(err, "failed to pack arguments")
   993  	}
   994  
   995  	//Gas limits in units
   996  	fromAddress := common.HexToAddress(ethereumAddress)
   997  
   998  	gasLimitUnits, err := b.ethereumClient.EstimateGas(ctx, eth.CallMsg{
   999  		To:   &contractAddress,
  1000  		From: fromAddress,
  1001  		Data: pack,
  1002  	})
  1003  	if err != nil {
  1004  		return nil, nil, errors.Wrap(err, "failed to estimate gas")
  1005  	}
  1006  
  1007  	//Update gas limits + 10%
  1008  	gasLimitUnits = addPercents(gasLimitUnits, 10).Uint64()
  1009  
  1010  	transactOpts := b.CreateSignedTransactionFromKeyStore(b.ethereumClient, gasLimitUnits)
  1011  
  1012  	// BridgeClient instance
  1013  	bridgeInstance, err := bridge.NewBridge(contractAddress, b.ethereumClient)
  1014  	if err != nil {
  1015  		return nil, nil, errors.Wrap(err, "failed to create bridge instance")
  1016  	}
  1017  
  1018  	return bridgeInstance, transactOpts, nil
  1019  }
  1020  
  1021  // getProviderType validates the provider url and exposes pre-defined type definition.
  1022  func (b *BridgeClient) getProviderType() int {
  1023  	if strings.Contains(b.EthereumNodeURL, "g.alchemy.com") {
  1024  		return AlchemyProvider
  1025  	} else if strings.Contains(b.EthereumNodeURL, "rpc.tenderly.co") {
  1026  		return TenderlyProvider
  1027  	} else {
  1028  		return UnknownProvider
  1029  	}
  1030  }
  1031  
  1032  // estimateTenderlyGasAmount performs gas amount estimation for the given transaction using Tenderly provider.
  1033  func (b *BridgeClient) estimateTenderlyGasAmount(ctx context.Context, from, to string, value int64) (float64, error) {
  1034  	return 8000000, nil
  1035  }
  1036  
  1037  // estimateAlchemyGasAmount performs gas amount estimation for the given transaction using Alchemy provider
  1038  func (b *BridgeClient) estimateAlchemyGasAmount(ctx context.Context, from, to, data string, value int64) (float64, error) {
  1039  	client := jsonrpc.NewClient(b.EthereumNodeURL)
  1040  
  1041  	valueHex := ConvertIntToHex(value)
  1042  
  1043  	resp, err := client.Call(ctx, "eth_estimateGas", &AlchemyGasEstimationRequest{
  1044  		From:  from,
  1045  		To:    to,
  1046  		Value: valueHex,
  1047  		Data:  data})
  1048  	if err != nil {
  1049  		return 0, errors.Wrap(err, "gas price estimation failed")
  1050  	}
  1051  
  1052  	if resp.Error != nil {
  1053  		return 0, errors.Wrap(errors.New(resp.Error.Error()), "gas price estimation failed")
  1054  	}
  1055  
  1056  	gasAmountRaw, ok := resp.Result.(string)
  1057  	if !ok {
  1058  		return 0, errors.New("failed to parse gas amount")
  1059  	}
  1060  
  1061  	gasAmountInt := new(big.Float)
  1062  	gasAmountInt.SetString(gasAmountRaw)
  1063  
  1064  	gasAmountFloat, _ := gasAmountInt.Float64()
  1065  
  1066  	return gasAmountFloat, nil
  1067  }
  1068  
  1069  // EstimateBurnWZCNGasAmount performs gas amount estimation for the given wzcn burn transaction.
  1070  //   - ctx go context instance to run the transaction
  1071  //   - from source address
  1072  //   - to target address
  1073  //   - amountTokens amount of tokens to burn
  1074  func (b *BridgeClient) EstimateBurnWZCNGasAmount(ctx context.Context, from, to, amountTokens string) (float64, error) {
  1075  	switch b.getProviderType() {
  1076  	case AlchemyProvider:
  1077  		abi, err := bridge.BridgeMetaData.GetAbi()
  1078  		if err != nil {
  1079  			return 0, errors.Wrap(err, "failed to get ABI")
  1080  		}
  1081  
  1082  		clientID := DefaultClientIDEncoder(zcncore.GetClientWalletID())
  1083  
  1084  		amount := new(big.Int)
  1085  		amount.SetString(amountTokens, 10)
  1086  
  1087  		var packRaw []byte
  1088  		packRaw, err = abi.Pack("burn", amount, clientID)
  1089  		if err != nil {
  1090  			return 0, errors.Wrap(err, "failed to pack arguments")
  1091  		}
  1092  
  1093  		pack := "0x" + hex.EncodeToString(packRaw)
  1094  
  1095  		return b.estimateAlchemyGasAmount(ctx, from, to, pack, 0)
  1096  	case TenderlyProvider:
  1097  		return b.estimateTenderlyGasAmount(ctx, from, to, 0)
  1098  	}
  1099  
  1100  	return 0, errors.New("used json-rpc does not allow to estimate gas amount")
  1101  }
  1102  
  1103  // EstimateMintWZCNGasAmount performs gas amount estimation for the given wzcn mint transaction.
  1104  //   - ctx go context instance to run the transaction
  1105  //   - from source address
  1106  //   - to target address
  1107  //   - zcnTransactionRaw zcn transaction (hashed)
  1108  //   - amountToken amount of tokens to mint
  1109  //   - nonceRaw nonce
  1110  //   - signaturesRaw authorizer signatures
  1111  func (b *BridgeClient) EstimateMintWZCNGasAmount(
  1112  	ctx context.Context, from, to, zcnTransactionRaw, amountToken string, nonceRaw int64, signaturesRaw [][]byte) (float64, error) {
  1113  	switch b.getProviderType() {
  1114  	case AlchemyProvider:
  1115  		amount := new(big.Int)
  1116  		amount.SetString(amountToken, 10)
  1117  
  1118  		zcnTransaction := DefaultClientIDEncoder(zcnTransactionRaw)
  1119  
  1120  		nonce := new(big.Int)
  1121  		nonce.SetInt64(nonceRaw)
  1122  
  1123  		fromRaw := common.HexToAddress(from)
  1124  
  1125  		abi, err := bridge.BridgeMetaData.GetAbi()
  1126  		if err != nil {
  1127  			return 0, errors.Wrap(err, "failed to get ABI")
  1128  		}
  1129  
  1130  		var packRaw []byte
  1131  		packRaw, err = abi.Pack("mint", fromRaw, amount, zcnTransaction, nonce, signaturesRaw)
  1132  		if err != nil {
  1133  			return 0, errors.Wrap(err, "failed to pack arguments")
  1134  		}
  1135  
  1136  		pack := "0x" + hex.EncodeToString(packRaw)
  1137  
  1138  		return b.estimateAlchemyGasAmount(ctx, from, to, pack, 0)
  1139  	case TenderlyProvider:
  1140  		return b.estimateTenderlyGasAmount(ctx, from, to, 0)
  1141  	}
  1142  
  1143  	return 0, errors.New("used json-rpc does not allow to estimate gas amount")
  1144  }
  1145  
  1146  // estimateTenderlyGasPrice performs gas estimation for the given transaction using Tenderly API.
  1147  func (b *BridgeClient) estimateTenderlyGasPrice(ctx context.Context) (float64, error) {
  1148  	return 1, nil
  1149  }
  1150  
  1151  // estimateAlchemyGasPrice performs gas estimation for the given transaction using Alchemy enhanced API returning
  1152  // approximate final gas fee.
  1153  func (b *BridgeClient) estimateAlchemyGasPrice(ctx context.Context) (float64, error) {
  1154  	client := jsonrpc.NewClient(b.EthereumNodeURL)
  1155  
  1156  	resp, err := client.Call(ctx, "eth_gasPrice")
  1157  	if err != nil {
  1158  		return 0, errors.Wrap(err, "gas price estimation failed")
  1159  	}
  1160  
  1161  	if resp.Error != nil {
  1162  		return 0, errors.Wrap(errors.New(resp.Error.Error()), "gas price estimation failed")
  1163  	}
  1164  
  1165  	gasPriceRaw, ok := resp.Result.(string)
  1166  	if !ok {
  1167  		return 0, errors.New("failed to parse gas price")
  1168  	}
  1169  
  1170  	gasPriceInt := new(big.Float)
  1171  	gasPriceInt.SetString(gasPriceRaw)
  1172  
  1173  	gasPriceFloat, _ := gasPriceInt.Float64()
  1174  
  1175  	return gasPriceFloat, nil
  1176  }
  1177  
  1178  // EstimateGasPrice performs gas estimation for the given transaction.
  1179  //   - ctx go context instance to run the transaction
  1180  func (b *BridgeClient) EstimateGasPrice(ctx context.Context) (float64, error) {
  1181  	switch b.getProviderType() {
  1182  	case AlchemyProvider:
  1183  		return b.estimateAlchemyGasPrice(ctx)
  1184  	case TenderlyProvider:
  1185  		return b.estimateTenderlyGasPrice(ctx)
  1186  	}
  1187  
  1188  	return 0, errors.New("used json-rpc does not allow to estimate gas price")
  1189  }