Skip to main content

gRPCurl

grpcurl is curl for gRPC. It is the quickest way to explore the chain: because the hub answers reflection requests, grpcurl can discover every service and message type on its own, with no .proto files on your machine. Install it by following the instructions in the link above.

Point it at an endpoint

Every example on this page reads the endpoint from $GRPC, so you can run them against any node by changing one line:

export GRPC=fullgrpc-sentinel.busurnode.com:443

The public endpoints are served over TLS on port 443, so no -plaintext flag is needed. Against a node you run yourself, use export GRPC=localhost:9090 and add -plaintext to each command. Endpoints lists the public gRPC endpoints that are currently up.

Explore the API in three commands

List the services the node serves:

grpcurl $GRPC list
cosmos.auth.v1beta1.Query
cosmos.authz.v1beta1.Query
cosmos.bank.v1beta1.Query
cosmos.base.node.v1beta1.Service
cosmos.base.reflection.v1beta1.ReflectionService
cosmos.base.reflection.v2alpha1.ReflectionService
cosmos.base.tendermint.v1beta1.Service
cosmos.consensus.v1.Query
cosmos.distribution.v1beta1.Query
cosmos.evidence.v1beta1.Query
cosmos.feegrant.v1beta1.Query
cosmos.gov.v1.Query
cosmos.gov.v1beta1.Query
cosmos.group.v1.Query
cosmos.mint.v1beta1.Query
cosmos.nft.v1beta1.Query
cosmos.params.v1beta1.Query
cosmos.slashing.v1beta1.Query
cosmos.staking.v1beta1.Query
cosmos.tx.v1beta1.Service
cosmos.upgrade.v1beta1.Query
cosmwasm.wasm.v1.Query
grpc.reflection.v1alpha.ServerReflection
ibc.applications.fee.v1.Query
ibc.applications.interchain_accounts.controller.v1.Query
ibc.applications.interchain_accounts.host.v1.Query
ibc.applications.transfer.v1.Query
ibc.core.channel.v1.Query
ibc.core.client.v1.Query
ibc.core.connection.v1.Query
sentinel.deposit.v1.QueryService
sentinel.lease.v1.QueryService
sentinel.node.v3.QueryService
sentinel.oracle.v1.QueryService
sentinel.plan.v3.QueryService
sentinel.provider.v2.QueryService
sentinel.provider.v3.QueryService
sentinel.session.v3.QueryService
sentinel.subscription.v2.QueryService
sentinel.subscription.v3.QueryService
sentinel.swap.v1.QueryService

Every entry is a Protobuf service, and each service exposes RPC methods you can call. The Service reference below covers the sentinel.* ones.

Describe a service to see its methods:

grpcurl $GRPC describe sentinel.node.v3.QueryService

Describe a message to see the fields a request takes:

grpcurl $GRPC describe sentinel.node.v3.QueryNodesRequest
message QueryNodesRequest {
.sentinel.types.v1.Status status = 1;
.cosmos.base.query.v1beta1.PageRequest pagination = 2;
}

These three commands are enough to work out any call on the chain without leaving the terminal.

Make a query

Pass the request as JSON with -d:

grpcurl \
-d '{"status":"STATUS_ACTIVE","pagination":{"limit":"5"}}' \
$GRPC \
sentinel.node.v3.QueryService/QueryNodes

status accepts STATUS_ACTIVE, STATUS_INACTIVE, STATUS_INACTIVE_PENDING or STATUS_UNSPECIFIED (which matches every node).

Pagination

List queries take the standard Cosmos PageRequest: limit, offset, key, count_total and reverse. Ask for the total on the first call, then follow nextKey:

grpcurl \
-d '{"status":"STATUS_ACTIVE","pagination":{"limit":"1","countTotal":true}}' \
$GRPC \
sentinel.node.v3.QueryService/QueryNodes
"pagination": {
"nextKey": "FAAUYmVP7gSlwnk0D7ZGEcXX3IpS",
"total": "1311"
}

Field names may be given in either snake_case or lowerCamelCase; responses always come back in lowerCamelCase.

Query historical state

Pass the block height as the x-cosmos-block-height gRPC metadata header:

HEIGHT=$(grpcurl $GRPC cosmos.base.tendermint.v1beta1.Service/GetLatestBlock \
| jq -r '.block.header.height')

