Open placer14 opened 6 years ago
I agree with @rmisio that the API is good enough in that it serves its purpose in allowing us to calculate prices based on one API call (/ob/exchangerate/btc
). That said, it imposes a lot of work on client developers to reconstruct the logic as per the diagram @placer14 included in this issue. This diagram took me about half a day to create and a lot of yelling at my computer, let alone the frustration it must cost to adhere to the logic in client code.
This goes back to the architectural philosophy of the server, which we have never formally stated or wrote down anywhere (but maybe we should), that should endeavour to facilitate the construction of 'thin clients'. What I mean by that is that generally we should make things as easy as possible for client developers, and abstract away complexity for the server. More specifically for this example, client developers shouldn't need to reimplement complex price conversions; the server should handle it.
I believe this could be resolved for API calls such as GET /ob/sales
and GET /ob/purchases
, as well as order-related notifications). For this issue, I want to focus on the current response of GET /ob/sales
and GET /ob/purchases
:
{
"queryCount": 1,
"sales": [
{
"buyerHandle": "",
"buyerId": "QmPNU7ZD2tU6F6aJF5h4sT13QdJncKyUFHrXidvBzpwdbm",
"coinType": "",
"moderated": false,
"orderId": "QmNc9GyyqvS4qjLhgqci5TH32vdmipDhT8uB6yHznHpuT5",
"paymentCoin": "ZEC",
"read": false,
"shippingAddress": "1060 W Addison ",
"shippingName": "Elwood Blues",
"slug": "vintage-dress-physical-w-options",
"state": "AWAITING_PAYMENT",
"thumbnail": "QmbjyAxYee4y3443kAMLcmRVwggZsRDKiyXnXus1qdJJWz",
"timestamp": "2018-10-10T07:15:26Z",
"title": "Vintage dress (physical; w/ options)",
"total": 804474,
"unreadChatMessages": 0
}
]
}
To make things easier, I suggest:
price
object. This would represent the price we paid in crypto to purchase the item.settingsPrice
(or localCurrencyPrice
, or something more elegant) that displays the price of the item at the current exchange rate against the currency selected in Settings (localCurrency)
.The price
object would be unchanging unlike the settingsPrice
that would change based on what the exchange rate or currency settings was at the time the API was hit.
An example of how it might look:
{
"queryCount": 1,
"sales": [{
"buyerHandle": "",
"buyerId": "QmPNU7ZD2tU6F6aJF5h4sT13QdJncKyUFHrXidvBzpwdbm",
"coinType": "",
"moderated": false,
"orderId": "QmNc9GyyqvS4qjLhgqci5TH32vdmipDhT8uB6yHznHpuT5",
"read": false,
"shippingAddress": "1060 W Addison ",
"shippingName": "Elwood Blues",
"slug": "vintage-dress-physical-w-options",
"state": "AWAITING_PAYMENT",
"thumbnail": "QmbjyAxYee4y3443kAMLcmRVwggZsRDKiyXnXus1qdJJWz",
"timestamp": "2018-10-10T07:15:26Z",
"title": "Vintage dress (physical; w/ options)",
"unreadChatMessages": 0,
"price": {
"amount": 804474,
"currencyCode": "ZEC",
"priceModifier": 0.0,
"coinDivisibility": 100000000
},
"settingsPrice": {
"amount": 89.68,
"currencyCode": "USD",
"priceModifier": 0.0,
"coinDivisibility": 100
}
}]
}
This makes it super easy for client developers to display the correct value of the order without needing to do any price conversion calculations, which are abstracted to the server.
I think Rob's suggestion that the server aggregate the exchange rates into one API is fine. We'll still need to leave the exchange rate part of each wallet implementation though. The server just needs aggregating logic.
We introduce a second object called settingsPrice (or localCurrencyPrice, or something more elegant) that displays the price of the item at the current exchange rate against the currency selected in Settings (localCurrency).
I guess that's all a matter of how you architect your client. For the desktop client, we wouldn't use something like that because we have the settings in memory and we also have the exchange rate in memory (which we are updating regularly), so we wouldn't even use something like this.
With that said, I guess I wouldn't have some huge objection to those suggestions as long as they were additive, so the desktop client can continue to use it's current approach.
I haven't read that logic diagram super intensively and it's very possible our flow is also like that, but it really doesn't seem that bad because we really decouple our data fetches and make them as efficient as possible. For example, settings are only fetched on start-up, not each time they're needed. The exchange rate is fetched on start-up and then every 5 minutes to keep it fresh. Fiat / crypto determinations as well as listing base units (e.g. dividing by 100000000) are based off of a local configuration file, so they don't require any calls to the server. Also, at this point, we've abstracted most of our currency related calculations into a currency module that does most of the heavy lifting for us.
Proposal by @rmisio:
Tasks:
Current price conversion logic: