kipcole9 / money

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

Working with crypto currencies #118

Closed cospin closed 4 years ago

cospin commented 4 years ago

Is there support for crypto currencies or custom currencies? If so, is there an example? I have seen some issues about this but they are somewhat outdated, I couldn't come up with something functional.

Thanks!

kipcole9 commented 4 years ago

@CristianOspina, you are right. I made a start on this and have not completed it. I could not resolve my mental model is a few areas:

  1. CLDR does not provide localisation data for crypto currencies which means that would not be able to be formatted in a localised fashion as all other currencies are.

  2. The crypto currency codes do not conform consistently with ISO4217 currency codes which means that serialisation to a database using the money_sql implementation would also need to be changed and the composite field changes.

  3. The exchange rate mechanism would need adjusting and I am not confident enough I know the best exchange to use (by default), the best refresh period (by default)

  4. I did arrive at a conclusion on the best way to serialise new user-defined currencies in such a way that they are defined once and then available in the money backend when the application is restarted.

All of these are solvable and I'm more than happy to implement if we can collaborate on resolving these and any other design issues.

WDYT?

kipcole9 commented 4 years ago

I also seem to recall that some crypto currencies trade with different symbols of different exchanges but I may not be recalling that correctly.

cospin commented 4 years ago

First of all I do not know if I am the right one, I do not have much experience with this package and less with the others (CLDR etc.), and I do not consider myself an expert in Elixir or crypto but I will try to help in some things:

  1. I really don't know how CDLR works, But to maintain the design, I suppose that is where we have to start by allowing to locate new coins.

  2. ISO4217 is a great plus of Money, but I see it difficult to do something for crypto currencies. Initially, we could simply configure to disable these validations. I really don't know if there is a standard for crypto currencies.

  3. Regarding the exchange, I think that there are not many changes to make, Open Exchange Rates already supports a few cryptocurrencies, Bitcoin in the main list, and others in the alternative list: https://oxr.readme.io/docs/supported-currencies It would be a matter for each developer to make their own Money.ExchangeRates, since there are definitely thousands of crypto exchanges for different markets and with different currencies, so it is very difficult to come up with one that fits everyone, a crypto alternative could be Cryptowatch API, as it supports many exchanges, markets, with history etc. in a single API, and they have a free tier, the problem is that is hard to find pairs for every fiat currencies, so I think OER fits more in this package for both, fiat and crypto currencies as a default, and since it is already implemented there is no need to work on it. Yes, it has a few crypto currencies, but will be compatible with all the already supported fiat currencies, seems fair.

  4. Indeed, first I come across elixirmoney/money, there you can configure a new currency with its options in config.ex and also with support for Money_SQL (at least for 3 char). I really liked the approach, but I didn't like that it was a bit short on operations, precision, options etc.

cospin commented 4 years ago

In the end, cryptocurrencies are not much different. With the options that we have already to set decimals, precision etc. is easy to support them, the big issue here is ISO4217, I guess.

kipcole9 commented 4 years ago

Would using this list of pseudo iso4217 codes cover your needs?

https://en.m.wikipedia.org/wiki/ISO_4217#Cryptocurrencies

If so I can add them as valid currencies and everything else except localisation should be ok.

Sent from my iPhone

On 9 Jul 2020, at 07:19, CristianOspina notifications@github.com wrote:

 In the end, cryptocurrencies are not much different. With the options that we have already to set decimals, precision etc. is easy to support them, the big issue here is ISO4217, I guess.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

cospin commented 4 years ago

Yes, it has the top currencies and also the exponent 👌

kipcole9 commented 4 years ago

OK, then I’ll implement those codes for now and see if that works “well enough” for now. I allows me to keep consistency with currency code length (3) which means serialisation is also fine. Its a static list which is easy to implement. As as you say, it specifies an exponent so I can infer rounding on output.

Expect I can get this done by the end of the weekend - that OK for you?

Regards, —Kip

On 9 Jul 2020, at 2:07 pm, CristianOspina notifications@github.com wrote:

Yes, it has the top currencies and also the exponent 👌

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/kipcole9/money/issues/118#issuecomment-655921633, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAD4F534ANI6IKGNNVDSCDR2VNANANCNFSM4OUL7KLQ.

cospin commented 4 years ago

Amazing! Yes, it's fine for me, thank you!

