| name | jb-deploy-ui |
| description | Generate minimal frontends for deploying Juicebox V5 projects and hooks. Creates standalone HTML files with wallet connection, transaction forms, and live status updates. |
Juicebox V5 Deployment UI Generator
Generate simple frontends for deploying Juicebox projects, hooks, and configurations. Uses shared styles and viem for blockchain interactions.
Philosophy
Show users exactly what they're doing. Make wallet connection trivial. Display transactions in flight.
- Single HTML file, no build step
- viem from ESM CDN
- Shared CSS from
/shared/styles.css
- Clear transaction previews before signing
Template: Project Deployment UI
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Deploy Juicebox Project</title>
<link rel="stylesheet" href="/shared/styles.css">
<style>
.preview { background: var(--bg-primary); border: 1px solid var(--border-color); border-radius: 4px; padding: 0.75rem; font-family: monospace; font-size: 0.75rem; margin: 0.5rem 0; white-space: pre-wrap; word-break: break-all; }
</style>
</head>
<body>
<div class="container" style="max-width: 640px;">
<h1>Deploy Juicebox Project</h1>
<div class="card">
<button class="btn" id="connect-btn" onclick="connectWallet()">Connect Wallet</button>
<div id="wallet-status" class="hidden" style="margin-top: 0.75rem;">
<span class="badge-success">Connected: <span id="wallet-address"></span> on <span id="network-name"></span></span>
</div>
</div>
<div class="card">
<h2>Project Details</h2>
<label>Project Name</label>
<input type="text" id="project-name" placeholder="My Project">
<label>Description</label>
<textarea id="project-description" rows="3" placeholder="What is this project for?"></textarea>
<label>Owner Address (defaults to connected wallet)</label>
<input type="text" id="owner-address" placeholder="0x...">
</div>
<div class="card">
<h2>Ruleset Configuration</h2>
<label>Duration (days, 0 = no cycles)</label>
<input type="number" id="duration" value="0" min="0">
<label>Tokens per ETH</label>
<input type="number" id="weight" value="1000000" min="0">
<label>Reserved Rate (%)</label>
<input type="number" id="reserved-rate" value="0" min="0" max="100">
<label>Cash Out Tax Rate (%)</label>
<input type="number" id="cash-out-tax" value="0" min="0" max="100">
</div>
<div class="card">
<h2>Transaction Preview</h2>
<div id="tx-preview" class="preview">Connect wallet to see preview</div>
<button class="btn" id="deploy-btn" onclick="deploy()" disabled>Deploy Project</button>
</div>
<div id="tx-status" class="card hidden"></div>
</div>
<script type="module">
import { createWalletClient, createPublicClient, custom, http, parseEther, zeroAddress } from 'https://esm.sh/viem';
import { mainnet, optimism, base, arbitrum, sepolia } from 'https://esm.sh/viem/chains';
import { CHAIN_CONFIGS, loadChainConfig, truncateAddress, getTxUrl } from '/shared/wallet-utils.js';
const CHAINS = { 1: mainnet, 10: optimism, 8453: base, 42161: arbitrum, 11155111: sepolia };
const CONTROLLER_ABI = [{
name: 'launchProjectFor', type: 'function', stateMutability: 'nonpayable',
inputs: [
{ name: 'owner', type: 'address' },
{ name: 'projectUri', type: 'string' },
{ name: 'rulesetConfigurations', type: 'tuple[]', components: [
{ name: 'mustStartAtOrAfter', type: 'uint256' },
{ name: 'duration', type: 'uint256' },
{ name: 'weight', type: },
{ : , : },
{ : , : },
{ : , : , : [
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : }
]},
{ : , : , : [] },
{ : , : , : [] }
]},
{ : , : , : [
{ : , : },
{ : , : , : [
{ : , : },
{ : , : },
{ : , : }
]}
]},
{ : , : }
],
: [{ : }]
}];
walletClient = ;
publicClient = ;
connectedAddress = ;
chainId = ;
chainConfig = ;
. = () {
(!.) { (); ; }
{
[address] = ..({ : });
connectedAddress = address;
chainId = ( ..({ : }), );
walletClient = ({ : [chainId], : (.) });
publicClient = ({ : [chainId], : ([chainId]?.) });
chainConfig = ();
.(). = (address);
.(). = [chainId]?. || ;
.()..();
.()..();
.(). = ;
();
} (e) { .(e); (); }
};
() {
{
: .(). || connectedAddress,
: ,
: ((.().) * ),
: (.().),
: ((.().) * ),
: ((.().) * )
};
}
() {
config = ();
.(). = .({
: config.,
: ,
: ,
: ,
:
}, , );
}
. = () {
config = ();
addresses = chainConfig?.[chainId]?.;
(!addresses) { (); ; }
(, );
{
rulesetConfig = {
: ,
: config.,
: config.,
: ,
: zeroAddress,
: {
: config.,
: config.,
: ,
: , : , : ,
: , : ,
: , : ,
: , : ,
: , : ,
: ,
: , : ,
: zeroAddress, :
},
: [],
: []
};
terminalConfig = {
: addresses.,
: [{ : , : , : }]
};
hash = walletClient.({
: addresses.,
: ,
: ,
: [config., config., [rulesetConfig], [terminalConfig], ],
: connectedAddress
});
(, );
receipt = publicClient.({ hash });
(, );
} (error) {
(, );
}
};
() {
el = .();
el. = ;
el. = message;
el..();
}
.().( el.(, updatePreview));
</script>
</body>
</html>
Template: 721 NFT Project Deployment
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Deploy NFT Project</title>
<link rel="stylesheet" href="/shared/styles.css">
<style>
.tier-card { border: 1px dashed var(--border-color); padding: 1rem; margin-bottom: 0.75rem; border-radius: 4px; }
.tier-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.75rem; }
.remove-tier { background: var(--error); padding: 0.25rem 0.5rem; width: auto; font-size: 0.75rem; }
.add-tier { : (--bg-tertiary); : ; }
Deploy NFT Project
Connect Wallet
Connected:
Collection Details
Collection Name
Symbol
Base URI (IPFS folder)
NFT Tiers
+ Add Tier
Treasury Settings
Reserved Rate (%)
Cash Out Tax Rate (%)
Deploy NFT Project
Template: Revnet Deployment
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Deploy Revnet</title>
<link rel="stylesheet" href="/shared/styles.css">
</head>
<body>
<div class="container" style="max-width: 640px;">
<h1>Deploy Revnet</h1>
<p style="color: var(--text-muted); margin-bottom: 1.5rem;">Launch an autonomous tokenized treasury</p>
<div class="card">
<button class="btn" id="connect-btn" onclick="connectWallet()">Connect Wallet</>
Connected:
Revnets are autonomous - configuration is permanent after deployment.
Token Configuration
Token Name
Token Symbol
Stage 1 Configuration
Initial Issuance (tokens per ETH)
Issuance Cut (% reduction per period)
Cut Frequency (days)
Cash Out Tax Rate (%)
Split to Operator (%)
Operator Address
Receives split percentage. Can update splits but not core rules.
Deploy Revnet
V5 Contract Addresses
Load from shared chain config:
import { loadChainConfig } from '/shared/wallet-utils.js';
const config = await loadChainConfig();
const controller = config.chains[1].contracts.JBController;
const terminal = config.chains[1].contracts.JBMultiTerminal;
const revDeployer = config.chains[1].contractsV5.REVDeployer;
Related Skills
/jb-project - Project configuration details
/jb-ruleset - Ruleset parameters
/jb-interact-ui - UIs for interacting with existing projects
/jb-omnichain-ui - Multi-chain deployments