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:
-
A public facing website available at www.constellations.zone.
-
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
}
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
}
}
}
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"
}
list collections where an addr owns at least one token
query Collections {
collections(tokenOwnerAddr: "stars1mtu407haz7g9s4hazdh8mtw94x3v52uaj8xx8c") {
collections {
name
}
}
}
list collections created by a given address
query Collections {
collections(creatorAddr: "stars1err5c2tc8ha6qa42tpvedgk585dpj35576ny29") {
collections {
name
mintedAt
createdByAddr
}
}
}
Contracts
Contracts contains informations about deployed contracts.
query Contracts {
contracts {
contractAddr
contractType
contractInfo
contractCodeId
contractVersion
contractLabel
blocked
blockHeight
createdAt
lastErrorAt
updatedAt
}
}
contract at a given address
query Contract {
contract(address: "stars13qd5kyn3larzjqm86ywyfw7tglnrmraww4suc0nnc668kskhspesem9ssq") {
contractAddr
contractType
contractInfo
contractCodeId
contractVersion
contractLabel
blocked
blockHeight
createdAt
lastErrorAt
updatedAt
}
}
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
}
}
}
}
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
}
}
}
}
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
}
}
}
}
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": []
}
name mints
query NameMints {
events(
contractFilters: [
{
contractType: "crates.io:name-minter"
events: [{ name: "wasm-mint-and-list", action: null }]
}
]
) {
edges {
node {
data
createdAt
}
}
}
}
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": []
}
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"
}
]
}
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
}
}
}
}
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
}
}
}
}
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
}
}
}
}
received offers on owned NFT
query receivedOffersOnOwnedNfts {
events(
filter: RECEIVED_OFFERS_ON_OWNED_NFT
forAddresses: ["stars15y38ehvexp6275ptmm4jj3qdds379nk07tw95r"]
isValid: true
) {
edges {
node {
eventName
data
isValid
}
}
}
}
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
}
}
}
}
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
}
}
}
}
all sales
query Sales {
events(
filter: SALES
sortBy: BLOCK_HEIGHT_DESC
) {
edges {
node {
eventName
action
createdAt
data
}
}
}
}
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
}
}
}
}
}
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
}
}
}
}
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
}
}
}
}
}
Names
Names contains all the minted Stargaze names.
query Names {
names {
limit
offset
total
names {
name
mintedAt
}
}
}
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
}
}
}
}
name with its record
query Name {
name(name: "sex") {
highestOfferEvent {
data
}
highestOffer
name
mintedAt
contractAddr
ownerAddr
associatedAddr
records {
recordName
recordValue
verified
}
}
}
query Name {
name(name: "penso") {
highestOfferEvent {
data
}
highestOffer
name
mintedAt
contractAddr
ownerAddr
associatedAddr
records {
recordName
recordValue
verified
}
}
}
name associated to a given address
query Name {
names(associatedAddr: "stars1mtu407haz7g9s4hazdh8mtw94x3v52uaj8xx8c") {
names {
name
mintedAt
}
}
}
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
}
}
}
tokens for a given owner
query Tokens {
tokens(
ownerAddr: "stars1mtu407haz7g9s4hazdh8mtw94x3v52uaj8xx8c"
) {
tokens {
collectionAddr
tokenId
name
}
}
}
tokens for a given collection
query Tokens {
tokens(filterByCollectionAddrs: ["stars1g4ucjvrcpwwxrrtmnnrprwkvtnud43294k3mrvtnlpdk2uza7hxseqr58k"]) {
tokens {
collectionAddr
tokenId
name
}
}
}
tokens listed as live auctions for a given collection
query Tokens {
tokens(filterForSale: "LIVE_AUCTIONS") {
tokens {
collectionAddr
tokenId
forSale
saleType
}
}
}
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
}
}
}
}
Contributors
Changelog
2023-11-29
- Added Messages and Transactions sections