skills$openclaw/jb-bendystraw
mejango9.3k

by mejango

jb-bendystraw – OpenClaw Skill

jb-bendystraw is an OpenClaw Skills integration for coding workflows. Bendystraw GraphQL API reference for querying Juicebox project data across all chains. Get project stats, payments, token holders, loans, NFT tiers, unified activity feeds, historical snapshots, and cross-chain aggregations.

9.3k stars4.2k forksSecurity L1
Updated Feb 7, 2026Created Feb 7, 2026coding

Skill Snapshot

namejb-bendystraw
descriptionBendystraw GraphQL API reference for querying Juicebox project data across all chains. Get project stats, payments, token holders, loans, NFT tiers, unified activity feeds, historical snapshots, and cross-chain aggregations. OpenClaw Skills integration.
ownermejango
repositorymejango/juicypath: jb-bendystraw
languageMarkdown
licenseMIT
topics
securityL1
installopenclaw add @mejango/juicy:jb-bendystraw
last updatedFeb 7, 2026

Maintainer

mejango

mejango

Maintains jb-bendystraw in the OpenClaw Skills directory.

View GitHub profile
File Explorer
1 files
jb-bendystraw
SKILL.md
61.0 KB
SKILL.md

name: jb-bendystraw description: Bendystraw GraphQL API reference for querying Juicebox project data across all chains. Get project stats, payments, token holders, loans, NFT tiers, unified activity feeds, historical snapshots, and cross-chain aggregations.

Bendystraw: Cross-Chain Juicebox Data API

Bendystraw is a GraphQL indexer for Juicebox V5 events across all supported chains. It aggregates data and provides unified cross-chain queries for projects, payments, token holders, and NFTs.

API Base URLs

Production: https://bendystraw.xyz/{API_KEY}/graphql
Testnet: https://testnet.bendystraw.xyz/{API_KEY}/graphql
Playground: https://bendystraw.xyz (browser-based GraphQL explorer)

Authentication

API key required. Contact @peripheralist on Twitter/X to get one.

const response = await fetch(`https://bendystraw.xyz/${API_KEY}/graphql`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    query: '...',
    variables: { ... }
  })
});

Important: Never expose API keys in frontend code. Use a server-side proxy.


Supported Chains

ChainChain IDNetwork
Ethereum1Mainnet
Optimism10Mainnet
Base8453Mainnet
Arbitrum42161Mainnet
Sepolia11155111Testnet

GraphQL Schema Reference

Project Entity

type Project {
  # Identifiers
  id: String!                    # "{chainId}-{projectId}-{version}"
  projectId: Int!
  chainId: Int!
  version: Int!                  # Protocol version (4 or 5)

  # Metadata
  handle: String
  name: String
  description: String
  logoUri: String
  infoUri: String
  owner: String!
  deployer: String

  # Financial
  balance: String!               # Current balance (wei)
  volume: String!                # Total received (wei)
  volumeUsd: String              # USD equivalent
  redeemVolume: String!          # Total redeemed (wei)
  redeemVolumeUsd: String

  # Tokens
  tokenSupply: String!           # Total token supply
  token: String                  # ERC20 address if deployed
  tokenSymbol: String

  # Activity counts
  paymentsCount: Int!
  redeemCount: Int!
  contributorsCount: Int!
  nftsMintedCount: Int!

  # Trending (7-day window)
  trendingScore: Float
  trendingVolume: String
  trendingPaymentsCount: Int

  # Omnichain
  suckerGroupId: String          # Linked cross-chain group

  # Timestamps
  createdAt: Int!
  deployedAt: Int
}

SuckerGroup Entity (Omnichain Projects)

type SuckerGroup {
  id: String!                    # Unique group identifier
  projects: [String!]!           # Array of project IDs

  # Aggregated totals across all chains
  volume: String!
  volumeUsd: String
  balance: String!
  tokenSupply: String!
  paymentsCount: Int!
  contributorsCount: Int!

  # Related projects (expanded)
  projects_rel: [Project!]!
}

Participant Entity (Token Holders)

type Participant {
  id: String!                    # "{chainId}-{projectId}-{address}"
  address: String!
  projectId: Int!
  chainId: Int!

  # Balances
  balance: String!               # Total balance (credits + ERC20)
  creditBalance: String!         # Unclaimed credits
  erc20Balance: String!          # Claimed ERC20 tokens

  # Activity
  volume: String!                # Total contributed
  volumeUsd: String
  paymentsCount: Int!
  redeemCount: Int!

  # Timestamps
  lastPaidAt: Int
  firstPaidAt: Int
}

PayEvent Entity

type PayEvent {
  id: String!
  projectId: Int!
  chainId: Int!
  rulesetId: Int!

  # Transaction
  txHash: String!
  timestamp: Int!
  logIndex: Int!
  blockNumber: Int!

  # Payment details
  from: String!                  # Payer address
  beneficiary: String!           # Token recipient
  amount: String!                # Payment amount (wei)
  amountUsd: String
  distributionFromPayAmount: String!

  # Tokens
  newlyIssuedTokenCount: String!
  beneficiaryTokenCount: String!

  # Metadata
  memo: String
  feeFromPayAmount: String
}

CashOutEvent Entity

type CashOutEvent {
  id: String!
  projectId: Int!
  chainId: Int!
  rulesetId: Int!

  # Transaction
  txHash: String!
  timestamp: Int!

  # Redemption details
  holder: String!
  beneficiary: String!
  cashOutCount: String!          # Tokens burned
  reclaimAmount: String!         # ETH received
  reclaimAmountUsd: String
  metadata: String
}

NFT Entity

type NFT {
  id: String!
  tokenId: Int!
  projectId: Int!
  chainId: Int!
  hook: String!                  # 721 hook address

  # Tier
  tierId: Int!
  tierCategory: Int

  # Ownership
  owner: String!
  createdAt: Int!

  # Metadata
  tokenUri: String
}

ActivityEvent Entity (Unified Activity Feed)

A polymorphic event type that provides a unified view of all project activity. Query this instead of individual event types when building activity feeds.

