This is a sidecar query server that is used for performing query tasks outside of the main chain. The high-level architecture is that the chain reads data at the end of the block, parses it and then stores it in RAM.
The sidecar query server then reads the parsed data from cache and serves it to the client via HTTP endpoints.
The use case for this is performing certain data and computationally intensive tasks outside of the chain node or the clients. For example, routing falls under this category because it requires all pool data for performing the complex routing algorithm.
Follow this link to find a guide on how to integrate with the sidecar query server.
(Note: apt install nano make build-essential gcc git jq chrony tar curl lz4 wget
)
The sidecar query server supports custom CosmWasm pools. There are two options of integrating them into the Osmosis router:
To enable support for either option, a config.json
must be updated accordingly. For option 1, add a new field under pools
and make a PR propagating
this config to be able to create a new custom pool type similar to transmuter. For option 2, simply
add your code id to general-cosmwasm-code-ids
in this repository. Tag @p0mvn
in the PR and
follow up that the config is deployed to the sidecar query server service in production.
Our team maintains 3 SQS deployment environments.
https://sqs.osmosis.zone
This is a geo-distributed deployment across 3 regions that is used by the
production application https://app.osmosis.zone
.
It operates on data from osmosis-1
mainnet.
Note that in this environment we block all endpoints at nginx level except for:
/router/quote
/router/custom-direct-quote
/tokens/prices
/pools
There is swagger available here
https://sqs.stage.osmosis.zone
This is a deployment with 2 nodes that is used by our stage app
https://stage.osmosis.zone/
. It is less stable and may experience downtime
due to experimental features.
It operates on data from osmosis-1
mainnet.
Note that in this environment we block all endpoints at nginx level except for:
/router/quote
/router/custom-direct-quote
/tokens/prices
/pools
https://sqs.testnet.osmosis.zone
This is a testnet deployment made against osmo-test-5
testnet state.
This environment exposes all endpoints listed below.
Note that there are more endpoints that can be found in the codebase but we do not expose them publicly in out production environment.
/pools?IDs=<IDs>
Description: Returns of pools if IDs parameter is not given. Otherwise, batch fetches specific pools by the given parameter pool IDs.
Parameter: IDs
- the list of pool IDs to batch fetch.
curl "http://localhost:9092/pools?IDs=1,2" | jq .
[
{
"chain_model": {
"address": "osmo1mw0ac6rwlp5r8wapwk3zs6g29h8fcscxqakdzw9emkne6c8wjp9q0t3v8t",
"id": 1,
"pool_params": {
"swap_fee": "0.002000000000000000",
"exit_fee": "0.000000000000000000"
},
"future_pool_governor": "24h",
"total_weight": "1073741824000000.000000000000000000",
"total_shares": {
"denom": "gamm/pool/1",
"amount": "68705408290810473783205087"
},
"pool_assets": [
{
"token": {
"denom": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2",
"amount": "1099147835604"
},
"weight": "536870912000000"
},
{
"token": {
"denom": "uosmo",
"amount": "6560821009725"
},
"weight": "536870912000000"
}
]
},
"balances": [
{
"denom": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2",
"amount": "1099147835604"
},
{
"denom": "ibc/9989AD6CCA39D1131523DB0617B50F6442081162294B4795E26746292467B525",
"amount": "1000000000"
},
{
"denom": "ibc/B9E0A1A524E98BB407D3CED8720EFEFD186002F90C1B1B7964811DD0CCC12228",
"amount": "999800"
},
{
"denom": "uosmo",
"amount": "6560821009725"
}
],
"type": 0,
"spread_factor": "0.002000000000000000"
},
...
]
/router/quote?tokenIn=<tokenIn>&tokenOutDenom=<tokenOutDenom>?singleRoute=<singleRoute>
Description: returns the best quote it can compute for the given tokenIn and tokenOutDenom. If singRoute
parameter is set to true, it gives the best single
quote while excluding splits
Parameters:
tokenIn
the string representation of the sdk.Coin for the token intokenOutDenom
the string representing the denom of the token outsingleRoute
(optional) boolean flag indicating whether to return single routes (no splits).
False (splits enabled) by default.humanReadable
(optional) boolean flag indicating whether a human readable denom is given as opposed to chain.Response example:
curl "https://sqs.osmosis.zone/router/quote?tokenIn=1000000uosmo&tokenOutDenom=uion?singleRoute=false" | jq .
{
"amount_in": {
"denom": "uosmo",
"amount": "1000000"
},
"amount_out": "1803",
"route": [
{
"pools": [
{
"id": 2,
"type": 0,
"balances": [],
"spread_factor": "0.005000000000000000",
"token_out_denom": "uion",
"taker_fee": "0.001000000000000000"
}
],
"out_amount": "1803",
"in_amount": "1000000"
}
],
"effective_fee": "0.006000000000000000"
}
/router/routes?tokenIn=<tokenIn>&tokenOutDenom=<tokenOutDenom>
Description: returns all routes that can be used for routing from tokenIn to tokenOutDenom
Parameters:
tokenIn
the string representation of the denom of the token intokenOutDenom
the string representing the denom of the token outhumanReadable
(optional) boolean flag indicating whether a human readable denom is given as opposed to chain.Response example:
curl "https://sqs.osmosis.zone/router/routes?tokenIn=uosmo&tokenOutDenom=uion" | jq .
{
"Routes": [
{
"Pools": [
{
"ID": 1100,
"TokenOutDenom": "uion"
}
]
},
{
"Pools": [
{
"ID": 2,
"TokenOutDenom": "uion"
}
]
},
{
"Pools": [
{
"ID": 1013,
"TokenOutDenom": "uion"
}
]
},
{
"Pools": [
{
"ID": 1092,
"TokenOutDenom": "ibc/E6931F78057F7CC5DA0FD6CEF82FF39373A6E0452BF1FD76910B93292CF356C1"
},
{
"ID": 476,
"TokenOutDenom": "uion"
}
]
},
{
"Pools": [
{
"ID": 1108,
"TokenOutDenom": "ibc/9712DBB13B9631EDFA9BF61B55F1B2D290B2ADB67E3A4EB3A875F3B6081B3B84"
},
{
"ID": 26,
"TokenOutDenom": "uion"
}
]
}
],
"UniquePoolIDs": {
"1013": {},
"1092": {},
"1100": {},
"1108": {},
"2": {},
"26": {},
"476": {}
}
}
/router/custom-direct-quote?tokenIn=<tokenIn>&tokenOutDenom=<tokenOutDenom>&poolIDs=<poolIDs>
Description: returns the quote over route with the given poolIDs. If such route does not exist, returns error. This endpoint does not use the router route search. As a result, it is not affected by the minimum liquidity parameter. As long as the pool exists on-chain, it will return a quote.
Parameters:
tokenIn
the string representation of the sdk.Coin for the token intokenOutDenom
the string representing the denom of the token outpoolID
comma-separated list of pool IDsResponse example:
curl "https://sqs.osmosis.zone/router/custom-direct-quote?tokenIn=1000000uosmo&tokenOutDenom=uion&poolID=2" | jq .
{
"amount_in": {
"denom": "uosmo",
"amount": "1000000"
},
"amount_out": "1803",
"route": [
{
"pools": [
{
"id": 2,
"type": 0,
"balances": [],
"spread_factor": "0.005000000000000000",
"token_out_denom": "uion",
"taker_fee": "0.001000000000000000"
}
],
"out_amount": "1803",
"in_amount": "1000000"
}
],
"effective_fee": "0.006000000000000000"
}
/tokens/metadata
Description: returns token metadata with chain denom, human denom and precision.
For testnet, uses osmo-test-5 asset list. For mainnet, uses osmosis-1 asset list.
See config.json
and config-testnet.json
in root for details.
Parameter: denoms (optional). A list of denoms. Can either be human or chain denoms. If none given, returns metadata for all denoms.
Response example:
curl "https://sqs.osmosis.zone/tokens/metadata/statom" | jq .
{
"chain_denom": "ibc/C140AFD542AE77BD7DCC83F13FDD8C5E5BB8C4929785E6EC2F4C636F98F17901",
"human_denom": "statom",
"precision": 6
}
/tokens/prices
Parameters:
base
Comma-separated list of base denominations (human-readable or chain format based on humanDenoms parameter)humanDenoms
Specify true if input denominations are in human-readable format; defaults to false.Response:
A map where each key is a base denomination (on-chain format), containing another map with a key as the quote denomination (on-chain format) and the value as the spot price.
curl https://sqs.osmosis.zone//tokens/prices?base=wbtc,dydx&humanDenoms=true
{
"ibc/831F0B1BBB1D08A2B75311892876D71565478C532967545476DF4C2D7492E48C": {
"ibc/D189335C6E4A68B513C10AB227BF1C1D38C746766278BA3EEB4FB14124F1D858": "3.104120355715761583051226000000000000"
},
"ibc/D1542AA8762DB13087D8364F3EA6509FD6F009A34F00426AF9E4F9FA85CBBF1F": {
"ibc/D189335C6E4A68B513C10AB227BF1C1D38C746766278BA3EEB4FB14124F1D858": "51334.702258726899383983572895277207392200"
}
}
/healthcheck
Description: returns 200 if the server is healthy. Validates the following conditions:
/metrics
Description: returns the prometheus metrics for the server
/version
Description: returns the version of the server
/config
Description: returns the configuration of the server, including the router.
Ensure the following in app.toml
[osmosis-sqs]
# SQS service is disabled by default.
is-enabled = "true"
# The hostname and address of the sidecar query server storage.
grpc-ingest-address = "localhost:50051"
grpc-ingest-max-call-size-bytes = "50000000"
To setup a development environment against mainnet, sync the node in the default home directory and then run the following commands:
# Starts the Osmosis node from the default $HOME directory
# Starts sqs from the config.json in the root of this repository
make all-start
For every chain pool, its pool model is written to cache.
Additionally, we instrument each pool model with bank balances and OSMO-denominated TVL.
Some pool models do not contain balances by default. As a result, clients have to requery balance for each pool model directly from chain. Having the balances in cache allows us to avoid this and serve pools with balances directly.
The routing algorithm requires the knowledge of TVL for prioritizing pools. As a result, each pool model is instrumented with OSMO-denominated TVL.
For routing, we must know about the taker fee for every denom pair. As a result, in the router repository, we store the taker fee keyed by the denom pair.
These taker fees are then read from cache to initialize the router.
The chain is agnostic to token precision. As a result, to compute OSMO-denominated TVL, we query chain registry file parse the precision exponent and use it scaling the spot price to the right value.
The following are the tokens that are either malformed or are missing from the chain registry file:
ibc/CD942F878C80FBE9DEAB8F8E57F592C7252D06335F193635AF002ACBD69139CC
ibc/FE2CD1E6828EC0FAB8AF39BAC45BC25B965BA67CCBC50C13A14BD610B0D1E2C4
ibc/4F3B0EC2FE2D370D10C3671A1B7B06D2A964C721470C305CBB846ED60E6CAA20
ibc/CD20AC50CE57F1CF2EA680D7D47733DA9213641D2D116C5806A880F508609A7A
ibc/52E12CF5CA2BB903D84F5298B4BFD725D66CAB95E09AA4FC75B2904CA5485FEB
ibc/49C2B2C444B7C5F0066657A4DBF19D676E0D185FF721CFD3E14FA253BCB9BC04
ibc/7ABF696369EFB3387DF22B6A24204459FE5EFD010220E8E5618DC49DB877047B
ibc/E27CD305D33F150369AB526AEB6646A76EC3FFB1A6CA58A663B5DE657A89D55D
factory/osmo130w50f7ta00dxkzpxemuxw7vnj6ks5mhe0fr8v/oDOGE
ibc/5BBB6F9C8ECA31508EE5B68F2E27B57532E1595C57D0AE5C8D64E1FBCB756247
ibc/00BC6883C29D45EAA021A55CFDD5884CA8EFF9D39F698A9FEF79E13819FF94F8
ibc/BCDB35B7390806F35E716D275E1E017999F8281A81B6F128F087EF34D1DFA761
ibc/020F5162B7BC40656FC5432622647091F00D53E82EE8D21757B43D3282F25424
ibc/D3A1900B2B520E45608B5671ADA461E1109628E89B4289099557C6D3996F7DAA
ibc/1271ACDB6421652A2230DECCAA365312A32770579C2B22D2B60A89FE39106611
ibc/DEA3B0BB0006C69E75D2247E8DC57878758790556487067F67748FDC237CE2AE
ibc/72D0C53912C461FC9251E3135459746380E9030C0BFDA13D45D3BAC47AE2910E
ibc/0E30775281643124D79B8670ACD3F478AC5FAB2B1CA1E32903D0775D8A8BB064
ibc/4E2A6E691D0EB60A25AE582F29D19B20671F723DF6978258F4043DA5692120AE
ibc/F2F19568D75125D7B88303ADC021653267443679780D6A0FD3E1EC318E0C51FD
factory/osmo19pw5d0jset8jlhawvkscj2gsfuyd5v524tfgek/TURKEY
Any pool containing these tokens would have the TVL error error set to non-empty string, leading to the pool being deprioritized from the router.
There are two sources of pricing data:
On-chain pricing has the following two-cases:
1. USDC Quote
At the start of SQS, we pre-compute prices for all listed tokens as defined by the asset list with USDC as the quote and store them in-memory (no expiration).
In subsequent blocks, whenever a pool is updated (swapped, LPed etc), we detect that and recompute the price during ingest time via background worker and update internal memory.
2. Non-USDC Quote Computed on-demand and result is stored in cache with TTL.
General computation logic:
Unless specified by using the parameter pricingSource
, the GET /tokens/prices endpoint uses the above chain pricing source by default in obtaining a price quote. Coingecko pricing source is also available by using the pricingSource
parameter. Coingecko pricing source also serves as a fallback mechanism if the following conditions are met:
Internally, the Coingecko pricing source looks for the price quote in the its pricing cache and return it if it exists. Otherwise, it fetches the price from the Coingecko API endpoint and store it in the cache with an expiration time specified in the config.json file.
See docs/architecture/config.md
for details.
For debugging SQS Docker containers with dlv, build the binary using make docker-build-debug
.
This builds the binary with the debug symbols and then builds the Docker image with the debug binary.
It also starts sqs via dlv inside the container while exposing port 4000.
A client can then attach their debugger via port 4000.
See .vscode/launch.json
for the "Debug Docker Container" configuration.