Skip to content

Commit

Permalink
dvr.QueryDenomMetadata
Browse files Browse the repository at this point in the history
  • Loading branch information
cyberbono3 committed Sep 8, 2021
1 parent ab7d6fb commit 24ae2f4
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 87 deletions.
122 changes: 55 additions & 67 deletions types/valuerenderer/valuerenderer.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package valuerenderer

import (
"context"
"errors"
"math"
"regexp"
"strconv"
"strings"
"unicode"


"github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank/keeper"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"

"golang.org/x/text/language"
Expand All @@ -21,52 +22,37 @@ type ValueRenderer interface {
Parse(string) (interface{}, error)
}


type DenomQuerierFunc func(string) banktypes.Metadata

// create default value rreenderer in CLI and then get context from CLI
type DefaultValueRenderer struct {
// /string is denom that user sents
denomQuerier DenomQuerierFunc// define in test only //convert DenomUnits to Display units
bankKeeper keeper.BaseKeeper // define in test only //convert DenomUnits to Display units
metaData banktypes.Metadata
}

func NewDefaultValueRenderer() DefaultValueRenderer {
return DefaultValueRenderer{}
func NewDefaultValueRenderer(bk keeper.BaseKeeper) DefaultValueRenderer {
return DefaultValueRenderer{bankKeeper: bk}
}

func NewDefaultValueRendererWithDenom(displayDenom string) DefaultValueRenderer {
getMetadata := func(denom string) banktypes.Metadata {
if strings.HasPrefix(denom, "m") || strings.HasPrefix(denom, "u") {
denom = denom[1:]
}
return banktypes.Metadata{
Description: "The native staking token of the Cosmos Hub.",
DenomUnits: []*banktypes.DenomUnit{
{
Denom: denom,
Exponent: 0,
Aliases: []string{denom},
},
{
Denom: "u" + denom,
Exponent: 6,
Aliases: []string{"micro" + denom},
},
{
Denom: "m" + denom,
Exponent: 3,
Aliases: []string{"mini" + denom},
},
},
Base: "uregen",
Display: displayDenom,
}
func (dvr DefaultValueRenderer) QueryDenomMetadata(ctx context.Context, coin types.Coin) (banktypes.Metadata, error) {
req := &banktypes.QueryDenomMetadataRequest{
Denom: coin.Denom,
}
return DefaultValueRenderer{getMetadata}
}

res, err := dvr.bankKeeper.DenomMetadata(ctx, req)
if err != nil {
return banktypes.Metadata{}, err
}

return res.Metadata, nil
}

func (dvr DefaultValueRenderer) GetDenomMetadata() banktypes.Metadata {
return dvr.metaData
}

func (dvr DefaultValueRenderer) SetDenomMetadata(metaData banktypes.Metadata) {
dvr.metaData = metaData
}
var _ ValueRenderer = &DefaultValueRenderer{}

// Format converts an empty interface into a string depending on interface type.
Expand Down Expand Up @@ -124,37 +110,9 @@ func (dvr DefaultValueRenderer) Format(x interface{}) (string, error) {
return "", errors.New("unable to cast empty interface to Coin")
}

metadata := dvr.denomQuerier(coin.Denom)
var coinExp, displayExp int64
for _, denomUnit := range metadata.DenomUnits {
// TODO test 23000000 mregen 3 => "regen" exp 0
if denomUnit.Denom == coin.Denom {
coinExp = int64(denomUnit.Exponent)
}
// check if dvr.metadata is not empty

if denomUnit.Denom == metadata.Display {
displayExp = int64(denomUnit.Exponent)
}
}

expSub := float64(displayExp - coinExp)
var amount int64

switch {
// negative , convert mregen to regen less zeroes
case math.Signbit(expSub):
// TODO or should i use math package?
amount = types.NewDecFromIntWithPrec(coin.Amount, int64(math.Abs(expSub))).TruncateInt64() // use Dec or just golang built in methods
// positive, convert mregen to uregen
case !math.Signbit(expSub):
amount = coin.Amount.Mul(types.NewInt(int64(math.Pow(10, expSub)))).Int64()
// == 0, convert regen to regen, amount does not change
default:
amount = coin.Amount.Int64()
}


newAmount, newDenom := p.Sprintf("%d", amount), metadata.Display
newAmount, newDenom := p.Sprintf("%d", dvr.ComputeAmount(coin)), dvr.metaData.Display
sb.WriteString(newAmount)
sb.WriteString(newDenom)

Expand All @@ -165,12 +123,42 @@ func (dvr DefaultValueRenderer) Format(x interface{}) (string, error) {
return sb.String(), nil
}

func (dvr DefaultValueRenderer) ComputeAmount(coin types.Coin) int64 {
var coinExp, displayExp int64
for _, denomUnit := range dvr.metaData.DenomUnits {
if denomUnit.Denom == coin.Denom {
coinExp = int64(denomUnit.Exponent)
}

if denomUnit.Denom == dvr.metaData.Display {
displayExp = int64(denomUnit.Exponent)
}
}

expSub := float64(displayExp - coinExp)
var amount int64

switch {
// negative , convert mregen to regen less zeroes
case math.Signbit(expSub):
// TODO or should i use math package?
amount = types.NewDecFromIntWithPrec(coin.Amount, int64(math.Abs(expSub))).TruncateInt64() // use Dec or just golang built in methods
// positive, convert mregen to uregen
case !math.Signbit(expSub):
amount = coin.Amount.Mul(types.NewInt(int64(math.Pow(10, expSub)))).Int64()
// == 0, convert regen to regen, amount does not change
default:
amount = coin.Amount.Int64()
}

return amount
}

// see QueryDenomMetadataRequest() test
/*
func (dvr DefaultValueRenderer) denomQuerier() banktypes.Metadata {
app := simapp.Setup(t, false)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})
Expand Down
82 changes: 62 additions & 20 deletions types/valuerenderer/valuerenderer_test.go
Original file line number Diff line number Diff line change
@@ -1,76 +1,116 @@
package valuerenderer_test

import (
"regexp"
// "context"
// "regexp"
"testing"

"github.com/stretchr/testify/require"

"github.com/cosmos/cosmos-sdk/simapp"
"github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/valuerenderer"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/bank/keeper"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"

"golang.org/x/text/language"
"golang.org/x/text/message"
)

const (
holder = "holder"
multiPerm = "multiple permissions account"
randomPerm = "random permission"
)

func initBankKeeperAndContext(t *testing.T) (keeper.BaseKeeper, types.Context) {
app := simapp.Setup(t, false)
c := app.BaseApp.NewContext(false, tmproto.Header{})
maccPerms := simapp.GetMaccPerms()
appCodec := simapp.MakeTestEncodingConfig().Codec

maccPerms[holder] = nil
maccPerms[authtypes.Burner] = []string{authtypes.Burner}
maccPerms[authtypes.Minter] = []string{authtypes.Minter}
maccPerms[multiPerm] = []string{authtypes.Burner, authtypes.Minter, authtypes.Staking}
maccPerms[randomPerm] = []string{"random"}
authKeeper := authkeeper.NewAccountKeeper(
appCodec, app.GetKey(banktypes.StoreKey), app.GetSubspace(banktypes.ModuleName),
authtypes.ProtoBaseAccount, maccPerms,
)
blockedAddrs := make(map[string]bool)

keeper := keeper.NewBaseKeeper(
appCodec, app.GetKey(banktypes.StoreKey), authKeeper,
app.GetSubspace(banktypes.ModuleName), blockedAddrs,
)

return keeper,c
}

// TODO add more test cases
func TestFormatCoin(t *testing.T) {

bk, c := initBankKeeperAndContext(t)
ctx := types.WrapSDKContext(c)
dvr := valuerenderer.NewDefaultValueRenderer(bk)
p := message.NewPrinter(language.English)

// TODO add test case to convert from mregen to uregen
tt := []struct {
name string
dvr valuerenderer.DefaultValueRenderer
coin types.Coin
expRes string
expErr bool
}{
{
"convert 1000000uregen to 1regen",
valuerenderer.NewDefaultValueRendererWithDenom("regen"),
types.NewCoin("uregen", types.NewInt(int64(1000000))),
"1regen",
"convert 1000000regen to 1000000regen",
types.NewCoin("regen", types.NewInt(int64(1000000))),
false,
},
/*
{
"convert 1000000000uregen to 1000regen",
valuerenderer.NewDefaultValueRendererWithDenom("regen"),
types.NewCoin("uregen", types.NewInt(int64(1000000000))),
"1,000regen",
false,
},
{
"convert 23000000mregen to 23000regen",
valuerenderer.NewDefaultValueRendererWithDenom("regen"),
types.NewCoin("mregen", types.NewInt(int64(23000000))),
"23,000regen",
false,
},
{
"convert 23000000mregen to 23000000000uregen",
valuerenderer.NewDefaultValueRendererWithDenom("uregen"),
types.NewCoin("mregen", types.NewInt(int64(23000000))),
"23,000,000,000uregen",
false,
},
{
"convert 23000000regen to 23000000000mregen",
valuerenderer.NewDefaultValueRendererWithDenom("mregen"),
types.NewCoin("regen", types.NewInt(int64(23000000))),
"23,000,000,000mregen",
false,
},
{
"convert 23000regen to 23000regen",
valuerenderer.NewDefaultValueRendererWithDenom("regen"),
types.NewCoin("regen", types.NewInt(int64(23000))),
"23,000regen",
false,
},
*/
}

for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
res, err := tc.dvr.Format(tc.coin)
metaData, err := dvr.QueryDenomMetadata(ctx, tc.coin)
require.NoError(t, err)
require.Equal(t, tc.expRes, res)

dvr.SetDenomMetadata(metaData)
res, err := dvr.Format(tc.coin)
require.NoError(t, err)

expAmount := p.Sprintf("%d", dvr.ComputeAmount(tc.coin))
expDenom := dvr.GetDenomMetadata().Display
require.Equal(t, expAmount + expDenom, res)
})
}
}
Expand All @@ -80,7 +120,7 @@ func TestFormatDec(t *testing.T) {
d valuerenderer.DefaultValueRenderer
)
// TODO add more cases and error cases

tt := []struct {
name string
input types.Dec
Expand Down Expand Up @@ -155,6 +195,7 @@ func TestFormatInt(t *testing.T) {
}

// TODO add more test cases
/*
func TestParseString(t *testing.T) {
re := regexp.MustCompile(`\d+[mu]?regen`)
dvr := valuerenderer.NewDefaultValueRenderer()
Expand Down Expand Up @@ -198,3 +239,4 @@ func TestParseString(t *testing.T) {
})
}
}
*/

0 comments on commit 24ae2f4

Please sign in to comment.