type ActivityEvent {
  id: String!
  chainId: Int!
  projectId: Int!
  suckerGroupId: String
  version: Int!

  # Transaction
  txHash: String!
  timestamp: Int!
  from: String!

  # Event type discriminator
  type: ActivityEventType!       # Determines which embedded event is populated

  # Embedded events (one will be non-null based on type)
  payEvent: PayEvent
  cashOutTokensEvent: CashOutTokensEvent
  mintNftEvent: MintNftEvent
  sendPayoutsEvent: SendPayoutsEvent
  sendPayoutToSplitEvent: SendPayoutToSplitEvent
  borrowLoanEvent: BorrowLoanEvent
  repayLoanEvent: RepayLoanEvent
  liquidateLoanEvent: LiquidateLoanEvent
  deployErc20Event: DeployErc20Event
  burnEvent: BurnEvent
  mintTokensEvent: MintTokensEvent
  projectCreateEvent: ProjectCreateEvent
  addToBalanceEvent: AddToBalanceEvent
  useAllowanceEvent: UseAllowanceEvent
  decorateBannyEvent: DecorateBannyEvent
  # ... and more

  # Relations
  project: Project
  suckerGroup: SuckerGroup
}

enum ActivityEventType {
  payEvent
  cashOutTokensEvent
  mintNftEvent
  sendPayoutsEvent
  sendPayoutToSplitEvent
  borrowLoanEvent
  repayLoanEvent
  liquidateLoanEvent
  reallocateLoanEvent
  deployErc20Event
  burnEvent
  mintTokensEvent
  manualMintTokensEvent
  manualBurnEvent
  autoIssueEvent
  projectCreateEvent
  addToBalanceEvent
  useAllowanceEvent
  sendReservedTokensToSplitEvent
  sendReservedTokensToSplitsEvent
  decorateBannyEvent
}

Loan Entity (RevLoans)

Active loan state from the RevLoans protocol.

type Loan {
  id: BigInt!                    # Loan ID (NFT token ID)
  projectId: Int!
  chainId: Int!
  version: Int!
  createdAt: Int!

  # Loan terms
  borrowAmount: BigInt!          # Amount borrowed (wei)
  collateral: BigInt!            # Collateral locked (project tokens)
  sourceFeeAmount: BigInt!       # Fee amount
  prepaidDuration: Int!          # Prepaid period in seconds
  prepaidFeePercent: Int!        # Fee percent (basis points)

  # Addresses
  beneficiary: String!           # Who receives borrowed funds
  owner: String!                 # Loan NFT owner
  token: String!                 # Collateral token address
  terminal: String!              # Terminal address

  # Metadata
  tokenUri: String               # Loan NFT metadata URI

  # Relations
  project: Project
  participant: Participant
  wallet: Wallet
}

Wallet Entity

Wallet-level aggregation across all project participations.

type Wallet {
  address: String!               # Wallet address

  # Aggregated stats
  volume: BigInt!                # Total volume across all projects
  volumeUsd: BigInt!             # USD equivalent (18 decimals)
  lastPaidTimestamp: Int         # Most recent payment timestamp

  # Relations
  participants: ParticipantPage  # All project participations
  nfts: NFTPage                  # All owned NFTs
}

NFTTier Entity

NFT tier configuration with pricing and supply.

type NFTTier {
  chainId: Int!
  projectId: Int!
  version: Int!
  tierId: Int!

  # Pricing
  price: BigInt!                 # Price in terminal token (wei)

  # Supply
  initialSupply: Int!            # Original supply
  remainingSupply: Int!          # Current available

  # Configuration
  allowOwnerMint: Boolean!       # Owner can mint without payment
  cannotBeRemoved: Boolean!      # Tier is permanent
  transfersPausable: Boolean!    # Transfers can be paused
  votingUnits: BigInt!           # Governance weight per NFT
  category: Int                  # Tier category
  reserveFrequency: Int          # Reserve rate
  reserveBeneficiary: String     # Reserve recipient

  # Metadata
  encodedIpfsUri: String         # IPFS hash (encoded)
  resolvedUri: String            # Full resolved URI
  metadata: JSON                 # Parsed metadata
  svg: String                    # SVG content if available

  createdAt: Int!

  # Relations
  hook: NFTHook
  nfts: NFTPage                  # Minted NFTs in this tier
  project: Project
}

NFTHook Entity

721 hook contract configuration.

type NFTHook {
  chainId: Int!
  projectId: Int!
  version: Int!
  createdAt: Int!

  address: String!               # Hook contract address
  name: String                   # Collection name
  symbol: String                 # Collection symbol

  # Relations
  nfts: NFTPage
  nftTiers: NFTTierPage
  project: Project
}

ProjectMoment Entity (Historical Snapshots)

Point-in-time snapshots of project state. Useful for historical charts and analytics.

type ProjectMoment {
  projectId: Int!
  chainId: Int!
  version: Int!

  # Snapshot point
  block: Int!                    # Block number
  timestamp: Int!                # Unix timestamp

  # State at snapshot
  volume: BigInt!
  volumeUsd: BigInt!
  balance: BigInt!
  trendingScore: BigInt!

  # Relations
  project: Project
}

SuckerGroupMoment Entity

Point-in-time snapshots of cross-chain aggregated state.

type SuckerGroupMoment {
  suckerGroupId: String!

  # Snapshot point
  block: Int!
  timestamp: Int!

  # Aggregated state
  volume: BigInt!
  volumeUsd: BigInt!
  balance: BigInt!
  tokenSupply: BigInt!

  # Relations
  suckerGroup: SuckerGroup
}

SuckerTransaction Entity (Cross-Chain Bridging)

Token bridging transactions between chains via suckers.

type SuckerTransaction {
  index: Int!                    # Transaction index
  projectId: Int!
  chainId: Int!                  # Source chain
  version: Int!
  suckerGroupId: String!
  createdAt: Int!

  # Bridge details
  token: String!                 # Token being bridged
  sucker: String!                # Source sucker address
  peer: String!                  # Destination sucker address
  peerChainId: Int!              # Destination chain
  beneficiary: String!           # Recipient address

  # Amounts
  projectTokenCount: BigInt!     # Project tokens bridged
  terminalTokenAmount: BigInt!   # Terminal tokens (if any)

  # State
  root: String                   # Merkle root
  status: SuckerTransactionStatus!

  # Relations
  suckerGroup: SuckerGroup
}

enum SuckerTransactionStatus {
  pending
  completed
  failed
}

SendPayoutsEvent Entity

Payout distribution events from the terminal.

