协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
jb-split-hook【开发与工程】Generate custom Juicebox V5 split hooks from natural language specifications. Creates Solidity contracts implementing IJBSplitHook with Foundry tests. Split hooks process individual payout or reserved token splits with custom logic like DeFi integrations.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Generate custom split hooks for Juicebox V5 projects based on natural language specifications.
Split hooks allow custom processing when funds are distributed through payout splits or reserved token splits. They're useful for:
Note: Split hooks can be added to Revnets for token distribution just like any other Juicebox project.
Split hooks implement a single function that receives funds optimistically and processes them.
interface IJBSplitHook is IERC165 {
/// @notice Process a single split with custom logic.
/// @dev Tokens and native currency are optimistically transferred to the hook.
/// @param context The context passed by the terminal or controller.
function processSplitWith(JBSplitHookContext calldata context) external payable;
}
struct JBSplitHookContext {
address token; // Token being distributed (address(0) for native)
uint256 amount; // Amount sent to this split
uint256 decimals; // Token decimals
uint256 projectId; // Project distributing funds
uint256 groupId; // Split group ID
JBSplit split; // The split configuration
}
struct JBSplit {
bool preferAddToBalance; // Add to project balance instead of paying
uint256 percent; // Percent of distribution (out of 1000000000)
uint256 projectId; // Project to pay (0 for wallet)
address payable beneficiary; // Wallet if projectId is 0
uint256 lockedUntil; // Timestamp until split is locked
IJBSplitHook hook; // This split hook address
}
contract BasicSplitHook is IJBSplitHook, ERC165 {
function processSplitWith(JBSplitHookContext calldata context) external payable override {
// Funds have been transferred to this contract
// Process them according to custom logic
if (context.token == address(0)) {
// Handle native currency (ETH)
uint256 amount = address(this).balance;
// ... custom logic
} else {
// Handle ERC20 token
uint256 amount = IERC20(context.token).balanceOf(address(this));
// ... custom logic
}
}
function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
return interfaceId == type(IJBSplitHook).interfaceId || super.supportsInterface(interfaceId);
}
}
Route payouts to a Uniswap V3 liquidity position.
contract UniswapV3LPSplitHook is IJBSplitHook, ERC165 {
INonfungiblePositionManager public immutable POSITION_MANAGER;
uint256 public tokenId; // LP position NFT ID
function processSplitWith(JBSplitHookContext calldata context) external payable override {
if (context.token == address(0)) {
// Wrap ETH to WETH
WETH.deposit{value: msg.value}();
}
// Add liquidity to existing position or create new one
// ...
}
}
Split funds further among multiple recipients.
contract MultiRecipientSplitHook is IJBSplitHook, ERC165 {
struct Recipient {
address payable addr;
uint256 percent; // Out of 10000
}
Recipient[] public recipients;
function processSplitWith(JBSplitHookContext calldata context) external payable override {
uint256 total = context.amount;
for (uint256 i; i < recipients.length; i++) {
uint256 share = (total * recipients[i].percent) / 10000;
if (context.token == address(0)) {
recipients[i].addr.transfer(share);
} else {
IERC20(context.token).transfer(recipients[i].addr, share);
}
}
}
}
Swap received tokens before forwarding.
contract SwapSplitHook is IJBSplitHook, ERC165 {
ISwapRouter public immutable ROUTER;
address public immutable OUTPUT_TOKEN;
address public immutable BENEFICIARY;
function processSplitWith(JBSplitHookContext calldata context) external payable override {
// Approve router
IERC20(context.token).approve(address(ROUTER), context.amount);
// Swap to output token
uint256 amountOut = ROUTER.exactInputSingle(
ISwapRouter.ExactInputSingleParams({
tokenIn: context.token,
tokenOut: OUTPUT_TOKEN,
fee: 3000,
recipient: BENEFICIARY,
amountIn: context.amount,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
})
);
}
}
To use a split hook, configure it in a project's split group:
JBSplit memory split = JBSplit({
preferAddToBalance: false,
percent: 100_000_000, // 10% (out of 1_000_000_000)
projectId: 0,
beneficiary: payable(address(0)),
lockedUntil: 0,
hook: IJBSplitHook(address(mySplitHook))
});
context.token to determine token typeGenerate:
src/src/interfaces/ if neededtest/script/ if requestedUse Foundry project structure with forge-std.