skills$openclaw/jb-v5-v51-contracts
mejango3.6k

by mejango

jb-v5-v51-contracts – OpenClaw Skill

jb-v5-v51-contracts is an OpenClaw Skills integration for devops workflows. |

3.6k stars1.0k forksSecurity L1
Updated Feb 7, 2026Created Feb 7, 2026devops

Skill Snapshot

namejb-v5-v51-contracts
description| OpenClaw Skills integration.
ownermejango
repositorymejango/juicypath: jb-v5-v51-contracts
languageMarkdown
licenseMIT
topics
securityL1
installopenclaw add @mejango/juicy:jb-v5-v51-contracts
last updatedFeb 7, 2026

Maintainer

mejango

mejango

Maintains jb-v5-v51-contracts in the OpenClaw Skills directory.

View GitHub profile
File Explorer
1 files
jb-v5-v51-contracts
SKILL.md
8.1 KB
SKILL.md

name: jb-v5-v51-contracts description: | Juicebox V5 vs V5.1 contract version separation rules. Use when: (1) determining which contracts to use for a project, (2) versioned contracts show unexpected behavior, (3) transactions fail with "invalid terminal" or similar errors, (4) deploying new projects vs interacting with existing projects. CRITICAL: Versioned contracts must never mix.

Juicebox V5 vs V5.1 Contract Separation

Problem

Juicebox has two contract versions: V5 (original) and V5.1 (upgraded). When contracts have both versions, mixing them causes transactions to fail. A project using JBController5_1 MUST use JBMultiTerminal5_1, not the V5 terminal.

Context / Trigger Conditions

  • Deploying new projects or terminals
  • Transactions failing with "invalid terminal" or permission errors
  • Querying rulesets returns unexpected data
  • Paying a project fails despite correct addresses
  • Determining which contracts to use for a given project

Solution

The Rule

When a contract has both V5 and V5.1 versions, you MUST use matching versions.

Contracts that only have ONE version (no 5_1 variant) work with both V5 and V5.1 projects.

Detecting Project Version (CRITICAL)

IMPORTANT: Some non-revnet projects also use V5.0 contracts. You cannot assume that a project is on V5.1 just because it's not a revnet. The ONLY authoritative way to determine which contracts a project uses is to query JBDirectory.controllerOf().

Always check JBDirectory.controllerOf() to get the actual controller address, then compare against known V5 and V5.1 controller addresses.

const JB_DIRECTORY = '0x0061e516886a0540f63157f112c0588ee0651dcf'
const JB_CONTROLLER_V5 = '0x27da30646502e2f642be5281322ae8c394f7668a'
const JB_CONTROLLER_V5_1 = '0xf3cc99b11bd73a2e3b8815fb85fe0381b29987e1'

// Query JBDirectory for the project's controller
const controller = await publicClient.readContract({
  address: JB_DIRECTORY,
  abi: JB_DIRECTORY_ABI,
  functionName: 'controllerOf',
  args: [BigInt(projectId)],
})

// Determine version by comparing controller address
const isV5 = controller.toLowerCase() === JB_CONTROLLER_V5.toLowerCase()
const isV5_1 = controller.toLowerCase() === JB_CONTROLLER_V5_1.toLowerCase()

if (isV5) {
  // Use V5.0 contracts (JBMultiTerminal, JBRulesets, etc.)
} else if (isV5_1) {
  // Use V5.1 contracts (JBMultiTerminal5_1, JBRulesets5_1, etc.)
} else {
  // Unknown controller - handle edge case
}

Why not check owner? While revnets are owned by REVDeployer and always use V5.0, some regular projects deployed before V5.1 also use V5.0. Checking the owner only tells you if it's a revnet, not which contract version the project actually uses.

Contract Address Reference

Shared Contracts (work with BOTH V5 and V5.1)

ContractAddress
JBProjects0x885f707efa18d2cb12f05a3a8eba6b4b26c8c1d4
JBTokens0x4d0edd347fb1fa21589c1e109b3474924be87636
JBDirectory0x0061e516886a0540f63157f112c0588ee0651dcf
JBSplits0x7160a322fea44945a6ef9adfd65c322258df3c5e
JBFundAccessLimits0x3a46b21720c8b70184b0434a2293b2fdcc497ce7
JBPermissions0xba948dab74e875b19cf0e2ca7a4546c0c2defc40
JBPrices0x6e92e3b5ce1e7a4344c6d27c0c54efd00df92fb6
JBFeelessAddresses0xf76f7124f73abc7c30b2f76121afd4c52be19442

V5.1 Contracts (for NEW projects)

ContractAddress
JBController5_10xf3cc99b11bd73a2e3b8815fb85fe0381b29987e1
JBMultiTerminal5_10x52869db3d61dde1e391967f2ce5039ad0ecd371c
JBRulesets5_10xd4257005ca8d27bbe11f356453b0e4692414b056
JBTerminalStore5_10x82239c5a21f0e09573942caa41c580fa36e27071
JBOmnichainDeployer5_10x587bf86677ec0d1b766d9ba0d7ac2a51c6c2fc71

V5 Contracts (for REVNETS)

ContractAddress
JBController0x27da30646502e2f642be5281322ae8c394f7668a
JBMultiTerminal0x2db6d704058e552defe415753465df8df0361846
JBRulesets0x6292281d69c3593fcf6ea074e5797341476ab428
REVDeployer0x2ca27bde7e7d33e353b44c27acfcf6c78dde251d

