Introduction

Constellations is a high-performance and real-time indexer developed by Fabien Penso originally for Stargaze and now for multiple Cosmos chains. Constellations is currently used by Stargaze for its main website to display data in the fastest way possible.

Constellations indexes blocks, transactions, messages and events and is able to index any Cosmos chain. It also indexes off-chain data like IPFS NFTs to store width, height, content_type and content_length as well as NFT metadata like traits and automatically calculate rarity. It also stores Stargaze specific informations like Stargaze names.

Constellations is:

  1. A public facing website available at www.constellations.zone.

  2. A set of API to fetch data Constellations has indexed. This API is available over GraphQL and self-generated GraphQL documentation is enabled.

You should already know about GraphQL to use Constellations. You can read more about GraphQL here.

GraphQL queries

You'll most likely be interested in transactions, messages and events.

Available nodes

As of now, I'm indexing Stargaze mainnet, Stargaze testnet, DYDX mainnet, Noble mainnet, and Neutron mainnet. This list is subject to change.

How is it built?

Constellations is built entirely in Rust, backed with Posgresql and hosted on bare metal servers for optimum performance. It fetches blocks live from the blockchain over websockets, and parse them instantly.

It ran fine on a single server, but we wanted to have redundancy to avoid downtimes. These days it manages way over 10 million requests per day.

Safety

While I try to minimize bugs and issues, Constellations sometimes (but rarily) stops indexing, or has an indexing issue which means it does not reflect the blockchain data. It is highly recommended to use Stargaze RPC servers to verify information if you are moving tokens. For example, if you want to send STARS to a given Stargaze name, you should use the RPC server to get the STARS address associated with that given name, and not Constellations which could lead to sending STARS to an old associated address if the indexer stopped.

Timestamps

All timestamps you get are UTC and don't include timezone.

User Agent

Add a way to contact you in the User-Agent header in case I need to get in touch with you if you are making too many requests.

Throttling

There is no throttling in place for now but will be soon. Please don't hammer the server with too many calls.

Contributing to this documentation

This documentation is available on GitHub and issues and feature requests can be posted on the GitHub issue tracker. Please consider opening a pull request.

I can't find how to fetch specific data

Create an issue with as many details as possible, contract type, information you want and why you want to fetch it.

License

The Constellations documentation are released under the Mozilla Public License v2.0.

API Endpoints

Stargaze Mainnet

https://constellations-api.mainnet.stargaze-apis.com/graphql

Stargaze Testnet

https://constellations-api.testnet.stargaze-apis.com/graphql

Noble Mainnet

https://www.mainnet.noble.constellations.zone/graphql

DYDX Mainnet

https://www.mainnet.dydx.constellations.zone/graphql

Neutron Mainnet

https://www.mainnet.neutron.constellations.zone/graphql

Pagination

Endpoints use both types pagination. When data isn't changing often, it uses offset-based pagination, when data is changing rapidly it uses cursor-based pagination.

The following is using offset pagination:

  • collections
  • names
  • tokens

The following is using cursor pagination:

  • events
  • sales

Collections

Query

query Collections($limit: Int, $sortBy: CollectionSortBy, $offset: Int, $tokenOwnerAddr: String, $creatorAddr: String) {
  collections(limit: $limit, sortBy: $sortBy, offset: $offset, tokenOwnerAddr: $tokenOwnerAddr, creatorAddr: $creatorAddr) {
    collections {
      image
      description
      tokensCount
      website
      createdAt
      collectionAddr
      name
      mintedAt
    }
    limit
    offset
    total
  }
}

Variables

{
  "limit": 2,
  "sortBy": null,
  "offset": null,
  "tokenOwnerAddr": null,
  "creatorAddr": null
}

Try it out live on explorer

Available sorting

  • TOKENS_COUNT_ASC: Sort by the smallest amount of tokens
  • TOKENS_COUNT_DESC: Sort by the highest amounf of tokens
  • MINTED_AT_ASC: Sort by the least recent minted collection time
  • MINTED_AT_DESC: Sort by the most recent minted collection time
  • VOLUME_24H_DESC: Sort by the past 24H sales volumes
  • VOLUME_7D_DESC: Sort by the past 7 days sales volumes

get all collections

GraphQL will have all internal attribute documentation.

query Collections {
  collections {
    collections {
      name
      mintedAt
    }
  }
}

Try it out live on explorer

get most recent minted collections

Query