type SendPayoutsEvent {
  id: String!
  chainId: Int!
  version: Int!
  projectId: Int!
  suckerGroupId: String

  # Transaction
  txHash: String!
  timestamp: Int!
  logIndex: Int!

  # Payout details
  caller: String!                # Who triggered payout
  from: String!                  # Source address
  rulesetId: Int!
  rulesetCycleNumber: Int!

  # Amounts
  amount: BigInt!                # Total payout amount
  amountUsd: BigInt!
  amountPaidOut: BigInt!         # Actually distributed
  amountPaidOutUsd: BigInt!
  netLeftoverPayoutAmount: BigInt!  # Remaining after splits
  fee: BigInt!                   # Protocol fee
  feeUsd: BigInt!

  # Relations
  project: Project
}

UseAllowanceEvent Entity

Surplus allowance usage events.

type UseAllowanceEvent {
  id: String!
  chainId: Int!
  version: Int!
  projectId: Int!

  # Transaction
  txHash: String!
  timestamp: Int!

  # Allowance details
  caller: String!
  beneficiary: String!           # Who receives funds
  rulesetId: Int!
  rulesetCycleNumber: Int!

  # Amounts
  amount: BigInt!                # Amount used
  amountUsd: BigInt!
  netAmount: BigInt!             # After fees
  netAmountUsd: BigInt!

  # Relations
  project: Project
}

PermissionHolder Entity

Operator permissions granted to accounts.

type PermissionHolder {
  chainId: Int!
  projectId: Int!
  version: Int!

  account: String!               # Account with permissions
  operator: String!              # Operator address
  permissions: [Int!]!           # Permission IDs granted
  isRevnetOperator: Boolean!     # Is a revnet operator

  # Relations
  project: Project
}

BorrowLoanEvent Entity

Loan creation events from RevLoans.

type BorrowLoanEvent {
  id: String!
  chainId: Int!
  version: Int!
  projectId: Int!
  suckerGroupId: String

  # Transaction
  txHash: String!
  timestamp: Int!
  caller: String!
  from: String!
  logIndex: Int!

  # Loan details
  borrowAmount: BigInt!          # Amount borrowed
  collateral: BigInt!            # Collateral locked
  sourceFeeAmount: BigInt!       # Fee paid
  prepaidDuration: Int!          # Prepaid period (seconds)
  prepaidFeePercent: Int!        # Fee percent (basis points)
  beneficiary: String!           # Loan recipient
  token: String!                 # Collateral token
  terminal: String!              # Terminal address

  # Relations
  project: Project
}

RepayLoanEvent Entity

Loan repayment events.

type RepayLoanEvent {
  id: String!
  chainId: Int!
  version: Int!
  projectId: Int!
  suckerGroupId: String

  # Transaction
  txHash: String!
  timestamp: Int!
  caller: String!
  from: String!
  logIndex: Int!

  # Repayment details
  loanId: BigInt!                # Loan being repaid
  paidOffLoanId: BigInt          # If fully paid off
  repayBorrowAmount: BigInt!     # Amount repaid
  collateralCountToReturn: BigInt!  # Collateral returned

  # Relations
  project: Project
}

LiquidateLoanEvent Entity

Loan liquidation events.

type LiquidateLoanEvent {
  id: String!
  chainId: Int!
  version: Int!
  projectId: Int!
  suckerGroupId: String

  # Transaction
  txHash: String!
  timestamp: Int!
  caller: String!
  from: String!
  logIndex: Int!

  # Liquidation details
  borrowAmount: BigInt!          # Outstanding borrow
  collateral: BigInt!            # Collateral seized

  # Relations
  project: Project
}

ReallocateLoanEvent Entity

Loan reallocation events (moving collateral between loans).

type ReallocateLoanEvent {
  id: String!
  chainId: Int!
  version: Int!
  projectId: Int!
  suckerGroupId: String

  # Transaction
  txHash: String!
  timestamp: Int!
  caller: String!
  from: String!
  logIndex: Int!

  # Reallocation details
  loanId: BigInt!                # Source loan
  reallocatedLoanId: BigInt!     # Target loan
  removedCollateralCount: BigInt!  # Collateral moved

  # Relations
  project: Project
}

BurnEvent Entity

Token burn events (from cash outs).

type BurnEvent {
  id: String!
  chainId: Int!
  version: Int!
  projectId: Int!
  suckerGroupId: String

  # Transaction
  txHash: String!
  timestamp: Int!
  from: String!

  # Burn details
  amount: BigInt!                # Total burned
  creditAmount: BigInt!          # Credits burned
  erc20Amount: BigInt!           # ERC20 burned

  # Relations
  project: Project
}

MintTokensEvent Entity

Token minting events (from payments).

type MintTokensEvent {
  id: String!
  chainId: Int!
  version: Int!
  projectId: Int!
  suckerGroupId: String

  # Transaction
  txHash: String!
  timestamp: Int!
  caller: String!
  from: String!
  logIndex: Int!

  # Mint details
  beneficiary: String!           # Token recipient
  beneficiaryTokenCount: BigInt! # Tokens to beneficiary
  reservedPercent: BigInt!       # Reserved rate
  tokenCount: BigInt!            # Total minted
  memo: String                   # Payment memo

  # Relations
  project: Project
}

ManualMintTokensEvent Entity

Manual token minting by project owner.

type ManualMintTokensEvent {
  id: String!
  chainId: Int!
  version: Int!
  projectId: Int!
  suckerGroupId: String

  # Transaction
  txHash: String!
  timestamp: Int!
  caller: String!
  from: String!
  logIndex: Int!

  # Mint details
  beneficiary: String!
  beneficiaryTokenCount: BigInt!
  reservedPercent: BigInt!
  tokenCount: BigInt!
  memo: String

  # Relations
  project: Project
}

ManualBurnEvent Entity

Manual token burning.

type ManualBurnEvent {
  id: String!
  chainId: Int!
  version: Int!
  projectId: Int!
  suckerGroupId: String

  # Transaction
  txHash: String!
  timestamp: Int!
  from: String!

  # Burn details
  amount: BigInt!
  creditAmount: BigInt!
  erc20Amount: BigInt!

  # Relations
  project: Project
}

MintNftEvent Entity

NFT minting events.

