kipcole9 / money

Elixir implementation of Money with Currency
https://hex.pm/packages/ex_money
Other
563 stars 48 forks source link

Working with custom currencies? #64

Closed eumend closed 6 years ago

eumend commented 6 years ago

The documentation states you can create new currencies via Cldr.Currency.new, and using that function truly creates new currencies, but how does one actually use them with "Money"? I found nothing on the docs for storing or working with custom currencies. Needless to say, I can:

Cldr.Currency.new(:XXX)

Alright, but when I try to use its atom (:XXX) for anything(eg, Money.new(:XXX, 100) ), I get:

{:error, {Money.UnknownCurrencyError, "The currency :XXX is invalid"}}

I thought of creating the currency and storing it in some module, but most of the Money module functions work with Money objects which need a valid atom.

For context, I'm working with cryptocurrencies and wanted to put them as custom currencies.

kipcole9 commented 6 years ago

@eumend Appreciate the report - definitely a bug. I never completed the original design for reasons outlined below.

I will fix this in the next few hours. I could use some feedback though.

Current Strictness of a currency code

My original intent was to be quite strict on what is a valid currency. The way to enforce that was intended to be:

  1. A currency had to be created before use
  2. The currency code had to comply with ISO4217 private currency standards (3 ASCII alphabetic characters in uppercase, starts with "X")

But now I'm thinking that that is really not a required. As long as (2) above is followed, pre-creating the currency itself may be overkill. And it also requires managing state which, for a single node application is easy - but for a distributed app is hard.

Current private use codes in the ISO4217 standard

These codes all valid in Money today since they are valid in ISO4217.

["XBB", "XEU", "XFO", "XTS", "XBA", "XRE", "XOF", "XCD", "XDR", "XPD", "XBD", "XFU", "XBC", "XUA", "XAG", "XAU", "XPT", "XPF", "XXX", "XSU", "XAF"]

Current Cryptocurrency codes

According to Wikipedia, the following is a basic list of codes commonly in use for the most well-known crypto currencies. Several of them don't follow the ISO4217 private use code format. Despite that, I think Money should continue to hold to ISO4217 and therefore DASH, ETH, VTC and ZEC would not be considered valid currency codes.

How much of an issue is that for you?

Code Num Fractional Digits Currency Notes
DASH _ 8 Dash  
ETH _ 18 Ether ETH conflicts with ISO 4217 because ET stands for Ethiopia.
VTC _ 8 Vertcoin  
XBC _ 8 Bitcoin Cash XBC conflicts with European Unit of Account 9 (E.U.A.-9)
XBT (formerly BTC) - 8 Bitcoin BTC conflicts with ISO 4217 because BT stands for Bhutan.
XLM _ 8 Stellar Lumen  
XMR _ 12 Monero  
XRP _ 6 Ripple  

ZEC

Proposed solution

  1. Validation of a currency code within Money is limited to simply being a valid ISO4217 currency code. Either in the defined list, or 3 Alphabetic ASCII characters starting with "X"

  2. No registration of a currency code is required in advance.

eumend commented 6 years ago

@kipcole9 I'm in favour of not requiring custom currencies to be pre-created (the library will throw anyway if I "misstype" the code in some operation after all).

At the moment and for my current use case I only need to handle a small amount of cryptocurrencies, so using, for example, "XET" for ETH should suffice.

However, I do think the ISO4217 will take its time to handle cryptos correctly, and with ERC20 Tokens thrown into the mix, that's even more 3-4 letter codes to handle. Plus, pretty much anyone can issue ERC20 tokens with their own code for use in their apps.

Most apps won't need all of them, indeed many will use either the most popular ones and/or their own issued currency, but having only two letters to define each cryptocurrency or token might cause troubles for bigger apps (eg. the project I'm working on will eventually need to support multiple cryptocurrencies).

In case supporting cryptocurrencies is on the short-term roadmap for this library, would supporting any alphabetic, 3-4 characters in length ASCII combination, plus a "This isnt a valid ISO code" warning, be ok? This is likely worth it's own Issue of course, and there might be better solutions.

kipcole9 commented 6 years ago