Code Pattern

// Known controller addresses (same on all chains via CREATE2)
const CONTROLLERS = {
  V5: '0x27da30646502e2f642be5281322ae8c394f7668a',
  V5_1: '0xf3cc99b11bd73a2e3b8815fb85fe0381b29987e1',
}

// Versioned contracts by version
const VERSIONED_CONTRACTS = {
  V5: {
    controller: '0x27da30646502e2f642be5281322ae8c394f7668a',  // JBController
    terminal: '0x2db6d704058e552defe415753465df8df0361846',    // JBMultiTerminal
    rulesets: '0x6292281d69c3593fcf6ea074e5797341476ab428',    // JBRulesets
  },
  V5_1: {
    controller: '0xf3cc99b11bd73a2e3b8815fb85fe0381b29987e1',  // JBController5_1
    terminal: '0x52869db3d61dde1e391967f2ce5039ad0ecd371c',    // JBMultiTerminal5_1
    rulesets: '0xd4257005ca8d27bbe11f356453b0e4692414b056',    // JBRulesets5_1
  },
}

// Shared contracts work with any project
const SHARED = {
  directory: '0x0061e516886a0540f63157f112c0588ee0651dcf',
  splits: '0x7160a322fea44945a6ef9adfd65c322258df3c5e',
  fundAccessLimits: '0x3a46b21720c8b70184b0434a2293b2fdcc497ce7',
  projects: '0x885f707efa18d2cb12f05a3a8eba6b4b26c8c1d4',
  tokens: '0x4d0edd347fb1fa21589c1e109b3474924be87636',
}

// Helper to get correct contracts for a project
// ALWAYS queries JBDirectory.controllerOf() - the ONLY authoritative source
async function getContractsForProject(projectId: number): Promise<{
  controller: `0x${string}`,
  terminal: `0x${string}`,
  rulesets: `0x${string}`,
  version: 'V5' | 'V5_1',
}> {
  // Step 1: Query JBDirectory for the project's actual controller
  const controller = await publicClient.readContract({
    address: SHARED.directory,
    abi: JB_DIRECTORY_ABI,
    functionName: 'controllerOf',
    args: [BigInt(projectId)],
  })

  // Step 2: Determine version by comparing controller address
  const controllerLower = controller.toLowerCase()

  if (controllerLower === CONTROLLERS.V5.toLowerCase()) {
    return { ...VERSIONED_CONTRACTS.V5, version: 'V5' }
  }

  if (controllerLower === CONTROLLERS.V5_1.toLowerCase()) {
    return { ...VERSIONED_CONTRACTS.V5_1, version: 'V5_1' }
  }

  // Unknown controller - this shouldn't happen for valid Juicebox projects
  throw new Error(`Unknown controller ${controller} for project ${projectId}`)
}

Cast Command to Check Version

# Get controller for a project - this is the authoritative source
cast call 0x0061e516886a0540f63157f112c0588ee0651dcf \
  "controllerOf(uint256)(address)" $PROJECT_ID --rpc-url $RPC_URL

# Compare result:
# 0x27da30646502e2f642be5281322ae8c394f7668a = V5.0
# 0xf3cc99b11bd73a2e3b8815fb85fe0381b29987e1 = V5.1

Verification

  • Payment transactions succeed without "invalid terminal" errors
  • Ruleset queries return expected data
  • Project creation uses correct contract set

Example

Paying NANA (Project #1, a Revnet):

cast call 0x0061e516886a0540f63157f112c0588ee0651dcf "controllerOf(uint256)(address)" 1
# Returns: 0x27da30646502e2f642be5281322ae8c394f7668a (V5 controller)
# → Use V5 contracts: JBMultiTerminal 0x2db6d704058e552defe415753465df8df0361846

Paying a project deployed with V5.1:

cast call 0x0061e516886a0540f63157f112c0588ee0651dcf "controllerOf(uint256)(address)" 123
# Returns: 0xf3cc99b11bd73a2e3b8815fb85fe0381b29987e1 (V5.1 controller)
# → Use V5.1 contracts: JBMultiTerminal5_1 0x52869db3d61dde1e391967f2ce5039ad0ecd371c

Notes

  • CRITICAL: Always check JBDirectory.controllerOf() to determine version - never assume based on ownership
  • Some non-revnet projects use V5.0 contracts (deployed before V5.1 existed)
  • All addresses are deterministic across all chains (Ethereum, Optimism, Base, Arbitrum)
  • JBOmnichainDeployer5_1 deploys to all chains at once using V5.1 contracts
  • Source of truth: nana-core-v5/deployments and docs.juicebox.money/dev/v5/addresses

References

  • JBDirectory (for controllerOf lookup): 0x0061e516886a0540f63157f112c0588ee0651dcf
  • JBController V5: 0x27da30646502e2f642be5281322ae8c394f7668a
  • JBController V5.1: 0xf3cc99b11bd73a2e3b8815fb85fe0381b29987e1
  • Official docs: https://docs.juicebox.money/dev/v5/addresses
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-v5-v51-contracts?

Run openclaw add @mejango/juicy:jb-v5-v51-contracts in your terminal. This installs jb-v5-v51-contracts 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.