grpcurl \
-H "x-cosmos-block-height: $((HEIGHT - 10000))" \
-d '{"address":"sent1uddk4mfqq3uyu2yzhym8spheqatqs30fzh5tmr"}' \
$GRPC \
cosmos.bank.v1beta1.Query/AllBalances
Public endpoints are not archive nodes

They prune old state. A height outside the retention window fails with failed to load state at height …; version does not exist. The public endpoints held roughly the last 100,000 blocks when this page was checked, about a week of history. For anything older you need an archive node, which you can run yourself with pruning = "nothing".

Sending transactions

You cannot invoke a MsgService here: the hub does not register them on its gRPC server, and grpcurl answers target server does not expose service. Transactions are signed offline and submitted as encoded bytes through cosmos.tx.v1beta1.Service/BroadcastTx; see Sending transactions.

The same service is also how you read transactions back: GetTx, GetTxsEvent and GetBlockWithTxs all work over gRPC:

grpcurl -d '{"hash":"<tx_hash>"}' $GRPC cosmos.tx.v1beta1.Service/GetTx

Service reference

Every method of every Sentinel service on hub v12, with the HTTP path the REST gateway maps it to. The annotations below record which legacy methods no longer answer, which reflection alone will not tell you. To regenerate any block yourself:

grpcurl $GRPC describe <service>
sentinel.deposit.v1.QueryService

service QueryService {
rpc QueryDeposits ( .sentinel.deposit.v1.QueryDepositsRequest ) returns ( .sentinel.deposit.v1.QueryDepositsResponse ) {
option (.google.api.http) = { get: "/sentinel/deposit/v1/deposits" };
}
rpc QueryDeposit ( .sentinel.deposit.v1.QueryDepositRequest ) returns ( .sentinel.deposit.v1.QueryDepositResponse ) {
option (.google.api.http) = { get: "/sentinel/deposit/v1/deposits/{address}" };
}
}

sentinel.lease.v1.QueryService

Added in hub v12.

service QueryService {
rpc QueryLeases ( .sentinel.lease.v1.QueryLeasesRequest ) returns ( .sentinel.lease.v1.QueryLeasesResponse ) {
option (.google.api.http) = { get: "/sentinel/lease/v1/leases" };
}
rpc QueryLeasesForNode ( .sentinel.lease.v1.QueryLeasesForNodeRequest ) returns ( .sentinel.lease.v1.QueryLeasesForNodeResponse ) {
option (.google.api.http) = { get: "/sentinel/lease/v1/nodes/{address}/leases" };
}
rpc QueryLeasesForProvider ( .sentinel.lease.v1.QueryLeasesForProviderRequest ) returns ( .sentinel.lease.v1.QueryLeasesForProviderResponse ) {
option (.google.api.http) = { get: "/sentinel/lease/v1/providers/{address}/leases" };
}
rpc QueryLease ( .sentinel.lease.v1.QueryLeaseRequest ) returns ( .sentinel.lease.v1.QueryLeaseResponse ) {
option (.google.api.http) = { get: "/sentinel/lease/v1/leases/{id}" };
}
rpc QueryParams ( .sentinel.lease.v1.QueryParamsRequest ) returns ( .sentinel.lease.v1.QueryParamsResponse ) {
option (.google.api.http) = { get: "/sentinel/lease/v1/params" };
}
}

sentinel.node.v3.QueryService

service QueryService {
rpc QueryNodes ( .sentinel.node.v3.QueryNodesRequest ) returns ( .sentinel.node.v3.QueryNodesResponse ) {
option (.google.api.http) = { get: "/sentinel/node/v3/nodes" };
}
rpc QueryNodesForPlan ( .sentinel.node.v3.QueryNodesForPlanRequest ) returns ( .sentinel.node.v3.QueryNodesForPlanResponse ) {
option (.google.api.http) = { get: "/sentinel/node/v3/plans/{id}/nodes" };
}
rpc QueryNode ( .sentinel.node.v3.QueryNodeRequest ) returns ( .sentinel.node.v3.QueryNodeResponse ) {
option (.google.api.http) = { get: "/sentinel/node/v3/nodes/{address}" };
}
rpc QueryParams ( .sentinel.node.v3.QueryParamsRequest ) returns ( .sentinel.node.v3.QueryParamsResponse ) {
option (.google.api.http) = { get: "/sentinel/node/v3/params" };
}
}