type MintNftEvent {
  id: String!
  chainId: Int!
  version: Int!
  projectId: Int!
  suckerGroupId: String

  # Transaction
  txHash: String!
  timestamp: Int!
  caller: String!
  from: String!
  logIndex: Int!

  # Mint details
  hook: String!                  # 721 hook address
  beneficiary: String!           # NFT recipient
  tierId: Int!                   # Tier minted
  tokenId: BigInt!               # Token ID
  totalAmountPaid: BigInt!       # Amount paid

  # Relations
  project: Project
  tier: NFTTier
  nft: NFT
}

DeployErc20Event Entity

ERC20 token deployment events.

type DeployErc20Event {
  id: String!
  chainId: Int!
  version: Int!
  projectId: Int!
  suckerGroupId: String

  # Transaction
  txHash: String!
  timestamp: Int!
  caller: String!
  from: String!
  logIndex: Int!

  # Token details
  symbol: String!                # Token symbol
  name: String!                  # Token name
  token: String!                 # Token address

  # Relations
  project: Project
}

ProjectCreateEvent Entity

Project creation events.

type ProjectCreateEvent {
  id: String!
  chainId: Int!
  version: Int!
  projectId: Int!
  suckerGroupId: String

  # Transaction
  txHash: String!
  timestamp: Int!
  caller: String!
  from: String!
  logIndex: Int!

  # Relations
  project: Project
}

AddToBalanceEvent Entity

Direct balance addition events.

type AddToBalanceEvent {
  id: String!
  chainId: Int!
  version: Int!
  projectId: Int!
  suckerGroupId: String

  # Transaction
  txHash: String!
  timestamp: Int!
  caller: String!
  from: String!
  logIndex: Int!

  # Details
  amount: BigInt!                # Amount added
  memo: String                   # Memo
  metadata: String               # Additional metadata
  returnedFees: BigInt           # Fees returned

  # Relations
  project: Project
}

SendPayoutToSplitEvent Entity

Individual split payout events.

type SendPayoutToSplitEvent {
  id: String!
  chainId: Int!
  version: Int!
  projectId: Int!
  suckerGroupId: String

  # Transaction
  txHash: String!
  timestamp: Int!
  caller: String!
  from: String!
  logIndex: Int!

  # Split details
  rulesetId: Int!
  group: BigInt!                 # Split group
  beneficiary: String!           # Split recipient
  splitProjectId: Int            # If split to project
  hook: String                   # Split hook if any

  # Amounts
  amount: BigInt!                # Gross amount
  netAmount: BigInt!             # After fees
  amountUsd: BigInt!
  percent: Int!                  # Split percent
  lockedUntil: BigInt            # Lock timestamp
  preferAddToBalance: Boolean!   # Add to balance vs pay

  # Relations
  project: Project
}

SendReservedTokensToSplitEvent Entity

Reserved token distribution to individual split.

type SendReservedTokensToSplitEvent {
  id: String!
  chainId: Int!
  version: Int!
  projectId: Int!
  suckerGroupId: String

  # Transaction
  txHash: String!
  timestamp: Int!
  caller: String!
  from: String!
  logIndex: Int!

  # Split details
  rulesetId: Int!
  groupId: BigInt!
  beneficiary: String!
  splitProjectId: Int
  hook: String
  tokenCount: BigInt!            # Tokens sent
  percent: Int!
  lockedUntil: BigInt
  preferAddToBalance: Boolean!

  # Relations
  project: Project
}

SendReservedTokensToSplitsEvent Entity

Batch reserved token distribution event.

type SendReservedTokensToSplitsEvent {
  id: String!
  chainId: Int!
  version: Int!
  projectId: Int!
  suckerGroupId: String

  # Transaction
  txHash: String!
  timestamp: Int!
  caller: String!
  from: String!
  logIndex: Int!

  # Distribution details
  rulesetId: Int!
  rulesetCycleNumber: Int!
  owner: String!                 # Project owner
  tokenCount: BigInt!            # Total distributed
  leftoverAmount: BigInt!        # Remaining after splits

  # Relations
  project: Project
}

AutoIssueEvent Entity

Auto-issuance events (revnet stage transitions).

type AutoIssueEvent {
  id: String!
  chainId: Int!
  version: Int!
  projectId: Int!

  # Transaction
  txHash: String!
  timestamp: Int!
  caller: String!
  from: String!
  logIndex: Int!

  # Issuance details
  stageId: BigInt!               # Revnet stage
  beneficiary: String!           # Token recipient
  count: BigInt!                 # Tokens issued

  # Relations
  project: Project
}

StoreAutoIssuanceAmountEvent Entity

Auto-issuance configuration events.

type StoreAutoIssuanceAmountEvent {
  id: String!
  chainId: Int!
  version: Int!
  projectId: Int!

  # Transaction
  txHash: String!
  timestamp: Int!
  caller: String!
  from: String!
  logIndex: Int!

  # Config details
  stageId: BigInt!               # Revnet stage
  beneficiary: String!           # Configured recipient
  count: BigInt!                 # Amount to auto-issue

  # Relations
  project: Project
}

DecorateBannyEvent Entity

Banny NFT decoration events.

type DecorateBannyEvent {
  id: String!
  chainId: Int!
  version: Int!

  # Transaction
  txHash: String!
  timestamp: Int!
  caller: String!
  from: String!
  logIndex: Int!

  # Decoration details
  bannyBodyId: BigInt!           # Banny being decorated
  outfitIds: [BigInt!]!          # Outfit NFT IDs
  backgroundId: BigInt           # Background NFT ID
  tokenUri: String               # Updated token URI
  tokenUriMetadata: JSON         # Parsed metadata

  # Relations
  bannyNft: NFT
}

CashOutTaxSnapshot Entity

Historical cash-out tax rate snapshots.

type CashOutTaxSnapshot {
  chainId: Int!
  projectId: Int!
  suckerGroupId: String
  version: Int!

  # Snapshot period
  start: BigInt!                 # Period start timestamp
  duration: BigInt!              # Period duration
  rulesetId: BigInt!             # Ruleset ID

  # Tax rate
  cashOutTax: Int!               # Tax rate (basis points)
}

ParticipantSnapshot Entity (GraphQL)

Historical participant balance snapshots via GraphQL (alternative to REST endpoint).

type ParticipantSnapshot {
  chainId: Int!
  projectId: Int!
  suckerGroupId: String
  version: Int!

  # Snapshot point
  block: Int!
  timestamp: Int!

  # Participant
  address: String!

  # Balances at snapshot
  balance: BigInt!
  creditBalance: BigInt!
  erc20Balance: BigInt!
  volume: BigInt!
  volumeUsd: BigInt!
}

Critical Concepts

Project Identity

A Juicebox project is uniquely identified by three fields: projectId + chainId + version.

This is crucial because:

  • V4 and V5 are completely different protocols. Project #64 on Ethereum V4 is NOT the same project as Project #64 on Ethereum V5.
  • The same projectId can exist on multiple chains (via suckers/omnichain), but those ARE the same project.
  • Always include version when querying or displaying projects.
// WRONG: Groups V4 and V5 together
const groupKey = `${project.projectId}-${project.chainId}`;

// CORRECT: Keeps V4 and V5 separate
const groupKey = `${project.projectId}-v${project.version}`;

Multi-Chain Grouping

When displaying "top projects" or aggregating stats:

  • Same projectId + version across chains → Group together (same project via suckers)
  • Same projectId, different version → Keep separate (completely different projects)
// Group projects by projectId + version (V4 and V5 are different projects!)
const grouped = new Map();

for (const project of projects) {
  const groupKey = `${project.projectId}-v${project.version || 4}`;
  const existing = grouped.get(groupKey);

  if (existing) {
    // Add chain to existing group
    if (!existing.chainIds.includes(project.chainId)) {
      existing.chainIds.push(project.chainId);
    }
    // Sum volumes
    existing.totalVolumeUsd += parseFloat(project.volumeUsd || '0');
  } else {
    grouped.set(groupKey, {
      ...project,
      chainIds: [project.chainId],
      totalVolumeUsd: parseFloat(project.volumeUsd || '0')
    });
  }
}

USD Value Formatting

The volumeUsd, amountUsd, and similar fields use 18 decimal format (like wei). You must convert properly:

function formatVolumeUsd(volumeUsd) {
  if (!volumeUsd || volumeUsd === '0') return '$0';

  try {
    // volumeUsd comes in 18 decimal format
    // Use BigInt to avoid precision loss on large numbers
    const raw = BigInt(volumeUsd.split('.')[0]);
    const usd = Number(raw / BigInt(1e12)) / 1e6; // Divide in steps

    if (usd >= 1_000_000) return `$${(usd / 1_000_000).toFixed(1)}M`;
    if (usd >= 1_000) return `$${(usd / 1_000).toFixed(1)}k`;
    if (usd >= 1) return `$${usd.toFixed(0)}`;
    return `$${usd.toFixed(2)}`;
  } catch {
    return '$0';
  }
}

Warning: Do NOT use parseFloat() directly on volumeUsd for large values—JavaScript loses precision beyond ~15 digits.

Filtering by Version

When querying projects, filter by version to avoid mixing V4 and V5 data:

# Get only V5 projects
query V5Projects($limit: Int!) {
  projects(
    where: { version: 5 }
    orderBy: "volumeUsd"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      projectId
      chainId
      version
      name
      volumeUsd
    }
  }
}

To display both versions, query them separately and handle grouping in your application.


Query Examples

Get Single Project

query GetProject($projectId: Int!, $chainId: Int!) {
  project(projectId: $projectId, chainId: $chainId) {
    id
    name
    handle
    owner
    balance
    volume
    volumeUsd
    tokenSupply
    paymentsCount
    contributorsCount
    suckerGroupId
  }
}

Get Participant (Token Holder)

query GetParticipant($projectId: Int!, $chainId: Int!, $address: String!) {
  participant(projectId: $projectId, chainId: $chainId, address: $address) {
    balance
    creditBalance
    erc20Balance
    volume
    volumeUsd
    paymentsCount
  }
}

Get Sucker Group (Omnichain Totals)

query GetSuckerGroup($id: String!) {
  suckerGroup(id: $id) {
    id
    volume
    volumeUsd
    balance
    tokenSupply
    paymentsCount
    contributorsCount
    projects_rel {
      projectId
      chainId
      name
      balance
      volume
    }
  }
}

List Projects

query ListProjects($chainId: Int, $version: Int, $limit: Int!, $offset: Int!) {
  projects(
    where: { chainId: $chainId, version: $version }
    orderBy: "volumeUsd"
    orderDirection: "desc"
    limit: $limit
    offset: $offset
  ) {
    items {
      projectId
      chainId
      version
      name
      handle
      volumeUsd
      balance
      paymentsCount
    }
    totalCount
  }
}

List Recent Payments

query ListPayments($projectId: Int!, $chainId: Int!, $limit: Int!) {
  payEvents(
    where: { projectId: $projectId, chainId: $chainId }
    orderBy: "timestamp"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      timestamp
      txHash
      from
      beneficiary
      amount
      amountUsd
      memo
      newlyIssuedTokenCount
    }
  }
}
query ListParticipants($projectId: Int!, $chainId: Int!, $limit: Int!) {
  participants(
    where: { projectId: $projectId, chainId: $chainId }
    orderBy: "balance"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      address
      balance
      creditBalance
      erc20Balance
      volume
      paymentsCount
    }
    totalCount
  }
}

Get Trending Projects

query TrendingProjects($limit: Int!) {
  projects(
    orderBy: "trendingScore"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      projectId
      chainId
      name
      handle
      trendingScore
      trendingVolume
      trendingPaymentsCount
    }
  }
}

List Cash Out Events

query ListCashOuts($projectId: Int!, $chainId: Int!, $limit: Int!) {
  cashOutEvents(
    where: { projectId: $projectId, chainId: $chainId }
    orderBy: "timestamp"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      timestamp
      txHash
      holder
      beneficiary
      cashOutCount
      reclaimAmount
      reclaimAmountUsd
    }
  }
}

List NFTs for Project

query ListNFTs($projectId: Int!, $chainId: Int!, $limit: Int!) {
  nfts(
    where: { projectId: $projectId, chainId: $chainId }
    orderBy: "createdAt"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      tokenId
      tierId
      tierCategory
      owner
      createdAt
      tokenUri
    }
  }
}

Get Unified Activity Feed

The most powerful query for building activity feeds. Returns all event types in a single query.

query GetActivityFeed($projectId: Int!, $chainId: Int!, $limit: Int!) {
  activityEvents(
    where: { projectId: $projectId, chainId: $chainId }
    orderBy: "timestamp"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      id
      timestamp
      txHash
      from
      type                        # Discriminator for event type

      # Include fields from each possible event type
      payEvent {
        amount
        amountUsd
        beneficiary
        memo
      }
      cashOutTokensEvent {
        cashOutCount
        reclaimAmount
        holder
      }
      mintNftEvent {
        tierId
        tokenId
      }
      sendPayoutsEvent {
        amount
        amountPaidOut
        fee
      }
      borrowLoanEvent {
        borrowAmount
        collateral
      }
    }
  }
}

Get Omnichain Activity Feed

Query activity across all chains for a sucker group.

query GetOmnichainActivity($suckerGroupId: String!, $limit: Int!) {
  activityEvents(
    where: { suckerGroupId: $suckerGroupId }
    orderBy: "timestamp"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      chainId
      timestamp
      type
      txHash
      from
      payEvent { amount, memo }
      cashOutTokensEvent { cashOutCount, reclaimAmount }
    }
  }
}

List Active Loans

query ListLoans($projectId: Int!, $chainId: Int!, $limit: Int!) {
  loans(
    where: { projectId: $projectId, chainId: $chainId }
    orderBy: "createdAt"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      id
      borrowAmount
      collateral
      prepaidDuration
      prepaidFeePercent
      owner
      beneficiary
      createdAt
    }
    totalCount
  }
}

Get Loan by ID

query GetLoan($id: BigInt!) {
  loan(id: $id) {
    id
    projectId
    chainId
    borrowAmount
    collateral
    sourceFeeAmount
    prepaidDuration
    prepaidFeePercent
    beneficiary
    owner
    token
    terminal
    tokenUri
    createdAt
  }
}

Get Wallet Portfolio

query GetWallet($address: String!) {
  wallet(address: $address) {
    address
    volume
    volumeUsd
    lastPaidTimestamp
    participants(limit: 100) {
      items {
        projectId
        chainId
        balance
        volume
      }
    }
    nfts(limit: 50) {
      items {
        projectId
        chainId
        tokenId
        tierId
      }
    }
  }
}

List NFT Tiers

query ListNFTTiers($projectId: Int!, $chainId: Int!, $limit: Int!) {
  nftTiers(
    where: { projectId: $projectId, chainId: $chainId }
    orderBy: "tierId"
    orderDirection: "asc"
    limit: $limit
  ) {
    items {
      tierId
      price
      initialSupply
      remainingSupply
      category
      votingUnits
      resolvedUri
      metadata
      svg
    }
  }
}

Get NFT Hook Details

query GetNFTHook($projectId: Int!, $chainId: Int!) {
  nftHooks(
    where: { projectId: $projectId, chainId: $chainId }
    limit: 1
  ) {
    items {
      address
      name
      symbol
      nftTiers(limit: 100) {
        items {
          tierId
          price
          remainingSupply
        }
      }
    }
  }
}

Get Historical Project Snapshots

query GetProjectHistory($projectId: Int!, $chainId: Int!, $limit: Int!) {
  projectMoments(
    where: { projectId: $projectId, chainId: $chainId }
    orderBy: "timestamp"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      block
      timestamp
      volume
      volumeUsd
      balance
      trendingScore
    }
  }
}

Get Cross-Chain Bridge Transactions

query GetSuckerTransactions($suckerGroupId: String!, $limit: Int!) {
  suckerTransactions(
    where: { suckerGroupId: $suckerGroupId }
    orderBy: "createdAt"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      index
      chainId
      peerChainId
      beneficiary
      projectTokenCount
      terminalTokenAmount
      status
      createdAt
    }
  }
}

List Payout Events

query ListPayouts($projectId: Int!, $chainId: Int!, $limit: Int!) {
  sendPayoutsEvents(
    where: { projectId: $projectId, chainId: $chainId }
    orderBy: "timestamp"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      timestamp
      txHash
      caller
      rulesetCycleNumber
      amount
      amountPaidOut
      fee
      netLeftoverPayoutAmount
    }
  }
}

List Allowance Usage

query ListAllowanceUsage($projectId: Int!, $chainId: Int!, $limit: Int!) {
  useAllowanceEvents(
    where: { projectId: $projectId, chainId: $chainId }
    orderBy: "timestamp"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      timestamp
      txHash
      caller
      beneficiary
      amount
      netAmount
      rulesetCycleNumber
    }
  }
}

List Loan Events (Borrow/Repay/Liquidate)

query ListBorrowEvents($projectId: Int!, $chainId: Int!, $limit: Int!) {
  borrowLoanEvents(
    where: { projectId: $projectId, chainId: $chainId }
    orderBy: "timestamp"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      timestamp
      txHash
      borrowAmount
      collateral
      prepaidDuration
      beneficiary
    }
  }
}

query ListRepayEvents($projectId: Int!, $chainId: Int!, $limit: Int!) {
  repayLoanEvents(
    where: { projectId: $projectId, chainId: $chainId }
    orderBy: "timestamp"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      timestamp
      txHash
      loanId
      repayBorrowAmount
      collateralCountToReturn
    }
  }
}

query ListLiquidations($projectId: Int!, $chainId: Int!, $limit: Int!) {
  liquidateLoanEvents(
    where: { projectId: $projectId, chainId: $chainId }
    orderBy: "timestamp"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      timestamp
      txHash
      borrowAmount
      collateral
    }
  }
}

List Token Burns

query ListBurns($projectId: Int!, $chainId: Int!, $limit: Int!) {
  burnEvents(
    where: { projectId: $projectId, chainId: $chainId }
    orderBy: "timestamp"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      timestamp
      txHash
      from
      amount
      creditAmount
      erc20Amount
    }
  }
}

List NFT Mints

query ListNFTMints($projectId: Int!, $chainId: Int!, $limit: Int!) {
  mintNftEvents(
    where: { projectId: $projectId, chainId: $chainId }
    orderBy: "timestamp"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      timestamp
      txHash
      beneficiary
      tierId
      tokenId
      totalAmountPaid
    }
  }
}

Get Permission Holders

query ListPermissionHolders($projectId: Int!, $chainId: Int!) {
  permissionHolders(
    where: { projectId: $projectId, chainId: $chainId }
    limit: 100
  ) {
    items {
      account
      operator
      permissions
      isRevnetOperator
    }
  }
}

List Project Creations