query Collections($collectionsLimit: Int, $sortBy: CollectionSortBy) {
  collections(limit: $collectionsLimit, sortBy: $sortBy) {
    collections {
      image
      description
      tokensCount
      website
      createdAt
      collectionAddr
      name
      mintedAt
    }
  }
}

Variables

{
  "collectionsLimit": 2,
  "sortBy": "MINTED_AT_DESC"
}

Try it out live on explorer

list collections where an addr owns at least one token

query Collections {
  collections(tokenOwnerAddr: "stars1mtu407haz7g9s4hazdh8mtw94x3v52uaj8xx8c") {
    collections {
      name
    }
  }
}

Try it out live on explorer

list collections created by a given address

query Collections {
  collections(creatorAddr: "stars1err5c2tc8ha6qa42tpvedgk585dpj35576ny29") {
    collections {
      name
      mintedAt
      createdByAddr
    }
  }
}

Try it out live on explorer

Contracts

Contracts contains informations about deployed contracts.

query Contracts {
  contracts {
    contractAddr
    contractType
    contractInfo
    contractCodeId
    contractVersion
    contractLabel
    blocked
    blockHeight
    createdAt
    lastErrorAt
    updatedAt
  }
}

Try it out live on explorer

contract at a given address

query Contract {
  contract(address: "stars13qd5kyn3larzjqm86ywyfw7tglnrmraww4suc0nnc668kskhspesem9ssq") {
      contractAddr
      contractType
      contractInfo
      contractCodeId
      contractVersion
      contractLabel
      blocked
      blockHeight
      createdAt
      lastErrorAt
      updatedAt
    
  }
}

Try it out live on explorer

Events

Events contains all blockchain cosmwasm events, mints, airdrops, sales. You have to know what events you're looking for, or what contract types. Unless they're very noisy and useless, all cosmwasm events are saved.

If you are publishing a new contract, using events and attributes is the best way to ensure this will be indexed by Constellations.

For example, the following get all events related to the pegasus contract, order by block height DESC (most recent event first):

query Events {
  events(
    contractFilters: [{ contractType: "crates.io:pegasus", events: [] }]
    sortBy: BLOCK_HEIGHT_DESC
  ) {
    edges {
      node {
        eventName
        action
        data
        createdAt
      }
    }
  }
}

Try it out live on explorer

Or another example for badge-hub contracts:

query Events {
  events(
    contractFilters: [{ contractType: "crates.io:badge-hub", events: [] }]
    sortBy: BLOCK_HEIGHT_DESC
  ) {
    edges {
      node {
        eventName
        action
        data
        createdAt
      }
    }
  }
}

Try it out live on explorer

This will show all wasm-fair-burn for the badge-hub contract:

query Events {
  events(
    contractFilters: [
      {
        contractType: "crates.io:badge-hub"
        events: [{ name: "wasm-fair-burn" }]
      }
    ]
    sortBy: BLOCK_HEIGHT_DESC
  ) {
    edges {
      node {
        eventName
        action
        data
        createdAt
      }
    }
  }
}

Try it out live on explorer

get all burns

This will get all burns for both the sg-721 contracts, it will not include other contracts like imago contracts. You have to add contracts specifically if you want others.

query Burns($filters: [ContractFilter!], $dataFilters: [DataFilter!]) {
  events(contractFilters: $filters, dataFilters: $dataFilters) {
    edges {
      node {
        contractType
        data
        createdAt
      }
    }
  }
}
{
  "filters": [
    {
      "contractType": "crates.io:sg-721",
      "events": [
        {
          "name": "wasm",
          "action": "burn"
        }
      ]
    },
    {
      "contractType": "crates.io:sg721-base",
      "events": [
        {
          "name": "wasm",
          "action": "burn"
        }
      ]
    }
  ],
  "dataFilters": []
}

Try it out live on explorer

name mints

query NameMints {
  events(
    contractFilters: [
      {
        contractType: "crates.io:name-minter"
        events: [{ name: "wasm-mint-and-list", action: null }]
      }
    ]
  ) {
    edges {
      node {
        data
        createdAt
      }
    }
  }
}

Try it out live on explorer

token bids

query Bids($filters: [ContractFilter!], $dataFilters: [DataFilter!]) {
  events(contractFilters: $filters, dataFilters: $dataFilters) {
    edges {
      node {
        data
      }
    }
  }
}
{
  "filters": [
    {
      "contractType": "crates.io:sg-marketplace",
      "events": [
        {
          "name": "wasm-set-bid",
          "action": null
        }
      ]
    }
  ],
  "dataFilters": []
}

