-
Notifications
You must be signed in to change notification settings - Fork 973
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
159 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
||
var ErrNoGasProvided = errors.New("gas limit was not set") | ||
|
||
// TxOptions specifies additional options that will be applied to the Tx. | ||
type TxOptions struct { | ||
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(), | ||
GasLimit: 0, | ||
Account: "", | ||
} | ||
} | ||
|
||
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 | ||
options.fee.isSet = jsonOpts.Fee.isSet | ||
options.GasLimit = jsonOpts.GasLimit | ||
options.Account = jsonOpts.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 | ||
} |