Claude Code Plugins

Community-maintained marketplace

Feedback

shelby-blockchain

@codehakase/shelby-skills
1
0

Interact with Shelby decentralized storage on Aptos blockchain. Use when uploading files, downloading blobs, checking APT/ShelbyUSD balances, listing transactions, or building Shelby-based applications. Covers blob storage, fungible assets, and WebDAV integration.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name shelby-blockchain
description Interact with Shelby decentralized storage on Aptos blockchain. Use when uploading files, downloading blobs, checking APT/ShelbyUSD balances, listing transactions, or building Shelby-based applications. Covers blob storage, fungible assets, and WebDAV integration.
allowed-tools Read, Glob, Grep, Bash(curl:*), Bash(npm:*), WebFetch

Shelby Blockchain Operations

This skill provides comprehensive guidance for interacting with Shelby, a decentralized storage network built on the Aptos blockchain.

Quick Reference

Operation Method Endpoint/SDK
Upload file SDK client.upload() + wallet signing
Download blob SDK client.download()
List blobs SDK client.coordination.getAccountBlobs()
APT balance REST POST /view with fungible asset
ShelbyUSD balance REST POST /view with fungible asset
List transactions REST GET /accounts/{addr}/transactions

Network Configuration

// Shelby Fullnode API
const SHELBY_FULLNODE = "https://api.shelbynet.shelby.xyz/v1";

// Token Metadata Addresses (Fungible Assets on Shelbynet)
const APT_METADATA = "0xa";
const SHELBY_USD_METADATA = "0x1b18363a9f1fe5e6ebf247daba5cc1c18052bb232efdc4c50f556053922d98e1";

// Decimal conversion (8 decimals)
const DECIMAL_DIVISOR = 100_000_000;

Core Operations

1. Fetching Token Balances

On Shelbynet, both APT and ShelbyUSD are stored as Fungible Assets, not legacy CoinStore. Use the primary_fungible_store::balance view function:

async function fetchBalance(address, metadataAddress) {
  const payload = {
    function: "0x1::primary_fungible_store::balance",
    type_arguments: ["0x1::fungible_asset::Metadata"],
    arguments: [address, metadataAddress],
  };

  const response = await fetch(`${SHELBY_FULLNODE}/view`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(payload),
  });

  const result = await response.json();
  return Array.isArray(result) && result.length > 0
    ? Number(result[0]) / 100_000_000
    : 0;
}

// Usage
const aptBalance = await fetchBalance(walletAddress, "0xa");
const susdBalance = await fetchBalance(walletAddress, SHELBY_USD_METADATA);

2. Uploading Files to Shelby

import { ShelbyBlobClient, expectedTotalChunksets } from "@aspect-build/shelby-sdk";

async function uploadToShelby(file, wallet) {
  const client = new ShelbyBlobClient();
  const blobData = new Uint8Array(await file.arrayBuffer());
  const account = AccountAddress.from(wallet.account.address);

  // Calculate expiration (365 days)
  const expirationMicros = BigInt(Date.now() + 365 * 24 * 60 * 60 * 1000) * 1000n;

  // Generate commitments
  const commitments = client.commitments(blobData);

  // Create registration payload
  const payload = client.createRegisterBlobPayload({
    account,
    blobName: file.name,
    blobSize: commitments.raw_data_size,
    blobMerkleRoot: commitments.blob_merkle_root,
    expirationMicros,
    numChunksets: expectedTotalChunksets(commitments.raw_data_size),
  });

  // Sign and submit transaction
  const txResponse = await wallet.signAndSubmitTransaction({ data: payload });
  await aptos.waitForTransaction({ transactionHash: txResponse.hash });

  // Upload blob data
  await client.rpc.putBlob({
    data: blobData,
    commitments,
    account,
    blobName: file.name,
    expirationMicros,
  });

  return {
    hash: commitments.blob_merkle_root,
    name: file.name,
    size: blobData.byteLength,
    txHash: txResponse.hash,
  };
}

3. Listing Blobs

