stellar / stellar-protocol

Developer discussion about possible changes to the protocol.
523 stars 303 forks source link

Proposal: Standardize "amount" representation in client libraries and api #20

Open nullstyle opened 9 years ago

nullstyle commented 9 years ago

The time is come for us to resolve one of the niggling issues left in the new stellar network: How the API and client libraries deal with numbers that represent an "amount" in a transaction. Example amounts:

Presently, stellar-core represents these amount as a 64-bit unsigned integer and interprets the number in no other way: It's always using integer math.

However, by convention we say that uint64 is actually a fixed-place decimal number with 7 digits of precision. I'm proposing that we bake this convention into horizon and the client libraries.

To do this, and to gain the most compatibility between language-specific representations and the json representation communicated by horizon, I propose that we should encode amounts as a string containing a decimal number. Some example side effects of this change:

  1. Amounts in horizon would go from being json numbers to strings:
{
                "amount": 50000000.0,
                "asset_type": "native",
}

would become:

{
                "amount": "5",
                "asset_type": "native",
}
  1. The ruby client library would assume the 7 fixed-place decimal representation of any numbers passed into it from user code:
tx = Stellar::Transaction.create_account({
  account:     master,
  destination: destination,
  sequence:    1,
  starting_balance:  50 * Stellar::ONE
})

Would become:

tx = Stellar::Transaction.create_account({
  account:     master,
  destination: destination,
  sequence:    1,
  starting_balance:  50  # or '50' or BigDecimal.new(50)
})
irisli commented 9 years ago

:+1: on using strings for numbers in JSON without decimal shifting.

Conversion errors have happened in the past (sending out million times amount intended) and using strings reduce the chances of error.

sacarlson commented 9 years ago

Sounds good to me to avoid confusion making it the same for all assets native and non-native. I thought 7 digits would be more than enuf but I note that at my crypto coin exchange I see they have 8 digit precision as seen in this last price for str to btc at https://bx.in.th/BTC/STR/ was shown at 0.00001075. I originally thought 5 digits was enuf as that's what my brokers like fidelity.com used. But I guess the price of crypto coins is so spread that a large number of digits could be used. I personally would be happy with 7 digits but 8 digits could be an option to think about. I guess with big orders small numbers add up.

FredericHeem commented 9 years ago

Setting the decimal place for all assets to 7 will lead to several issues. First of all, it will not be possible to issue BTC which has a precision of 8. The other issue is for asset without decimal place such as the south Korean won and the Zimbabwe dollar, the maximum amount than can handled will be severely limited. One way to overcome this issue is to know the decimal place for the asset that has been issued, this information can be stored out of ledger or inside the ledger, I would prefer the later but it would introduce a new operation that called be called _createasset. Unfortunately, the idea to create a new _createasset operation has been rejected: https://github.com/stellar/stellar-core/issues/715

nullstyle commented 9 years ago

BTC which has a precision of 8

We've talked internally about this in the past and we're fine with the fact that the smallest sum of BTC transferable is 10 satoshi

The other issue is for asset without decimal place such as the south Korean won and the Zimbabwe dollar

We imagine a scenario where certain asset codes would represent multiples of a certain asset code to handle cases for this. For example, a convention could be established by a Zimbabwean gateway that their issued asset is "ZWD3", or 10^3 ZWD per whole unit of ZWD3.

jedmccaleb commented 9 years ago

@FredericHeem: Keep in mind it is really just an integer. The way the asset is displayed to the user is purely a client side convention. For example if you want the full precision of bitcoin send around mBTC. If you want to issue zimbabwe dollars you probably want to deal in 1000s. Humans have trouble dealing with more than a couple decimal places so just denominate the asset in bigger or smaller aggregations so the decimal place makes sense.

FredericHeem commented 9 years ago

With the current javascript api and horizon, the user inputs amount in string which are then converted by horizon with a fixed 7 decimal place, so precision is lost unless the client app decides mBTC instead of BTC, i,e 1 BTC = "1000". Assets are different and need a variable decimal place which can even be 0 such as equities, bonds etc ...

nullstyle commented 9 years ago

I believe that's incorrect... the javascript API currently does not conform to this proposal (I'll be adding it shortly) and horizon does not do any conversion of transactional data passed to it from a client. Horizon simply passes the transaction along after confirming that it is not malformed and not already in the ledger.

Horizon does, however, interpret transaction information imported into the history in the way you describe.

jedmccaleb commented 9 years ago

@FredericHeem: all assets are really integers. It is up to the client to put the decimal place in whatever place is the convention. We suggest people use fixed 7 decimal places but they don't have to. In the case of bitcoin it probably makes sense for people to deal in mBTC rather BTC as in the asset should be called "mBTC" like it is on some exchanges now.

FredericHeem commented 9 years ago

7 decimal place is not a suggestion, it is hard coded in horizon when getting the order book for instance, e.g the PriceAsString function

theaeolianmachine commented 5 years ago

@tomquisel — this has been around for a few years, and wanted to see if this was on your radar. Should we keep this open for a SEP to be written? It'd likely be a short one, and IIRC something about this was brought up not too long ago (in particular, strings vs. integers).

cybertriton commented 5 years ago

why not introduce a precision or decimal to ensure the structure is more flexible to allow client apps handle both divisible and indivisible tokens?