Skip to content
Draft
Show file tree
Hide file tree
Changes from 5 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* (x/warden) Return error if analyzer's address is not bech32
* (x/act) [724](https://github.com/warden-protocol/wardenprotocol/issues/724) Add `creator` filter param to `Rules` query
* (wardend) Validate bech32 format in add-genesis-keychain and add-genesis-space
* (keychain-sdk) Add AutoEstimateGas and GasAdjustmentFactor parameters to automatically estimate gas before broadcasting tx

### Bug Fixes
* (x/gmp) Remove the GMP default params from genesis
Expand Down
36 changes: 20 additions & 16 deletions cmd/wardenkms/wardenkms.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ type Config struct {
KeyringMnemonic string `env:"KEYRING_MNEMONIC, required"`
KeyringPassword string `env:"KEYRING_PASSWORD, required"`

BatchInterval time.Duration `env:"BATCH_INTERVAL, default=8s"`
BatchSize int `env:"BATCH_SIZE, default=7"`
GasLimit uint64 `env:"GAS_LIMIT, default=400000"`
TxTimeout time.Duration `env:"TX_TIMEOUT, default=120s"`
TxFee int64 `env:"TX_FEE, default=400000"`
BatchInterval time.Duration `env:"BATCH_INTERVAL, default=8s"`
BatchSize int `env:"BATCH_SIZE, default=7"`
GasLimit uint64 `env:"GAS_LIMIT, default=400000"`
AutoEstimateGas bool `env:"AUTO_ESTIMATE_GAS, default=false"`
GasAdjustmentFactor float64 `env:"GAS_ADJUSTMENT_FACTOR, default=1.1"`
TxTimeout time.Duration `env:"TX_TIMEOUT, default=120s"`
TxFee int64 `env:"TX_FEE, default=400000"`

HttpAddr string `env:"HTTP_ADDR, default=:8080"`

Expand All @@ -57,17 +59,19 @@ func main() {
}

app := keychain.NewApp(keychain.Config{
Logger: logger,
ChainID: cfg.ChainID,
GRPCURL: cfg.GRPCURL,
GRPCInsecure: cfg.GRPCInsecure,
Mnemonic: cfg.Mnemonic,
KeychainID: cfg.KeychainId,
GasLimit: cfg.GasLimit,
BatchInterval: cfg.BatchInterval,
BatchSize: cfg.BatchSize,
TxTimeout: cfg.TxTimeout,
TxFees: sdk.NewCoins(sdk.NewCoin("award", math.NewInt(cfg.TxFee))),
Logger: logger,
ChainID: cfg.ChainID,
GRPCURL: cfg.GRPCURL,
GRPCInsecure: cfg.GRPCInsecure,
Mnemonic: cfg.Mnemonic,
KeychainID: cfg.KeychainId,
GasLimit: cfg.GasLimit,
AutoEstimateGas: cfg.AutoEstimateGas,
GasAdjustmentFactor: cfg.GasAdjustmentFactor,
BatchInterval: cfg.BatchInterval,
BatchSize: cfg.BatchSize,
TxTimeout: cfg.TxTimeout,
TxFees: sdk.NewCoins(sdk.NewCoin("award", math.NewInt(cfg.TxFee))),
})

app.SetKeyRequestHandler(func(w keychain.KeyResponseWriter, req *keychain.KeyRequest) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,11 @@ func main() {
DerivationPath: "m/44'/118'/0'/0/0",

// setup throughput for batching responses
GasLimit: 400000,
BatchTimeout: 8 * time.Second,
BatchSize: 10,
GasLimit: 400000,
AutoEstimateGas: false,
GasAdjustmentFactor: 1.2,
BatchTimeout: 8 * time.Second,
BatchSize: 10,
})
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,20 @@ In this section, we will walk you through different modules of the **Keychain SD

```go
type Config struct {
Logger *slog.Logger
ChainID string
GRPCURL string
GRPCInsecure bool
KeychainID uint64
DerivationPath string
Mnemonic string
BatchInterval time.Duration
BatchSize int
GasLimit uint64
TxFees sdk.Coins
TxTimeout time.Duration
Logger *slog.Logger
ChainID string
GRPCURL string
GRPCInsecure bool
KeychainID uint64
DerivationPath string
Mnemonic string
BatchInterval time.Duration
BatchSize int
GasLimit uint64
AutoEstimateGas bool
GasAdjustmentFactor float64
TxFees sdk.Coins
TxTimeout time.Duration
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,23 @@ The `Config` struct holds the environment variable configurations required by th

```go
type Config struct {
ChainID string `env:"CHAIN_ID, default=warden"`
GRPCURL string `env:"GRPC_URL, default=localhost:9090"`
GRPCInsecure bool `env:"GRPC_INSECURE, default=true"`
DerivationPath string `env:"DERIVATION_PATH, default=m/44'/118'/0'/0/0"`
Mnemonic string `env:"MNEMONIC, default=exclude try nephew main..."`
KeychainId uint64 `env:"KEYCHAIN_ID, default=1"`
KeyringMnemonic string `env:"KEYRING_MNEMONIC, required"`
KeyringPassword string `env:"KEYRING_PASSWORD, required"`
BatchInterval time.Duration `env:"BATCH_INTERVAL, default=8s"`
BatchSize int `env:"BATCH_SIZE, default=7"`
GasLimit uint64 `env:"GAS_LIMIT, default=400000"`
TxTimeout time.Duration `env:"TX_TIMEOUT, default=120s"`
TxFee int64 `env:"TX_FEE, default=400000"`
HttpAddr string `env:"HTTP_ADDR, default=:8080"`
LogLevel slog.Level `env:"LOG_LEVEL, default=debug"`
ChainID string `env:"CHAIN_ID, default=warden"`
GRPCURL string `env:"GRPC_URL, default=localhost:9090"`
GRPCInsecure bool `env:"GRPC_INSECURE, default=true"`
DerivationPath string `env:"DERIVATION_PATH, default=m/44'/118'/0'/0/0"`
Mnemonic string `env:"MNEMONIC, default=exclude try nephew main..."`
KeychainId uint64 `env:"KEYCHAIN_ID, default=1"`
KeyringMnemonic string `env:"KEYRING_MNEMONIC, required"`
KeyringPassword string `env:"KEYRING_PASSWORD, required"`
BatchInterval time.Duration `env:"BATCH_INTERVAL, default=8s"`
BatchSize int `env:"BATCH_SIZE, default=7"`
GasLimit uint64 `env:"GAS_LIMIT, default=400000"`
AutoEstimateGas bool `env:"AUTO_ESTIMATE_GAS, default=false"`
GasAdjustmentFactor float64 `env:"GAS_ADJUSTMENT_FACTOR, default=1.1"`
TxTimeout time.Duration `env:"TX_TIMEOUT, default=120s"`
TxFee int64 `env:"TX_FEE, default=400000"`
HttpAddr string `env:"HTTP_ADDR, default=:8080"`
LogLevel slog.Level `env:"LOG_LEVEL, default=debug"`
}
```

Expand Down
57 changes: 51 additions & 6 deletions go-client/tx_raw_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ import (
)

var (
DefaultGasLimit = uint64(300000000000000000)
DefaultFees = types.NewCoins(types.NewCoin("award", math.NewInt(1000000000000000)))
DefaultGasLimit = uint64(300000000000000000)
DefaultGasAdjustmentFactor = 1.2
DefaultFees = types.NewCoins(types.NewCoin("award", math.NewInt(1000000000000000)))

queryTimeout = 250 * time.Millisecond
)
Expand Down Expand Up @@ -73,7 +74,14 @@ type Msger interface {

// Build a transaction with the given messages and sign it.
// Sequence and account numbers will be fetched automatically from the chain.
func (c *RawTxClient) BuildTx(ctx context.Context, gasLimit uint64, fees types.Coins, msgers ...Msger) ([]byte, error) {
func (c *RawTxClient) BuildTx(
ctx context.Context,
autoEstimateGas bool,
gasAdjustmentFactor float64,
gasLimit uint64,
fees types.Coins,
msgers ...Msger,
) ([]byte, error) {
account, err := c.accountFetcher.Account(ctx, c.Identity.Address.String())
if err != nil {
return nil, fmt.Errorf("fetch account: %w", err)
Expand All @@ -86,8 +94,10 @@ func (c *RawTxClient) BuildTx(ctx context.Context, gasLimit uint64, fees types.C
if err != nil {
return nil, fmt.Errorf("create temp dir: %w", err)
}

defer os.RemoveAll(dname)
appConfig.Set(flags.FlagHome, dname)

app, err := app.New(
log.NewNopLogger(),
db.NewMemDB(),
Expand All @@ -104,7 +114,9 @@ func (c *RawTxClient) BuildTx(ctx context.Context, gasLimit uint64, fees types.C
signMode := app.TxConfig().SignModeHandler().DefaultMode()

// build unsigned tx
txBuilder.SetGasLimit(gasLimit)
if !autoEstimateGas {
txBuilder.SetGasLimit(gasLimit)
}
txBuilder.SetFeeAmount(fees)

msgs := make([]sdk.Msg, len(msgers))
Expand All @@ -131,6 +143,19 @@ func (c *RawTxClient) BuildTx(ctx context.Context, gasLimit uint64, fees types.C
return nil, fmt.Errorf("set empty signature: %w", err)
}

if autoEstimateGas {
txBytes, err := app.TxConfig().TxEncoder()(txBuilder.GetTx())
if err != nil {
return nil, fmt.Errorf("encode tx: %w", err)
}

gasLimit, err = c.EstimateGas(ctx, txBytes, gasAdjustmentFactor, gasLimit)
if err != nil {
return nil, fmt.Errorf("estimage gas: %w", err)
}
txBuilder.SetGasLimit(gasLimit)
}

// Second round: all signer infos are set, so each signer can sign.
signerData := xauthsigning.SignerData{
ChainID: c.chainID,
Expand All @@ -157,12 +182,12 @@ func (c *RawTxClient) BuildTx(ctx context.Context, gasLimit uint64, fees types.C
return nil, fmt.Errorf("set signature: %w", err)
}

txBytes, err := app.TxConfig().TxEncoder()(txBuilder.GetTx())
txBytesRes, err := app.TxConfig().TxEncoder()(txBuilder.GetTx())
if err != nil {
return nil, fmt.Errorf("encode tx: %w", err)
}

return txBytes, nil
return txBytesRes, nil
}

// SendTx broadcasts a signed transaction and returns its hash.
Expand Down Expand Up @@ -209,3 +234,23 @@ func (c *RawTxClient) WaitForTx(ctx context.Context, hash string) error {
}
}
}

// EstimateGas estimates gas by simulating the transaction when autoEstimateGas is set to true.
// Otherwise, GasLimit is used.
func (c *RawTxClient) EstimateGas(
ctx context.Context,
txBytes []byte,
gasAdjustmentFactor float64,
gasLimit uint64,
) (uint64, error) {
gasInfo, err := c.client.Simulate(ctx, &txtypes.SimulateRequest{
TxBytes: txBytes,
})
if err != nil {
return 0, fmt.Errorf("estimate gas, simulate tx: %w", err)
}

estimatedGas := uint64(float64(gasInfo.GasInfo.GasUsed) * gasAdjustmentFactor)

return min(estimatedGas, gasLimit), nil
}
13 changes: 13 additions & 0 deletions keychain-sdk/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,19 @@ type Config struct {
// more gas is needed.
GasLimit uint64

// AutoEstimateGas is a flag to estimate gas before broadcasting the transaction
// and use it as GasLimit.
//
// When AutoEstimateGas == true then GasLimit = min(EstimatedGas, GasLimit)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// When AutoEstimateGas == true then GasLimit = min(EstimatedGas, GasLimit)
// When AutoEstimateGas == true then GasLimit = min(EstimatedGas * GasAdjustmentFactor, GasLimit)

// Otherwise, GasLimit is used
AutoEstimateGas bool

// GasAdjustmentFactor is a float factor applied to the estimated gas values
// as a safety margin.
//
// Example: 1.2 means a 20% safety margin will be added to the estimated gas.
GasAdjustmentFactor float64

// TxFees are the coins used as fees for the outgoing transactions of this
// Keychain.
TxFees sdk.Coins
Expand Down
8 changes: 5 additions & 3 deletions keychain-sdk/example_keychain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ func Main() {
Mnemonic: "virus boat radio apple pilot ask vault exhaust again state doll stereo slide exhibit scissors miss attack boat budget egg bird mask more trick",

// setup throughput for batching responses
GasLimit: 400000,
BatchInterval: 8 * time.Second,
BatchSize: 10,
GasLimit: 400000,
AutoEstimateGas: false,
GasAdjustmentFactor: 1.2,
BatchInterval: 8 * time.Second,
BatchSize: 10,
})

app.SetKeyRequestHandler(func(w keychain.KeyResponseWriter, req *keychain.KeyRequest) {
Expand Down
13 changes: 12 additions & 1 deletion keychain-sdk/internal/writer/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ type W struct {

GasLimit uint64

AutoEstimateGas bool

GasAdjustmentFactor float64

Fees sdk.Coins

// Lock to prevent trying to send multiple transactions at once.
Expand Down Expand Up @@ -90,6 +94,13 @@ func (w *W) gasLimit() uint64 {
return w.GasLimit
}

func (w *W) gasAdjustmentFactor() float64 {
if w.GasAdjustmentFactor == 0 {
return client.DefaultGasAdjustmentFactor
}
return w.GasAdjustmentFactor
}

func (w *W) fees() sdk.Coins {
if w.Fees == nil {
return client.DefaultFees
Expand Down Expand Up @@ -131,7 +142,7 @@ func (w *W) sendWaitTx(ctx context.Context, msgs ...client.Msger) error {

w.Logger.Info("flushing batch", "count", len(msgs))

tx, err := w.Client.BuildTx(ctx, w.gasLimit(), w.fees(), msgs...)
tx, err := w.Client.BuildTx(ctx, w.AutoEstimateGas, w.gasAdjustmentFactor(), w.gasLimit(), w.fees(), msgs...)
if err != nil {
return err
}
Expand Down
3 changes: 3 additions & 0 deletions keychain-sdk/keychain.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,10 @@ func (a *App) initConnections() error {

txClient := client.NewTxClient(identity, a.config.ChainID, conn, query)
a.txWriter = writer.New(txClient, a.config.BatchSize, a.config.BatchInterval, a.config.TxTimeout, a.logger())

a.txWriter.Fees = a.config.TxFees
a.txWriter.AutoEstimateGas = a.config.AutoEstimateGas
a.txWriter.GasAdjustmentFactor = a.config.GasAdjustmentFactor
a.txWriter.GasLimit = a.config.GasLimit

return nil
Expand Down