Try it out live on explorer

all events for a given collection

query EventsForCollection($data_filters: [DataFilter!]) {
  events(dataFilters: $data_filters) {
    edges {
      node {
        contractType
        eventName
        action
        data
        createdAt
      }
    }
  }
}
{
  "dataFilters": [
    {
      "name": "collection",
      "value": "stars18d7ver7mmjdt06mz6x0pz09862060kupju75kpka5j0r7huearcsq0gyg0",
      "operator": "EQUAL"
    }
  ]
}

Try it out live on explorer

all events for a given token

query EventsForTokenId {
  events(
    filter: TOKEN_METADATAS
    forToken: {
      collectionAddr: "stars1ery3ph276meayswezstaulm4vekzz9u825ppf8gx6cxjteplvrrqt33m8k"
      tokenId: "127"
    }
  ) {
    edges {
      node {
        contractType
        eventName
        action
        data
        createdAt
        blockHeight
      }
    }
  }
}

Try it out live on explorer

all offers sent for a given address

query SentOffersForGivenAddress {
  events(
    filter: SENT_NAME_OFFERS
    forAddresses: ["stars10l56qdud5g6qt3pzywy65urm9cle0mx7aur6zd"]
  ) {
    edges {
      node {
        contractType
        eventName
        action
        data
        createdAt
        blockHeight
        isValid
        expired
      }
    }
  }
}

Try it out live on explorer

bids for a given collection and given bidder

query BidsForGivenBidder {
  events(
    dataFilters: [
      {
        name: "bidder"
        value: "stars1rmxl4fps24pe8s9uv8an3nqpng3ggyf8sfavr0"
        operator: EQUAL
      }
      {
        name: "collection"
        value: "stars19jq6mj84cnt9p7sagjxqf8hxtczwc8wlpuwe4sh62w45aheseues57n420"
        operator: EQUAL
      }
    ]
    contractFilters: [
      {
        contractType: "crates.io:sg-marketplace"
        events: [{ name: "wasm-set-bid", action: null }]
      }
    ]
    sortBy: BLOCK_HEIGHT_DESC
  ) {
    edges {
      node {
        txHash
        action
        contractInfo
        createdAt
        isValid
        eventName
        action
        data
      }
    }
  }
}

Try it out live on explorer

received offers on owned NFT

query receivedOffersOnOwnedNfts {
  events(
    filter: RECEIVED_OFFERS_ON_OWNED_NFT
    forAddresses: ["stars15y38ehvexp6275ptmm4jj3qdds379nk07tw95r"]
    isValid: true
  ) {
    edges {
      node {
        eventName
        data
        isValid
      }
    }
  }
}

Try it out live on explorer

sales for a given collection

query Sales {
  events(
    filter: SALES
    dataFilters: [
      {
        name: "collection"
        value: "stars1ewpdt2s5t2ktkyzsv3rhe88yeyxpjrfvqg209rac5pn46gffr75qvt8rq3"
        operator: EQUAL
      }
    ]
    sortBy: BLOCK_HEIGHT_DESC
  ) {
    edges {
      node {
        eventName
        action
        createdAt
        data
      }
    }
  }
}

Try it out live on explorer

List of tokens current as live auctions

query ValidLiveAuctions {
  events(
    contractFilters: [
      {
        contractType: "crates.io:stargaze-reserve-auction"
        events: [{ name: "wasm-create-auction", action: null }]
      }
    ]
    dataFilters: [
      {
        name: "seller"
        value: "stars1rpnqgfdr79zv7zn6d8y888u7vqvpgtr5cqdnuw"
        operator: EQUAL
      }
    ]
    isValid: true
  ) {
    edges {
      node {
        data
      }
    }
  }
}

Try it out live on explorer

all sales

query Sales {
  events(
    filter: SALES
    sortBy: BLOCK_HEIGHT_DESC
  ) {
    edges {
      node {
        eventName
        action
        createdAt
        data
      }
    }
  }
}

Try it out live on explorer

Messages

Messages contains all blockchain messages

For example, the following gets all /cosmos.staking.v1beta1.MsgDelegate messages including its transaction hash and block height.

query Messages {
  messages(typeUrls: ["/cosmos.staking.v1beta1.MsgDelegate"]) {
    edges {
      node {
        typeUrl
        data
        blockHeight
        transaction {
          hash
        }
      }
    }
  }
}

Try it out live on explorer

