Writing Skills
writing-skills
效率与协作技能,来自 obra/superpowers,查看原文获取完整用途和操作说明。
jb-fund-access-limits【部署与运维】Query and display Juicebox V5 fund access limits (payout limits, surplus allowances). Use when: (1) surplusAllowancesOf or payoutLimitsOf returns empty but values exist, (2) need to detect "unlimited" values which can be various max integers not just uint256, (3) fund access limits show as zero for USDC-based projects when querying with ETH token, (4…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Two distinct issues with fund access limits:
Configuration: When deploying projects, fundAccessLimitGroups: [] (empty array) means
ZERO payouts allowed, NOT unlimited. This is a common mistake - to allow unlimited
payouts, you must explicitly set payoutLimits with uint224.max.
Querying: When querying JBFundAccessLimits for payout limits or surplus allowances, the queries may return empty results even when values are set in the contract. Additionally, detecting "unlimited" values is tricky because the protocol uses various max integers.
Configuration (when deploying):
fundAccessLimitGroups: [] was generated (WRONG - means zero payouts)Querying (when reading):
surplusAllowancesOf or payoutLimitsOf returns empty arraystageOf or configurationOf functions revertEmpty fundAccessLimitGroups: [] = NO payouts allowed (not unlimited!)
To allow unlimited payouts, you MUST specify a payout limit with uint224.max:
// uint224.max - the maximum value the JBFundAccessLimits registry accepts
const UINT224_MAX = '26959946667150639794667015087019630673637144422540572481103610249215'
// WRONG - this means ZERO payouts allowed
const wrongConfig = {
fundAccessLimitGroups: []
}
// CORRECT - unlimited payouts for ETH
const correctConfigETH = {
fundAccessLimitGroups: [{
terminal: '0x3f75f7e52ed15c2850b0a6a49c234d5221576dbe', // JBMultiTerminal (ETH)
token: '0x000000000000000000000000000000000000EEEe', // Native token
payoutLimits: [{
amount: UINT224_MAX,
currency: 1 // ETH currency
}],
surplusAllowances: []
}]
}
// CORRECT - unlimited payouts for USDC (Ethereum mainnet)
const correctConfigUSDC = {
fundAccessLimitGroups: [{
terminal: '0x52869db3d61dde1e391967f2ce5039ad0ecd371c', // JBMultiTerminal5_1
token: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC on Ethereum
payoutLimits: [{
amount: UINT224_MAX,
currency: 909516616 // USDC currency (from accounting context)
}],
surplusAllowances: []
}]
}
When to use payout limits:
When to use surplus allowance (owner access + cash outs both available):
Key difference:
When to use zero payouts + zero allowance:
Fund access limits are keyed by (projectId, rulesetId, terminal, token). USDC-based projects won't have results when querying with ETH token:
const NATIVE_TOKEN = '0x000000000000000000000000000000000000EEEe'
// USDC addresses per chain
const USDC_ADDRESSES: Record<number, string> = {
1: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // Ethereum
10: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', // Optimism
8453: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // Base
42161: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', // Arbitrum
}
// Try ETH first, then USDC
let limits = await fetchLimitsForToken(rulesetId, NATIVE_TOKEN)
if (!limits) {
const usdcToken = USDC_ADDRESSES[chainId]
if (usdcToken) {
limits = await fetchLimitsForToken(rulesetId, usdcToken)
}
}
If no limits found for current rulesetId, walk back through basedOnId:
let currentRsId = BigInt(rulesetId)
while (attempts < maxAttempts) {
const ruleset = await publicClient.readContract({
address: JB_CONTRACTS.JBRulesets,
abi: JB_RULESETS_ABI,
functionName: 'getRulesetOf',
args: [BigInt(projectId), currentRsId],
})
const basedOnId = BigInt(ruleset.basedOnId)
if (basedOnId === 0n || basedOnId === currentRsId) break
const limits = await fetchLimitsForRuleset(basedOnId)
if (limits) return limits
currentRsId = basedOnId
}
The protocol uses various max values (uint256, uint224, uint128, etc.). Use threshold:
const isUnlimited = (amount: string | undefined): boolean => {
if (!amount) return false
try {
// Any value > 10^30 is effectively unlimited
return amount.length > 30 || BigInt(amount) > BigInt('1000000000000000000000000000000')
} catch {
return false
}
}
stageOf doesn't exist on all REVDeployer versions. Calculate current stage from timestamps:
// Instead of calling stageOf (may not exist)
try {
const config = await publicClient.readContract({
address: REV_DEPLOYER,
abi: REV_DEPLOYER_ABI,
functionName: 'configurationOf',
args: [BigInt(projectId)],
})
// Calculate current stage from timestamps
const now = Math.floor(Date.now() / 1000)
let currentStage = 1
for (let i = 0; i < stageConfigs.length; i++) {
if (Number(stageConfigs[i].startsAtOrAfter) <= now) {
currentStage = i + 1
}
}
} catch {
// configurationOf may revert on older deployments - handle gracefully
}
For Artizen (project 6 on Base chain 8453):
26959946667150640000000000000000000000000 which is "unlimited"fundAccessLimitGroups = ZERO payouts, not unlimited. This is the
most common configuration mistake. Always use uint224.max for unlimited payouts.uint224.max = 269599466671506397946670150870196306736371444225405724811036102492150x52869db3d61dde1e391967f2ce5039ad0ecd371c) is same on all chainsfundAccessLimitGroups must match the terminal that will receive payments// JBFundAccessLimits
function payoutLimitsOf(
uint256 projectId,
uint256 rulesetId,
address terminal,
address token
) external view returns (JBCurrencyAmount[] memory);
function surplusAllowancesOf(
uint256 projectId,
uint256 rulesetId,
address terminal,
address token
) external view returns (JBCurrencyAmount[] memory);
0x52869db3d61dde1e391967f2ce5039ad0ecd371c