sentinel.oracle.v1.QueryService

Added in hub v12.

service QueryService {
rpc QueryAssets ( .sentinel.oracle.v1.QueryAssetsRequest ) returns ( .sentinel.oracle.v1.QueryAssetsResponse ) {
option (.google.api.http) = { get: "/sentinel/oracle/v1/assets" };
}
rpc QueryAsset ( .sentinel.oracle.v1.QueryAssetRequest ) returns ( .sentinel.oracle.v1.QueryAssetResponse ) {
option (.google.api.http) = { get: "/sentinel/oracle/v1/assets/{denom}" };
}
rpc QueryParams ( .sentinel.oracle.v1.QueryParamsRequest ) returns ( .sentinel.oracle.v1.QueryParamsResponse ) {
option (.google.api.http) = { get: "/sentinel/oracle/v1/params" };
}
}

sentinel.plan.v3.QueryService

gRPC only. Hub v12 registers this query service on the gRPC server but not on the REST gateway, so the HTTP paths below are not reachable. Query these methods over gRPC.

service QueryService {
rpc QueryPlans ( .sentinel.plan.v3.QueryPlansRequest ) returns ( .sentinel.plan.v3.QueryPlansResponse ) {
option (.google.api.http) = { get: "/sentinel/plan/v3/plans" };
}
rpc QueryPlansForProvider ( .sentinel.plan.v3.QueryPlansForProviderRequest ) returns ( .sentinel.plan.v3.QueryPlansForProviderResponse ) {
option (.google.api.http) = { get: "/sentinel/plan/v3/providers/{address}/plans" };
}
rpc QueryPlan ( .sentinel.plan.v3.QueryPlanRequest ) returns ( .sentinel.plan.v3.QueryPlanResponse ) {
option (.google.api.http) = { get: "/sentinel/plan/v3/plans/{id}" };
}
}

sentinel.provider.v2.QueryService

Retained for backwards compatibility. QueryProviders and QueryProvider still work; QueryParams returns Unimplemented, so use sentinel.provider.v3.QueryService/QueryParams instead.

service QueryService {
rpc QueryProviders ( .sentinel.provider.v2.QueryProvidersRequest ) returns ( .sentinel.provider.v2.QueryProvidersResponse ) {
option (.google.api.http) = { get: "/sentinel/provider/v2/providers" };
}
rpc QueryProvider ( .sentinel.provider.v2.QueryProviderRequest ) returns ( .sentinel.provider.v2.QueryProviderResponse ) {
option (.google.api.http) = { get: "/sentinel/provider/v2/providers/{address}" };
}
rpc QueryParams ( .sentinel.provider.v2.QueryParamsRequest ) returns ( .sentinel.provider.v2.QueryParamsResponse ) {
option (.google.api.http) = { get: "/sentinel/provider/v2/params" };
}
}

sentinel.provider.v3.QueryService

gRPC only. Registered on the gRPC server but not on the REST gateway in hub v12.

service QueryService {
rpc QueryParams ( .sentinel.provider.v3.QueryParamsRequest ) returns ( .sentinel.provider.v3.QueryParamsResponse ) {
option (.google.api.http) = { get: "/sentinel/provider/v3/params" };
}
}

sentinel.session.v3.QueryService

service QueryService {
rpc QuerySessions ( .sentinel.session.v3.QuerySessionsRequest ) returns ( .sentinel.session.v3.QuerySessionsResponse ) {
option (.google.api.http) = { get: "/sentinel/session/v3/sessions" };
}
rpc QuerySessionsForAccount ( .sentinel.session.v3.QuerySessionsForAccountRequest ) returns ( .sentinel.session.v3.QuerySessionsForAccountResponse ) {
option (.google.api.http) = { get: "/sentinel/session/v3/accounts/{address}/sessions" };
}
rpc QuerySessionsForNode ( .sentinel.session.v3.QuerySessionsForNodeRequest ) returns ( .sentinel.session.v3.QuerySessionsForNodeResponse ) {
option (.google.api.http) = { get: "/sentinel/session/v3/nodes/{address}/sessions" };
}
rpc QuerySessionsForSubscription ( .sentinel.session.v3.QuerySessionsForSubscriptionRequest ) returns ( .sentinel.session.v3.QuerySessionsForSubscriptionResponse ) {
option (.google.api.http) = { get: "/sentinel/session/v3/subscriptions/{id}/sessions" };
}
rpc QuerySessionsForAllocation ( .sentinel.session.v3.QuerySessionsForAllocationRequest ) returns ( .sentinel.session.v3.QuerySessionsForAllocationResponse ) {
option (.google.api.http) = {
get: "/sentinel/session/v3/subscriptions/{id}/allocations/{address}/sessions"
};
}
rpc QuerySession ( .sentinel.session.v3.QuerySessionRequest ) returns ( .sentinel.session.v3.QuerySessionResponse ) {
option (.google.api.http) = { get: "/sentinel/session/v3/sessions/{id}" };
}
rpc QueryParams ( .sentinel.session.v3.QueryParamsRequest ) returns ( .sentinel.session.v3.QueryParamsResponse ) {
option (.google.api.http) = { get: "/sentinel/session/v3/params" };
}
}

