Data source
Before implementing, learn about data sources here. Also see juice-delegate-template.
Specs
A contract can become a treasury data source by adhering to IJBFundingCycleDataSource3_1_1:
interface IJBFundingCycleDataSource3_1_1 is IERC165 {
function payParams(
JBPayParamsData calldata data
)
external
view
returns (
uint256 weight,
string memory memo,
JBPayDelegateAllocation3_1_1[] memory delegateAllocations
);
function redeemParams(
JBRedeemParamsData calldata data
)
external
view
returns (
uint256 reclaimAmount,
string memory memo,
JBRedemptionDelegateAllocation3_1_1[] memory delegateAllocations
);
}
There are two functions that must be implemented, payParams(...) and redeemParams(...). Either one can be left empty if the intent is to only extend the treasury's pay functionality or redeem functionality.
Pay
When extending the pay functionality with a data source, the protocol will pass a JBPayParamsData to the payParams(...) function:
struct JBPayParamsData {
IJBPaymentTerminal terminal;
address payer;
JBTokenAmount amount;
uint256 projectId;
uint256 currentFundingCycleConfiguration;
address beneficiary;
uint256 weight;
uint256 reservedRate;
string memo;
bytes metadata;
}
struct JBTokenAmount {
address token;
uint256 value;
uint256 decimals;
uint256 currency;
}
Using these params, the data source's payParams(...) function is responsible for either reverting or returning a few bits of information:
weightis a fixed point number with 18 decimals that the protocol can use to base arbitrary calculations on. For example, payment terminals based on theJBPayoutRedemptionPaymentTerminal3_1_1, such asJBETHPaymentTerminal3_1_1's andJBERC20PaymentTerminal3_1_1's, use theweightto determine how many project tokens to mint when a project receives a payment (see the calculation). By default, the protocol will use theweightof the project's current funding cycle, which is provided to the data source function inJBPayParamsData.weight. Increasing the weight will mint more tokens and decreasing the weight will mint fewer tokens, both as a function of the amount paid. Return theJBPayParamsData.weightvalue if no altered functionality is desired.memois a string emitted within thePayevent and sent along to anydelegatethat this function also returns. By default, the protocol will use thememodirectly passed in by the payer, which is provided to this data source function inJBPayParamsData.memo. Return theJBPayParamsData.memovalue if no altered functionality is desired.delegateAllocationsis an array containing delegates, amounts to send them, and metadata to pass to them.delegateAllocations.delegateis the address of a contract that adheres toIJBPayDelegate3_1_1whosedidPay(...)function will be called once the protocol finishes its standard payment routine. Check out how to build a pay delegate for more details. If the same contract is being used as the data source and the pay delegate, returnaddress(this). Return the zero address if no additional functionality is desired.delegateAllocations.amountis the amount of tokens to send to the delegate.delegateAllocations.metadatais the metadata to pass to the delegate.
The payParams(...) function can also revert if it's presented with any conditions it does not want to accept payments under.
The payParams(...) function has implicit permission to JBController3_1.mintTokensOf(...) for the project.
Redeem
When extending redeem functionality with a data source, the protocol will pass a JBRedeemParamsData to the redeemParams(...) function:
struct JBRedeemParamsData {
IJBPaymentTerminal terminal;
address holder;
uint256 projectId;
uint256 currentFundingCycleConfiguration;
uint256 tokenCount;
uint256 totalSupply;
uint256 overflow;
JBTokenAmount reclaimAmount;
bool useTotalOverflow;
uint256 redemptionRate;
uint256 ballotRedemptionRate;
string memo;
bytes metadata;
}
Using these params, the data source's redeemParams(...) function is responsible for either reverting or returning a few bits of information:
reclaimAmountis the amount of tokens in the treasury that the terminal should send out to the redemption beneficiary as a result of burning the amount of project tokens tokens specified inJBRedeemParamsData.tokenCount, as a fixed point number with the same amount of decimals asJBRedeemParamsData.decimals. By default, the protocol will use a reclaim amount determined by the standard protocol bonding curve based on the redemption rate the project has configured into its current funding cycle, which is provided to the data source function inJBRedeemParamsData.reclaimAmount. Return theJBRedeemParamsData.reclaimAmountvalue if no altered functionality is desired.memois a string emitted within theRedeemTokensevent and sent along to anydelegatethat this function also returns. By default, the protocol will use thememopassed in directly by the redeemer, which is provided to this data source function inJBRedeemParamsData.memo. Return theJBRedeemParamsData.memovalue if no altered functionality is desired.delegateAllocationsis an array containing delegates, amounts to send them, and metadata to pass to them.delegateAllocations.delegateis the address of a contract that adheres toIJBRedemptionDelegate3_1_1whosedidRedeem(...)function will be called once the protocol finishes its standard redemption routine (but before the reclaimed amount is sent to the beneficiary). Check out how to build a redemption delegate for more details. If the same contract is being used as the data source and the redemption delegate, returnaddress(this). Return the zero address if no additional functionality is desired.delegateAllocations.amountis the amount of tokens to send to the delegate.delegateAllocations.metadatais the metadata to pass to the delegate.
The redeemParams(...) function can also revert if it's presented with any conditions it does not want to accept redemptions under.
Attaching
New data source contracts should be deployed independently. Once deployed, its address can be configured into a project's funding cycle metadata to take effect while that funding cycle is active. Additionally, the metadata's useDataSourceForPay and/or useDataSourceForRedeem should be set to true if the respective data source hook should be referenced by the protocol.
Examples
import '@openzeppelin/contracts/token/ERC721/ERC721.sol';
import '@jbx-protocol/contracts-v2/contracts/interfaces/IJBFundingCycleDataSource.sol';
contract AllowlistDataSource is IJBFundingCycleDataSource {
error NOT_ALLOWED();
mapping(address => bool) allowed;
function payParams(JBPayParamsData calldata _data)
external
view
override
returns (
uint256 weight,
string memory memo,
IJBPayDelegate delegate
)
{
if (!allowed[_data.payer]) revert NOT_ALLOWED();
// Forward the recieved weight and memo, and use no delegate.
return (_data.weight, _data.memo, IJBPayDelegate(address(0)));
}
// This is unused but needs to be included to fulfill IJBFundingCycleDataSource.
function redeemParams(JBRedeemParamsData calldata _data)
external
pure
override
returns (
uint256 reclaimAmount,
string memory memo,
IJBRedemptionDelegate delegate
)
{
// Return the default values.
return (_data.reclaimAmount.value, _data.memo, IJBRedemptionDelegate(address(0)));
}
}