Skip to content

Commit

Permalink
feat: add tx options struct
Browse files Browse the repository at this point in the history
  • Loading branch information
vgonkivs committed Jun 4, 2024
1 parent bfda360 commit 53c04e0
Showing 1 changed file with 159 additions and 0 deletions.
159 changes: 159 additions & 0 deletions state/tx_options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package state

import (
"context"
"encoding/json"
"errors"
"fmt"
"math"

"github.com/celestiaorg/celestia-app/app"
sdktypes "github.com/cosmos/cosmos-sdk/types"

"github.com/celestiaorg/celestia-app/pkg/user"
apptypes "github.com/celestiaorg/celestia-app/x/blob/types"
)

// gasMultiplier is used to increase gas limit in case if tx has additional options.
const gasMultiplier = 1.1

Check failure on line 18 in state/tx_options.go

View workflow job for this annotation

GitHub Actions / go-ci / Lint

gasMultiplier redeclared in this block

var ErrNoGasProvided = errors.New("gas limit was not set")

// TxOptions specifies additional options that will be applied to the Tx.
type TxOptions struct {

Check failure on line 23 in state/tx_options.go

View workflow job for this annotation

GitHub Actions / go-ci / Lint

TxOptions redeclared in this block
fee *Fee
GasLimit uint64

// Specifies the key that should be used to sign transactions.
// NOTE: This `Account` should be available in the `Keystore`.
Account string
}

func DefaultTxOptions() TxOptions {
return TxOptions{
fee: DefaultFee(),

Check failure on line 34 in state/tx_options.go

View workflow job for this annotation

GitHub Actions / go-ci / Lint

unknown field fee in struct literal of type TxOptions
GasLimit: 0,
Account: "",

Check failure on line 36 in state/tx_options.go

View workflow job for this annotation

GitHub Actions / go-ci / Lint

unknown field Account in struct literal of type TxOptions
}
}

type jsonTxOptions struct {
Fee *Fee `json:"fee"`
GasLimit uint64 `json:"gasLimit"`
Account string `json:"account, omitempty"`
}

func (options *TxOptions) UnmarshalJSON(data []byte) error {
var jsonOpts *jsonTxOptions
err := json.Unmarshal(data, jsonOpts)
if err != nil {
return fmt.Errorf("unmarshalling TxOptions:%w", err)
}

options.fee.Amount = jsonOpts.Fee.Amount

Check failure on line 53 in state/tx_options.go

View workflow job for this annotation

GitHub Actions / go-ci / Lint

options.fee undefined (type *TxOptions has no field or method fee, but does have Fee)
options.fee.isSet = jsonOpts.Fee.isSet

Check failure on line 54 in state/tx_options.go

View workflow job for this annotation

GitHub Actions / go-ci / Lint

options.fee undefined (type *TxOptions has no field or method fee, but does have Fee)
options.GasLimit = jsonOpts.GasLimit
options.Account = jsonOpts.Account

Check failure on line 56 in state/tx_options.go

View workflow job for this annotation

GitHub Actions / go-ci / Lint

options.Account undefined (type *TxOptions has no field or method Account)
return nil
}

// SetFeeAmount sets fee for the transaction.
func (options *TxOptions) SetFeeAmount(amount int64) {
if amount >= 0 {
options.fee.Amount = amount
options.fee.isSet = true
}
}

// getFee returns fee amount as a `user.TxOption` that can be applied to the transactions.
// It will calculate fee in case user haven't provided it.
// NOTE: it is very important to ensure that gas limit was set.
func (options *TxOptions) getFee(minGasPrice float64) (user.TxOption, error) {
if options.GasLimit == 0 {
return nil, ErrNoGasProvided
}

if !options.fee.isSet {
options.fee.Amount = int64(math.Ceil(minGasPrice * float64(options.GasLimit)))
}

fee := sdktypes.NewCoins(sdktypes.NewCoin(app.BondDenom, sdktypes.NewInt(options.fee.Amount)))
return user.SetFeeAmount(fee), nil
}

// getGas returns a gas limit as a `user.TxOption` that can be applied to the transactions.
// GasLimit will be estimated in case it has not been set.
// NOTE: final result of the estimation will be multiplied by the `gasMultiplier`(1.1) to cover additional options of the Tx,
// as not all options are counted in the `EstimateGas`
func (options *TxOptions) getGas(ctx context.Context, client *user.TxClient, msg sdktypes.Msg, opts ...user.TxOption) (user.TxOption, error) {
if options.GasLimit == 0 {
// Gas estimation depends on the amount of bytes of the original TX, including all options(Fee, Granter, etc.),
// so, it is very important to pass ALL options that will be applied to the tx.
// 1utia as fee is ok at this point(as fee may not be calculated yet).
opts = append([]user.TxOption{user.SetFee(1)}, opts...)
gasLimit, err := client.EstimateGas(ctx, []sdktypes.Msg{msg}, opts...)
if err != nil {
return nil, fmt.Errorf("estimating gas: %w", err)
}
options.GasLimit = uint64(float64(gasLimit) * gasMultiplier)
}
return user.SetGasLimit(options.GasLimit), nil
}

// getGasForBlobs returns a gas limit as a `user.TxOption` that can be applied to the `MsgPayForBlob` transactions.
// NOTE: final result of the estimation will be multiplied by the `gasMultiplier`(1.1) to cover additional options of the Tx.
func (options *TxOptions) getGasForBlobs(blobSizes []uint32) user.TxOption {
if options.GasLimit != 0 {
gasLimit := apptypes.DefaultEstimateGas(blobSizes)
options.GasLimit = uint64(float64(gasLimit) * gasMultiplier)
}
return user.SetGasLimit(options.GasLimit)
}

type Fee struct {
Amount int64
isSet bool
}

func DefaultFee() *Fee {
return &Fee{
Amount: -1,
}
}

type jsonFee struct {
Amount int64 `json:"amount,omitempty"`
IsSet bool `json:"isSet,omitempty"`
}

func (f *Fee) MarshalJSON() ([]byte, error) {
fee := jsonFee{
Amount: f.Amount,
IsSet: f.isSet,
}
return json.Marshal(fee)
}

func (f *Fee) UnmarshalJSON(data []byte) error {
var fee *jsonFee
err := json.Unmarshal(data, fee)
if err != nil {
return err
}

f.Amount = fee.Amount
f.isSet = fee.IsSet
if !f.isSet {
f.Amount = -1
}
return nil
}

func parseAddressFromString(addrStr string) (Address, error) {
var address Address
err := address.UnmarshalJSON([]byte(addrStr))
if err != nil {
return address, err
}
return address, nil
}

0 comments on commit 53c04e0

Please sign in to comment.