Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: add gas amount estimation feature to zcnbridge #1443

Merged
merged 5 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
module github.com/0chain/gosdk

go 1.21

toolchain go1.21.5
go 1.18
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the reason to downgrade the version?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the latest PR I downgraded it to 1.20, cause in all the workflows we use 1.20.3 version, so there were failing


require (
github.com/0chain/common v0.0.6-0.20230127095721-8df4d1d72565
Expand Down
7 changes: 0 additions & 7 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46f
github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
Expand Down Expand Up @@ -158,15 +157,13 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/ethereum/go-ethereum v1.10.26 h1:i/7d9RBBwiXCEuyduBQzJw/mKmnvzsN14jqBmytw72s=
github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c=
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
Expand Down Expand Up @@ -254,7 +251,6 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
Expand Down Expand Up @@ -291,7 +287,6 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=
github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0=
github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
Expand Down Expand Up @@ -577,7 +572,6 @@ go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
Expand Down Expand Up @@ -830,7 +824,6 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU=
Expand Down
16 changes: 16 additions & 0 deletions wasmsdk/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,22 @@ func getNotProcessedZCNBurnTickets() string {
return string(result)
}

// estimateGasAmount performs gas amount estimation for the given transaction.
func estimateGasAmount(from, to string, value int64) string { // nolint:golint,unused
estimateGasAmountResponse, err := bridge.EstimateGasAmount(context.Background(), from, to, value)
if err != nil {
return errors.Wrap("estimateGasAmount", "failed to estimate gas price", err).Error()
}

var result []byte
result, err = json.Marshal(estimateGasAmountResponse)
if err != nil {
return errors.Wrap("estimateGasAmount", "failed to marshal gas amount estimation result", err).Error()
}

return string(result)
}

// estimateGasPrice performs gas estimation for the given transaction using Alchemy enhanced API returning
// approximate final gas fee.
func estimateGasPrice(from, to string, value int64) string { // nolint:golint,unused
Expand Down
1 change: 1 addition & 0 deletions wasmsdk/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ func main() {
"getMintWZCNPayload": getMintWZCNPayload,
"getNotProcessedWZCNBurnEvents": getNotProcessedWZCNBurnEvents,
"getNotProcessedZCNBurnTickets": getNotProcessedZCNBurnTickets,
"estimateGasAmount": estimateGasAmount,
"estimateGasPrice": estimateGasPrice,

//zcn
Expand Down
84 changes: 61 additions & 23 deletions zcnbridge/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -998,66 +998,104 @@ func (b *BridgeClient) prepareBridge(ctx context.Context, ethereumAddress, metho
return bridgeInstance, transactOpts, nil
}

// isEstimateGasPriceAvailable checks if currently selected ethereum node url can be used for gas estimation.
func (b *BridgeClient) isEstimateGasPriceAvailable() bool {
return strings.Contains(b.EthereumNodeURL, "eth-mainnet.g.alchemy.com")
// getProviderType validates the provider url and exposes pre-defined type definition.
func (b *BridgeClient) getProviderType() int {
if strings.Contains(b.EthereumNodeURL, "g.alchemy.com") {
return AlchemyProvider
} else if strings.Contains(b.EthereumNodeURL, "rpc.tenderly.co") {
return TenderlyProvider
} else {
return UnknownProvider
}
}

// EstimateGasPrice performs gas estimation for the given transaction using Alchemy enhanced API returning
// approximate final gas fee.
func (b *BridgeClient) EstimateGasPrice(ctx context.Context, from, to string, value int64) (*GasPriceEstimationResult, error) {
if !b.isEstimateGasPriceAvailable() {
return nil, errors.New("used json-rpc does not allow to estimate gas price")
}
// estimateTenderlyGasAmount performs gas amount estimation for the given transaction using Tenderly provider.
func (b *BridgeClient) estimateTenderlyGasAmount(ctx context.Context, from, to string, value int64) (float64, error) {
return 8000000, nil
}

// estimateAlchemyGasAmount performs gas amount estimation for the given transaction using Alchemy provider
func (b *BridgeClient) estimateAlchemyGasAmount(ctx context.Context, from, to string, value int64) (float64, error) {
client := jsonrpc.NewClient(b.EthereumNodeURL)

valueHex := ConvertIntToHex(value)

resp, err := client.Call(ctx, "eth_estimateGas", &GasEstimationRequest{
resp, err := client.Call(ctx, "eth_estimateGas", &AlchemyGasEstimationRequest{
From: from, To: to, Value: valueHex})
if err != nil {
return nil, errors.Wrap(err, "gas price estimation failed")
return 0, errors.Wrap(err, "gas price estimation failed")
}

if resp.Error != nil {
return nil, errors.Wrap(errors.New(resp.Error.Error()), "gas price estimation failed")
return 0, errors.Wrap(errors.New(resp.Error.Error()), "gas price estimation failed")
}

gasAmountRaw, ok := resp.Result.(string)
if !ok {
return nil, errors.New("failed to parse gas amount")
return 0, errors.New("failed to parse gas amount")
}

gasAmountInt := new(big.Float)
gasAmountInt.SetString(gasAmountRaw)

gasAmountFloat, _ := gasAmountInt.Float64()

fmt.Println(gasAmountFloat)
return gasAmountFloat, nil
}

// EstimateGasAmount performs gas amount estimation for the given transaction.
func (b *BridgeClient) EstimateGasAmount(ctx context.Context, from, to string, value int64) (float64, error) {
switch b.getProviderType() {
case AlchemyProvider:
return b.estimateAlchemyGasAmount(ctx, from, to, value)
case TenderlyProvider:
return b.estimateTenderlyGasAmount(ctx, from, to, value)
}

return 0, errors.New("used json-rpc does not allow to estimate gas amount")
}

resp, err = client.Call(ctx, "eth_gasPrice")
// estimateTenderlyGasPrice performs gas estimation for the given transaction using Tenderly API.
func (b *BridgeClient) estimateTenderlyGasPrice(ctx context.Context, from, to string, value int64) (float64, error) {
return 0, nil
}

// estimateAlchemyGasPrice performs gas estimation for the given transaction using Alchemy enhanced API returning
// approximate final gas fee.
func (b *BridgeClient) estimateAlchemyGasPrice(ctx context.Context, from, to string, value int64) (float64, error) {
client := jsonrpc.NewClient(b.EthereumNodeURL)

resp, err := client.Call(ctx, "eth_gasPrice")
if err != nil {
return nil, errors.Wrap(err, "gas price estimation failed")
return 0, errors.Wrap(err, "gas price estimation failed")
}

if resp.Error != nil {
return nil, errors.Wrap(errors.New(resp.Error.Error()), "gas price estimation failed")
return 0, errors.Wrap(errors.New(resp.Error.Error()), "gas price estimation failed")
}

var gasPriceRaw string
gasPriceRaw, ok = resp.Result.(string)
gasPriceRaw, ok := resp.Result.(string)
if !ok {
return nil, errors.New("failed to parse gas price")
return 0, errors.New("failed to parse gas price")
}

gasPriceInt := new(big.Float)
gasPriceInt.SetString(gasPriceRaw)

gasPriceFloat, _ := gasPriceInt.Float64()

fmt.Println(gasPriceFloat)
return gasPriceFloat, nil
}

// EstimateGasPrice performs gas estimation for the given transaction using Alchemy enhanced API returning
// approximate final gas fee.
func (b *BridgeClient) EstimateGasPrice(ctx context.Context, from, to string, value int64) (float64, error) {
switch b.getProviderType() {
case AlchemyProvider:
return b.estimateAlchemyGasPrice(ctx, from, to, value)
case TenderlyProvider:
return b.estimateTenderlyGasPrice(ctx, from, to, value)
}

return &GasPriceEstimationResult{
Value: gasPriceFloat * gasAmountFloat}, nil
return 0, errors.New("used json-rpc does not allow to estimate gas price")
}
9 changes: 2 additions & 7 deletions zcnbridge/bridge_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,13 @@ import (
"github.com/pkg/errors"
)

// GasEstimationRequest describes request used for Alchemy enhanced JSON-RPC API.
type GasEstimationRequest struct {
// AlchemyGasEstimationRequest describes request used for Alchemy enhanced JSON-RPC API.
type AlchemyGasEstimationRequest struct {
From string `json:"from"`
To string `json:"to"`
Value string `json:"value"`
}

// GasPriceEstimationResult represents result of the gas price estimation operation execution.
type GasPriceEstimationResult struct {
Value float64 `json:"value"`
}

// BancorTokenDetails describes Bancor ZCN zcntoken pool details
type BancorTokenDetails struct {
Data struct {
Expand Down
16 changes: 12 additions & 4 deletions zcnbridge/bridge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ import (
const (
ethereumAddress = "0xD8c9156e782C68EE671C09b6b92de76C97948432"

alchemyEthereumNodeURL = "https://eth-mainnet.g.alchemy.com/v2/9VanLUbRE0pLmDHwCHGJlhs9GHosrfD9"
infuraEthereumNodeURL = "https://mainnet.infura.io/v3/7238211010344719ad14a89db874158c"
value = 1e+10
alchemyEthereumNodeURL = "https://eth-mainnet.g.alchemy.com/v2/9VanLUbRE0pLmDHwCHGJlhs9GHosrfD9"
tenderlyEthereumNodeURL = "https://rpc.tenderly.co/fork/835ecb4e-1f60-4129-adc2-b0c741193839"
infuraEthereumNodeURL = "https://mainnet.infura.io/v3/7238211010344719ad14a89db874158c"
value = 1e+10

password = "02289b9"

Expand Down Expand Up @@ -628,13 +629,20 @@ func Test_ZCNBridge(t *testing.T) {
))
})

t.Run("should check if gas price estimation works with correct ethereum node url", func(t *testing.T) {
t.Run("should check if gas price estimation works with correct alchemy ethereum node url", func(t *testing.T) {
bridgeClient = getBridgeClient(bancorMockServerURL, alchemyEthereumNodeURL, ethereumClient, transactionProvider, keyStore)

_, err := bridgeClient.EstimateGasPrice(context.Background(), tokenAddress, bridgeAddress, value)
require.Contains(t, err.Error(), "Must be authenticated!")
})

t.Run("should check if gas price estimation works with correct tenderly ethereum node url", func(t *testing.T) {
bridgeClient = getBridgeClient(bancorMockServerURL, tenderlyEthereumNodeURL, ethereumClient, transactionProvider, keyStore)

_, err := bridgeClient.EstimateGasPrice(context.Background(), tokenAddress, bridgeAddress, value)
require.NoError(t, err)
})

t.Run("should check if gas price estimation works with incorrect ethereum node url", func(t *testing.T) {
bridgeClient = getBridgeClient(bancorMockServerURL, infuraEthereumNodeURL, ethereumClient, transactionProvider, keyStore)

Expand Down
6 changes: 6 additions & 0 deletions zcnbridge/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ import (
"github.com/spf13/viper"
)

const (
TenderlyProvider = iota
AlchemyProvider
UnknownProvider
)

const (
ZChainsClientConfigName = "config.yaml"
ZChainWalletConfigName = "wallet.json"
Expand Down
Loading