kipcole9 commented 4 years ago

Not quite done yet I'm afraid. I'm taking a more generalised approach in which all currency codes must still be ISO4217 compliant. You will be able to create your own currencies using the ISO4217 private use codes that start with "X". And I'm extending the currency definition to add a field called :alt_code which can be whatever you need it to be.

I think this makes it straight forward for creating any currency you want, still be standards compliant and yet retain the exchange codes relevant for cryptocurrencies.

This would, for now, put the onus on you to return an exchange rates map with the "correct" currency codes when doing currency conversion. This doesn't require any chance to the ex_money since passing in the conversion map is always a requirement and everything else is just convenience.

I will finish up the work today (Monday my time) and revert here.

kipcole9 commented 4 years ago

I now have a development version for you to try should you be interested. It supports the creation of "private use" currencies. If no major bugs are found I will publish it to hex on Wednesday.

Configuration

  1. Add {:ex_cldr_currencies, GitHub: "elixir-cldr/cldr_currencies", override: true} to your mix.exs
  2. Add a supervisor to your application module. This is required in order to manage an :ets table that holds the private use currencies. An example:

    defmodule MyApp do
    use Application
    
    def start(_type, _args) do
    
    # Start the service which maintains the
    # :ets table that holds the private use currencies
    children = [
      Cldr.Currency
      ...
    ]
    
    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
    end
    end

    Alternatively you can call Cldr.Currency.start_link to manually start the private use supervisor but this is not the preferred approach except for testing.

Usage

  1. In your application code call Cldr.Currency.new/2 to create a new currency. Example:
    iex> Cldr.Currency.new :XBT, name: "Bitcoin", alt_code: :BTC, digits: 8, symbol: "₿" 
    {:ok,
    %Cldr.Currency{
    alt_code: :BTC,
    cash_digits: 8,
    cash_rounding: nil,
    code: :XBT,
    count: %{other: "Bitcoin"},
    digits: 8,
    from: nil,
    iso_digits: 8,
    name: "Bitcoin",
    narrow_symbol: nil,
    rounding: 0,
    symbol: "₿",
    tender: false,
    to: nil
    }}

A private use currency operates like any other currency so you should be good to go.

Notes

  1. The private use currencies are not persisted anywhere between app restarts. It is a developer responsibility to call Cldr.Currency.new/2 when the app starts.

  2. Money.to_string/2 will work on custom currencies however the results to do apply pluralisation or localisation since the data is not available to do so.

Feedback welcome. I will add some documentation to ex_money and then publish this update to ex_cldr_currencies and the enhanced docs for ex_money.

cospin commented 4 years ago

Nice approach. I've done a few tests and for now everything I need seems to work perfectly. Thank you! 🙌

BTW, the keyword github should be lowercase. It took me some time to figure out the problem, since it always took the stable version haha: {:ex_cldr_currencies, github: "elixir-cldr/cldr_currencies", override: true}

kipcole9 commented 4 years ago

Arrrggh, spell correct did that, apologies.

I have submitted a PR to the eternal project (which I am using to do the best thing I can to keep the :ets table alive) to add a callback when the table is created so that you can populate the currencies you want. I’ll finish up tests and docs on the current version and get that pushed so you can use it directly and update eternal as a dep if/when the PR is accepted.

Appreciate the collaboration and thanks for the feedback.

Last thing, you can actually do pluralization on your new currencies by setting the undocumented :count option. It’s a bit complex to explain since its not normally exposed this way - but if you care about 20 bitcoin versus 20 bitcoins let me know and I’ll explain.

Sent from my iPad

On 14 Jul 2020, at 4:49 pm, CristianOspina notifications@github.com wrote:

 Nice approach. I've done a few tests and for now everything I need seems to work perfectly. Thank you! 🙌

BTW, the keyword github should be lowercase. It took me some time to figure out the problem, since it always took the stable version haha: {:ex_cldr_currencies, github: "elixir-cldr/cldr_currencies", override: true}

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

kipcole9 commented 4 years ago

I have published ex_cldr_currencies version 2.6.0 and pushed an update to the ex_money README.md with a section on Private Use Currencies.

You should be able to update with mix dep.update ex_cldr_currencies.

Thanks for the collaboration and the issue, keep them coming. I'll close this issue now, please re-open if you spot something incorrect.