cli: convert coins to smallest unit registered (#7777)
* cli: convert coins to smallest unit registered fixes: #7623 - test order of decimal operations - support both int and decimal coins, truncate when normalizing to base unit * Update types/coin_test.go * Update types/coin_test.go Co-authored-by: Alessio Treglia <alessio@tendermint.com> Co-authored-by: Amaury <amaury.martiny@protonmail.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
parent
0d0c9696e8
commit
54201d11e6
|
@ -120,7 +120,7 @@ func (f Factory) WithGas(gas uint64) Factory {
|
|||
|
||||
// WithFees returns a copy of the Factory with an updated fee.
|
||||
func (f Factory) WithFees(fees string) Factory {
|
||||
parsedFees, err := sdk.ParseCoins(fees)
|
||||
parsedFees, err := sdk.ParseCoinsNormalized(fees)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa
|
|||
addr = info.GetAddress()
|
||||
}
|
||||
|
||||
coins, err := sdk.ParseCoins(args[1])
|
||||
coins, err := sdk.ParseCoinsNormalized(args[1])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse coins: %w", err)
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa
|
|||
vestingEnd, _ := cmd.Flags().GetInt64(flagVestingEnd)
|
||||
vestingAmtStr, _ := cmd.Flags().GetString(flagVestingAmt)
|
||||
|
||||
vestingAmt, err := sdk.ParseCoins(vestingAmtStr)
|
||||
vestingAmt, err := sdk.ParseCoinsNormalized(vestingAmtStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse vesting amount: %w", err)
|
||||
}
|
||||
|
|
|
@ -600,7 +600,7 @@ var (
|
|||
// a letter, a number or a separator ('/').
|
||||
reDnmString = `[a-zA-Z][a-zA-Z0-9/]{2,127}`
|
||||
reAmt = `[[:digit:]]+`
|
||||
reDecAmt = `[[:digit:]]*\.[[:digit:]]+`
|
||||
reDecAmt = `[[:digit:]]+(?:\.[[:digit:]]+)?|\.[[:digit:]]+`
|
||||
reSpc = `[[:space:]]*`
|
||||
reDnm = returnReDnm
|
||||
reCoin = returnReCoin
|
||||
|
@ -664,33 +664,18 @@ func ParseCoin(coinStr string) (coin Coin, err error) {
|
|||
return NewCoin(denomStr, amount), nil
|
||||
}
|
||||
|
||||
// ParseCoins will parse out a list of coins separated by commas. If the parsing is successuful,
|
||||
// the provided coins will be sanitized by removing zero coins and sorting the coin set. Lastly
|
||||
// a validation of the coin set is executed. If the check passes, ParseCoins will return the sanitized coins.
|
||||
// ParseCoinsNormalized will parse out a list of coins separated by commas, and normalize them by converting to smallest
|
||||
// unit. If the parsing is successuful, the provided coins will be sanitized by removing zero coins and sorting the coin
|
||||
// set. Lastly a validation of the coin set is executed. If the check passes, ParseCoinsNormalized will return the
|
||||
// sanitized coins.
|
||||
// Otherwise it will return an error.
|
||||
// If an empty string is provided to ParseCoins, it returns nil Coins.
|
||||
// If an empty string is provided to ParseCoinsNormalized, it returns nil Coins.
|
||||
// ParseCoinsNormalized supports decimal coins as inputs, and truncate them to int after converted to smallest unit.
|
||||
// Expected format: "{amount0}{denomination},...,{amountN}{denominationN}"
|
||||
func ParseCoins(coinsStr string) (Coins, error) {
|
||||
coinsStr = strings.TrimSpace(coinsStr)
|
||||
if len(coinsStr) == 0 {
|
||||
return nil, nil
|
||||
func ParseCoinsNormalized(coinStr string) (Coins, error) {
|
||||
coins, err := ParseDecCoins(coinStr)
|
||||
if err != nil {
|
||||
return Coins{}, err
|
||||
}
|
||||
|
||||
coinStrs := strings.Split(coinsStr, ",")
|
||||
coins := make(Coins, len(coinStrs))
|
||||
for i, coinStr := range coinStrs {
|
||||
coin, err := ParseCoin(coinStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
coins[i] = coin
|
||||
}
|
||||
|
||||
newCoins := sanitizeCoins(coins)
|
||||
if err := newCoins.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newCoins, nil
|
||||
return NormalizeCoins(coins), nil
|
||||
}
|
||||
|
|
|
@ -660,18 +660,18 @@ func (s *coinTestSuite) TestParseCoins() {
|
|||
{"98 bar , 1 foo ", true, sdk.Coins{{"bar", sdk.NewInt(98)}, {"foo", one}}},
|
||||
{" 55\t \t bling\n", true, sdk.Coins{{"bling", sdk.NewInt(55)}}},
|
||||
{"2foo, 97 bar", true, sdk.Coins{{"bar", sdk.NewInt(97)}, {"foo", sdk.NewInt(2)}}},
|
||||
{"5 mycoin,", false, nil}, // no empty coins in a list
|
||||
{"2 3foo, 97 bar", false, nil}, // 3foo is invalid coin name
|
||||
{"11me coin, 12you coin", false, nil}, // no spaces in coin names
|
||||
{"1.2btc", false, nil}, // amount must be integer
|
||||
{"5foo:bar", false, nil}, // invalid separator
|
||||
{"5 mycoin,", false, nil}, // no empty coins in a list
|
||||
{"2 3foo, 97 bar", false, nil}, // 3foo is invalid coin name
|
||||
{"11me coin, 12you coin", false, nil}, // no spaces in coin names
|
||||
{"1.2btc", true, sdk.Coins{{"btc", sdk.NewInt(1)}}}, // amount can be decimal, will get truncated
|
||||
{"5foo:bar", false, nil}, // invalid separator
|
||||
{"10atom10", true, sdk.Coins{{"atom10", sdk.NewInt(10)}}},
|
||||
{"200transfer/channelToA/uatom", true, sdk.Coins{{"transfer/channelToA/uatom", sdk.NewInt(200)}}},
|
||||
{"50ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", true, sdk.Coins{{"ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdk.NewInt(50)}}},
|
||||
}
|
||||
|
||||
for tcIndex, tc := range cases {
|
||||
res, err := sdk.ParseCoins(tc.input)
|
||||
res, err := sdk.ParseCoinsNormalized(tc.input)
|
||||
if !tc.valid {
|
||||
s.Require().Error(err, "%s: %#v. tc #%d", tc.input, res, tcIndex)
|
||||
} else if s.Assert().Nil(err, "%s: %+v", tc.input, err) {
|
||||
|
|
|
@ -352,8 +352,11 @@ func (s *decCoinTestSuite) TestParseDecCoins() {
|
|||
expectedErr bool
|
||||
}{
|
||||
{"", nil, false},
|
||||
{"4stake", nil, true},
|
||||
{"5.5atom,4stake", nil, true},
|
||||
{"4stake", sdk.DecCoins{sdk.NewDecCoinFromDec("stake", sdk.NewDecFromInt(sdk.NewInt(4)))}, false},
|
||||
{"5.5atom,4stake", sdk.DecCoins{
|
||||
sdk.NewDecCoinFromDec("atom", sdk.NewDecWithPrec(5500000000000000000, sdk.Precision)),
|
||||
sdk.NewDecCoinFromDec("stake", sdk.NewDec(4)),
|
||||
}, false},
|
||||
{"0.0stake", sdk.DecCoins{}, false}, // remove zero coins
|
||||
{"10.0btc,1.0atom,20.0btc", nil, true},
|
||||
{
|
||||
|
|
|
@ -8,6 +8,9 @@ import (
|
|||
// multipliers (e.g. 1atom = 10^-6uatom).
|
||||
var denomUnits = map[string]Dec{}
|
||||
|
||||
// baseDenom is the denom of smallest unit registered
|
||||
var baseDenom string = ""
|
||||
|
||||
// RegisterDenom registers a denomination with a corresponding unit. If the
|
||||
// denomination is already registered, an error will be returned.
|
||||
func RegisterDenom(denom string, unit Dec) error {
|
||||
|
@ -20,6 +23,10 @@ func RegisterDenom(denom string, unit Dec) error {
|
|||
}
|
||||
|
||||
denomUnits[denom] = unit
|
||||
|
||||
if baseDenom == "" || unit.LT(denomUnits[baseDenom]) {
|
||||
baseDenom = denom
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -38,6 +45,14 @@ func GetDenomUnit(denom string) (Dec, bool) {
|
|||
return unit, true
|
||||
}
|
||||
|
||||
// GetBaseDenom returns the denom of smallest unit registered
|
||||
func GetBaseDenom() (string, error) {
|
||||
if baseDenom == "" {
|
||||
return "", fmt.Errorf("no denom is registered")
|
||||
}
|
||||
return baseDenom, nil
|
||||
}
|
||||
|
||||
// ConvertCoin attempts to convert a coin to a given denomination. If the given
|
||||
// denomination is invalid or if neither denomination is registered, an error
|
||||
// is returned.
|
||||
|
@ -60,5 +75,73 @@ func ConvertCoin(coin Coin, denom string) (Coin, error) {
|
|||
return NewCoin(denom, coin.Amount), nil
|
||||
}
|
||||
|
||||
return NewCoin(denom, coin.Amount.ToDec().Mul(srcUnit.Quo(dstUnit)).TruncateInt()), nil
|
||||
return NewCoin(denom, coin.Amount.ToDec().Mul(srcUnit).Quo(dstUnit).TruncateInt()), nil
|
||||
}
|
||||
|
||||
// ConvertDecCoin attempts to convert a decimal coin to a given denomination. If the given
|
||||
// denomination is invalid or if neither denomination is registered, an error
|
||||
// is returned.
|
||||
func ConvertDecCoin(coin DecCoin, denom string) (DecCoin, error) {
|
||||
if err := ValidateDenom(denom); err != nil {
|
||||
return DecCoin{}, err
|
||||
}
|
||||
|
||||
srcUnit, ok := GetDenomUnit(coin.Denom)
|
||||
if !ok {
|
||||
return DecCoin{}, fmt.Errorf("source denom not registered: %s", coin.Denom)
|
||||
}
|
||||
|
||||
dstUnit, ok := GetDenomUnit(denom)
|
||||
if !ok {
|
||||
return DecCoin{}, fmt.Errorf("destination denom not registered: %s", denom)
|
||||
}
|
||||
|
||||
if srcUnit.Equal(dstUnit) {
|
||||
return NewDecCoinFromDec(denom, coin.Amount), nil
|
||||
}
|
||||
|
||||
return NewDecCoinFromDec(denom, coin.Amount.Mul(srcUnit).Quo(dstUnit)), nil
|
||||
}
|
||||
|
||||
// NormalizeCoin try to convert a coin to the smallest unit registered,
|
||||
// returns original one if failed.
|
||||
func NormalizeCoin(coin Coin) Coin {
|
||||
base, err := GetBaseDenom()
|
||||
if err != nil {
|
||||
return coin
|
||||
}
|
||||
newCoin, err := ConvertCoin(coin, base)
|
||||
if err != nil {
|
||||
return coin
|
||||
}
|
||||
return newCoin
|
||||
}
|
||||
|
||||
// NormalizeDecCoin try to convert a decimal coin to the smallest unit registered,
|
||||
// returns original one if failed.
|
||||
func NormalizeDecCoin(coin DecCoin) DecCoin {
|
||||
base, err := GetBaseDenom()
|
||||
if err != nil {
|
||||
return coin
|
||||
}
|
||||
newCoin, err := ConvertDecCoin(coin, base)
|
||||
if err != nil {
|
||||
return coin
|
||||
}
|
||||
return newCoin
|
||||
}
|
||||
|
||||
// NormalizeCoins normalize and truncate a list of decimal coins
|
||||
func NormalizeCoins(coins []DecCoin) Coins {
|
||||
if coins == nil {
|
||||
return nil
|
||||
}
|
||||
result := make([]Coin, 0, len(coins))
|
||||
|
||||
for _, coin := range coins {
|
||||
newCoin, _ := NormalizeDecCoin(coin).TruncateDecimal()
|
||||
result = append(result, newCoin)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ func (s *internalDenomTestSuite) TestRegisterDenom() {
|
|||
s.Require().Equal(ZeroDec(), res)
|
||||
|
||||
// reset registration
|
||||
baseDenom = ""
|
||||
denomUnits = map[string]Dec{}
|
||||
}
|
||||
|
||||
|
@ -52,6 +53,21 @@ func (s *internalDenomTestSuite) TestConvertCoins() {
|
|||
natomUnit := NewDecWithPrec(1, 9) // 10^-9 (nano)
|
||||
s.Require().NoError(RegisterDenom(natom, natomUnit))
|
||||
|
||||
res, err := GetBaseDenom()
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(res, natom)
|
||||
s.Require().Equal(NormalizeCoin(NewCoin(uatom, NewInt(1))), NewCoin(natom, NewInt(1000)))
|
||||
s.Require().Equal(NormalizeCoin(NewCoin(matom, NewInt(1))), NewCoin(natom, NewInt(1000000)))
|
||||
s.Require().Equal(NormalizeCoin(NewCoin(atom, NewInt(1))), NewCoin(natom, NewInt(1000000000)))
|
||||
|
||||
coins, err := ParseCoinsNormalized("1atom,1matom,1uatom")
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(coins, Coins{
|
||||
Coin{natom, NewInt(1000000000)},
|
||||
Coin{natom, NewInt(1000000)},
|
||||
Coin{natom, NewInt(1000)},
|
||||
})
|
||||
|
||||
testCases := []struct {
|
||||
input Coin
|
||||
denom string
|
||||
|
@ -87,5 +103,90 @@ func (s *internalDenomTestSuite) TestConvertCoins() {
|
|||
}
|
||||
|
||||
// reset registration
|
||||
baseDenom = ""
|
||||
denomUnits = map[string]Dec{}
|
||||
}
|
||||
|
||||
func (s *internalDenomTestSuite) TestConvertDecCoins() {
|
||||
atomUnit := OneDec() // 1 (base denom unit)
|
||||
s.Require().NoError(RegisterDenom(atom, atomUnit))
|
||||
|
||||
matomUnit := NewDecWithPrec(1, 3) // 10^-3 (milli)
|
||||
s.Require().NoError(RegisterDenom(matom, matomUnit))
|
||||
|
||||
uatomUnit := NewDecWithPrec(1, 6) // 10^-6 (micro)
|
||||
s.Require().NoError(RegisterDenom(uatom, uatomUnit))
|
||||
|
||||
natomUnit := NewDecWithPrec(1, 9) // 10^-9 (nano)
|
||||
s.Require().NoError(RegisterDenom(natom, natomUnit))
|
||||
|
||||
res, err := GetBaseDenom()
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(res, natom)
|
||||
s.Require().Equal(NormalizeDecCoin(NewDecCoin(uatom, NewInt(1))), NewDecCoin(natom, NewInt(1000)))
|
||||
s.Require().Equal(NormalizeDecCoin(NewDecCoin(matom, NewInt(1))), NewDecCoin(natom, NewInt(1000000)))
|
||||
s.Require().Equal(NormalizeDecCoin(NewDecCoin(atom, NewInt(1))), NewDecCoin(natom, NewInt(1000000000)))
|
||||
|
||||
coins, err := ParseCoinsNormalized("0.1atom,0.1matom,0.1uatom")
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(coins, Coins{
|
||||
Coin{natom, NewInt(100000000)},
|
||||
Coin{natom, NewInt(100000)},
|
||||
Coin{natom, NewInt(100)},
|
||||
})
|
||||
|
||||
testCases := []struct {
|
||||
input DecCoin
|
||||
denom string
|
||||
result DecCoin
|
||||
expErr bool
|
||||
}{
|
||||
{NewDecCoin("foo", ZeroInt()), atom, DecCoin{}, true},
|
||||
{NewDecCoin(atom, ZeroInt()), "foo", DecCoin{}, true},
|
||||
{NewDecCoin(atom, ZeroInt()), "FOO", DecCoin{}, true},
|
||||
|
||||
// 0.5atom
|
||||
{NewDecCoinFromDec(atom, NewDecWithPrec(5, 1)), matom, NewDecCoin(matom, NewInt(500)), false}, // atom => matom
|
||||
{NewDecCoinFromDec(atom, NewDecWithPrec(5, 1)), uatom, NewDecCoin(uatom, NewInt(500000)), false}, // atom => uatom
|
||||
{NewDecCoinFromDec(atom, NewDecWithPrec(5, 1)), natom, NewDecCoin(natom, NewInt(500000000)), false}, // atom => natom
|
||||
|
||||
{NewDecCoin(uatom, NewInt(5000000)), matom, NewDecCoin(matom, NewInt(5000)), false}, // uatom => matom
|
||||
{NewDecCoin(uatom, NewInt(5000000)), natom, NewDecCoin(natom, NewInt(5000000000)), false}, // uatom => natom
|
||||
{NewDecCoin(uatom, NewInt(5000000)), atom, NewDecCoin(atom, NewInt(5)), false}, // uatom => atom
|
||||
|
||||
{NewDecCoin(matom, NewInt(5000)), natom, NewDecCoin(natom, NewInt(5000000000)), false}, // matom => natom
|
||||
{NewDecCoin(matom, NewInt(5000)), uatom, NewDecCoin(uatom, NewInt(5000000)), false}, // matom => uatom
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
res, err := ConvertDecCoin(tc.input, tc.denom)
|
||||
s.Require().Equal(
|
||||
tc.expErr, err != nil,
|
||||
"unexpected error; tc: #%d, input: %s, denom: %s", i+1, tc.input, tc.denom,
|
||||
)
|
||||
s.Require().Equal(
|
||||
tc.result, res,
|
||||
"invalid result; tc: #%d, input: %s, denom: %s", i+1, tc.input, tc.denom,
|
||||
)
|
||||
}
|
||||
|
||||
// reset registration
|
||||
baseDenom = ""
|
||||
denomUnits = map[string]Dec{}
|
||||
}
|
||||
|
||||
func (s *internalDenomTestSuite) TestDecOperationOrder() {
|
||||
dec, err := NewDecFromStr("11")
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(RegisterDenom("unit1", dec))
|
||||
dec, err = NewDecFromStr("100000011")
|
||||
s.Require().NoError(RegisterDenom("unit2", dec))
|
||||
|
||||
coin, err := ConvertCoin(NewCoin("unit1", NewInt(100000011)), "unit2")
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(coin, NewCoin("unit2", NewInt(11)))
|
||||
|
||||
// reset registration
|
||||
baseDenom = ""
|
||||
denomUnits = map[string]Dec{}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ func TestBaseReq_Sanitize(t *testing.T) {
|
|||
|
||||
func TestBaseReq_ValidateBasic(t *testing.T) {
|
||||
fromAddr := "cosmos1cq0sxam6x4l0sv9yz3a2vlqhdhvt2k6jtgcse0"
|
||||
tenstakes, err := types.ParseCoins("10stake")
|
||||
tenstakes, err := types.ParseCoinsNormalized("10stake")
|
||||
require.NoError(t, err)
|
||||
onestake, err := types.ParseDecCoins("1.0stake")
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -56,7 +56,7 @@ func TestRandStringOfLength(t *testing.T) {
|
|||
}
|
||||
|
||||
func mustParseCoins(s string) sdk.Coins {
|
||||
coins, err := sdk.ParseCoins(s)
|
||||
coins, err := sdk.ParseCoinsNormalized(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ timestamp.`,
|
|||
return err
|
||||
}
|
||||
|
||||
amount, err := sdk.ParseCoins(args[1])
|
||||
amount, err := sdk.ParseCoinsNormalized(args[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ ignored as it is implied from [from_key_or_address].`,
|
|||
return err
|
||||
}
|
||||
|
||||
coins, err := sdk.ParseCoins(args[2])
|
||||
coins, err := sdk.ParseCoinsNormalized(args[2])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ ignored as it is implied from [from_key_or_address].`,
|
|||
return err
|
||||
}
|
||||
|
||||
coins, err := sdk.ParseCoins(args[2])
|
||||
coins, err := sdk.ParseCoinsNormalized(args[2])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -256,7 +256,7 @@ $ %s tx distribution fund-community-pool 100uatom --from mykey
|
|||
}
|
||||
|
||||
depositorAddr := clientCtx.GetFromAddress()
|
||||
amount, err := sdk.ParseCoins(args[0])
|
||||
amount, err := sdk.ParseCoinsNormalized(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -315,12 +315,12 @@ Where proposal.json contains:
|
|||
return err
|
||||
}
|
||||
|
||||
amount, err := sdk.ParseCoins(proposal.Amount)
|
||||
amount, err := sdk.ParseCoinsNormalized(proposal.Amount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deposit, err := sdk.ParseCoins(proposal.Deposit)
|
||||
deposit, err := sdk.ParseCoinsNormalized(proposal.Deposit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@ $ %s gentx my-key-name --home=/path/to/home/dir --keyring-backend=os --chain-id=
|
|||
}
|
||||
|
||||
amount, _ := cmd.Flags().GetString(cli.FlagAmount)
|
||||
coins, err := sdk.ParseCoins(amount)
|
||||
coins, err := sdk.ParseCoinsNormalized(amount)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to parse coins")
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ $ %s tx gov submit-proposal --title="Test Proposal" --description="My awesome pr
|
|||
return fmt.Errorf("failed to parse proposal: %w", err)
|
||||
}
|
||||
|
||||
amount, err := sdk.ParseCoins(proposal.Deposit)
|
||||
amount, err := sdk.ParseCoinsNormalized(proposal.Deposit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ $ %s tx gov deposit 1 10stake --from mykey
|
|||
from := clientCtx.GetFromAddress()
|
||||
|
||||
// Get amount of coins
|
||||
amount, err := sdk.ParseCoins(args[1])
|
||||
amount, err := sdk.ParseCoinsNormalized(args[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ Where proposal.json contains:
|
|||
proposal.Title, proposal.Description, proposal.Changes.ToParamChanges(),
|
||||
)
|
||||
|
||||
deposit, err := sdk.ParseCoins(proposal.Deposit)
|
||||
deposit, err := sdk.ParseCoinsNormalized(proposal.Deposit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ func NewCmdSubmitUpgradeProposal() *cobra.Command {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
deposit, err := sdk.ParseCoins(depositStr)
|
||||
deposit, err := sdk.ParseCoinsNormalized(depositStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ func NewCmdSubmitCancelUpgradeProposal() *cobra.Command {
|
|||
return err
|
||||
}
|
||||
|
||||
deposit, err := sdk.ParseCoins(depositStr)
|
||||
deposit, err := sdk.ParseCoinsNormalized(depositStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue