jb-multi-currency

1
1
Source

Handle Juicebox V5 multi-currency projects (ETH vs USDC accounting). Use when: (1) building UI that displays currency labels (ETH vs USDC), (2) sending transactions that require currency parameter, (3) configuring fund access limits or accounting contexts for new rulesets, (4) querying project balance/surplus with correct token, (5) debugging "wrong currency" issues in payout or allowance transactions, (6) need currency code constants (NATIVE_CURRENCY=61166, USDC varies by chain), (7) cash out modal shows wrong return currency (ETH instead of USDC), (8) need shared chain constants (names, explorers) across multiple modals. Currency in JBAccountingContext is uint32(uint160(tokenAddress)), NOT 1 or 2. Covers baseCurrency detection, decimal handling, terminal accounting, currency codes, dynamic labels, cash out return display, and shared chain constants patterns.

Install

mkdir -p .claude/skills/jb-multi-currency && curl -L -o skill.zip "https://mcp.directory/api/skills/download/7520" && unzip -o skill.zip -d .claude/skills/jb-multi-currency && rm skill.zip

Installs to .claude/skills/jb-multi-currency

About this skill

Juicebox V5 Multi-Currency Support

Problem

Juicebox V5 projects can be denominated in either ETH (baseCurrency=1) or USD (baseCurrency=2). UI components and transactions must use the correct currency value, token address, and display labels. Hardcoding "ETH" or currency=1 causes failures for USDC-based projects.

Context / Trigger Conditions

  • UI shows "ETH" when project is USDC-based
  • Payout or allowance transaction fails silently
  • Fund access limits set with wrong currency
  • Currency mismatch between ruleset config and terminal accounting
  • Need to display correct currency symbol in forms

Solution

1. Detect Project Currency from Ruleset

// baseCurrency is in ruleset metadata
// 1 = ETH, 2 = USD (USDC)
const baseCurrency = ruleset?.metadata?.baseCurrency || 1
const currencyLabel = baseCurrency === 2 ? 'USDC' : 'ETH'

2. Use Dynamic Currency in Transactions

When calling terminal functions (sendPayoutsOf, useAllowanceOf), use the project's baseCurrency:

// WRONG - hardcoded
args: [projectId, token, amount, 1n, minTokensPaidOut, beneficiary]

// CORRECT - dynamic
args: [projectId, token, amount, BigInt(baseCurrency), minTokensPaidOut, beneficiary]

3. Fund Access Limits Currency

When queuing new rulesets or displaying limits, match the existing project currency. Use correct decimals: Query from ERC20.decimals() or use 18 for native tokens.

import { parseUnits } from 'viem'

// Get currency from existing config
const currency = existingConfig?.baseCurrency || 1

// Get decimals from the token contract (or 18 for native token)
// For USDC: 6 decimals. For native token (ETH): 18 decimals.
const decimals = await getTokenDecimals(tokenAddress, publicClient)

// Use in fund access limit configuration
const payoutLimits = [{
  amount: parseUnits(limitAmount, decimals).toString(),
  currency, // Match project's base currency (1 or 2)
}]

4. Two Different "currency" Concepts (CRITICAL)

Juicebox V5 has TWO different concepts called "currency" that are often confused:

FieldLocationValuesPurpose
baseCurrencyRuleset metadata1 = ETH, 2 = USDIssuance rate calculation
JBCurrencyAmount.currencyFund access limits1 = ETH, 2 = USDPayout/allowance limits
JBAccountingContext.currencyTerminal configuint32(uint160(token))Terminal accounting

The currency value in JBAccountingContext is NOT 1 or 2. It's uint32(uint160(tokenAddress)).

// NATIVE_TOKEN (ETH) - same on all chains - from JBConstants.NATIVE_TOKEN
const NATIVE_TOKEN = '0x000000000000000000000000000000000000EEEe'
const NATIVE_CURRENCY = 61166 // uint32(uint160(NATIVE_TOKEN)) = 0x0000EEEe

// USDC addresses and currency codes per chain
const USDC_CONFIG: Record<number, { address: string; currency: number }> = {
  1: {      // Ethereum
    address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
    currency: 909516616,
  },
  10: {     // Optimism
    address: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85',
    currency: 3530704773,
  },
  8453: {   // Base
    address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
    currency: 3169378579,
  },
  42161: {  // Arbitrum
    address: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
    currency: 1156540465,
  },
}

How to calculate currency from any token address:

const calculateCurrency = (tokenAddress: string): number => {
  // Take last 4 bytes of address as uint32
  return Number(BigInt(tokenAddress) & BigInt(0xFFFFFFFF))
}

5. Decimal Handling

General rule: Get decimals from ERC20.decimals() for any token. Native tokens (ETH, MATIC, etc.) always use 18 decimals.

Common cases:

  • Native token (ETH): 18 decimals
  • USDC: 6 decimals
  • Most ERC-20s: varies - always query decimals()
import { erc20Abi } from 'viem'

// For native token
const NATIVE_DECIMALS = 18

// For ERC-20 tokens - query the contract
const getTokenDecimals = async (tokenAddress: string, publicClient: PublicClient) => {
  if (tokenAddress === NATIVE_TOKEN) return NATIVE_DECIMALS
  return await publicClient.readContract({
    address: tokenAddress as `0x${string}`,
    abi: erc20Abi,
    functionName: 'decimals',
  })
}

// Shortcut for known tokens (use with caution)
const KNOWN_DECIMALS: Record<string, number> = {
  [NATIVE_TOKEN]: 18,
  // USDC on all chains uses 6 decimals
}

6. Terminal Accounting Contexts

When configuring terminals, set accounting context to match. The currency MUST be derived from the token address.

// ETH-based project
{
  terminal: JB_MULTI_TERMINAL,
  accountingContextsToAccept: [{
    token: NATIVE_TOKEN,        // 0x000000000000000000000000000000000000EEEe
    decimals: 18,
    currency: 61166,            // uint32(uint160(NATIVE_TOKEN)) = 0x0000EEEe
  }]
}

// USDC-based project (example: Ethereum)
{
  terminal: JB_MULTI_TERMINAL,
  accountingContextsToAccept: [{
    token: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
    decimals: 6,
    currency: 909516616,        // uint32(uint160(USDC_ADDRESS))
  }]
}

WRONG: Using currency: 1 for ETH or currency: 2 for USD RIGHT: Using the calculated uint32 value from the token address

Component Pattern

For React components that handle multiple currencies:

interface ChainData {
  chainId: number
  projectId: number
  baseCurrency: number // 1 = ETH, 2 = USD
  // ... other fields
}

function MyComponent({ chainData }: { chainData: ChainData }) {
  const baseCurrency = chainData?.baseCurrency || 1
  const currencyLabel = baseCurrency === 2 ? 'USDC' : 'ETH'

  return (
    <div>
      <span>Amount: {amount} {currencyLabel}</span>
      {/* Use currencyLabel throughout, never hardcode "ETH" */}
    </div>
  )
}

7. Cash Out Modal Currency Display

When cashing out, users burn project tokens and receive funds in the project's base currency. The modal must show the correct currency for the return amount:

interface CashOutModalProps {
  projectId: string
  tokenAmount: string      // Tokens being burned
  tokenSymbol: string      // e.g., "NANA", "REV"
  estimatedReturn: number  // Amount user will receive
  currencySymbol: 'ETH' | 'USDC'  // CRITICAL: matches project's base currency
}

function CashOutModal({
  tokenAmount,
  tokenSymbol,
  estimatedReturn,
  currencySymbol = 'ETH',  // Default to ETH for backwards compatibility
}: CashOutModalProps) {
  // Format decimals based on currency
  const decimals = currencySymbol === 'USDC' ? 2 : 4

  return (
    <div>
      <div>Burning: {tokenAmount} {tokenSymbol}</div>
      <div>You receive: ~{estimatedReturn.toFixed(decimals)} {currencySymbol}</div>
    </div>
  )
}

Key insight: The component receiving cash out data must pass currencySymbol based on the project's baseCurrency, not hardcode "ETH". For USDC-based projects, show "~5.00 USDC" not "~0.002 ETH".

8. Shared Chain Constants Pattern

Avoid duplicating chain info across modals. Create a shared constants file:

// constants/index.ts
export const CHAINS: Record<number, {
  name: string
  shortName: string
  explorerTx: string
  explorerAddress: string
}> = {
  1: {
    name: 'Ethereum',
    shortName: 'ETH',
    explorerTx: 'https://etherscan.io/tx/',
    explorerAddress: 'https://etherscan.io/address/',
  },
  10: {
    name: 'Optimism',
    shortName: 'OP',
    explorerTx: 'https://optimistic.etherscan.io/tx/',
    explorerAddress: 'https://optimistic.etherscan.io/address/',
  },
  8453: {
    name: 'Base',
    shortName: 'BASE',
    explorerTx: 'https://basescan.org/tx/',
    explorerAddress: 'https://basescan.org/address/',
  },
  42161: {
    name: 'Arbitrum',
    shortName: 'ARB',
    explorerTx: 'https://arbiscan.io/tx/',
    explorerAddress: 'https://arbiscan.io/address/',
  },
}

export const NATIVE_TOKEN = '0x000000000000000000000000000000000000EEEe' as const

Then import in modals:

import { CHAINS, NATIVE_TOKEN } from '../constants'

const chainInfo = CHAINS[chainId] || CHAINS[1]
const explorerLink = `${chainInfo.explorerTx}${txHash}`

Verification

  • USDC-based projects display "USDC" labels (not "ETH")
  • Cash out modals show return in correct currency (ETH or USDC)
  • Transactions use correct currency parameter
  • Fund access limits store correct currency
  • Balance queries use correct token address

Example

For Artizen (project 6 on Base):

  • baseCurrency = 2 (USD)
  • Display "USDC" in all labels
  • Use USDC token address for balance queries: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
  • Fund access limits use currency: 2

Common Mistakes

  1. Hardcoded labels: Using "ETH" string instead of currencyLabel
  2. Hardcoded currency: Using 1n instead of BigInt(baseCurrency)
  3. Wrong decimals: Using 18 decimals for USDC (should be 6)
  4. Token mismatch: Querying with NATIVE_TOKEN for USDC project

Notes

  • Default to ETH (baseCurrency=1) if not specified
  • JBSwapTerminal accepts any token and swaps to project's base currency
  • Cash out returns funds in the project's base currency
  • Price feeds convert between currencies when needed

References

  • JBMultiTerminal5_1: 0x52869db3d61dde1e391967f2ce5039ad0ecd371c
  • JBSwapTerminal: 0x0c02e48e55f4451a499e48a53595de55c40f3574
  • JBPrices: 0x6e92e3b5ce1e7a4344c6d27c0c54efd00df92fb6

fivem

openclaw

Fix, create, or validate FiveM server resources for QBCore/ESX (config.lua, fxmanifest.lua, items, housing/furniture, scripts, MLOs). Use when asked to debug resource errors, convert ESX↔QB, update fxmanifest versions, add items, or source scripts from GitHub. Also use for SSH key generation for SFTP access.

582395

a-stock-analysis

openclaw

A股实时行情与分时量能分析。获取沪深股票实时价格、涨跌、成交量,分析分时量能分布(早盘/尾盘放量)、主力动向(抢筹/出货信号)、涨停封单。支持持仓管理和盈亏分析。Use when: (1) 查询A股实时行情, (2) 分析主力资金动向, (3) 查看分时成交量分布, (4) 管理股票持仓, (5) 分析持仓盈亏。

802305

research-paper-writer

openclaw

Creates formal academic research papers following IEEE/ACM formatting standards with proper structure, citations, and scholarly writing style. Use when the user asks to write a research paper, academic paper, or conference paper on any topic.

85173

keyword-research

openclaw

Discovers high-value keywords with search intent analysis, difficulty assessment, and content opportunity mapping. Essential for starting any SEO or GEO content strategy.

465118

html-to-ppt

openclaw

Convert HTML/Markdown to PowerPoint presentations using Marp

36494

weread

openclaw

WeChat Reading (微信读书) CLI tool for fetching notes and highlights. Use when: (1) user asks about weread/微信读书 notes or highlights, (2) fetching today's or recent reading notes, (3) exporting book highlights, (4) managing reading bookshelf, (5) any task involving reading notes from WeChat Reading.

12186

You might also like

ui-ux-pro-max

nextlevelbuilder

"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."

3,2262,762

pdf-to-markdown

aliceisjustplaying

Convert entire PDF documents to clean, structured Markdown for full context loading. Use this skill when the user wants to extract ALL text from a PDF into context (not grep/search), when discussing or analyzing PDF content in full, when the user mentions "load the whole PDF", "bring the PDF into context", "read the entire PDF", or when partial extraction/grepping would miss important context. This is the preferred method for PDF text extraction over page-by-page or grep approaches.

4,2681,834

flutter-development

aj-geddes

Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.

2,2241,672

drawio-diagrams-enhanced

jgtolentino

Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.

2,3651,519

godot

bfollington

This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.

2,6721,284

nano-banana-pro

garg-aayush

Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.

2,0781,002