Thanks for the feedback. Definitely cryptocurrencies are something I want to support - and be driven by what other developers need.

I'll go ahead and firstly get "any valid ISO4217 code" working in the next few hours. I'd be very happy to collaborate on expanding cryptocurrency support and what that means after that, starting with your suggestion.

kipcole9 commented 6 years ago

I am close to finishing up. The only area of incompatibility is going to be formatting output. By default, the database schema stores money with a precision of (20,8) which, according to Wikipedia, isn't enough fractional precision for Ethereum and Monero. So you would have to adjust the migration and/or the database yourself. You ok with that?

The other part then is formatted output. Clearly there is no CLDR source of localised currency names, rounding requirements or other metadata that CLDR maintains for known currency codes.

My plan is just to default all the metadata to some hopefully "sensible defaults". Hopefully thats ok too - because otherwise I would need to maintain a database of cryptocurrencies which would be onerous.

If there an open source database or cryptocurrency metadata?

eumend commented 6 years ago

@kipcole9 Adjusting the migration seems simple enough, thanks!

As for a cryptocurrency metadata repository, I think this one from cryptocompare will do?

https://www.cryptocompare.com/api/#-api-data-coinlist-

It brings an array of all the coins from the website, since cryptos just keep coming I think a list this extensive is good enough for now? Also notice how some of the symbols have asterisks on them or are number (eg. "404").

kipcole9 commented 6 years ago

Sorry for losing a few days on this but I'm back on it now.

  1. Cldr master branch now allows the use of any valid ISO4217 alphabetic currency code, including the "X" currency codes.

  2. As a result of (1) above, the functions in the Money module work as expected - with the major exception of to_string/2.

  3. to_string/2 assumes that there is a currency definition in place (symbols, minor units, pluralisation rules and so on). I am not determined yet how to handle that for a valid but unknown currency code.

  4. I am also unsure how to treat valid but unknown codes in the function Money.known_currencies/0 and related functions. I am inclined to exclude them from the return results since they are, in fact, unknown. The alternative would be to revert to the idea of "currency registration" but as we discussed I don't plan that for now.

Next step

I need to update the package Cldr_Numbers because its actually the library that does number formatting, including currencies. My intention is to create a default currency definition for each valid but unknown currency code. In this first version this is not going to be very performant since it will create the structure on each formatting request.

Future work

  1. All paths seem, eventually, to lead to requiring a more formal ability (not requirement) to formally define a currency to support proper formatting. Since this shouldn't be a compile-time configuration thing i need to decide on a serialisation strategy.

Crypto currencies

Once this first release (allowing the use of any valid ISO4217 code including "X" codes) is complete I'll add a new module that is a struct called Money.Crypto and some support modules that dynamically maintain, probably using crypto compare, a set of currency definitions that automate the overall process. I'll create a crypto supervisor and worker in a similar fashion to the exchange rates mechanism. The reason for a separate Money.Crypto is to allow pattern matching and also so allow for a different serialisation strategy (like different precision 4-character currency codes) but still be compatible with Money.

kipcole9 commented 6 years ago

Cldr version 1.5.2 is now published on hex. This version supports "X" currencies. So all Money functions except to_string/2 should work with that version. I am working on to_string/2 now.

kipcole9 commented 6 years ago

Cldr Numbers version 1.4.2 is published on hex and it supports to_string/2 for valid but unknown currencies.

This should complete phase 1 of support for crypto currencies but I appreciated its far from what you need. Hopefully this lets you move forward using "X" codes for now while I work on a Money.Crypto module and friends.

To use this, all you should have to do is update ex_cldr and ex_cldr_numbers dependencies.

mix deps.update ex_cldr
mix deps.update ex_cldr_numbers
kipcole9 commented 6 years ago

I just pushed Money version 2.5.0 to hex. I had to fix one test to reflect the changes to ex_cldr and ex_cldr_numbers. It also includes another unrelated feature.

I'm going to keep this issue open until I complete the Money.Crypto module. Please feel free to keep the suggestions and feedback coming.

kipcole9 commented 6 years ago

I'm going to close this issue and open a new one specifically on crypto currencies.