import { DefaultGasPriceStep , FeeType , IAmountConfig , IFeeConfig , IGasConfig , } from "./types" ; import { TxChainSetter } from "./chain" ; import { ChainGetter , CoinPrimitive } from "@keplr-wallet/stores" ; import { action , computed , makeObservable , observable } from "mobx" ; import { Coin , CoinPretty , Dec , DecUtils , Int } from "@keplr-wallet/unit" ; import { Currency } from "@keplr-wallet/types" ; import { computedFn } from "mobx-utils" ; import { StdFee } from "@cosmjs/launchpad" ; import { useState } from "react" ; import { ObservableQueryBalances } from "@keplr-wallet/stores/build/query/balances" ; import { InsufficientFeeError , NotLoadedFeeError } from "./errors" ; export class FeeConfig extends TxChainSetter implements IFeeConfig { @ observable . ref protected queryBalances : ObservableQueryBalances ; @ observable protected _sender : string ; @ observable protected _feeType : FeeType | undefined = undefined ; @ observable protected _manualFee : CoinPrimitive | undefined = undefined ; protected @ observable protected additionAmountToNeedFee : boolean = true ; @ observable protected _disableBalanceCheck : boolean = false ; constructor ( chainGetter , initialChainId , sender , queryBalances , protected readonly amountConfig , protected readonly gasConfig , additionAmountToNeedFee = true ) { super ( chainGetter , initialChainId ) ; this . _sender = sender ; this . queryBalances = queryBalances ; this . additionAmountToNeedFee = additionAmountToNeedFee ; makeObservable ( this ) ; } @ action setAdditionAmountToNeedFee ( additionAmountToNeedFee ) { this . additionAmountToNeedFee = additionAmountToNeedFee ; } @ action setQueryBalances ( queryBalances ) { this . queryBalances = queryBalances ; } @ action setSender ( sender ) { this . _sender = sender ; } @ action setFeeType ( feeType ) { this . _feeType = feeType ; this . _manualFee = undefined ; } get isManual ( ) : boolean { return this . feeType === undefined ; } get feeType ( ) : FeeType | undefined { return this . _feeType ; } @ action setManualFee ( fee ) { this . _manualFee = fee ; this . _feeType = undefined ; } get feeCurrencies ( ) : Currency [ ] { return this . chainInfo . feeCurrencies ; } get feeCurrency ( ) : Currency | undefined { return this . chainInfo . feeCurrencies [ 0 ] ; } toStdFee ( ) { const amount = this . getFeePrimitive ( ) ; if ( ! amount ) { return { gas : this . gasConfig . gas . toString ( ) , amount : [ ] , } ; } return { gas : this . gasConfig . gas . toString ( ) , amount : [ amount ] , } ; } @ computed get fee ( ) : CoinPretty | undefined { if ( ! this . feeCurrency ) { return undefined ; } const feePrimitive = this . getFeePrimitive ( ) ; if ( ! feePrimitive ) { return undefined ; } return new CoinPretty ( this . feeCurrency , new Int ( feePrimitive . amount ) ) ; } getFeePrimitive ( ) { if ( ! this . feeCurrency ) { return undefined ; } if ( this . _manualFee ) { return this . _manualFee ; } if ( this . feeType ) { return this . getFeeTypePrimitive ( this . feeType ) ; } return undefined ; } protected getFeeTypePrimitive ( feeType ) { if ( ! this . feeCurrency ) { throw new Error ( "Fee currency not set" ) ; } const gasPriceStep = this . chainInfo . gasPriceStep ? this . chainInfo . gasPriceStep : DefaultGasPriceStep ; const gasPrice = new Dec ( gasPriceStep [ feeType ] . toString ( ) ) ; const feeAmount = gasPrice . mul ( new Dec ( this . gasConfig . gas ) ) ; return { denom : this . feeCurrency . coinMinimalDenom , amount : feeAmount . truncate ( ) . toString ( ) , } ; } readonly getFeeTypePretty = computedFn ( ( feeType ) => { if ( ! this . feeCurrency ) { throw new Error ( "Fee currency not set" ) ; } const feeTypePrimitive = this . getFeeTypePrimitive ( feeType ) ; const feeCurrency = this . feeCurrency ; return new CoinPretty ( feeCurrency , new Int ( feeTypePrimitive . amount ) ) . maxDecimals ( feeCurrency . coinDecimals ) ; } ) ; getError ( ) { if ( this . gasConfig . getError ( ) ) { return this . gasConfig . getError ( ) ; } if ( this . disableBalanceCheck ) { return undefined ; } const fee = this . getFeePrimitive ( ) ; if ( ! fee ) { return undefined ; } const amount = this . amountConfig . getAmountPrimitive ( ) ; let need ; if ( this . additionAmountToNeedFee && fee && fee . denom === amount . denom ) { need = new Coin ( fee . denom , new Int ( fee . amount ) . add ( new Int ( amount . amount ) ) ) ; } else { need = new Coin ( fee . denom , new Int ( fee . amount ) ) ; } if ( need . amount . gt ( new Int ( 0 ) ) ) { const bal = this . queryBalances . getQueryBech32Address ( this . _sender ) . balances . find ( ( bal ) => { return bal . currency . coinMinimalDenom === need . denom ; } ) ; if ( ! bal ) { return new InsufficientFeeError ( "insufficient fee" ) ; } else if ( ! bal . response && ! bal . error ) { return new NotLoadedFeeError ( `${ bal . currency . coinDenom } is not loaded yet` ) ; } else if ( bal . balance . toDec ( ) . mul ( DecUtils . getPrecisionDec ( bal . currency . coinDecimals ) ) . truncate ( ) . lt ( need . amount ) ) { return new InsufficientFeeError ( "insufficient fee" ) ; } } } @ action setDisableBalanceCheck ( bool ) { this . _disableBalanceCheck = bool ; } get disableBalanceCheck ( ) : boolean { return this . _disableBalanceCheck ; } } export const useFeeConfig = ( chainGetter , chainId , sender , queryBalances , amountConfig , gasConfig , additionAmountToNeedFee = true ) => { const [ config ] = useState ( ( ) => new FeeConfig ( chainGetter , chainId , sender , queryBalances , amountConfig , gasConfig , additionAmountToNeedFee ) ) ; config . setChain ( chainId ) ; config . setQueryBalances ( queryBalances ) ; config . setSender ( sender ) ; config . setAdditionAmountToNeedFee ( additionAmountToNeedFee ) ; return config ; } ;