feat: implement parse method for Int,Dec,Coin (#13696)

* wip: parse coins

* review changes

* update tests

* update tests

* Use ValueOfString

* fix tests

* fix coin tests

* FIXES

* Add empty coins test case

* fix tests

* fix tests

* review changes

* fix tests

* fix tests

* revert format.go

* revert `format.go`

* review changes

* fix tests & review changes

* review changes

* review changes

* review changes

* review changes

* review changes

* review changes

* proposal shorter code

* review changes

* conflicts

* review changes

* wip

* ParseRepeated for coins

* Revert listpb

* remove duplicate case

* revert some more

Co-authored-by: Amaury M <1293565+amaurym@users.noreply.github.com>
Co-authored-by: Marko <marbar3778@yahoo.com>
This commit is contained in:
atheeshp
2022-12-02 14:26:25 +01:00
committed by GitHub
co-authored by Amaury M Marko
parent bcfb7dc330
commit 9d12a5fce3
12 changed files with 467 additions and 166 deletions
+6
View File
@@ -10,6 +10,8 @@ import (
"cosmossdk.io/math"
)
const emptyCoins = "zero"
// formatCoin formats a sdk.Coin into a value-rendered string, using the
// given metadata about the denom. It returns the formatted coin string, the
// display denom, and an optional error.
@@ -78,6 +80,10 @@ func FormatCoins(coins []*basev1beta1.Coin, metadata []*bankv1beta1.Metadata) (s
}
}
if len(coins) == 0 {
return emptyCoins, nil
}
// Sort the formatted coins by display denom.
sort.SliceStable(formatted, func(i, j int) bool {
denomI := strings.Split(formatted[i], " ")[1]
@@ -50,11 +50,13 @@ Value Renderers describe how values of different Protobuf types should be encode
* an array of `coin` is display as the concatenation of each `coin` encoded as the specification above, the joined together with the delimiter `", "` (a comma and a space, no quotes around).
* the list of coins is ordered by unicode code point of the display denom: `A-Z` < `a-z`. For example, the string `aAbBcC` would be sorted `ABCabc`.
- if the coins list had 0 items in it then it'll be rendered as `zero`
### Example
* `["3cosm", "2000000uatom"]` -> `2 atom, 3 COSM` (assuming the display denoms are `atom` and `COSM`)
* `["10atom", "20Acoin"]` -> `20 Acoin, 10 atom` (assuming the display denoms are `atom` and `Acoin`)
- `[]` -> `zero`
### `repeated`
+54 -54
View File
@@ -1,137 +1,137 @@
[
{
"proto": {"amount": "0", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"text": "0 COSM"
},
{
"proto": {"amount": "1", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"text": "0.000001 COSM"
},
{
"proto": {"amount": "10", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"text": "0.00001 COSM"
},
{
"proto": {"amount": "100", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"text": "0.0001 COSM"
},
{
"proto": {"amount": "1000", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"text": "0.001 COSM"
},
{
"proto": {"amount": "10000", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"text": "0.01 COSM"
},
{
"proto": {"amount": "100000", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"text": "0.1 COSM"
},
{
"proto": {"amount": "1000000", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"text": "1 COSM"
},
{
"proto": {"amount": "10000000", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"text": "10 COSM"
},
{
"proto": {"amount": "0", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}, {"denom":"stake", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}, {"denom":"stake", "exponent": 0}]},
"text": "0 COSM"
},
{
"proto": {"amount": "1", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}, {"denom":"stake", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}, {"denom":"stake", "exponent": 0}]},
"text": "0.000001 COSM"
},
{
"proto": {"amount": "10", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}, {"denom":"stake", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}, {"denom":"stake", "exponent": 0}]},
"text": "0.00001 COSM"
},
{
"proto": {"amount": "100", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}, {"denom":"stake", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}, {"denom":"stake", "exponent": 0}]},
"text": "0.0001 COSM"
},
{
"proto": {"amount": "1000", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}, {"denom":"stake", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}, {"denom":"stake", "exponent": 0}]},
"text": "0.001 COSM"
},
{
"proto": {"amount": "10000", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}, {"denom":"stake", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}, {"denom":"stake", "exponent": 0}]},
"text": "0.01 COSM"
},
{
"proto": {"amount": "100000", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}, {"denom":"stake", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}, {"denom":"stake", "exponent": 0}]},
"text": "0.1 COSM"
},
{
"proto": {"amount": "1000000", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}, {"denom":"stake", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}, {"denom":"stake", "exponent": 0}]},
"text": "1 COSM"
},
{
"proto": {"amount": "10000000", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}, {"denom":"stake", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}, {"denom":"stake", "exponent": 0}]},
"text": "10 COSM"
},
{
"proto": {"amount": "0", "denom": "COSM"},
"metadata": {"display": "ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "ucosm", "base":"COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"text": "0 ucosm"
},
{
"proto": {"amount": "0.000001", "denom": "COSM"},
"metadata": {"display": "ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "ucosm", "base":"COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"text": "1 ucosm"
},
{
"proto": {"amount": "0.00001", "denom": "COSM"},
"metadata": {"display": "ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "ucosm", "base":"COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"text": "10 ucosm"
},
{
"proto": {"amount": "0.0001", "denom": "COSM"},
"metadata": {"display": "ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "ucosm", "base":"COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"text": "100 ucosm"
},
{
"proto": {"amount": "0.001", "denom": "COSM"},
"metadata": {"display": "ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "ucosm", "base":"COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"text": "1'000 ucosm"
},
{
"proto": {"amount": "0.01", "denom": "COSM"},
"metadata": {"display": "ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "ucosm", "base":"COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"text": "10'000 ucosm"
},
{
"proto": {"amount": "0.1", "denom": "COSM"},
"metadata": {"display": "ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "ucosm", "base":"COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"text": "100'000 ucosm"
},
{
"proto": {"amount": "1", "denom": "COSM"},
"metadata": {"display": "ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "ucosm", "base":"COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"text": "1'000'000 ucosm"
},
{
"proto": {"amount": "10", "denom": "COSM"},
"metadata": {"display": "ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "ucosm", "base":"COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"text": "10'000'000 ucosm"
},
{
@@ -217,137 +217,137 @@
},
{
"proto": {"amount": "0", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 2}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 2}]},
"text": "0 ucosm"
},
{
"proto": {"amount": "1", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 2}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 2}]},
"text": "1 ucosm"
},
{
"proto": {"amount": "10", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 2}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 2}]},
"text": "10 ucosm"
},
{
"proto": {"amount": "100", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 2}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 2}]},
"text": "100 ucosm"
},
{
"proto": {"amount": "1000", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 2}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 2}]},
"text": "1'000 ucosm"
},
{
"proto": {"amount": "10000", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 2}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 2}]},
"text": "10'000 ucosm"
},
{
"proto": {"amount": "100000", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 2}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 2}]},
"text": "100'000 ucosm"
},
{
"proto": {"amount": "1000000", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 2}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 2}]},
"text": "1'000'000 ucosm"
},
{
"proto": {"amount": "10000000", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 2}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 2}]},
"text": "10'000'000 ucosm"
},
{
"proto": {"amount": "0", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 2}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 2}, {"denom": "ucosm", "exponent": 0}]},
"text": "0 COSM"
},
{
"proto": {"amount": "1", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 2}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 2}, {"denom": "ucosm", "exponent": 0}]},
"text": "0.01 COSM"
},
{
"proto": {"amount": "10", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 2}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 2}, {"denom": "ucosm", "exponent": 0}]},
"text": "0.1 COSM"
},
{
"proto": {"amount": "100", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 2}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 2}, {"denom": "ucosm", "exponent": 0}]},
"text": "1 COSM"
},
{
"proto": {"amount": "1000", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 2}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 2}, {"denom": "ucosm", "exponent": 0}]},
"text": "10 COSM"
},
{
"proto": {"amount": "10000", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 2}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 2}, {"denom": "ucosm", "exponent": 0}]},
"text": "100 COSM"
},
{
"proto": {"amount": "100000", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 2}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 2}, {"denom": "ucosm", "exponent": 0}]},
"text": "1'000 COSM"
},
{
"proto": {"amount": "1000000", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 2}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 2}, {"denom": "ucosm", "exponent": 0}]},
"text": "10'000 COSM"
},
{
"proto": {"amount": "10000000", "denom": "ucosm"},
"metadata": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 2}, {"denom": "ucosm", "exponent": 0}]},
"metadata": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 2}, {"denom": "ucosm", "exponent": 0}]},
"text": "100'000 COSM"
},
{
"proto": {"amount": "0", "denom": "point"},
"metadata": {"display": "POINT", "denom_units": [{"denom": "point", "exponent": 0}, {"denom": "POINT", "exponent": 0}]},
"metadata": {"display": "POINT", "base": "point", "denom_units": [{"denom": "point", "exponent": 0}, {"denom": "POINT", "exponent": 0}]},
"text": "0 POINT"
},
{
"proto": {"amount": "1", "denom": "point"},
"metadata": {"display": "POINT", "denom_units": [{"denom": "point", "exponent": 0}, {"denom": "POINT", "exponent": 0}]},
"metadata": {"display": "POINT", "base": "point", "denom_units": [{"denom": "point", "exponent": 0}, {"denom": "POINT", "exponent": 0}]},
"text": "1 POINT"
},
{
"proto": {"amount": "10", "denom": "point"},
"metadata": {"display": "POINT", "denom_units": [{"denom": "point", "exponent": 0}, {"denom": "POINT", "exponent": 0}]},
"metadata": {"display": "POINT", "base": "point", "denom_units": [{"denom": "point", "exponent": 0}, {"denom": "POINT", "exponent": 0}]},
"text": "10 POINT"
},
{
"proto": {"amount": "100", "denom": "point"},
"metadata": {"display": "POINT", "denom_units": [{"denom": "point", "exponent": 0}, {"denom": "POINT", "exponent": 0}]},
"metadata": {"display": "POINT", "base": "point", "denom_units": [{"denom": "point", "exponent": 0}, {"denom": "POINT", "exponent": 0}]},
"text": "100 POINT"
},
{
"proto": {"amount": "1000", "denom": "point"},
"metadata": {"display": "POINT", "denom_units": [{"denom": "point", "exponent": 0}, {"denom": "POINT", "exponent": 0}]},
"metadata": {"display": "POINT", "base": "point", "denom_units": [{"denom": "point", "exponent": 0}, {"denom": "POINT", "exponent": 0}]},
"text": "1'000 POINT"
},
{
"proto": {"amount": "10000", "denom": "point"},
"metadata": {"display": "POINT", "denom_units": [{"denom": "point", "exponent": 0}, {"denom": "POINT", "exponent": 0}]},
"metadata": {"display": "POINT", "base": "point", "denom_units": [{"denom": "point", "exponent": 0}, {"denom": "POINT", "exponent": 0}]},
"text": "10'000 POINT"
},
{
"proto": {"amount": "100000", "denom": "point"},
"metadata": {"display": "POINT", "denom_units": [{"denom": "point", "exponent": 0}, {"denom": "POINT", "exponent": 0}]},
"metadata": {"display": "POINT", "base": "point", "denom_units": [{"denom": "point", "exponent": 0}, {"denom": "POINT", "exponent": 0}]},
"text": "100'000 POINT"
},
{
"proto": {"amount": "1000000", "denom": "point"},
"metadata": {"display": "POINT", "denom_units": [{"denom": "point", "exponent": 0}, {"denom": "POINT", "exponent": 0}]},
"metadata": {"display": "POINT", "base": "point", "denom_units": [{"denom": "point", "exponent": 0}, {"denom": "POINT", "exponent": 0}]},
"text": "1'000'000 POINT"
},
{
"proto": {"amount": "10000000", "denom": "point"},
"metadata": {"display": "POINT", "denom_units": [{"denom": "point", "exponent": 0}, {"denom": "POINT", "exponent": 0}]},
"metadata": {"display": "POINT", "base": "point", "denom_units": [{"denom": "point", "exponent": 0}, {"denom": "POINT", "exponent": 0}]},
"text": "10'000'000 POINT"
},
{"text":"", "error": true},
+14 -14
View File
@@ -2,15 +2,15 @@
{
"proto": [],
"metadata":{},
"text": ""
"text": "zero"
},
{
"proto": [
{ "amount": "1", "denom": "ucosm" }
],
"metadata":{
"ucosm": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"ustake": {"display": "STAKE", "denom_units": [{"denom": "STAKE", "exponent": 6}, {"denom": "ustake", "exponent": 0}]}
"ucosm": {"display": "COSM", "base": "ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"ustake": {"display": "STAKE", "base": "ustake", "denom_units": [{"denom": "STAKE", "exponent": 6}, {"denom": "ustake", "exponent": 0}]}
},
"text": "0.000001 COSM"
},
@@ -20,8 +20,8 @@
{ "amount": "3", "denom": "ustake" }
],
"metadata":{
"ucosm": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"ustake": {"display": "STAKE", "denom_units": [{"denom": "STAKE", "exponent": 6}, {"denom": "ustake", "exponent": 0}]}
"ucosm": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"ustake": {"display": "STAKE", "base":"ustake", "denom_units": [{"denom": "STAKE", "exponent": 6}, {"denom": "ustake", "exponent": 0}]}
},
"text": "0.000001 COSM, 0.000003 STAKE"
},
@@ -31,8 +31,8 @@
{ "amount": "1", "denom": "ucosm" }
],
"metadata": {
"ucosm": {"display": "COSM", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"ustake": {"display": "STAKE", "denom_units": [{"denom": "STAKE", "exponent": 6}, {"denom": "ustake", "exponent": 0}]}
"ucosm": {"display": "COSM", "base":"ucosm", "denom_units": [{"denom": "COSM", "exponent": 6}, {"denom": "ucosm", "exponent": 0}]},
"ustake": {"display": "STAKE", "base":"ustake", "denom_units": [{"denom": "STAKE", "exponent": 6}, {"denom": "ustake", "exponent": 0}]}
},
"text": "0.000001 COSM, 0.000003 STAKE"
},
@@ -43,9 +43,9 @@
{ "amount": "3", "denom": "uatom" }
],
"metadata": {
"uaa": {"display": "AA", "denom_units": [{"denom": "AA", "exponent": 6}, {"denom": "uaa", "exponent": 0}]},
"ubb": {"display": "BB", "denom_units": [{"denom": "BB", "exponent": 6}, {"denom": "ubb", "exponent": 0}]},
"uatom": {"display": "atom", "denom_units": [{"denom": "atom", "exponent": 6}, {"denom": "uatom", "exponent": 0}]}
"uaa": {"display": "AA", "base":"uaa", "denom_units": [{"denom": "AA", "exponent": 6}, {"denom": "uaa", "exponent": 0}]},
"ubb": {"display": "BB", "base":"ubb", "denom_units": [{"denom": "BB", "exponent": 6}, {"denom": "ubb", "exponent": 0}]},
"uatom": {"display": "atom", "base":"uatom", "denom_units": [{"denom": "atom", "exponent": 6}, {"denom": "uatom", "exponent": 0}]}
},
"text": "0.000001 AA, 0.000002 BB, 0.000003 atom"
},
@@ -57,10 +57,10 @@
{ "amount": "1", "denom": "uxa" }
],
"metadata": {
"uxa": {"display": "xA", "denom_units": [{"denom": "xA", "exponent": 6}, {"denom": "uxa", "exponent": 0}]},
"uxb": {"display": "xB", "denom_units": [{"denom": "xB", "exponent": 6}, {"denom": "uxb", "exponent": 0}]},
"uxc": {"display": "xC", "denom_units": [{"denom": "xC", "exponent": 6}, {"denom": "uxc", "exponent": 0}]},
"uxc1": {"display": "xC1", "denom_units": [{"denom": "xC1", "exponent": 6}, {"denom": "uxc1", "exponent": 0}]}
"uxa": {"display": "xA", "base":"uxa", "denom_units": [{"denom": "xA", "exponent": 6}, {"denom": "uxa", "exponent": 0}]},
"uxb": {"display": "xB", "base":"uxb", "denom_units": [{"denom": "xB", "exponent": 6}, {"denom": "uxb", "exponent": 0}]},
"uxc": {"display": "xC", "base":"uxc", "denom_units": [{"denom": "xC", "exponent": 6}, {"denom": "uxc", "exponent": 0}]},
"uxc1": {"display": "xC1", "base":"uxc1", "denom_units": [{"denom": "xC1", "exponent": 6}, {"denom": "uxc1", "exponent": 0}]}
},
"text": "0.000001 xA, 0.000002 xB, 0.000003 xC, 0.000004 xC1"
},
+18 -4
View File
@@ -48,7 +48,7 @@ func TestMetadataQuerier(t *testing.T) {
require.NoError(t, err)
_, err = vr.Format(context.Background(), protoreflect.ValueOf((&basev1beta1.Coin{}).ProtoReflect()))
require.ErrorIs(t, err, expErr)
_, err = vr.Format(context.Background(), protoreflect.ValueOf(NewGenericList([]*basev1beta1.Coin{{}})))
_, err = vr.(valuerenderer.RepeatedValueRenderer).FormatRepeated(context.Background(), protoreflect.ValueOf(NewGenericList([]*basev1beta1.Coin{{}})))
require.ErrorIs(t, err, expErr)
}
@@ -67,6 +67,10 @@ func TestCoinJsonTestcases(t *testing.T) {
t.Run(tc.Text, func(t *testing.T) {
if tc.Proto != nil {
ctx := context.WithValue(context.Background(), mockCoinMetadataKey(tc.Proto.Denom), tc.Metadata)
if tc.Metadata != nil {
ctx = context.WithValue(ctx, mockCoinMetadataKey(tc.Metadata.Display), tc.Metadata)
}
screens, err := vr.Format(ctx, protoreflect.ValueOf(tc.Proto.ProtoReflect()))
if tc.Error {
@@ -77,10 +81,20 @@ func TestCoinJsonTestcases(t *testing.T) {
require.NoError(t, err)
require.Equal(t, 1, len(screens))
require.Equal(t, tc.Text, screens[0].Text)
}
// TODO Add parsing tests
// https://github.com/cosmos/cosmos-sdk/issues/13153
// Round trip.
value, err := vr.Parse(ctx, screens)
if tc.Error {
require.Error(t, err)
return
}
require.NoError(t, err)
coin, ok := value.Message().Interface().(*basev1beta1.Coin)
require.True(t, ok)
checkCoinEqual(t, coin, tc.Proto)
}
})
}
}
+178 -48
View File
@@ -3,14 +3,18 @@ package valuerenderer
import (
"context"
"fmt"
"strings"
"google.golang.org/protobuf/reflect/protoreflect"
bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1"
basev1beta1 "cosmossdk.io/api/cosmos/base/v1beta1"
corecoins "cosmossdk.io/core/coins"
"cosmossdk.io/math"
)
const emptyCoins = "zero"
// NewCoinsValueRenderer returns a ValueRenderer for SDK Coin and Coins.
func NewCoinsValueRenderer(q CoinMetadataQueryFn) ValueRenderer {
return coinsValueRenderer{q}
@@ -31,61 +35,187 @@ func (vr coinsValueRenderer) Format(ctx context.Context, v protoreflect.Value) (
return nil, fmt.Errorf("expected non-nil coin metadata querier")
}
// Check whether we have a Coin or some Coins.
switch protoCoins := v.Interface().(type) {
// If it's a repeated Coin:
case protoreflect.List:
{
coins, metadatas := make([]*basev1beta1.Coin, protoCoins.Len()), make([]*bankv1beta1.Metadata, protoCoins.Len())
var err error
for i := 0; i < protoCoins.Len(); i++ {
coin := protoCoins.Get(i).Interface().(protoreflect.Message).Interface().(*basev1beta1.Coin)
coins[i] = coin
metadatas[i], err = vr.coinMetadataQuerier(ctx, coin.Denom)
if err != nil {
return nil, err
}
}
// Since this value renderer has a FormatRepeated method, the Format one
// here only handles single coin.
coin := v.Interface().(protoreflect.Message).Interface().(*basev1beta1.Coin)
formatted, err := corecoins.FormatCoins(coins, metadatas)
if err != nil {
return nil, err
}
return []Screen{{Text: formatted}}, nil
}
// If it's a single Coin:
case protoreflect.Message:
{
coin := v.Interface().(protoreflect.Message).Interface().(*basev1beta1.Coin)
metadata, err := vr.coinMetadataQuerier(ctx, coin.Denom)
if err != nil {
return nil, err
}
formatted, err := corecoins.FormatCoins([]*basev1beta1.Coin{coin}, []*bankv1beta1.Metadata{metadata})
if err != nil {
return nil, err
}
return []Screen{{Text: formatted}}, nil
}
default:
return nil, fmt.Errorf("got invalid type %t for coins", v.Interface())
metadata, err := vr.coinMetadataQuerier(ctx, coin.Denom)
if err != nil {
return nil, err
}
}
func (vr coinsValueRenderer) Parse(_ context.Context, screens []Screen) (protoreflect.Value, error) {
// ref: https://github.com/cosmos/cosmos-sdk/issues/13153
panic("implement me, see #13153")
formatted, err := corecoins.FormatCoins([]*basev1beta1.Coin{coin}, []*bankv1beta1.Metadata{metadata})
if err != nil {
return nil, err
}
return []Screen{{Text: formatted}}, nil
}
func (vr coinsValueRenderer) FormatRepeated(ctx context.Context, v protoreflect.Value) ([]Screen, error) {
return vr.Format(ctx, v)
if vr.coinMetadataQuerier == nil {
return nil, fmt.Errorf("expected non-nil coin metadata querier")
}
protoCoins := v.List()
coins, metadatas := make([]*basev1beta1.Coin, protoCoins.Len()), make([]*bankv1beta1.Metadata, protoCoins.Len())
var err error
for i := 0; i < protoCoins.Len(); i++ {
coin := protoCoins.Get(i).Interface().(protoreflect.Message).Interface().(*basev1beta1.Coin)
coins[i] = coin
metadatas[i], err = vr.coinMetadataQuerier(ctx, coin.Denom)
if err != nil {
return nil, err
}
}
formatted, err := corecoins.FormatCoins(coins, metadatas)
if err != nil {
return nil, err
}
return []Screen{{Text: formatted}}, nil
}
func (vr coinsValueRenderer) Parse(ctx context.Context, screens []Screen) (protoreflect.Value, error) {
if len(screens) != 1 {
return nilValue, fmt.Errorf("expected single screen: %v", screens)
}
if screens[0].Text == emptyCoins {
return protoreflect.ValueOfMessage((&basev1beta1.Coin{}).ProtoReflect()), nil
}
parsed, err := vr.parseCoins(ctx, screens[0].Text)
if err != nil {
return nilValue, err
}
return protoreflect.ValueOfMessage(parsed[0].ProtoReflect()), err
}
func (vr coinsValueRenderer) ParseRepeated(ctx context.Context, screens []Screen, l protoreflect.List) error {
// ref: https://github.com/cosmos/cosmos-sdk/issues/13153
panic("implement me, see #13153")
if len(screens) != 1 {
return fmt.Errorf("expected single screen: %v", screens)
}
if screens[0].Text == emptyCoins {
return nil
}
parsed, err := vr.parseCoins(ctx, screens[0].Text)
if err != nil {
return err
}
for _, c := range parsed {
l.Append(protoreflect.ValueOf(c.ProtoReflect()))
}
return nil
}
func (vr coinsValueRenderer) parseCoins(ctx context.Context, coinsStr string) ([]*basev1beta1.Coin, error) {
coins := strings.Split(coinsStr, ", ")
metadatas := make([]*bankv1beta1.Metadata, len(coins))
var err error
for i, coin := range coins {
coinArr := strings.Split(coin, " ")
if len(coinArr) != 2 {
return nil, fmt.Errorf("invalid coin %s", coin)
}
metadatas[i], err = vr.coinMetadataQuerier(ctx, coinArr[1])
if err != nil {
return nil, err
}
}
if len(coins) != len(metadatas) {
return []*basev1beta1.Coin{}, fmt.Errorf("formatCoins expect one metadata for each coin; expected %d, got %d", len(coins), len(metadatas))
}
parsedCoins := make([]*basev1beta1.Coin, len(coins))
for i, coinStr := range coins {
coin, err := parseCoin(coinStr, metadatas[i])
if err != nil {
return nil, err
}
parsedCoins[i] = coin
}
return parsedCoins, nil
}
// parseCoin parses a single value-rendered coin into the Coin struct.
// It shares a lot of code with `cosmos-sdk.io/core/coins.Format`,
// so this code might be refactored once we have
// a core Parse function for coins.
func parseCoin(coinStr string, metadata *bankv1beta1.Metadata) (*basev1beta1.Coin, error) {
coinArr := strings.Split(coinStr, " ")
amt1 := coinArr[0]
coinDenom := coinArr[1]
if metadata == nil || metadata.Base == "" || coinArr[1] == metadata.Base {
dec, err := parseDec(amt1)
if err != nil {
return nil, err
}
return &basev1beta1.Coin{
Amount: dec,
Denom: coinDenom,
}, nil
}
baseDenom := metadata.Base
// Find exponents of both denoms.
foundCoinExp, foundBaseExp := false, false
var coinExp, baseExp uint32
for _, unit := range metadata.DenomUnits {
if coinDenom == unit.Denom {
coinExp = unit.Exponent
foundCoinExp = true
}
if baseDenom == unit.Denom {
baseExp = unit.Exponent
foundBaseExp = true
}
}
// If we didn't find either exponent, then we return early.
if !foundCoinExp || !foundBaseExp {
amt, err := parseDec(amt1)
if err != nil {
return nil, err
}
return &basev1beta1.Coin{
Amount: amt,
Denom: baseDenom,
}, nil
}
// remove 1000 separators, (ex: 1'000'000 -> 1000000)
amt1 = strings.ReplaceAll(amt1, "'", "")
amt, err := math.LegacyNewDecFromStr(amt1)
if err != nil {
return nil, err
}
if coinExp > baseExp {
amt = amt.Mul(math.LegacyNewDec(10).Power(uint64(coinExp - baseExp)))
} else {
amt = amt.Quo(math.LegacyNewDec(10).Power(uint64(baseExp - coinExp)))
}
amtStr, err := parseDec(amt.String())
if err != nil {
return nil, err
}
return &basev1beta1.Coin{
Amount: amtStr,
Denom: baseDenom,
}, nil
}
+48 -8
View File
@@ -8,6 +8,7 @@ import (
bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1"
basev1beta1 "cosmossdk.io/api/cosmos/base/v1beta1"
"cosmossdk.io/math"
"cosmossdk.io/tx/textual/valuerenderer"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/reflect/protoreflect"
@@ -22,6 +23,7 @@ func TestCoinsJsonTestcases(t *testing.T) {
textual := valuerenderer.NewTextual(mockCoinMetadataQuerier)
vr, err := textual.GetFieldValueRenderer(fieldDescriptorFromName("COINS"))
vrr := vr.(valuerenderer.RepeatedValueRenderer)
require.NoError(t, err)
for _, tc := range testcases {
@@ -30,29 +32,67 @@ func TestCoinsJsonTestcases(t *testing.T) {
// Create a context.Context containing all coins metadata, to simulate
// that they are in state.
ctx := context.Background()
for _, coin := range tc.Proto {
ctx = context.WithValue(ctx, mockCoinMetadataKey(coin.Denom), tc.Metadata[coin.Denom])
for _, v := range tc.Metadata {
ctx = context.WithValue(ctx, mockCoinMetadataKey(v.Base), v)
ctx = context.WithValue(ctx, mockCoinMetadataKey(v.Display), v)
}
listValue := NewGenericList(tc.Proto)
screens, err := vr.Format(ctx, protoreflect.ValueOf(listValue))
screens, err := vrr.FormatRepeated(ctx, protoreflect.ValueOf(listValue))
require.NoError(t, err)
require.Equal(t, 1, len(screens))
require.Equal(t, tc.Text, screens[0].Text)
// Round trip.
parsedValue := NewGenericList([]*basev1beta1.Coin{})
err = vrr.ParseRepeated(ctx, screens, parsedValue)
if tc.Error {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, 1, len(screens))
require.Equal(t, tc.Text, screens[0].Text)
checkCoinsEqual(t, listValue, parsedValue)
}
// TODO Add parsing tests
// https://github.com/cosmos/cosmos-sdk/issues/13153
})
}
}
// checkCoinsEqual checks that the 2 lists of Coins contain the same
// **set** of coins. It does not check that the order of coins are
// equal, because in Textual, we sort the coins alphabetically after
// rendering, so we lose initial Coins ordering. Instead, we just check
// set equality using a map.
func checkCoinsEqual(t *testing.T, l1, l2 protoreflect.List) {
require.Equal(t, l1.Len(), l2.Len())
var coinsMap = make(map[string]*basev1beta1.Coin, l1.Len())
for i := 0; i < l1.Len(); i++ {
coin, ok := l1.Get(i).Message().Interface().(*basev1beta1.Coin)
require.True(t, ok)
coinsMap[coin.Denom] = coin
}
for i := 0; i < l2.Len(); i++ {
coin, ok := l2.Get(i).Message().Interface().(*basev1beta1.Coin)
require.True(t, ok)
coin1 := coinsMap[coin.Denom]
checkCoinEqual(t, coin, coin1)
}
}
func checkCoinEqual(t *testing.T, coin, coin1 *basev1beta1.Coin) {
require.Equal(t, coin1.Denom, coin.Denom)
v, err := math.LegacyNewDecFromStr(coin.Amount)
require.NoError(t, err)
v1, err := math.LegacyNewDecFromStr(coin1.Amount)
require.NoError(t, err)
require.True(t, v.Equal(v1))
}
// coinsJsonTest is the type of test cases in the testdata file.
// If the test case has a Proto, try to Format() it. If Error is set, expect
// an error, otherwise match Text, then Parse() the text and expect it to
+30 -1
View File
@@ -2,6 +2,8 @@ package valuerenderer
import (
"context"
"fmt"
"strings"
"google.golang.org/protobuf/reflect/protoreflect"
@@ -27,5 +29,32 @@ func (vr decValueRenderer) Format(_ context.Context, v protoreflect.Value) ([]Sc
}
func (vr decValueRenderer) Parse(_ context.Context, screens []Screen) (protoreflect.Value, error) {
panic("implement me")
if n := len(screens); n != 1 {
return nilValue, fmt.Errorf("expected 1 screen, got: %d", n)
}
parsed, err := parseDec(screens[0].Text)
if err != nil {
return nilValue, err
}
return protoreflect.ValueOfString(parsed), nil
}
func parseDec(v string) (string, error) {
parts := strings.Split(v, ".")
if len(parts) > 2 {
return "", fmt.Errorf("invalid decimal: too many points in %s", v)
}
intPart, err := parseInt(parts[0])
if err != nil {
return "", err
}
if len(parts) == 1 {
return intPart, nil
}
return intPart + "." + parts[1], nil
}
+66 -4
View File
@@ -2,6 +2,9 @@ package valuerenderer
import (
"context"
"fmt"
"strconv"
"strings"
"cosmossdk.io/math"
"google.golang.org/protobuf/reflect/protoreflect"
@@ -9,11 +12,13 @@ import (
// NewIntValueRenderer returns a ValueRenderer for uint32, uint64, int32 and
// int64, and sdk.Int scalars.
func NewIntValueRenderer() ValueRenderer {
return intValueRenderer{}
func NewIntValueRenderer(fd protoreflect.FieldDescriptor) ValueRenderer {
return intValueRenderer{fd}
}
type intValueRenderer struct{}
type intValueRenderer struct {
fd protoreflect.FieldDescriptor
}
var _ ValueRenderer = intValueRenderer{}
@@ -26,5 +31,62 @@ func (vr intValueRenderer) Format(_ context.Context, v protoreflect.Value) ([]Sc
}
func (vr intValueRenderer) Parse(_ context.Context, screens []Screen) (protoreflect.Value, error) {
panic("implement me")
if n := len(screens); n != 1 {
return nilValue, fmt.Errorf("expected 1 screen, got: %d", n)
}
parsedInt, err := parseInt(screens[0].Text)
if err != nil {
return nilValue, err
}
switch vr.fd.Kind() {
case protoreflect.Uint32Kind:
value, err := strconv.ParseUint(parsedInt, 10, 32)
if err != nil {
return nilValue, err
}
return protoreflect.ValueOfUint32(uint32(value)), nil //nolint:gosec
case protoreflect.Uint64Kind:
value, err := strconv.ParseUint(parsedInt, 10, 64)
if err != nil {
return nilValue, err
}
return protoreflect.ValueOfUint64(value), nil
case protoreflect.Int32Kind:
value, err := strconv.ParseInt(parsedInt, 10, 32)
if err != nil {
return nilValue, err
}
return protoreflect.ValueOfInt32(int32(value)), nil //nolint:gosec
case protoreflect.Int64Kind:
value, err := strconv.ParseInt(parsedInt, 10, 64)
if err != nil {
return nilValue, err
}
return protoreflect.ValueOfInt64(value), nil
case protoreflect.StringKind:
return protoreflect.ValueOfString(parsedInt), nil
default:
return nilValue, fmt.Errorf("parsing integers into a %s field is not supported", vr.fd.Kind())
}
}
// parseInt parses a value-rendered string into an integer
func parseInt(v string) (string, error) {
sign := ""
if v[0] == '-' {
sign = "-"
v = v[1:]
}
// remove the 1000 separators (ex: 1'000'000 -> 1000000)
v = strings.ReplaceAll(v, "'", "")
return sign + v, nil
}
+35 -21
View File
@@ -25,32 +25,34 @@ func TestIntJsonTestcases(t *testing.T) {
textual := valuerenderer.NewTextual(nil)
for _, tc := range testcases {
// Parse test case strings as protobuf uint64
i, err := strconv.ParseUint(tc[0], 10, 64)
if err == nil {
r, err := textual.GetFieldValueRenderer(fieldDescriptorFromName("UINT64"))
require.NoError(t, err)
t.Run(tc[0], func(t *testing.T) {
// Parse test case strings as protobuf uint64
i, err := strconv.ParseUint(tc[0], 10, 64)
if err == nil {
r, err := textual.GetFieldValueRenderer(fieldDescriptorFromName("UINT64"))
require.NoError(t, err)
checkNumberTest(t, r, protoreflect.ValueOf(i), tc[1])
}
checkNumberTest(t, r, protoreflect.ValueOf(i), tc[1])
}
// Parse test case strings as protobuf uint32
i, err = strconv.ParseUint(tc[0], 10, 32)
if err == nil {
r, err := textual.GetFieldValueRenderer(fieldDescriptorFromName("UINT32"))
require.NoError(t, err)
// Parse test case strings as protobuf uint32
i, err = strconv.ParseUint(tc[0], 10, 32)
if err == nil {
r, err := textual.GetFieldValueRenderer(fieldDescriptorFromName("UINT32"))
require.NoError(t, err)
checkNumberTest(t, r, protoreflect.ValueOf(i), tc[1])
}
checkNumberTest(t, r, protoreflect.ValueOf(i), tc[1])
}
// Parse test case strings as sdk.Ints
_, ok := math.NewIntFromString(tc[0])
if ok {
r, err := textual.GetFieldValueRenderer(fieldDescriptorFromName("SDKINT"))
require.NoError(t, err)
// Parse test case strings as sdk.Ints
_, ok := math.NewIntFromString(tc[0])
if ok {
r, err := textual.GetFieldValueRenderer(fieldDescriptorFromName("SDKINT"))
require.NoError(t, err)
checkNumberTest(t, r, protoreflect.ValueOf(tc[0]), tc[1])
}
checkNumberTest(t, r, protoreflect.ValueOf(tc[0]), tc[1])
}
})
}
}
@@ -64,4 +66,16 @@ func checkNumberTest(t *testing.T, r valuerenderer.ValueRenderer, pv protoreflec
require.Equal(t, false, screens[0].Expert)
require.Equal(t, expected, screens[0].Text)
// Round trip.
value, err := r.Parse(context.Background(), screens)
require.NoError(t, err)
v, err := math.LegacyNewDecFromStr(pv.String())
require.NoError(t, err)
v1, err := math.LegacyNewDecFromStr(value.String())
require.NoError(t, err)
require.True(t, v.Equal(v1))
}
+13 -9
View File
@@ -19,6 +19,9 @@ import (
// metadata. It is meant to be passed as an argument into `NewTextual`.
type CoinMetadataQueryFn func(ctx context.Context, denom string) (*bankv1beta1.Metadata, error)
// ValueRendererCreator is a function returning a ValueRenderer.
type ValueRendererCreator func(protoreflect.FieldDescriptor) ValueRenderer
// Textual holds the configuration for dispatching
// to specific value renderers for SIGN_MODE_TEXTUAL.
type Textual struct {
@@ -28,9 +31,9 @@ type Textual struct {
// server-side code) or a gRPC query client (for client-side code).
coinMetadataQuerier CoinMetadataQueryFn
// scalars defines a registry for Cosmos scalars.
scalars map[string]ValueRenderer
// messages defines a registry for custom message renderers, as defined in
// point #9 in the spec. Note that we also use this same registry for the
scalars map[string]ValueRendererCreator
// messages defines a registry for custom message renderers.
// Note that we also use this same registry for the
// following messages, as they can be thought of custom message rendering:
// - SDK coin and coins
// - Protobuf timestamp
@@ -62,7 +65,7 @@ func (r *Textual) GetFieldValueRenderer(fd protoreflect.FieldDescriptor) (ValueR
return nil, fmt.Errorf("got empty value renderer for scalar %s", scalar)
}
return vr, nil
return vr(fd), nil
}
return NewStringValueRenderer(), nil
@@ -74,7 +77,7 @@ func (r *Textual) GetFieldValueRenderer(fd protoreflect.FieldDescriptor) (ValueR
fd.Kind() == protoreflect.Uint64Kind ||
fd.Kind() == protoreflect.Int32Kind ||
fd.Kind() == protoreflect.Int64Kind:
return NewIntValueRenderer(), nil
return NewIntValueRenderer(fd), nil
case fd.Kind() == protoreflect.EnumKind:
return NewEnumValueRenderer(fd), nil
@@ -87,6 +90,7 @@ func (r *Textual) GetFieldValueRenderer(fd protoreflect.FieldDescriptor) (ValueR
if found {
return vr, nil
}
if fd.IsMap() {
return nil, fmt.Errorf("value renderers cannot format value of type map")
}
@@ -111,9 +115,9 @@ func (r *Textual) GetMessageValueRenderer(md protoreflect.MessageDescriptor) (Va
func (r *Textual) init() {
if r.scalars == nil {
r.scalars = map[string]ValueRenderer{}
r.scalars["cosmos.Int"] = NewIntValueRenderer()
r.scalars["cosmos.Dec"] = NewDecValueRenderer()
r.scalars = map[string]ValueRendererCreator{}
r.scalars["cosmos.Int"] = func(fd protoreflect.FieldDescriptor) ValueRenderer { return NewIntValueRenderer(fd) }
r.scalars["cosmos.Dec"] = func(_ protoreflect.FieldDescriptor) ValueRenderer { return NewDecValueRenderer() }
}
if r.messages == nil {
r.messages = map[protoreflect.FullName]ValueRenderer{}
@@ -125,7 +129,7 @@ func (r *Textual) init() {
}
// DefineScalar adds a value renderer to the given Cosmos scalar.
func (r *Textual) DefineScalar(scalar string, vr ValueRenderer) {
func (r *Textual) DefineScalar(scalar string, vr ValueRendererCreator) {
r.init()
r.scalars[scalar] = vr
}
@@ -17,9 +17,9 @@ func TestDispatcher(t *testing.T) {
expErr bool
expValueRenderer valuerenderer.ValueRenderer
}{
{"UINT32", false, valuerenderer.NewIntValueRenderer()},
{"UINT64", false, valuerenderer.NewIntValueRenderer()},
{"SDKINT", false, valuerenderer.NewIntValueRenderer()},
{"UINT32", false, valuerenderer.NewIntValueRenderer(fieldDescriptorFromName("UINT32"))},
{"UINT64", false, valuerenderer.NewIntValueRenderer(fieldDescriptorFromName("UINT64"))},
{"SDKINT", false, valuerenderer.NewIntValueRenderer(fieldDescriptorFromName("SDKINT"))},
{"SDKDEC", false, valuerenderer.NewDecValueRenderer()},
{"BYTES", false, valuerenderer.NewBytesValueRenderer()},
{"TIMESTAMP", false, valuerenderer.NewTimestampValueRenderer()},