Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs/touchups #119

Merged
merged 5 commits into from
Mar 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,61 @@

This repository contains the core protocol contracts for Bananapus' Juicebox v4. Juicebox is a flexible toolkit for launching and managing a treasury-backed token on Ethereum and L2s.

### Basics

Projects are represented by a 721 NFT (`src/JBProjects.sol`) owned by some address. Each project has a controller (`src/interfaces/IJBController.sol`) that is responsible for interactions with the project’s tokens (`src/JBTokens.sol`), splits (`src/JBSplits.sol`), and rulesets (`src/JBRulesets.sol`), and any number of payment terminals (`src/interfaces/IJBTerminal.sol`) to accept payments and give access to funds through. A project’s controller and terminals can be found through the directory (`src/JBDirectory.sol`).

A well-known and trusted controller, multi terminal, directory, project contract, token contract, split contract, and ruleset contract will be deployed by JuiceboxDAO (`script/Deploy.s.sol`) for projects to use, but project owners can always bring their own.

Get a project's current terminals using `directory.terminalsOf(…)` (`src/JBDirectory.sol`), it's primary terminal for given inbound token using `directory.primaryTerminalOf(…)` (`src/JBDirectory.sol`), and its controller using `directory.controllerOf(…)` (`src/JBDirectory.sol`).

Learn how everything fits together by launching a new project using `controller.launchProjectFor(…)` (`src/JBController.sol`).

Next, try paying a project using `terminal.pay(…)` (`src/JBMultiTerminal.sol`) using a payment terminal specified when launching the project.

Next, distribute scheduled payouts from the project using `terminal.distributePayoutsOf(…)` (`src/JBMultiTerminal.sol`), use the project’s surplus if you’re the owner using `terminal.useSurplusAllowanceOf(…)` (`src/JBMultiTerminal.sol`), or redeem the project’s tokens for access to treasury funds using `terminal.redeemTokensOf(…)` (`src/JBMultiTerminal`). The specifics of how funds can be accessed in both cases depends on the rulesets and fund access constraints specified when launching the project.

If reserved tokens have accumulated as payments have come in, distribute them to the prespecified recipients using `controller.sendReservedTokensToSplitsOf(…)` (`src/JBController.sol`).

If you, the project’s owner, wish to queue a new ruleset to take effect after the current one, use `controller.queueRulesetsOf(…)` (`src/JBController.sol`).

### Multi Terminal

The multi terminal (`src/JBMultiTerminal.sol`) allows projects to receive and store native tokens and any ERC-20 a project wishes to mint its tokens with and keep exposure to.

### Hooks

A project can attach a data hook (`src/interfaces/IJBRulesetDataHook.sol`) address to its rulesets. The data hook can specify custom contract code that runs when the project gets paid (`src/interfaces/IJBPayHook.sol`) or when the project’s tokens are redeemed (`src/interfaces/IJBRedeemHook.sol`).

A project can also schedule payouts to split hooks (`src/interfaces/IJBSplitHook.sol`) alongside splits to addresses and/or other Juicebox projects.

When a project queues new rulesets, its manifestation depends on an optional approval hook (`src/interfaces/IJBRulesetApprovalHook.sol`) of the preceding ruleset. This can be used to prevent scheduled rule changes unless certain conditions are met.

### Rulesets

A project has one active ruleset at a time, and can queue any number of rulesets to become active over time. Each project's rulesets are stored in `JBRulesets` (`src/JBRulesetStore.sol`), which also handles their timing and scheduling.

### Tokens

By default, each project uses a simple internal accounting mechanism to manage token issuance and redemptions. At any time, project's can optionally deploy an ERC-20 (`src/JBERC20.sol`) for it's community of holders to claim in place of the default internal token, which can then be used across ERC20 compatible ecosystems.

### Permissions

Addresses can give other operator addresses permissions to manage certain ecosystem actions on their behalf.

### Prices

