Drafted Date | May 9 2024 |
Implementation | May 13 2024 - July 12 2024 |
Description | Develop open source DEXScreener integration |
Authors | Sourabh Niyogi and Matthias Funke |
Dexscreener is a leading user interface for active traders, summarizing recent DEX activity. Currently DEXScreener represents Stellaswap (on Moonbeam) but otherwise has poor coverage of the Polkadot ecosystem. This proposal concerns developing Dexscreener in the next 60 days for:
It is expected that other defi Polkadot parachains can use the endpoint of this to implement similar DEX indexing for their chain.
The implementation will be done with a completely self-contained Docker container, with a Node.js based indexer using a MySQL backend, following the Dexscreener specification. A small number of tables block
, asset
, pair
, event
will be used for indexing and support the following 4 endpoints
GET /latest-block
Returns the latest block where data from /events
will be available
{
"block": {
"blockNumber": 12341234,
"blockTimestamp": 1698126147
}
}
blockTimestamp
is from set.timestamp
GET /asset?id=:string
Returns information about an asset on the specific parachain. We use the id
from the assets
pallet in AssetHub (e.g. 1984 for USDT) or assetRegistry
pallet in HydraDX (e.g. 26 for NODL)
How do we tell dexscreener which assets we have? Is that a separate endpoint?
{
"asset": {
"id": "1000019",
"name": "DED",
"symbol": "DED",
"totalSupply": 10000000,
"circulatingSupply": 900000,
"coinGeckoId": "moonbeam"
}
}
assetRegistry.assets
and xyk.poolAssets
(we need to join some off-chain metadata, e.g. symbol and coinGeckoId)Which fields are optional? There may not be a coinGeckoId for all assets, or the circulating supply may not be known.
assets.asset
and poolassets.asset
GET /pair?id=:string
We agree that the IDs will always be sorted in ascending order (using numbers, not strings), so we can use a string like 5-100019
for the pair of assets with IDs 5 and 100019.
The other pair does not exist.
{
"pair": {
"id": "5-100019",
"dexKey": "hydradx",
"asset0Id": "5",
"asset1Id": "100019",
"feeBps": 30
}
}
id
will be from xyk.poolAssets
or poolassets.asset
, with asset0Id
and asset1ID
resolvablefeeBps
for HydradxGET /events?fromBlock=:number&toBlock=:number
Returns Swap events (eventType = swap) and Pool add/remove liquidity events
{
"events": [
{
"block": {
"blockNumber": 12344321,
"blockTimestamp": 1673319600
},
"eventType": "swap",
"txnId": "0x1118d6bde171a4df1238f5eb69c4b9fff4d4e0169c91268dfa3661d6571faea9",
"txnIndex": 3,
"eventIndex": "12344321-5",
"maker": "7KjNuVyjY5Jv3znWGXSBBHt7Ls6Uwm1LmhswrGGDSYNUAwKW",
"pairId": "5-100019",
"asset0In": 10000,
"asset1Out": 20000,
"priceNative": 2,
"reserves": { "asset0": 100, "asset1": 50 }
},
...
]
}
txnID
is the ExtrinsicHashtxnIndex
is the ExtrinsicIDmaker
is the ss58 address of the signer of the extrinsic (DISCUSS: technically the signer is the taker)pairId
matches the pair
asset0In
and asset1Out
are taken directly from the event. (NO: we need to map the routed swap to the pair)reserves
and pool add/remove events in Hydradx omnipool?Our expectation is that the Dexscreener will link users directly to a swap interface:
and for pools:
Question: Is this touch point correct? We believe this has implications on what the assetid and pairid should be, and directly constrains the following question.
Assumption is that most people using dexScreener are degens or arbitrageurs. They cannot buy LRNA, therefore we can pretend LRNA does not exist? The only issue is that some transactions will be censored, but there are situations where LPs are paid in LRNA, and the LRNA is then swapped for something else.
Question: How should complex trades be mapped to events?
We need to calculate the mapping from a routed swap to one or more pairs, implemented in the dexscreener indexer. Matthias recommends we create the pairs dynamically based on some threshold of volume, in which case its a question of which pairs to "virtually" create.
iBTC to USDT might go like this:
... If someone actually has 2-pool or 4-pool tokens we map them as follows:
HydraDX Examples:
https://hydradx.subscan.io/extrinsic/5081726-2
Should ingest as: 450.391786 USDT -> 0.15 WETH
https://hydradx.subscan.io/extrinsic/5081141-2
Should ingest as: 500 USDT -> 0.00808666 WBTC
https://hydradx.subscan.io/extrinsic/5080488-0
80.9981538781 vDOT > 51616.002281331759 HDX
https://hydradx.subscan.io/extrinsic/4589219-2?event=4589219-13
Should show: 10799.9040616235 INTR -> 772.6816006249018 4-Pool (force mapped to USDT*)
Comment: in this case the user really wanted to own 4-pool.
https://hydradx.subscan.io/extrinsic/4705161-3?event=4705161-37
Should show: 8899.9696696365 INTR -> 38.8922321335 vDOT