Or get all cosmwasm related messages for two cosmwasm methods:

query Message {
  messages(
    typeUrls: ["/cosmwasm.wasm.v1.MsgExecuteContract"]
    cosmwasmMethods: ["swap_nfts_for_tokens", "approve_all"]
  ) {
    edges {
      node {
        typeUrl
        data
        blockHeight
      }
    }
  }
}

Try it out live on explorer

Filtering messages on attributes

You can filter on anything you see from the data field using jsonpath. Please note the value has to be including the type, for if you want to filter based on a String value use "\"value\"", for Integer use "14".

query MessagesFundRenewalForTokenId {
  messages(
    cosmwasmMethods: ["fund_renewal"]
    attributeFilters: {operator: AND, filters: [{path: ["$.msg.fund_renewal.token_id"], value: "\"allegedly\"", operator: EQUAL}]}
  ) {
    edges {
      node {
        cosmwasmMethod
        data
        blockHeight
        transaction {
          errorMessage
        }
      }
    }
  }
}

Try it out live on explorer

Names

Names contains all the minted Stargaze names.

query Names {
  names {
    limit
    offset
    total
    names {
      name
      mintedAt
    }
  }
}

Try it out live on explorer

all names sorted by offer

query Names {
  names(sortBy: OFFERS_DESC) {
    limit
    offset
    total
    names {
      highestOffer
      highestOfferEvent {
        data
      }
      name
      mintedAt
      contractAddr
      ownerAddr
      associatedAddr
      records {
        recordName
        recordValue
        verified
      }
    }
  }
}

Try it out live on explorer

name with its record

query Name {
  name(name: "sex") {
    highestOfferEvent {
      data
    }
    highestOffer
    name
    mintedAt
    contractAddr
    ownerAddr
    associatedAddr
    records {
      recordName
      recordValue
      verified
    }
  }
}

Try it out live on explorer

query Name {
  name(name: "penso") {
    highestOfferEvent {
      data
    }
    highestOffer
    name
    mintedAt
    contractAddr
    ownerAddr
    associatedAddr
    records {
      recordName
      recordValue
      verified
    }
  }
}

Try it out live on explorer

name associated to a given address

query Name {
  names(associatedAddr: "stars1mtu407haz7g9s4hazdh8mtw94x3v52uaj8xx8c") {
    names {
      name
      mintedAt
    }
  }
}

Try it out live on explorer

Tokens

Tokens contains all NFT tokens. Always use a collection filter or the query will be slow

query Tokens {
  tokens(
    filterByCollectionAddrs: [
      "stars1g4ucjvrcpwwxrrtmnnrprwkvtnud43294k3mrvtnlpdk2uza7hxseqr58k"
    ]
  ) {
    tokens {
      collectionAddr
      tokenId
      mintedAt
      ownerAddr
      name
      description
      forSale
      imageUrl
      rarityOrder
      rarityScore
      price {
        amount
      }
      priceExpiresAt
      saleType
    }
  }
}

Try it out live on explorer

tokens for a given owner

query Tokens {
  tokens(
    ownerAddr: "stars1mtu407haz7g9s4hazdh8mtw94x3v52uaj8xx8c"
  ) {
    tokens {
      collectionAddr
      tokenId
      name
    }
  }
}

Try it out live on explorer

tokens for a given collection

query Tokens {
  tokens(filterByCollectionAddrs: ["stars1g4ucjvrcpwwxrrtmnnrprwkvtnud43294k3mrvtnlpdk2uza7hxseqr58k"]) {
    tokens {
      collectionAddr
      tokenId
      name
    }
  }
}

Try it out live on explorer

tokens listed as live auctions for a given collection

query Tokens {
  tokens(filterForSale: "LIVE_AUCTIONS") {
    tokens {
      collectionAddr
      tokenId
      forSale
      saleType
    }
  }
}

Try it out live on explorer

Transactions

Transactions contains all blockchain transactions

For example, the following gets all transactions related to a specific transaction hash (Note: a hash can happen multiple times, to ensure you get the right one you have to check the block_height as well).

query Transactions {
  transactions (
    hashes: ["000B1766A13ABE292D7CB861E9BA977E3C4E8B73F15A9F7CB01243FBE3963213"]
    sortBy: BLOCK_HEIGHT_DESC
  ) {
    edges {
      node {
        hash
        blockHeight
      }
    }
  }
}

Try it out live on explorer

Contributors

Changelog

2023-11-29

  • Added Messages and Transactions sections