It is possible for a project to accept and store ETH, but issue its $TOKENs relative to USD (i.e. 1,000 $TOKENs issued per 1 USD worth of ETH paid). If a project is managing it's accounting in terms of a certain currency but accepting and storing a token with a different currency, the `src/JBPrices.sol` contract is used to normalize the prices to maintain consistent accounting.

### Splits

A project may manage payouts and reserved token distributions to groups of addresses, other projects, and split hooks. These references are stored in `src/JBSplits.sol` to allow access to various groups of splits across various rulesets over time.

### Fund Access Limits

A project may give itself access to accumulated funds from its treasury either for scheduled payouts or discretionary surplus spending. The limits of its access are stored in `src/JBFundAccessLimits.sol`.


To learn more about the protocol, visit the [Juicebox Docs](https://docs.juicebox.money/). If you have questions, reach out on [Discord](https://discord.com/invite/ErQYmth4dS).

## Install
Expand Down
1 change: 1 addition & 0 deletions src/JBChainlinkV3PriceFeed.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity 0.8.23;

import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

import {IJBPriceFeed} from "./interfaces/IJBPriceFeed.sol";
import {JBFixedPointNumber} from "./libraries/JBFixedPointNumber.sol";

Expand Down
51 changes: 18 additions & 33 deletions src/JBController.sol
Original file line number Diff line number Diff line change
@@ -1,43 +1,44 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {JBPermissionIds} from "@bananapus/permission-ids/src/JBPermissionIds.sol";
import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
import {mulDiv} from "@prb/math/src/Common.sol";
import {JBPermissionIds} from "@bananapus/permission-ids/src/JBPermissionIds.sol";

import {JBPermissioned} from "./abstract/JBPermissioned.sol";
import {JBApprovalStatus} from "./enums/JBApprovalStatus.sol";
import {IJBController} from "./interfaces/IJBController.sol";
import {IJBDirectory} from "./interfaces/IJBDirectory.sol";
import {IJBFundAccessLimits} from "./interfaces/IJBFundAccessLimits.sol";
import {IJBRulesets} from "./interfaces/IJBRulesets.sol";
import {IJBDirectoryAccessControl} from "./interfaces/IJBDirectoryAccessControl.sol";
import {IJBFundAccessLimits} from "./interfaces/IJBFundAccessLimits.sol";
import {IJBMigratable} from "./interfaces/IJBMigratable.sol";
import {IJBPermissioned} from "./interfaces/IJBPermissioned.sol";
import {IJBRulesetDataHook} from "./interfaces/IJBRulesetDataHook.sol";
import {IJBPermissions} from "./interfaces/IJBPermissions.sol";
import {IJBTerminal} from "./interfaces/terminal/IJBTerminal.sol";
import {IJBProjects} from "./interfaces/IJBProjects.sol";
import {IJBProjectUriRegistry} from "./interfaces/IJBProjectUriRegistry.sol";
import {IJBRulesets} from "./interfaces/IJBRulesets.sol";
import {IJBRulesetDataHook} from "./interfaces/IJBRulesetDataHook.sol";
import {IJBSplitHook} from "./interfaces/IJBSplitHook.sol";
import {IJBSplits} from "./interfaces/IJBSplits.sol";
import {IJBTerminal} from "./interfaces/IJBTerminal.sol";
import {IJBToken} from "./interfaces/IJBToken.sol";
import {IJBTokens} from "./interfaces/IJBTokens.sol";
import {JBConstants} from "./libraries/JBConstants.sol";
import {JBRulesetMetadataResolver} from "./libraries/JBRulesetMetadataResolver.sol";
import {JBSplitGroupIds} from "./libraries/JBSplitGroupIds.sol";
import {JBRuleset} from "./structs/JBRuleset.sol";
import {JBRulesetWithMetadata} from "./structs/JBRulesetWithMetadata.sol";
import {JBRulesetConfig} from "./structs/JBRulesetConfig.sol";
import {JBRulesetMetadata} from "./structs/JBRulesetMetadata.sol";
import {JBTerminalConfig} from "./structs/JBTerminalConfig.sol";
import {JBRulesetWithMetadata} from "./structs/JBRulesetWithMetadata.sol";
import {JBSplit} from "./structs/JBSplit.sol";
import {JBSplitGroup} from "./structs/JBSplitGroup.sol";
import {JBSplitHookContext} from "./structs/JBSplitHookContext.sol";
import {JBTerminalConfig} from "./structs/JBTerminalConfig.sol";

/// @notice Stitches together rulesets and project tokens, making sure all activity is accounted for and correct.
contract JBController is JBPermissioned, ERC2771Context, ERC165, IJBController, IJBMigratable {
Expand All @@ -51,14 +52,14 @@ contract JBController is JBPermissioned, ERC2771Context, ERC165, IJBController,
// --------------------------- custom errors ------------------------- //
//*********************************************************************//

error RULESET_ALREADY_LAUNCHED();
error CREDIT_TRANSFERS_PAUSED();
error INVALID_BASE_CURRENCY();
error INVALID_REDEMPTION_RATE();
error INVALID_RESERVED_RATE();
error CONTROLLER_MIGRATION_NOT_ALLOWED();
error MINT_NOT_ALLOWED_AND_NOT_TERMINAL_OR_HOOK();
error NO_BURNABLE_TOKENS();
error CREDIT_TRANSFERS_PAUSED();
error RULESET_ALREADY_LAUNCHED();
error ZERO_TOKENS_TO_MINT();

//*********************************************************************//
Expand Down Expand Up @@ -509,18 +510,9 @@ contract JBController is JBPermissioned, ERC2771Context, ERC165, IJBController,
/// @dev If the project has no reserved token splits, or they don't add up to 100%, the leftover tokens are minted
/// to the project's owner.
/// @param projectId The ID of the project to which the reserved tokens belong.
/// @param memo A memo to pass along to the emitted event.
/// @return The amount of reserved tokens minted and sent.
function sendReservedTokensToSplitsOf(
uint256 projectId,
string calldata memo
)
external
virtual
override
returns (uint256)
{
return _sendReservedTokensToSplitsOf(projectId, memo);
function sendReservedTokensToSplitsOf(uint256 projectId) external virtual override returns (uint256) {
return _sendReservedTokensToSplitsOf(projectId);
}

/// @notice Allows other controllers to signal to this one that a migration is expected for the specified project.
Expand Down Expand Up @@ -562,7 +554,7 @@ contract JBController is JBPermissioned, ERC2771Context, ERC165, IJBController,

// All reserved tokens must be minted before migrating.
if (pendingReservedTokenBalanceOf[projectId] != 0) {
_sendReservedTokensToSplitsOf(projectId, "");
_sendReservedTokensToSplitsOf(projectId);
}

// Make sure the new controller is prepped for the migration.
Expand Down Expand Up @@ -785,15 +777,8 @@ contract JBController is JBPermissioned, ERC2771Context, ERC165, IJBController,
/// @dev If the project has no reserved token splits, or they don't add up to 100%, the leftover tokens are minted
/// to the project's owner.
/// @param projectId The ID of the project the reserved tokens belong to.
/// @param memo A memo to pass along to the emitted event.
/// @return tokenCount The number of reserved tokens minted/sent.
function _sendReservedTokensToSplitsOf(
uint256 projectId,
string memory memo
)
internal
returns (uint256 tokenCount)
{
function _sendReservedTokensToSplitsOf(uint256 projectId) internal returns (uint256 tokenCount) {
// Get the current ruleset to read the reserved rate from.
JBRuleset memory ruleset = RULESETS.currentOf(projectId);

Expand All @@ -818,7 +803,7 @@ contract JBController is JBPermissioned, ERC2771Context, ERC165, IJBController,
}

emit SendReservedTokensToSplits(
ruleset.id, ruleset.cycleNumber, projectId, owner, tokenCount, leftoverTokenCount, memo, _msgSender()
ruleset.id, ruleset.cycleNumber, projectId, owner, tokenCount, leftoverTokenCount, _msgSender()
);
}

Expand Down
1 change: 1 addition & 0 deletions src/JBDeadline.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity 0.8.23;

import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {JBApprovalStatus} from "./enums/JBApprovalStatus.sol";
import {IJBRulesetApprovalHook} from "./interfaces/IJBRulesetApprovalHook.sol";
import {JBRuleset} from "./structs/JBRuleset.sol";
Expand Down
5 changes: 3 additions & 2 deletions src/JBDirectory.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {JBPermissionIds} from "@bananapus/permission-ids/src/JBPermissionIds.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {JBPermissionIds} from "@bananapus/permission-ids/src/JBPermissionIds.sol";

import {JBPermissioned} from "./abstract/JBPermissioned.sol";
import {IJBDirectory} from "./interfaces/IJBDirectory.sol";
import {IJBDirectoryAccessControl} from "./interfaces/IJBDirectoryAccessControl.sol";
import {IJBPermissions} from "./interfaces/IJBPermissions.sol";
import {IJBTerminal} from "./interfaces/terminal/IJBTerminal.sol";
import {IJBTerminal} from "./interfaces/IJBTerminal.sol";
import {IJBProjects} from "./interfaces/IJBProjects.sol";

/// @notice Tracks which terminal contracts each project is currently accepting funds through, and which controller
Expand Down
3 changes: 2 additions & 1 deletion src/JBERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
pragma solidity 0.8.23;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ERC20Votes, ERC20} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
import {ERC20Permit, Nonces} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import {ERC20Votes, ERC20} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";

import {IJBToken} from "./interfaces/IJBToken.sol";

/// @notice An ERC-20 token that can be used by a project in the `JBTokens`.
Expand Down
1 change: 1 addition & 0 deletions src/JBFeelessAddresses.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity 0.8.23;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {IJBFeelessAddresses} from "./interfaces/IJBFeelessAddresses.sol";

/// @notice Stores and manages addresses that shouldn't incur fees when being paid towards or from.
Expand Down
5 changes: 3 additions & 2 deletions src/JBFundAccessLimits.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
pragma solidity 0.8.23;

import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";

import {JBControlled} from "./abstract/JBControlled.sol";
import {IJBFundAccessLimits} from "./interfaces/IJBFundAccessLimits.sol";
import {IJBDirectory} from "./interfaces/IJBDirectory.sol";
import {JBFundAccessLimitGroup} from "./structs/JBFundAccessLimitGroup.sol";
import {IJBFundAccessLimits} from "./interfaces/IJBFundAccessLimits.sol";
import {JBCurrencyAmount} from "./structs/JBCurrencyAmount.sol";
import {JBFundAccessLimitGroup} from "./structs/JBFundAccessLimitGroup.sol";

/// @notice Stores and manages terminal fund access limits for each project.
/// @dev See the `JBFundAccessLimitGroup` struct to learn about payout limits and surplus allowances.
Expand Down
39 changes: 20 additions & 19 deletions src/JBMultiTerminal.sol
Original file line number Diff line number Diff line change
@@ -1,49 +1,50 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {JBPermissionIds} from "@bananapus/permission-ids/src/JBPermissionIds.sol";
import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {mulDiv} from "@prb/math/src/Common.sol";
import {IAllowanceTransfer} from "@uniswap/permit2/src/interfaces/IAllowanceTransfer.sol";
import {IPermit2} from "@uniswap/permit2/src/interfaces/IPermit2.sol";
import {IAllowanceTransfer} from "@uniswap/permit2/src/interfaces/IPermit2.sol";
import {JBPermissionIds} from "@bananapus/permission-ids/src/JBPermissionIds.sol";

import {JBPermissioned} from "./abstract/JBPermissioned.sol";
import {IJBController} from "./interfaces/IJBController.sol";
import {IJBDirectory} from "./interfaces/IJBDirectory.sol";
import {IJBFeelessAddresses} from "./interfaces/IJBFeelessAddresses.sol";
import {IJBSplits} from "./interfaces/IJBSplits.sol";
import {IJBFeeTerminal} from "./interfaces/IJBFeeTerminal.sol";
import {IJBMultiTerminal} from "./interfaces/IJBMultiTerminal.sol";
import {IJBPayoutTerminal} from "./interfaces/IJBPayoutTerminal.sol";
import {IJBPermissioned} from "./interfaces/IJBPermissioned.sol";
import {IJBPermitTerminal} from "./interfaces/IJBPermitTerminal.sol";
import {IJBPermissions} from "./interfaces/IJBPermissions.sol";
import {IJBProjects} from "./interfaces/IJBProjects.sol";
import {IJBTerminalStore} from "./interfaces/IJBTerminalStore.sol";
import {IJBRedeemTerminal} from "./interfaces/IJBRedeemTerminal.sol";
import {IJBSplitHook} from "./interfaces/IJBSplitHook.sol";
import {IJBSplits} from "./interfaces/IJBSplits.sol";
import {IJBTerminal} from "./interfaces/IJBTerminal.sol";
import {IJBTerminalStore} from "./interfaces/IJBTerminalStore.sol";
import {JBConstants} from "./libraries/JBConstants.sol";
import {JBFees} from "./libraries/JBFees.sol";
import {JBRulesetMetadataResolver} from "./libraries/JBRulesetMetadataResolver.sol";
import {JBMetadataResolver} from "./libraries/JBMetadataResolver.sol";
import {JBAfterRedeemRecordedContext} from "./structs/JBAfterRedeemRecordedContext.sol";
import {JBRulesetMetadataResolver} from "./libraries/JBRulesetMetadataResolver.sol";
import {JBAccountingContext} from "./structs/JBAccountingContext.sol";
import {JBAfterPayRecordedContext} from "./structs/JBAfterPayRecordedContext.sol";
import {JBAfterRedeemRecordedContext} from "./structs/JBAfterRedeemRecordedContext.sol";
import {JBFee} from "./structs/JBFee.sol";
import {JBRuleset} from "./structs/JBRuleset.sol";
import {JBPayHookSpecification} from "./structs/JBPayHookSpecification.sol";
import {JBRedeemHookSpecification} from "./structs/JBRedeemHookSpecification.sol";
import {JBRuleset} from "./structs/JBRuleset.sol";
import {JBSingleAllowanceContext} from "./structs/JBSingleAllowanceContext.sol";
import {JBSplit} from "./structs/JBSplit.sol";
import {JBSplitHookContext} from "./structs/JBSplitHookContext.sol";
import {JBAccountingContext} from "./structs/JBAccountingContext.sol";
import {JBTokenAmount} from "./structs/JBTokenAmount.sol";
import {JBPermissioned} from "./abstract/JBPermissioned.sol";
import {IJBMultiTerminal} from "./interfaces/terminal/IJBMultiTerminal.sol";
import {IJBFeeTerminal} from "./interfaces/terminal/IJBFeeTerminal.sol";
import {IJBTerminal} from "./interfaces/terminal/IJBTerminal.sol";
import {IJBRedeemTerminal} from "./interfaces/terminal/IJBRedeemTerminal.sol";
import {IJBPayoutTerminal} from "./interfaces/terminal/IJBPayoutTerminal.sol";
import {IJBPermitTerminal} from "./interfaces/terminal/IJBPermitTerminal.sol";

/// @notice Generic terminal managing inflows and outflows of funds into the protocol ecosystem.
contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
Expand Down
Loading