Cosmos-sdk: coins exposed to clients should be map instead of array

Created on 9 Apr 2019  Â·  15Comments  Â·  Source: cosmos/cosmos-sdk

Summary

Expose sdk.Coins as a map[string]sdk.Int instead of a slice of coins ([]sdk.Coin )

Problem Definition

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

Proposal

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


For Admin Use

  • [ ] Not duplicate issue
  • [ ] Appropriate labels applied
  • [ ] Appropriate contributors tagged
  • [ ] Contributor assigned/self-assigned
REST api-breaking proposal stale bank

Most helpful comment

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
}

All 15 comments

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ValarDragon picture ValarDragon  Â·  3Comments

ValarDragon picture ValarDragon  Â·  3Comments

fedekunze picture fedekunze  Â·  3Comments

rigelrozanski picture rigelrozanski  Â·  3Comments

cwgoes picture cwgoes  Â·  3Comments