Skip to content

osmosis-labs/sqs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Sidecar Query Server

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.

alt text

Integrator Guide

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)

Custom CosmWasm Pools

The sidecar query server supports custom CosmWasm pools. There are two options of integrating them into the Osmosis router:

  1. Implement a pool type similar to transmuter
    • This assumes that the pool quote and spot price logic is trivial enough for implementing it directly in SQS without having to interact with the chain.
  2. Utilize a generalized CosmWasm pool type
    • This assumes that the pool quote and spot price logic is complex enough for requiring interaction with the chain.
    • For quotes and spot prices, SQS service would make network API queries to the chain.
    • This is the simplest approach but it is less performant than the first option.
    • Due to performance reasons, the routes containing these pools are not utilized in more performant split quotes. Only direct quotes are supported.

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.

Osmosis Deployments

Our team maintains 3 SQS deployment environments.

Production

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
  • all debug and infra endpoints

There is swagger available here

Staging

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
  • all debug and infra endpoints

Testnet

https://sqs.testnet.osmosis.zone

This is a testnet deployment made against osmo-test-5 testnet state.

This environment exposes all endpoints listed below.

Supported Endpoints

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 Resource

  1. GET /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 Resource

  1. GET /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 in
  • tokenOutDenom the string representing the denom of the token out
  • singleRoute (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"
}
  1. GET /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 in
  • tokenOutDenom the string representing the denom of the token out
  • humanReadable (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": {}
  }
}
  1. GET /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 in
  • tokenOutDenom the string representing the denom of the token out
  • poolID comma-separated list of pool IDs

Response 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 Resource

  1. GET /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
}
  1. GET /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"
    }
}

System Resource

  1. GET /healthcheck

Description: returns 200 if the server is healthy. Validates the following conditions:

  • Node is reachable
  • Node is not syncing
  • The latest height in cache is within threshold of the latest height in the node
  • The latest height in cache was updated within a configurable number of seconds
  1. GET /metrics

Description: returns the prometheus metrics for the server

  1. GET /version

Description: returns the version of the server

  1. GET /config

Description: returns the configuration of the server, including the router.

Development Setup

Mainnet

Node Configuration

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

Data

Pools

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.

Router

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.

Token Precision

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.

Pricing

There are two sources of pricing data:

  1. On-chain
  2. CoinGecko

Chain

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:

  1. Compute routes between 2 tokens
  2. Compute spot price over pools in that route
  3. If there occurs an error in computing spot price, we fallback to computing a price by swapping 10 units of the quote token (which in most cases today should be USDC). The choise of 10 is such that we do not consider extremely low-liquidity routes that may change frequently while also derisk the price impact with high-value non-USDC quotes.

CoinGecko

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:

  1. The quote from on-chain pricing is unavailable for any reason.
  2. The quote is USDC quote.

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.

Configuration

See docs/architecture/config.md for details.

Debugging

Containers

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.

Useful Osmosis Resources