query ListProjectCreations($chainId: Int!, $limit: Int!) {
  projectCreateEvents(
    where: { chainId: $chainId }
    orderBy: "timestamp"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      timestamp
      txHash
      projectId
      caller
      project {
        name
        handle
        owner
      }
    }
  }
}

List ERC20 Deployments

query ListERC20Deployments($chainId: Int!, $limit: Int!) {
  deployErc20Events(
    where: { chainId: $chainId }
    orderBy: "timestamp"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      timestamp
      txHash
      projectId
      name
      symbol
      token
    }
  }
}

Get Cash Out Tax History

query GetCashOutTaxHistory($projectId: Int!, $chainId: Int!, $limit: Int!) {
  cashOutTaxSnapshots(
    where: { projectId: $projectId, chainId: $chainId }
    orderBy: "start"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      start
      duration
      rulesetId
      cashOutTax
    }
  }
}

Get Participant History (GraphQL Snapshots)

query GetParticipantHistory(
  $projectId: Int!,
  $chainId: Int!,
  $address: String!,
  $limit: Int!
) {
  participantSnapshots(
    where: { projectId: $projectId, chainId: $chainId, address: $address }
    orderBy: "timestamp"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      block
      timestamp
      balance
      creditBalance
      erc20Balance
      volume
      volumeUsd
    }
  }
}

List Reserved Token Distributions

query ListReservedDistributions($projectId: Int!, $chainId: Int!, $limit: Int!) {
  sendReservedTokensToSplitsEvents(
    where: { projectId: $projectId, chainId: $chainId }
    orderBy: "timestamp"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      timestamp
      txHash
      rulesetCycleNumber
      tokenCount
      leftoverAmount
      owner
    }
  }
}

List Split Payouts

query ListSplitPayouts($projectId: Int!, $chainId: Int!, $limit: Int!) {
  sendPayoutToSplitEvents(
    where: { projectId: $projectId, chainId: $chainId }
    orderBy: "timestamp"
    orderDirection: "desc"
    limit: $limit
  ) {
    items {
      timestamp
      txHash
      beneficiary
      splitProjectId
      amount
      netAmount
      percent
    }
  }
}

Filtering

The where clause supports these operators:

where: {
  # Exact match
  projectId: 1
  chainId: 1

  # Multiple values (OR)
  chainId_in: [1, 10, 8453]

  # Comparison operators
  balance_gt: "1000000000000000000"
  balance_gte: "1000000000000000000"
  balance_lt: "100000000000000000000"
  balance_lte: "100000000000000000000"
  timestamp_gte: 1704067200

  # Text search
  name_contains: "dao"
  handle_starts_with: "jb"
}

Sorting

orderBy: "volume"           # Field to sort by
orderDirection: "desc"      # "asc" or "desc"

Sortable Fields by Entity:

EntitySortable Fields
Projectvolume, balance, tokenSupply, paymentsCount, createdAt, trendingScore
Participantbalance, volume, paymentsCount
PayEventtimestamp, amount
CashOutEventtimestamp, reclaimAmount
NFTcreatedAt, tokenId
ActivityEventtimestamp
LoancreatedAt, borrowAmount, collateral
Walletvolume, volumeUsd, lastPaidTimestamp
NFTTiertierId, price, createdAt
ProjectMomenttimestamp, block
SuckerTransactioncreatedAt
SendPayoutsEventtimestamp, amount
UseAllowanceEventtimestamp, amount
BorrowLoanEventtimestamp, borrowAmount
RepayLoanEventtimestamp, repayBorrowAmount
LiquidateLoanEventtimestamp
BurnEventtimestamp, amount
MintTokensEventtimestamp, tokenCount
MintNftEventtimestamp, tokenId
DeployErc20Eventtimestamp
ProjectCreateEventtimestamp
SendPayoutToSplitEventtimestamp, amount
CashOutTaxSnapshotstart
ParticipantSnapshottimestamp, block

Pagination

All list queries support pagination:

query PaginatedPayments(
  $projectId: Int!,
  $chainId: Int!,
  $limit: Int!,
  $offset: Int!
) {
  payEvents(
    where: { projectId: $projectId, chainId: $chainId }
    limit: $limit
    offset: $offset
  ) {
    items { ... }
    totalCount
  }
}

Parameters:

  • limit: Max items to return (default: 100, max: 1000)
  • offset: Number of items to skip

Special Endpoints

Participant Snapshots

Get historical participant balances at a specific timestamp. Useful for governance snapshots and airdrops.

POST https://bendystraw.xyz/{API_KEY}/participants

Request:

{
  "suckerGroupId": "0x...",
  "timestamp": 1704067200
}

Response:

{
  "participants": [
    {
      "address": "0x...",
      "balance": "1000000000000000000000",
      "chains": {
        "1": "600000000000000000000",
        "10": "400000000000000000000"
      }
    }
  ]
}

Complete JavaScript Example

const BENDYSTRAW_URL = 'https://bendystraw.xyz';
const API_KEY = process.env.BENDYSTRAW_API_KEY;

/**
 * Execute GraphQL query
 */
async function query(graphql, variables = {}) {
  const response = await fetch(`${BENDYSTRAW_URL}/${API_KEY}/graphql`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ query: graphql, variables })
  });

  const result = await response.json();

  if (result.errors) {
    throw new Error(result.errors[0].message);
  }

  return result.data;
}

/**
 * Get project stats
 */
async function getProject(projectId, chainId) {
  const data = await query(`
    query($projectId: Int!, $chainId: Int!) {
      project(projectId: $projectId, chainId: $chainId) {
        name
        handle
        owner
        balance
        volume
        volumeUsd
        tokenSupply
        paymentsCount
        contributorsCount
        suckerGroupId
      }
    }
  `, { projectId, chainId });

  return data.project;
}

/**
 * Get omnichain totals for sucker group
 */
async function getOmnichainStats(suckerGroupId) {
  const data = await query(`
    query($id: String!) {
      suckerGroup(id: $id) {
        volume
        volumeUsd
        balance
        tokenSupply
        contributorsCount
        projects_rel {
          chainId
          name
          balance
          volume
        }
      }
    }
  `, { id: suckerGroupId });

  return data.suckerGroup;
}

/**
 * Get recent payments
 */
