CounterpartyXCP / Documentation

Official Documentation of the Counterparty Project
http://counterparty.io
MIT License
54 stars 58 forks source link

API Blueprint #184

Closed ouziel-slama closed 5 months ago

droplister commented 5 months ago

Is there a way to introduce name and quantity normalization on all API results in a way that's still performant and maintainable?

It seems like there could be a way to join asset_longname and divisible fields with most queries to support normalization but is there a way to perform the normalization on top of that?

Name For example, if the asset is always/only returned it's necessary to check if an asset_longname exists for display purposes due to how subassets have been implemented.

In practice, I find myself doing a lot of querying/caching/mapping to achieve this. Maybe there's a DRY method to join asset_longname from the Assets table with all queries in the ledger.py?

I think the ideal would be to offer something like an asset_normalized, so that developers don't need to write their own conditional ternary operator as a utility function that's used everywhere.

    {
        "result": [
            {
                 ...
                "asset": "A1976569558204240008",
                "asset_longname": "BITCORN.GLASSES" // joined
                ...
            },
            {
                 ...
                "asset": "A1976569558204240008",
                "asset_normalized": "BITCORN.GLASSES" // computed from join
                ...
            },
        ]
    }

If this did not get added, I would probably index the /assets endpoint and map this field onto API results within a caching layer.

Quantity Normalizing quantities is another big quality of life improvement that's worth thinking about. The same Assets table join that is used for selecting asset_longname could also select divisible.

That would allow for a utility function to format quantities for display by passing the quantity and a divisibility boolean.

However, like with asset names, I think the ideal is that this would be done by Counterparty. It's more computation than a join and may not make sense, but it would help a great deal.

    {
        "result": [
            {
                 ...
                "quantity": 100000000,
                "asset_divisible": true // joined
                ...
            },
            {
                 ...
                "quantity": 100000000,
                "quantity_normalized": 1.00000000 // computed from join
                ...
            },
        ]
    }

Joining asset_longname and divisible would be plenty helpful, but normalization would be epic. (Needs some thought around naming for things like order matches with two assets.)

droplister commented 5 months ago

Related to quantity normalization, because you need to normalize quantities before calculating an exchange rate, are prices.

Exchange Rates: Orders/Order Matches This is something that Counterblock used to do, but might not make sense to spend time on right now. If we maintain an array of quote assets, we could compute exchange rates on order and order match API results.

    {
        "result": [
            {
                "give_asset": "PEPECASH",
                "give_quantity": 6966600000000,
                "give_quantity_normalized": 69666.00000000,
                "give_remaining": 900000000000,
                "give_remaining_normalized": 9000.00000000,
                "get_asset": "XCP",
                "get_quantity": 11076894000,
                "get_quantity_normalized": 110.76894000,
                "get_remaining": 1431000000,
                "get_remaining_normalized": 14.31000000,
                "market_pair": "PEPECASH/XCP",
                "market_dir": "Sell",
                "market_rate": "628.9308176100629", // What level of precision here can be tricky to decide 
            }
        ]
    }

It's not necessary to maintain a quote asset array to improve on this for developers, but it does help and once you do its easy to decide whether its a sell or a buy order.

Exchange Rates: Dispensers/Dispenses It's also something that could be improved for dispenses and dispensers too. In this case, the quote asset is always BTC. But you need to lookup the dispenser on a dispense to see what the satoshirate and dispense quantity were. And if the quantity dispensed is greater than 1 you need to do an additional calculation to determine what the effective price per unit is.

Also, from the dispense alone it's not clear whether the dispense triggered more than one dispense from the dispenser. So, a lot of lookups and checks start to be necessary to make these trades understandable.

    {
        "result": [
            {
                "tx_index": 2726580,
                "dispense_index": 0,
                "tx_hash": "e7f0f2c9bef7a492b714a5952ec61b283be344419c5bc33f405f9af41ebfa48b",
                "block_index": 840322,
                "source": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2",
                "destination": "bc1qzcdkhnexpjc8wvkyrpyrsn0f5xzcpu877mjmgj",
                "asset": "FLOCK",
                "dispense_quantity": 90000000000,
                "dispense_quantity_normalized": 900.00000000,
                "dispenser_tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a"
                "number_of_dispenses": 12,
                "sats_per_dispense": 12356,
                "sats_per_unit": 12345 
            }
        ]
    }

I think it's reasonable to expect explorers and apps to compute some of these things for themselves, but if Counterparty API takes on some of this or at least joins the fields that are needed to perform these normalizations and calculations you'll get fewer apps re-implementing and maintaining their own state of the network.

In a perfect world, marketplace explorers are figuring out volume and trading prices in dollar terms and aggregating that type of information in their databases.

ouziel-slama commented 5 months ago

@droplister yes it can actually be interesting! I need to do some performance testing to find the best way to do this. I opened an issue for this: https://github.com/CounterpartyXCP/counterparty-core/issues/1731