sentinel.subscription.v2.QueryService

Retained for backwards compatibility, but only QueryAllocation and QueryAllocations are implemented. Every other method on this service returns Unimplemented; use sentinel.subscription.v3.QueryService instead.

The payout methods have no replacement. In hub v12 the v2 payout records were migrated into the lease module, where the payout schedule is a property of a lease rather than a separately queryable record. Query sentinel.lease.v1.QueryService instead.

service QueryService {
rpc QueryAllocations ( .sentinel.subscription.v2.QueryAllocationsRequest ) returns ( .sentinel.subscription.v2.QueryAllocationsResponse ) {
option (.google.api.http) = { get: "/sentinel/subscription/v2/subscriptions/{id}/allocations" };
}
rpc QueryAllocation ( .sentinel.subscription.v2.QueryAllocationRequest ) returns ( .sentinel.subscription.v2.QueryAllocationResponse ) {
option (.google.api.http) = { get: "/sentinel/subscription/v2/subscriptions/{id}/allocations/{address}" };
}
}

sentinel.subscription.v3.QueryService

service QueryService {
rpc QuerySubscriptions ( .sentinel.subscription.v3.QuerySubscriptionsRequest ) returns ( .sentinel.subscription.v3.QuerySubscriptionsResponse ) {
option (.google.api.http) = { get: "/sentinel/subscription/v3/subscriptions" };
}
rpc QuerySubscriptionsForAccount ( .sentinel.subscription.v3.QuerySubscriptionsForAccountRequest ) returns ( .sentinel.subscription.v3.QuerySubscriptionsForAccountResponse ) {
option (.google.api.http) = { get: "/sentinel/subscription/v3/accounts/{address}/subscriptions" };
}
rpc QuerySubscriptionsForPlan ( .sentinel.subscription.v3.QuerySubscriptionsForPlanRequest ) returns ( .sentinel.subscription.v3.QuerySubscriptionsForPlanResponse ) {
option (.google.api.http) = { get: "/sentinel/subscription/v3/plans/{id}/subscriptions" };
}
rpc QuerySubscription ( .sentinel.subscription.v3.QuerySubscriptionRequest ) returns ( .sentinel.subscription.v3.QuerySubscriptionResponse ) {
option (.google.api.http) = { get: "/sentinel/subscription/v3/subscriptions/{id}" };
}
rpc QueryParams ( .sentinel.subscription.v3.QueryParamsRequest ) returns ( .sentinel.subscription.v3.QueryParamsResponse ) {
option (.google.api.http) = { get: "/sentinel/subscription/v3/params" };
}
}

sentinel.swap.v1.QueryService

service QueryService {
rpc QuerySwaps ( .sentinel.swap.v1.QuerySwapsRequest ) returns ( .sentinel.swap.v1.QuerySwapsResponse ) {
option (.google.api.http) = { get: "/sentinel/swap/v1/swaps" };
}
rpc QuerySwap ( .sentinel.swap.v1.QuerySwapRequest ) returns ( .sentinel.swap.v1.QuerySwapResponse ) {
option (.google.api.http) = { get: "/sentinel/swap/v1/swaps/{tx_hash}" };
}
rpc QueryParams ( .sentinel.swap.v1.QueryParamsRequest ) returns ( .sentinel.swap.v1.QueryParamsResponse ) {
option (.google.api.http) = { get: "/sentinel/swap/v1/params" };
}
}