async function getRecentPayments(projectId, chainId, limit = 20) {
  const data = await query(`
    query($projectId: Int!, $chainId: Int!, $limit: Int!) {
      payEvents(
        where: { projectId: $projectId, chainId: $chainId }
        orderBy: "timestamp"
        orderDirection: "desc"
        limit: $limit
      ) {
        items {
          timestamp
          from
          beneficiary
          amount
          amountUsd
          memo
          newlyIssuedTokenCount
        }
      }
    }
  `, { projectId, chainId, limit });

  return data.payEvents.items;
}

/**
 * Get top token holders
 */
async function getTopHolders(projectId, chainId, limit = 100) {
  const data = await query(`
    query($projectId: Int!, $chainId: Int!, $limit: Int!) {
      participants(
        where: { projectId: $projectId, chainId: $chainId }
        orderBy: "balance"
        orderDirection: "desc"
        limit: $limit
      ) {
        items {
          address
          balance
          creditBalance
          erc20Balance
          volume
        }
        totalCount
      }
    }
  `, { projectId, chainId, limit });

  return {
    holders: data.participants.items,
    total: data.participants.totalCount
  };
}

/**
 * Get participant balance
 */
async function getParticipant(projectId, chainId, address) {
  const data = await query(`
    query($projectId: Int!, $chainId: Int!, $address: String!) {
      participant(projectId: $projectId, chainId: $chainId, address: $address) {
        balance
        creditBalance
        erc20Balance
        volume
        volumeUsd
        paymentsCount
      }
    }
  `, { projectId, chainId, address });

  return data.participant;
}

/**
 * Get trending projects
 */
async function getTrendingProjects(chainId = null, limit = 10) {
  const where = chainId ? { chainId } : {};

  const data = await query(`
    query($where: ProjectWhereInput, $limit: Int!) {
      projects(
        where: $where
        orderBy: "trendingScore"
        orderDirection: "desc"
        limit: $limit
      ) {
        items {
          projectId
          chainId
          name
          handle
          trendingScore
          trendingVolume
          trendingPaymentsCount
        }
      }
    }
  `, { where, limit });

  return data.projects.items;
}

/**
 * Get historical snapshot for governance
 */
async function getSnapshot(suckerGroupId, timestamp) {
  const response = await fetch(`${BENDYSTRAW_URL}/${API_KEY}/participants`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ suckerGroupId, timestamp })
  });

  return await response.json();
}

/**
 * Get unified activity feed (all event types)
 */
async function getActivityFeed(projectId, chainId, limit = 50) {
  const data = await query(`
    query($projectId: Int!, $chainId: Int!, $limit: Int!) {
      activityEvents(
        where: { projectId: $projectId, chainId: $chainId }
        orderBy: "timestamp"
        orderDirection: "desc"
        limit: $limit
      ) {
        items {
          id
          timestamp
          txHash
          from
          type
          payEvent { amount, amountUsd, beneficiary, memo }
          cashOutTokensEvent { cashOutCount, reclaimAmount, holder }
          mintNftEvent { tierId, tokenId }
          sendPayoutsEvent { amount, amountPaidOut, fee }
          borrowLoanEvent { borrowAmount, collateral }
        }
      }
    }
  `, { projectId, chainId, limit });

  return data.activityEvents.items;
}

/**
 * Get active loans for a project
 */
async function getLoans(projectId, chainId, limit = 100) {
  const data = await query(`
    query($projectId: Int!, $chainId: Int!, $limit: Int!) {
      loans(
        where: { projectId: $projectId, chainId: $chainId }
        orderBy: "createdAt"
        orderDirection: "desc"
        limit: $limit
      ) {
        items {
          id
          borrowAmount
          collateral
          prepaidDuration
          prepaidFeePercent
          owner
          beneficiary
          createdAt
        }
        totalCount
      }
    }
  `, { projectId, chainId, limit });

  return {
    loans: data.loans.items,
    total: data.loans.totalCount
  };
}

/**
 * Get wallet portfolio across all projects
 */
async function getWalletPortfolio(address) {
  const data = await query(`
    query($address: String!) {
      wallet(address: $address) {
        address
        volume
        volumeUsd
        lastPaidTimestamp
        participants(limit: 100) {
          items {
            projectId
            chainId
            balance
            volume
            project { name, handle }
          }
        }
      }
    }
  `, { address });

  return data.wallet;
}

/**
 * Get NFT tier details with metadata
 */
async function getNFTTiers(projectId, chainId) {
  const data = await query(`
    query($projectId: Int!, $chainId: Int!) {
      nftTiers(
        where: { projectId: $projectId, chainId: $chainId }
        orderBy: "tierId"
        orderDirection: "asc"
        limit: 100
      ) {
        items {
          tierId
          price
          initialSupply
          remainingSupply
          category
          votingUnits
          resolvedUri
          metadata
          svg
        }
      }
    }
  `, { projectId, chainId });

  return data.nftTiers.items;
}

/**
 * Get historical project snapshots for charting
 */
async function getProjectHistory(projectId, chainId, limit = 100) {
  const data = await query(`
    query($projectId: Int!, $chainId: Int!, $limit: Int!) {
      projectMoments(
        where: { projectId: $projectId, chainId: $chainId }
        orderBy: "timestamp"
        orderDirection: "desc"
        limit: $limit
      ) {
        items {
          block
          timestamp
          volume
          volumeUsd
          balance
          trendingScore
        }
      }
    }
  `, { projectId, chainId, limit });

  return data.projectMoments.items;
}

// Example usage
async function main() {
  // Get project on Ethereum mainnet
  const project = await getProject(1, 1);
  console.log(`${project.name}: ${project.balance} wei balance`);

  // If omnichain, get aggregated stats
  if (project.suckerGroupId) {
    const omni = await getOmni
README.md

No README available.

Permissions & Security

Security level L1: Low-risk skills with minimal permissions. Review inputs and outputs before running in production.

Requirements

  • OpenClaw CLI installed and configured.
  • Language: Markdown
  • License: MIT
  • Topics:

FAQ

How do I install jb-bendystraw?

Run openclaw add @mejango/juicy:jb-bendystraw in your terminal. This installs jb-bendystraw into your OpenClaw Skills catalog.

Does this skill run locally or in the cloud?

OpenClaw Skills execute locally by default. Review the SKILL.md and permissions before running any skill.

Where can I verify the source code?

The source repository is available at https://github.com/openclaw/skills/tree/main/skills/mejango/juicy. Review commits and README documentation before installing.