async function listBlobs(walletAddress) {
  const client = new ShelbyBlobClient();
  const account = AccountAddress.from(walletAddress);
  const blobs = await client.coordination.getAccountBlobs({ account });

  return blobs.map(blob => ({
    name: blob.blobNameSuffix || blob.name || blob.blobName,
    hash: Buffer.from(blob.blobMerkleRoot).toString("hex"),
    size: blob.size,
    createdAt: Number(blob.creationMicros) / 1000,
    expiresAt: Number(blob.expirationMicros) / 1000,
  }));
}

4. Downloading Blobs

async function downloadBlob(blobName, walletAddress) {
  const client = new ShelbyBlobClient();
  const account = AccountAddress.from(walletAddress);
  const data = await client.download({ account, blobName });
  return data; // Uint8Array
}

// Create downloadable URL in browser
function createBlobUrl(data, mimeType) {
  const blob = new Blob([data], { type: mimeType });
  return URL.createObjectURL(blob);
}

5. Fetching Transactions

async function fetchTransactions(address, limit = 20) {
  const response = await fetch(
    `${SHELBY_FULLNODE}/accounts/${address}/transactions?limit=${limit}`
  );
  const txData = await response.json();

  return txData.map(tx => ({
    hash: tx.hash,
    version: tx.version,
    success: tx.success,
    gasUsed: tx.gas_used,
    timestamp: tx.timestamp,
    type: parseTransactionType(tx.payload?.function),
  }));
}

function parseTransactionType(fn) {
  if (!fn) return "unknown";
  if (fn.includes("register_blob")) return "upload";
  if (fn.includes("transfer")) return "transfer";
  if (fn.includes("mint")) return "mint";
  if (fn.includes("faucet")) return "faucet";
  return "other";
}

6. Cost Calculation

function calculateUploadCost(sizeBytes) {
  const STORAGE_RATE_PER_GB_MONTH = 0.05; // $0.05 per GB per month
  const DURATION_MONTHS = 12;

  const sizeGB = sizeBytes / (1024 * 1024 * 1024);
  const totalCost = sizeGB * STORAGE_RATE_PER_GB_MONTH * DURATION_MONTHS;

  return {
    sizeGB,
    costPerGBMonth: STORAGE_RATE_PER_GB_MONTH,
    durationMonths: DURATION_MONTHS,
    totalCost,
    currency: "ShelbyUSD",
  };
}

Required Dependencies

{
  "@aspect-build/shelby-sdk": "latest",
  "@aptos-labs/ts-sdk": "^1.33.1",
  "@aptos-labs/wallet-adapter-react": "^3.x"
}

Environment Variables

VITE_SHELBY_API_KEY=your_api_key    # Frontend SDK key
SHELBY_API_KEY=your_api_key          # Server-side SDK key
APTOS_NETWORK=testnet                # Network setting

Common Patterns

React Hook for Balances

const useBalances = () => {
  const [apt, setApt] = useState(null);
  const [susd, setSusd] = useState(null);
  const { account, connected } = useWallet();

  useEffect(() => {
    if (connected && account?.address) {
      const addr = account.address.toString();
      fetchBalance(addr, "0xa").then(setApt);
      fetchBalance(addr, SHELBY_USD_METADATA).then(setSusd);
    }
  }, [connected, account?.address]);

  return { apt, susd };
};

Error Handling

try {
  const balance = await fetchBalance(address, metadata);
} catch (err) {
  if (err.message?.includes("RESOURCE_NOT_FOUND")) {
    return 0; // Account has no balance
  }
  throw err;
}

Additional Resources

Key Insights

  1. APT is a Fungible Asset on Shelbynet - Don't look for CoinStore, use primary_fungible_store::balance
  2. All amounts use 8 decimals - Divide raw values by 100,000,000
  3. Blobs expire automatically - No delete operation needed; set expiration on upload
  4. Wallet signing required for uploads - Downloads are read-only
  5. WebDAV provides filesystem access - Mount blobs as a virtual drive (read-only)