Expose sdk.Coins as a map[string]sdk.Int instead of a slice of coins ([]sdk.Coin )
currently sdk.Coins is defined as an array of sdk.Coin instead as a map (object), which makes it harder for clients to lookup a specific denomination since they have to filter the array
sdk.Coin should remain as a it is rn, sdk.Coins should be refactored to a map[string]sdk.Int:
Proposed JSON
{
"uatom": 10000000,
"photon": 1500000
}
Current JSON
[
{
"denom": "uatom",
"amount": 10000000
},
{
"denom": "photon",
"amount": 1500000
}
]
I've experienced this pain point myself on Lunie as well as @sabau, @faboweb and @jbibla
I recall there was a reason we stuck with a slice instead of a map. For starters, maps are non-deterministic. So storing them as such may yield different application hashes. Not to mention, I doubt amino can even encode maps?
cc @ValarDragon
For starters, maps are non-deterministic.
yeah that's what @alessio told me
Great! Want to close this?
oh, no, I meant that I think we should still export the map to clients. Even tho if we keep the slice internally
You mean you want to implement Coins#ToMap? What would this provide? If you want to know if a denom exists or how much of a denom there is, use AmountOf (albeit it's slower).
to get the amount for a specific denom (eg uatom), clients need to implement a filter like the following:
const uatoms = coins.filter(coin => coin.denom === `uatom`)
I understand now. What you're suggesting is creating a custom JSON encoder/decoder. However, I don't see anything inherently wrong with needing to filter imho. Now coins won't be a true representation of their internal structure.
Surely theres a standard serializable golang hashmap?
I googled this and one of the top results was #553 lol
If you want simple JSON map just for the clients, I think the following will suffice:
types/coin.go
// ----------------------------------------------------------------------------
// Encoding
type CoinJSON map[string]Int
func (coins Coins) MarshalJSON() ([]byte, error) {
cj := make(CoinJSON)
for _, c := range coins {
cj[c.Denom] = c.Amount
}
return json.Marshal(cj)
}
func (coins *Coins) UnmarshalJSON(b []byte) error {
cj := make(CoinJSON)
if err := json.Unmarshal(b, &cj); err != nil {
return err
}
for denom, amount := range cj {
(*coins) = append((*coins), NewCoin(denom, amount))
}
coins.Sort()
return nil
}
I think this will make life slightly easier for clients.
We are talking just about REST interfaces or also CLI (if someone want to build some sort of local client that fetch data from CLI)?
RPC seems out of context here right?
Just REST would suffice for the scope of this issue I think
With the code I showed above, it's as simple as that. You don't have to change anything else :)
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Most helpful comment
If you want simple JSON map just for the clients, I think the following will suffice: