Skip to main content

Using grpcurl with Sui gRPC

grpcurl is a command-line tool for interacting with gRPC servers, similar to how curl works with REST APIs. It's invaluable for quick API testing, debugging, and scripting without writing code.

Why Use grpcurl?#

  • Rapid prototyping - Test API calls instantly without writing client code
  • Debugging - Inspect request/response structures and troubleshoot issues
  • CI/CD integration - Automate gRPC endpoint testing in pipelines
  • Service discovery - Explore available methods and message structures
  • Shell scripting - Build automation scripts with gRPC calls

Installation#

macOS#

brew install grpcurl

Linux#

# Download latest release
VERSION=1.8.9
wget https://github.com/fullstorydev/grpcurl/releases/download/v${VERSION}/grpcurl_${VERSION}_linux_x86_64.tar.gz

# Extract and install
tar -xzf grpcurl_${VERSION}_linux_x86_64.tar.gz
sudo mv grpcurl /usr/local/bin/
chmod +x /usr/local/bin/grpcurl

# Verify installation
grpcurl --version

Windows#

# Using Chocolatey
choco install grpcurl

# Or download from GitHub releases
# https://github.com/fullstorydev/grpcurl/releases

From Source#

go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest

Getting the Proto Files#

grpcurl can work with gRPC reflection (no proto files needed), but having the proto definitions locally is useful for understanding the API structure and generating code.

Clone Sui APIs Repository#

# Clone the Sui APIs repository
git clone https://github.com/MystenLabs/sui-apis.git
cd sui-apis

# Navigate to proto definitions
cd protos

The proto files are located in protos/:

  • sui/ledger.proto - Ledger service (checkpoints, objects, transactions)
  • sui/state.proto - State service (balances, objects)
  • sui/move_package.proto - Package service
  • sui/transaction.proto - Transaction service
  • sui/signature_verification.proto - Signature verification

Optional: Copy Proto Files#

# Create a protos directory in your project
mkdir -p ~/sui-grpc/protos

# Copy proto files
cp -r sui ~/sui-grpc/protos/
tip

grpcurl works without proto files using server reflection, which is enabled on Dwellir endpoints. You only need the proto files if you want to inspect the schemas locally or generate client code.

Dwellir Connection Setup#

Environment Variables#

Create a .env file for your Dwellir credentials:

# .env
export DWELLIR_ENDPOINT="api-sui-mainnet-full.n.dwellir.com:443"
export DWELLIR_API_KEY="your_api_key_here"

Load the environment:

source .env

Authentication Header#

All Dwellir gRPC calls require the x-api-key header:

grpcurl \
-H "x-api-key: ${DWELLIR_API_KEY}" \
${DWELLIR_ENDPOINT} \
list

Service Discovery#

List All Available Services#

grpcurl \
-H "x-api-key: ${DWELLIR_API_KEY}" \
${DWELLIR_ENDPOINT} \
list

Expected output:

grpc.reflection.v1alpha.ServerReflection
sui.rpc.v2.LedgerService
sui.rpc.v2.MovePackageService
sui.rpc.v2.SignatureVerificationService
sui.rpc.v2.StateService
sui.rpc.v2.TransactionService

List Methods in a Service#

grpcurl \
-H "x-api-key: ${DWELLIR_API_KEY}" \
${DWELLIR_ENDPOINT} \
list sui.rpc.v2.StateService

Output:

sui.rpc.v2.StateService.GetBalance
sui.rpc.v2.StateService.GetCoinInfo
sui.rpc.v2.StateService.ListBalances
sui.rpc.v2.StateService.ListDynamicFields
sui.rpc.v2.StateService.ListOwnedObjects
sui.rpc.v2.StateService.SimulateTransaction

Describe a Method#

grpcurl \
-H "x-api-key: ${DWELLIR_API_KEY}" \
${DWELLIR_ENDPOINT} \
describe sui.rpc.v2.StateService.GetBalance

Common API Calls#

Get Current Checkpoint#

grpcurl \
-H "x-api-key: ${DWELLIR_API_KEY}" \
-d '{}' \
${DWELLIR_ENDPOINT} \
sui.rpc.v2.LedgerService/GetCheckpoint

Get Object by ID#

OBJECT_ID="0x5"

grpcurl \
-H "x-api-key: ${DWELLIR_API_KEY}" \
-d "{\"object_id\": \"${OBJECT_ID}\"}" \
${DWELLIR_ENDPOINT} \
sui.rpc.v2.LedgerService/GetObject

Get Balance for Address#

ADDRESS="0x742d35cc6634c0532925a3b844bc9e7eb503c114a04bd3e02c7681a09e58b01d"
COIN_TYPE="0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI"

grpcurl \
-H "x-api-key: ${DWELLIR_API_KEY}" \
-d "{\"owner\": \"${ADDRESS}\", \"coin_type\": \"${COIN_TYPE}\"}" \
${DWELLIR_ENDPOINT} \
sui.rpc.v2.StateService/GetBalance

Get Coin Metadata#

grpcurl \
-H "x-api-key: ${DWELLIR_API_KEY}" \
-d '{"coin_type": "0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI"}' \
${DWELLIR_ENDPOINT} \
sui.rpc.v2.StateService/GetCoinInfo

Get Current Epoch#

grpcurl \
-H "x-api-key: ${DWELLIR_API_KEY}" \
-d '{}' \
${DWELLIR_ENDPOINT} \
sui.rpc.v2.LedgerService/GetEpoch

Get Move Package#

PACKAGE_ID="0x0000000000000000000000000000000000000000000000000000000000000002"

grpcurl \
-H "x-api-key: ${DWELLIR_API_KEY}" \
-d "{\"package_id\": \"${PACKAGE_ID}\"}" \
${DWELLIR_ENDPOINT} \
sui.rpc.v2.MovePackageService/GetPackage

Advanced Usage#

Using Field Masks#

Request only specific fields to reduce response size:

grpcurl \
-H "x-api-key: ${DWELLIR_API_KEY}" \
-d '{
"read_mask": {
"paths": ["sequence_number", "timestamp_ms", "transactions"]
}
}' \
${DWELLIR_ENDPOINT} \
sui.rpc.v2.LedgerService/GetCheckpoint

Formatted Output with jq#

Parse and format JSON responses:

grpcurl \
-H "x-api-key: ${DWELLIR_API_KEY}" \
-d '{}' \
${DWELLIR_ENDPOINT} \
sui.rpc.v2.LedgerService/GetEpoch | jq '.epoch.epoch'

Save Response to File#

grpcurl \
-H "x-api-key: ${DWELLIR_API_KEY}" \
-d '{}' \
${DWELLIR_ENDPOINT} \
sui.rpc.v2.LedgerService/GetCheckpoint > checkpoint.json

Using Request from File#

# Create request file
cat > request.json <<EOF
{
"owner": "0x742d35cc6634c0532925a3b844bc9e7eb503c114a04bd3e02c7681a09e58b01d",
"coin_type": "0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI"
}
EOF

# Execute request
grpcurl \
-H "x-api-key: ${DWELLIR_API_KEY}" \
-d @ \
${DWELLIR_ENDPOINT} \
sui.rpc.v2.StateService/GetBalance < request.json

Practical Scripts#

Monitor New Checkpoints#

#!/bin/bash
# monitor-checkpoints.sh

ENDPOINT="api-sui-mainnet-full.n.dwellir.com:443"
API_KEY="your_api_key_here"

while true; do
CHECKPOINT=$(grpcurl \
-H "x-api-key: ${API_KEY}" \
-d '{"read_mask": {"paths": ["sequence_number", "timestamp_ms"]}}' \
${ENDPOINT} \
sui.rpc.v2.LedgerService/GetCheckpoint | jq -r '.checkpoint.sequenceNumber')

echo "[$(date)] Current checkpoint: ${CHECKPOINT}"

sleep 2
done

Check Multiple Balances#

#!/bin/bash
# check-balances.sh

ENDPOINT="api-sui-mainnet-full.n.dwellir.com:443"
API_KEY="your_api_key_here"
ADDRESS="$1"

if [ -z "$ADDRESS" ]; then
echo "Usage: $0 <sui_address>"
exit 1
fi

echo "Fetching balances for: ${ADDRESS}"

# Get all balances
grpcurl \
-H "x-api-key: ${API_KEY}" \
-d "{\"owner\": \"${ADDRESS}\"}" \
${ENDPOINT} \
sui.rpc.v2.StateService/ListBalances | \
jq -r '.balances[] | "\(.coinType): \(.balance)"'

Validate Package Deployment#

#!/bin/bash
# validate-package.sh

ENDPOINT="api-sui-mainnet-full.n.dwellir.com:443"
API_KEY="your_api_key_here"
PACKAGE_ID="$1"

if [ -z "$PACKAGE_ID" ]; then
echo "Usage: $0 <package_id>"
exit 1
fi

echo "Validating package: ${PACKAGE_ID}"

# Get package info
RESPONSE=$(grpcurl \
-H "x-api-key: ${API_KEY}" \
-d "{\"package_id\": \"${PACKAGE_ID}\"}" \
${ENDPOINT} \
sui.rpc.v2.MovePackageService/GetPackage 2>&1)

if echo "$RESPONSE" | grep -q "NOT_FOUND"; then
echo "❌ Package not found"
exit 1
else
echo "✓ Package exists"
MODULE_COUNT=$(echo "$RESPONSE" | jq '.package.modules | length')
echo "✓ Contains ${MODULE_COUNT} modules"
fi

CI/CD Integration#

GitHub Actions Example#

name: Test Sui gRPC Endpoint

on:
schedule:
- cron: '*/15 * * * *' # Every 15 minutes
workflow_dispatch:

jobs:
test-grpc:
runs-on: ubuntu-latest
steps:
- name: Install grpcurl
run: |
curl -sSL "https://github.com/fullstorydev/grpcurl/releases/download/v1.8.9/grpcurl_1.8.9_linux_x86_64.tar.gz" | tar -xz
sudo mv grpcurl /usr/local/bin/

- name: Test GetCheckpoint
env:
API_KEY: ${{ secrets.DWELLIR_API_KEY }}
run: |
grpcurl \
-H "x-api-key: ${API_KEY}" \
-d '{}' \
api-sui-mainnet-full.n.dwellir.com:443 \
sui.rpc.v2.LedgerService/GetCheckpoint

- name: Test GetEpoch
env:
API_KEY: ${{ secrets.DWELLIR_API_KEY }}
run: |
grpcurl \
-H "x-api-key: ${API_KEY}" \
-d '{}' \
api-sui-mainnet-full.n.dwellir.com:443 \
sui.rpc.v2.LedgerService/GetEpoch

Docker Container#

FROM alpine:latest

RUN apk add --no-cache curl jq bash

# Install grpcurl
RUN curl -sSL "https://github.com/fullstorydev/grpcurl/releases/download/v1.8.9/grpcurl_1.8.9_linux_x86_64.tar.gz" | tar -xz && \
mv grpcurl /usr/local/bin/ && \
chmod +x /usr/local/bin/grpcurl

# Copy scripts
COPY scripts/ /scripts/
RUN chmod +x /scripts/*.sh

CMD ["/bin/bash"]

Troubleshooting#

Connection Issues#

Problem: Failed to dial target host

Solution: Verify endpoint and port

# Test connectivity
nc -zv api-sui-mainnet-full.n.dwellir.com 443

# Check endpoint in environment
echo $DWELLIR_ENDPOINT

Authentication Errors#

Problem: UNAUTHENTICATED: Missing or invalid API key

Solution: Verify your API key header

# Check if API key is set
echo $DWELLIR_API_KEY

# Test with explicit key
grpcurl \
-H "x-api-key: your_actual_api_key" \
api-sui-mainnet-full.n.dwellir.com:443 \
list

Invalid Request Format#

Problem: INVALID_ARGUMENT: Invalid request

Solution: Describe the method to see expected format

grpcurl \
-H "x-api-key: ${DWELLIR_API_KEY}" \
${DWELLIR_ENDPOINT} \
describe sui.rpc.v2.LedgerService.GetObject

TLS/SSL Issues#

Problem: x509: certificate signed by unknown authority

Solution: Use -insecure flag (not recommended for production)

grpcurl -insecure \
-H "x-api-key: ${DWELLIR_API_KEY}" \
${DWELLIR_ENDPOINT} \
list

Best Practices#

1. Use Environment Variables#

Never hardcode API keys in scripts:

# Good
grpcurl -H "x-api-key: ${DWELLIR_API_KEY}" ...

# Bad - API key exposed in history
grpcurl -H "x-api-key: sk-abc123..." ...

2. Add Timeouts#

Prevent hanging requests:

grpcurl \
-max-time 10 \
-H "x-api-key: ${DWELLIR_API_KEY}" \
${DWELLIR_ENDPOINT} \
sui.rpc.v2.LedgerService/GetCheckpoint

3. Log Requests for Debugging#

grpcurl -v \
-H "x-api-key: ${DWELLIR_API_KEY}" \
-d '{}' \
${DWELLIR_ENDPOINT} \
sui.rpc.v2.LedgerService/GetCheckpoint 2>&1 | tee debug.log

4. Use Field Masks#

Request only the data you need:

# Efficient - only get sequence number
grpcurl -d '{"read_mask": {"paths": ["sequence_number"]}}' ...

# Inefficient - gets entire checkpoint
grpcurl -d '{}' ...

Useful Aliases#

Add to your ~/.bashrc or ~/.zshrc:

# Sui gRPC aliases
export DWELLIR_ENDPOINT="api-sui-mainnet-full.n.dwellir.com:443"
export DWELLIR_API_KEY="your_api_key_here"

alias sui-grpc='grpcurl -H "x-api-key: ${DWELLIR_API_KEY}" ${DWELLIR_ENDPOINT}'
alias sui-list='sui-grpc list'
alias sui-checkpoint='sui-grpc -d '"'"'{}'"'"' sui.rpc.v2.LedgerService/GetCheckpoint | jq'
alias sui-epoch='sui-grpc -d '"'"'{}'"'"' sui.rpc.v2.LedgerService/GetEpoch | jq'

Usage:

sui-list
sui-checkpoint
sui-epoch

Need help? Contact support@dwellir.com