@bananapus/721-hook-v6 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gas-snapshot +152 -0
- package/LICENSE +21 -0
- package/README.md +253 -0
- package/SKILLS.md +140 -0
- package/docs/book.css +13 -0
- package/docs/book.toml +12 -0
- package/docs/solidity.min.js +74 -0
- package/docs/src/README.md +253 -0
- package/docs/src/SUMMARY.md +38 -0
- package/docs/src/src/JB721TiersHook.sol/contract.JB721TiersHook.md +645 -0
- package/docs/src/src/JB721TiersHookDeployer.sol/contract.JB721TiersHookDeployer.md +99 -0
- package/docs/src/src/JB721TiersHookProjectDeployer.sol/contract.JB721TiersHookProjectDeployer.md +288 -0
- package/docs/src/src/JB721TiersHookStore.sol/contract.JB721TiersHookStore.md +1096 -0
- package/docs/src/src/README.md +11 -0
- package/docs/src/src/abstract/ERC721.sol/abstract.ERC721.md +430 -0
- package/docs/src/src/abstract/JB721Hook.sol/abstract.JB721Hook.md +309 -0
- package/docs/src/src/abstract/README.md +5 -0
- package/docs/src/src/interfaces/IJB721Hook.sol/interface.IJB721Hook.md +29 -0
- package/docs/src/src/interfaces/IJB721TiersHook.sol/interface.IJB721TiersHook.md +203 -0
- package/docs/src/src/interfaces/IJB721TiersHookDeployer.sol/interface.IJB721TiersHookDeployer.md +25 -0
- package/docs/src/src/interfaces/IJB721TiersHookProjectDeployer.sol/interface.IJB721TiersHookProjectDeployer.md +64 -0
- package/docs/src/src/interfaces/IJB721TiersHookStore.sol/interface.IJB721TiersHookStore.md +265 -0
- package/docs/src/src/interfaces/IJB721TokenUriResolver.sol/interface.IJB721TokenUriResolver.md +12 -0
- package/docs/src/src/interfaces/README.md +9 -0
- package/docs/src/src/libraries/JB721Constants.sol/library.JB721Constants.md +14 -0
- package/docs/src/src/libraries/JB721TiersRulesetMetadataResolver.sol/library.JB721TiersRulesetMetadataResolver.md +68 -0
- package/docs/src/src/libraries/JBBitmap.sol/library.JBBitmap.md +82 -0
- package/docs/src/src/libraries/JBIpfsDecoder.sol/library.JBIpfsDecoder.md +61 -0
- package/docs/src/src/libraries/README.md +7 -0
- package/docs/src/src/structs/JB721InitTiersConfig.sol/struct.JB721InitTiersConfig.md +27 -0
- package/docs/src/src/structs/JB721Tier.sol/struct.JB721Tier.md +59 -0
- package/docs/src/src/structs/JB721TierConfig.sol/struct.JB721TierConfig.md +60 -0
- package/docs/src/src/structs/JB721TiersHookFlags.sol/struct.JB721TiersHookFlags.md +26 -0
- package/docs/src/src/structs/JB721TiersMintReservesConfig.sol/struct.JB721TiersMintReservesConfig.md +16 -0
- package/docs/src/src/structs/JB721TiersRulesetMetadata.sol/struct.JB721TiersRulesetMetadata.md +20 -0
- package/docs/src/src/structs/JB721TiersSetDiscountPercentConfig.sol/struct.JB721TiersSetDiscountPercentConfig.md +16 -0
- package/docs/src/src/structs/JBBitmapWord.sol/struct.JBBitmapWord.md +19 -0
- package/docs/src/src/structs/JBDeploy721TiersHookConfig.sol/struct.JBDeploy721TiersHookConfig.md +34 -0
- package/docs/src/src/structs/JBLaunchProjectConfig.sol/struct.JBLaunchProjectConfig.md +23 -0
- package/docs/src/src/structs/JBLaunchRulesetsConfig.sol/struct.JBLaunchRulesetsConfig.md +22 -0
- package/docs/src/src/structs/JBPayDataHookRulesetConfig.sol/struct.JBPayDataHookRulesetConfig.md +51 -0
- package/docs/src/src/structs/JBPayDataHookRulesetMetadata.sol/struct.JBPayDataHookRulesetMetadata.md +66 -0
- package/docs/src/src/structs/JBQueueRulesetsConfig.sol/struct.JBQueueRulesetsConfig.md +21 -0
- package/docs/src/src/structs/JBStored721Tier.sol/struct.JBStored721Tier.md +42 -0
- package/docs/src/src/structs/README.md +18 -0
- package/foundry.lock +11 -0
- package/foundry.toml +22 -0
- package/package.json +31 -0
- package/remappings.txt +1 -0
- package/script/Deploy.s.sol +140 -0
- package/script/helpers/Hook721DeploymentLib.sol +81 -0
- package/slither-ci.config.json +10 -0
- package/sphinx.lock +476 -0
- package/src/JB721TiersHook.sol +765 -0
- package/src/JB721TiersHookDeployer.sol +114 -0
- package/src/JB721TiersHookProjectDeployer.sol +413 -0
- package/src/JB721TiersHookStore.sol +1195 -0
- package/src/abstract/ERC721.sol +484 -0
- package/src/abstract/JB721Hook.sol +279 -0
- package/src/interfaces/IJB721Hook.sol +21 -0
- package/src/interfaces/IJB721TiersHook.sol +135 -0
- package/src/interfaces/IJB721TiersHookDeployer.sol +22 -0
- package/src/interfaces/IJB721TiersHookProjectDeployer.sol +76 -0
- package/src/interfaces/IJB721TiersHookStore.sol +220 -0
- package/src/interfaces/IJB721TokenUriResolver.sol +10 -0
- package/src/libraries/JB721Constants.sol +7 -0
- package/src/libraries/JB721TiersRulesetMetadataResolver.sol +44 -0
- package/src/libraries/JBBitmap.sol +57 -0
- package/src/libraries/JBIpfsDecoder.sol +95 -0
- package/src/structs/JB721InitTiersConfig.sol +20 -0
- package/src/structs/JB721Tier.sol +39 -0
- package/src/structs/JB721TierConfig.sol +40 -0
- package/src/structs/JB721TiersHookFlags.sol +17 -0
- package/src/structs/JB721TiersMintReservesConfig.sol +9 -0
- package/src/structs/JB721TiersRulesetMetadata.sol +12 -0
- package/src/structs/JB721TiersSetDiscountPercentConfig.sol +9 -0
- package/src/structs/JBBitmapWord.sol +11 -0
- package/src/structs/JBDeploy721TiersHookConfig.sol +25 -0
- package/src/structs/JBLaunchProjectConfig.sol +18 -0
- package/src/structs/JBLaunchRulesetsConfig.sol +17 -0
- package/src/structs/JBPayDataHookRulesetConfig.sol +44 -0
- package/src/structs/JBPayDataHookRulesetMetadata.sol +46 -0
- package/src/structs/JBQueueRulesetsConfig.sol +13 -0
- package/src/structs/JBStored721Tier.sol +24 -0
- package/test/721HookAttacks.t.sol +396 -0
- package/test/E2E/Pay_Mint_Redeem_E2E.t.sol +944 -0
- package/test/invariants/TierLifecycleInvariant.t.sol +187 -0
- package/test/invariants/TieredHookStoreInvariant.t.sol +81 -0
- package/test/invariants/handlers/TierLifecycleHandler.sol +262 -0
- package/test/invariants/handlers/TierStoreHandler.sol +155 -0
- package/test/unit/JB721TiersRulesetMetadataResolver.t.sol +141 -0
- package/test/unit/JBBitmap.t.sol +169 -0
- package/test/unit/JBIpfsDecoder.t.sol +131 -0
- package/test/unit/M6_TierSupplyCheck.t.sol +220 -0
- package/test/unit/adjustTier_Unit.t.sol +1740 -0
- package/test/unit/deployer_Unit.t.sol +103 -0
- package/test/unit/getters_constructor_Unit.t.sol +548 -0
- package/test/unit/mintFor_mintReservesFor_Unit.t.sol +443 -0
- package/test/unit/pay_Unit.t.sol +1537 -0
- package/test/unit/redeem_Unit.t.sol +459 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.23;
|
|
3
|
+
|
|
4
|
+
import {IJBCashOutHook} from "@bananapus/core-v6/src/interfaces/IJBCashOutHook.sol";
|
|
5
|
+
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
6
|
+
import {IJBPayHook} from "@bananapus/core-v6/src/interfaces/IJBPayHook.sol";
|
|
7
|
+
import {IJBRulesetDataHook} from "@bananapus/core-v6/src/interfaces/IJBRulesetDataHook.sol";
|
|
8
|
+
import {IJBTerminal} from "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
|
|
9
|
+
import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
10
|
+
import {JBMetadataResolver} from "@bananapus/core-v6/src/libraries/JBMetadataResolver.sol";
|
|
11
|
+
import {JBAfterPayRecordedContext} from "@bananapus/core-v6/src/structs/JBAfterPayRecordedContext.sol";
|
|
12
|
+
import {JBAfterCashOutRecordedContext} from "@bananapus/core-v6/src/structs/JBAfterCashOutRecordedContext.sol";
|
|
13
|
+
import {JBBeforePayRecordedContext} from "@bananapus/core-v6/src/structs/JBBeforePayRecordedContext.sol";
|
|
14
|
+
import {JBBeforeCashOutRecordedContext} from "@bananapus/core-v6/src/structs/JBBeforeCashOutRecordedContext.sol";
|
|
15
|
+
import {JBCashOutHookSpecification} from "@bananapus/core-v6/src/structs/JBCashOutHookSpecification.sol";
|
|
16
|
+
import {JBPayHookSpecification} from "@bananapus/core-v6/src/structs/JBPayHookSpecification.sol";
|
|
17
|
+
import {JBRuleset} from "@bananapus/core-v6/src/structs/JBRuleset.sol";
|
|
18
|
+
import {IERC2981} from "@openzeppelin/contracts/interfaces/IERC2981.sol";
|
|
19
|
+
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
20
|
+
import {mulDiv} from "@prb/math/src/Common.sol";
|
|
21
|
+
|
|
22
|
+
import {ERC721} from "./ERC721.sol";
|
|
23
|
+
import {IJB721Hook} from "../interfaces/IJB721Hook.sol";
|
|
24
|
+
|
|
25
|
+
/// @title JB721Hook
|
|
26
|
+
/// @notice When a project which uses this hook is paid, this hook may mint NFTs to the payer, depending on this hook's
|
|
27
|
+
/// setup, the amount paid, and information specified by the payer. The project's owner can enable NFT cash outs.
|
|
28
|
+
/// through this hook, allowing the NFT holders to burn their NFTs to reclaim funds from the project (in proportion to
|
|
29
|
+
/// the NFT's price).
|
|
30
|
+
abstract contract JB721Hook is ERC721, IJB721Hook {
|
|
31
|
+
//*********************************************************************//
|
|
32
|
+
// --------------------------- custom errors ------------------------- //
|
|
33
|
+
//*********************************************************************//
|
|
34
|
+
|
|
35
|
+
error JB721Hook_InvalidCashOut();
|
|
36
|
+
error JB721Hook_InvalidPay();
|
|
37
|
+
error JB721Hook_UnauthorizedToken(uint256 tokenId, address holder);
|
|
38
|
+
error JB721Hook_UnexpectedTokenCashedOut();
|
|
39
|
+
|
|
40
|
+
//*********************************************************************//
|
|
41
|
+
// --------------- public immutable stored properties ---------------- //
|
|
42
|
+
//*********************************************************************//
|
|
43
|
+
|
|
44
|
+
/// @notice The directory of terminals and controllers for projects.
|
|
45
|
+
IJBDirectory public immutable override DIRECTORY;
|
|
46
|
+
|
|
47
|
+
/// @notice The ID used when parsing metadata.
|
|
48
|
+
address public immutable override METADATA_ID_TARGET;
|
|
49
|
+
|
|
50
|
+
//*********************************************************************//
|
|
51
|
+
// -------------------- public stored properties --------------------- //
|
|
52
|
+
//*********************************************************************//
|
|
53
|
+
|
|
54
|
+
/// @notice The ID of the project that this contract is associated with.
|
|
55
|
+
uint256 public override PROJECT_ID;
|
|
56
|
+
|
|
57
|
+
//*********************************************************************//
|
|
58
|
+
// -------------------------- constructor ---------------------------- //
|
|
59
|
+
//*********************************************************************//
|
|
60
|
+
|
|
61
|
+
/// @param directory A directory of terminals and controllers for projects.
|
|
62
|
+
constructor(IJBDirectory directory) {
|
|
63
|
+
DIRECTORY = directory;
|
|
64
|
+
// Store the address of the original hook deploy. Clones will each use the address of the instance they're based
|
|
65
|
+
// on.
|
|
66
|
+
METADATA_ID_TARGET = address(this);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
//*********************************************************************//
|
|
70
|
+
// ------------------------- external views -------------------------- //
|
|
71
|
+
//*********************************************************************//
|
|
72
|
+
|
|
73
|
+
/// @notice The data calculated before a payment is recorded in the terminal store. This data is provided to the
|
|
74
|
+
/// terminal's `pay(...)` transaction.
|
|
75
|
+
/// @dev Sets this contract as the pay hook. Part of `IJBRulesetDataHook`.
|
|
76
|
+
/// @param context The payment context passed to this contract by the `pay(...)` function.
|
|
77
|
+
/// @return weight The new `weight` to use, overriding the ruleset's `weight`.
|
|
78
|
+
/// @return hookSpecifications The amount and data to send to pay hooks (this contract) instead of adding to the
|
|
79
|
+
/// terminal's balance.
|
|
80
|
+
function beforePayRecordedWith(JBBeforePayRecordedContext calldata context)
|
|
81
|
+
public
|
|
82
|
+
view
|
|
83
|
+
virtual
|
|
84
|
+
override
|
|
85
|
+
returns (uint256 weight, JBPayHookSpecification[] memory hookSpecifications)
|
|
86
|
+
{
|
|
87
|
+
// Forward the received weight and memo, and use this contract as the only pay hook.
|
|
88
|
+
weight = context.weight;
|
|
89
|
+
hookSpecifications = new JBPayHookSpecification[](1);
|
|
90
|
+
hookSpecifications[0] = JBPayHookSpecification({hook: this, amount: 0, metadata: bytes("")});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/// @notice The data calculated before a cash out is recorded in the terminal store. This data is provided to the
|
|
94
|
+
/// terminal's `cashOutTokensOf(...)` transaction.
|
|
95
|
+
/// @dev Sets this contract as the cash out hook. Part of `IJBRulesetDataHook`.
|
|
96
|
+
/// @dev This function is used for NFT cash outs, and will only be called if the project's ruleset has
|
|
97
|
+
/// `useDataHookForCashOut` set to `true`.
|
|
98
|
+
/// @param context The cash out context passed to this contract by the `cashOutTokensOf(...)` function.
|
|
99
|
+
/// @return cashOutTaxRate The cash out tax rate influencing the reclaim amount.
|
|
100
|
+
/// @return cashOutCount The amount of tokens that should be considered cashed out.
|
|
101
|
+
/// @return totalSupply The total amount of tokens that are considered to be existing.
|
|
102
|
+
/// @return hookSpecifications The amount and data to send to cash out hooks (this contract) instead of returning to
|
|
103
|
+
/// the beneficiary.
|
|
104
|
+
function beforeCashOutRecordedWith(JBBeforeCashOutRecordedContext calldata context)
|
|
105
|
+
public
|
|
106
|
+
view
|
|
107
|
+
virtual
|
|
108
|
+
override
|
|
109
|
+
returns (
|
|
110
|
+
uint256 cashOutTaxRate,
|
|
111
|
+
uint256 cashOutCount,
|
|
112
|
+
uint256 totalSupply,
|
|
113
|
+
JBCashOutHookSpecification[] memory hookSpecifications
|
|
114
|
+
)
|
|
115
|
+
{
|
|
116
|
+
// Make sure (fungible) project tokens aren't also being cashed out.
|
|
117
|
+
if (context.cashOutCount > 0) revert JB721Hook_UnexpectedTokenCashedOut();
|
|
118
|
+
|
|
119
|
+
// Fetch the cash out hook metadata using the corresponding metadata ID.
|
|
120
|
+
(bool metadataExists, bytes memory metadata) = JBMetadataResolver.getDataFor({
|
|
121
|
+
id: JBMetadataResolver.getId({purpose: "cashOut", target: METADATA_ID_TARGET}), metadata: context.metadata
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Use this contract as the only cash out hook.
|
|
125
|
+
hookSpecifications = new JBCashOutHookSpecification[](1);
|
|
126
|
+
hookSpecifications[0] = JBCashOutHookSpecification({hook: this, amount: 0, metadata: bytes("")});
|
|
127
|
+
|
|
128
|
+
uint256[] memory decodedTokenIds;
|
|
129
|
+
|
|
130
|
+
// Decode the metadata.
|
|
131
|
+
if (metadataExists) decodedTokenIds = abi.decode(metadata, (uint256[]));
|
|
132
|
+
|
|
133
|
+
// Use the cash out weight of the provided 721s.
|
|
134
|
+
cashOutCount = cashOutWeightOf({tokenIds: decodedTokenIds, context: context});
|
|
135
|
+
|
|
136
|
+
// Use the total cash out weight of the 721s.
|
|
137
|
+
totalSupply = totalCashOutWeight(context);
|
|
138
|
+
|
|
139
|
+
// Use the cash out tax rate from the context.
|
|
140
|
+
cashOutTaxRate = context.cashOutTaxRate;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/// @notice Required by the IJBRulesetDataHook interfaces. Return false to not leak any permissions.
|
|
144
|
+
function hasMintPermissionFor(uint256, JBRuleset memory, address) external pure returns (bool) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
//*********************************************************************//
|
|
149
|
+
// -------------------------- public views --------------------------- //
|
|
150
|
+
//*********************************************************************//
|
|
151
|
+
|
|
152
|
+
/// @notice Returns the cumulative cash out weight of the specified token IDs relative to the
|
|
153
|
+
/// `totalCashOutWeight`.
|
|
154
|
+
/// @param tokenIds The NFT token IDs to calculate the cumulative cash out weight of.
|
|
155
|
+
/// @param context The cash out context passed to this contract by the `cashOutTokensOf(...)` function.
|
|
156
|
+
/// @return The cumulative cash out weight of the specified token IDs.
|
|
157
|
+
function cashOutWeightOf(
|
|
158
|
+
uint256[] memory tokenIds,
|
|
159
|
+
JBBeforeCashOutRecordedContext calldata context
|
|
160
|
+
)
|
|
161
|
+
public
|
|
162
|
+
view
|
|
163
|
+
virtual
|
|
164
|
+
returns (uint256)
|
|
165
|
+
{
|
|
166
|
+
tokenIds; // Prevents unused var compiler and natspec complaints.
|
|
167
|
+
context; // Prevents unused var compiler and natspec complaints.
|
|
168
|
+
return 0;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/// @notice Indicates if this contract adheres to the specified interface.
|
|
172
|
+
/// @dev See {IERC165-supportsInterface}.
|
|
173
|
+
/// @param interfaceId The ID of the interface to check for adherence to.
|
|
174
|
+
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, IERC165) returns (bool) {
|
|
175
|
+
return interfaceId == type(IJB721Hook).interfaceId || interfaceId == type(IJBRulesetDataHook).interfaceId
|
|
176
|
+
|| interfaceId == type(IJBPayHook).interfaceId || interfaceId == type(IJBCashOutHook).interfaceId
|
|
177
|
+
|| interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/// @notice Calculates the cumulative cash out weight of all NFT token IDs.
|
|
181
|
+
/// @param context The cash out context passed to this contract by the `cashOutTokensOf(...)` function.
|
|
182
|
+
/// @return The total cumulative cash out weight of all NFT token IDs.
|
|
183
|
+
function totalCashOutWeight(JBBeforeCashOutRecordedContext calldata context) public view virtual returns (uint256) {
|
|
184
|
+
context; // Prevents unused var compiler and natspec complaints.
|
|
185
|
+
return 0;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
//*********************************************************************//
|
|
189
|
+
// ---------------------- external transactions ---------------------- //
|
|
190
|
+
//*********************************************************************//
|
|
191
|
+
|
|
192
|
+
/// @notice Mints one or more NFTs to the `context.beneficiary` upon payment if conditions are met. Part of
|
|
193
|
+
/// `IJBPayHook`.
|
|
194
|
+
/// @dev Reverts if the calling contract is not one of the project's terminals.
|
|
195
|
+
/// @param context The payment context passed in by the terminal.
|
|
196
|
+
// slither-disable-next-line locked-ether
|
|
197
|
+
function afterPayRecordedWith(JBAfterPayRecordedContext calldata context) external payable virtual override {
|
|
198
|
+
uint256 projectId = PROJECT_ID;
|
|
199
|
+
|
|
200
|
+
// Make sure the caller is a terminal of the project, and that the call is being made on behalf of an
|
|
201
|
+
// interaction with the correct project.
|
|
202
|
+
if (
|
|
203
|
+
msg.value != 0 || !DIRECTORY.isTerminalOf({projectId: projectId, terminal: IJBTerminal(msg.sender)})
|
|
204
|
+
|| context.projectId != projectId
|
|
205
|
+
) revert JB721Hook_InvalidPay();
|
|
206
|
+
|
|
207
|
+
// Process the payment.
|
|
208
|
+
_processPayment(context);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/// @notice Burns the specified NFTs upon token holder cash out, reclaiming funds from the project's balance for
|
|
212
|
+
/// `context.beneficiary`. Part of `IJBCashOutHook`.
|
|
213
|
+
/// @dev Reverts if the calling contract is not one of the project's terminals.
|
|
214
|
+
/// @param context The cash out context passed in by the terminal.
|
|
215
|
+
// slither-disable-next-line locked-ether
|
|
216
|
+
function afterCashOutRecordedWith(JBAfterCashOutRecordedContext calldata context)
|
|
217
|
+
external
|
|
218
|
+
payable
|
|
219
|
+
virtual
|
|
220
|
+
override
|
|
221
|
+
{
|
|
222
|
+
// Keep a reference to the project ID.
|
|
223
|
+
uint256 projectId = PROJECT_ID;
|
|
224
|
+
|
|
225
|
+
// Make sure the caller is a terminal of the project, and that the call is being made on behalf of an
|
|
226
|
+
// interaction with the correct project.
|
|
227
|
+
if (
|
|
228
|
+
msg.value != 0 || !DIRECTORY.isTerminalOf({projectId: projectId, terminal: IJBTerminal(msg.sender)})
|
|
229
|
+
|| context.projectId != projectId
|
|
230
|
+
) revert JB721Hook_InvalidCashOut();
|
|
231
|
+
|
|
232
|
+
// Fetch the cash out hook metadata using the corresponding metadata ID.
|
|
233
|
+
(bool metadataExists, bytes memory metadata) = JBMetadataResolver.getDataFor({
|
|
234
|
+
id: JBMetadataResolver.getId({purpose: "cashOut", target: METADATA_ID_TARGET}),
|
|
235
|
+
metadata: context.cashOutMetadata
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
uint256[] memory decodedTokenIds;
|
|
239
|
+
|
|
240
|
+
// Decode the metadata.
|
|
241
|
+
if (metadataExists) decodedTokenIds = abi.decode(metadata, (uint256[]));
|
|
242
|
+
|
|
243
|
+
// Iterate through the NFTs, burning them if the owner is correct.
|
|
244
|
+
for (uint256 i; i < decodedTokenIds.length; i++) {
|
|
245
|
+
// Set the current NFT's token ID.
|
|
246
|
+
uint256 tokenId = decodedTokenIds[i];
|
|
247
|
+
|
|
248
|
+
// Make sure the token's owner is correct.
|
|
249
|
+
if (_ownerOf(tokenId) != context.holder) revert JB721Hook_UnauthorizedToken(tokenId, context.holder);
|
|
250
|
+
|
|
251
|
+
// Burn the token.
|
|
252
|
+
_burn(tokenId);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Call the hook.
|
|
256
|
+
_didBurn(decodedTokenIds);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
//*********************************************************************//
|
|
260
|
+
// ---------------------- internal transactions ---------------------- //
|
|
261
|
+
//*********************************************************************//
|
|
262
|
+
|
|
263
|
+
/// @notice Initializes the contract by associating it with a project and adding ERC721 details.
|
|
264
|
+
/// @param projectId The ID of the project that this contract is associated with.
|
|
265
|
+
/// @param name The name of the NFT collection.
|
|
266
|
+
/// @param symbol The symbol representing the NFT collection.
|
|
267
|
+
function _initialize(uint256 projectId, string memory name, string memory symbol) internal {
|
|
268
|
+
ERC721._initialize({name_: name, symbol_: symbol});
|
|
269
|
+
PROJECT_ID = projectId;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/// @notice Executes after NFTs have been burned via cash out.
|
|
273
|
+
/// @param tokenIds The token IDs of the NFTs that were burned.
|
|
274
|
+
function _didBurn(uint256[] memory tokenIds) internal virtual;
|
|
275
|
+
|
|
276
|
+
/// @notice Process a received payment.
|
|
277
|
+
/// @param context The payment context passed in by the terminal.
|
|
278
|
+
function _processPayment(JBAfterPayRecordedContext calldata context) internal virtual;
|
|
279
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import {IJBCashOutHook} from "@bananapus/core-v6/src/interfaces/IJBCashOutHook.sol";
|
|
5
|
+
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
6
|
+
import {IJBPayHook} from "@bananapus/core-v6/src/interfaces/IJBPayHook.sol";
|
|
7
|
+
import {IJBRulesetDataHook} from "@bananapus/core-v6/src/interfaces/IJBRulesetDataHook.sol";
|
|
8
|
+
|
|
9
|
+
interface IJB721Hook is IJBRulesetDataHook, IJBPayHook, IJBCashOutHook {
|
|
10
|
+
/// @notice The directory of terminals and controllers for projects.
|
|
11
|
+
/// @return The directory contract.
|
|
12
|
+
function DIRECTORY() external view returns (IJBDirectory);
|
|
13
|
+
|
|
14
|
+
/// @notice The ID used when parsing metadata.
|
|
15
|
+
/// @return The address of the metadata ID target.
|
|
16
|
+
function METADATA_ID_TARGET() external view returns (address);
|
|
17
|
+
|
|
18
|
+
/// @notice The ID of the project that this contract is associated with.
|
|
19
|
+
/// @return The project ID.
|
|
20
|
+
function PROJECT_ID() external view returns (uint256);
|
|
21
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import {IJBPrices} from "@bananapus/core-v6/src/interfaces/IJBPrices.sol";
|
|
5
|
+
import {IJBRulesets} from "@bananapus/core-v6/src/interfaces/IJBRulesets.sol";
|
|
6
|
+
|
|
7
|
+
import {IJB721Hook} from "./IJB721Hook.sol";
|
|
8
|
+
import {IJB721TiersHookStore} from "./IJB721TiersHookStore.sol";
|
|
9
|
+
import {IJB721TokenUriResolver} from "./IJB721TokenUriResolver.sol";
|
|
10
|
+
import {JB721InitTiersConfig} from "../structs/JB721InitTiersConfig.sol";
|
|
11
|
+
import {JB721TierConfig} from "../structs/JB721TierConfig.sol";
|
|
12
|
+
import {JB721TiersHookFlags} from "../structs/JB721TiersHookFlags.sol";
|
|
13
|
+
import {JB721TiersMintReservesConfig} from "../structs/JB721TiersMintReservesConfig.sol";
|
|
14
|
+
import {JB721TiersSetDiscountPercentConfig} from "../structs/JB721TiersSetDiscountPercentConfig.sol";
|
|
15
|
+
|
|
16
|
+
interface IJB721TiersHook is IJB721Hook {
|
|
17
|
+
event AddPayCredits(
|
|
18
|
+
uint256 indexed amount, uint256 indexed newTotalCredits, address indexed account, address caller
|
|
19
|
+
);
|
|
20
|
+
event AddTier(uint256 indexed tierId, JB721TierConfig tier, address caller);
|
|
21
|
+
event Mint(
|
|
22
|
+
uint256 indexed tokenId,
|
|
23
|
+
uint256 indexed tierId,
|
|
24
|
+
address indexed beneficiary,
|
|
25
|
+
uint256 totalAmountPaid,
|
|
26
|
+
address caller
|
|
27
|
+
);
|
|
28
|
+
event MintReservedNft(uint256 indexed tokenId, uint256 indexed tierId, address indexed beneficiary, address caller);
|
|
29
|
+
event RemoveTier(uint256 indexed tierId, address caller);
|
|
30
|
+
event SetBaseUri(string indexed baseUri, address caller);
|
|
31
|
+
event SetContractUri(string indexed uri, address caller);
|
|
32
|
+
event SetDiscountPercent(uint256 indexed tierId, uint256 discountPercent, address caller);
|
|
33
|
+
event SetEncodedIPFSUri(uint256 indexed tierId, bytes32 encodedUri, address caller);
|
|
34
|
+
event SetTokenUriResolver(IJB721TokenUriResolver indexed resolver, address caller);
|
|
35
|
+
event UsePayCredits(
|
|
36
|
+
uint256 indexed amount, uint256 indexed newTotalCredits, address indexed account, address caller
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
/// @notice The contract storing and managing project rulesets.
|
|
40
|
+
/// @return The rulesets contract.
|
|
41
|
+
function RULESETS() external view returns (IJBRulesets);
|
|
42
|
+
|
|
43
|
+
/// @notice The contract that stores and manages data for this contract's NFTs.
|
|
44
|
+
/// @return The store contract.
|
|
45
|
+
function STORE() external view returns (IJB721TiersHookStore);
|
|
46
|
+
|
|
47
|
+
/// @notice The base URI for the NFT `tokenUris`.
|
|
48
|
+
/// @return The base URI string.
|
|
49
|
+
function baseURI() external view returns (string memory);
|
|
50
|
+
|
|
51
|
+
/// @notice This contract's metadata URI.
|
|
52
|
+
/// @return The contract URI string.
|
|
53
|
+
function contractURI() external view returns (string memory);
|
|
54
|
+
|
|
55
|
+
/// @notice The first owner of an NFT.
|
|
56
|
+
/// @param tokenId The token ID of the NFT to get the first owner of.
|
|
57
|
+
/// @return The address of the NFT's first owner.
|
|
58
|
+
function firstOwnerOf(uint256 tokenId) external view returns (address);
|
|
59
|
+
|
|
60
|
+
/// @notice The amount of NFT credits the address has.
|
|
61
|
+
/// @param addr The address to get the NFT credits balance of.
|
|
62
|
+
/// @return The amount of credits the address has.
|
|
63
|
+
function payCreditsOf(address addr) external view returns (uint256);
|
|
64
|
+
|
|
65
|
+
/// @notice Context for the pricing of this hook's tiers.
|
|
66
|
+
/// @return currency The currency used for tier prices.
|
|
67
|
+
/// @return decimals The amount of decimals being used in tier prices.
|
|
68
|
+
/// @return prices The prices contract used to resolve the value of payments in other currencies.
|
|
69
|
+
function pricingContext() external view returns (uint256 currency, uint256 decimals, IJBPrices prices);
|
|
70
|
+
|
|
71
|
+
/// @notice Add or remove tiers.
|
|
72
|
+
/// @param tiersToAdd The tiers to add, as an array of `JB721TierConfig` structs.
|
|
73
|
+
/// @param tierIdsToRemove The tiers to remove, as an array of tier IDs.
|
|
74
|
+
function adjustTiers(JB721TierConfig[] calldata tiersToAdd, uint256[] calldata tierIdsToRemove) external;
|
|
75
|
+
|
|
76
|
+
/// @notice Initializes a cloned copy of the original `JB721TiersHook` contract.
|
|
77
|
+
/// @param projectId The ID of the project this hook is associated with.
|
|
78
|
+
/// @param name The name of the NFT collection.
|
|
79
|
+
/// @param symbol The symbol representing the NFT collection.
|
|
80
|
+
/// @param baseUri The URI to use as a base for full NFT `tokenUri`s.
|
|
81
|
+
/// @param tokenUriResolver An optional contract responsible for resolving the token URI for each NFT.
|
|
82
|
+
/// @param contractUri A URI where this contract's metadata can be found.
|
|
83
|
+
/// @param tiersConfig The NFT tiers and pricing context to initialize the hook with.
|
|
84
|
+
/// @param flags A set of additional options which dictate how the hook behaves.
|
|
85
|
+
function initialize(
|
|
86
|
+
uint256 projectId,
|
|
87
|
+
string memory name,
|
|
88
|
+
string memory symbol,
|
|
89
|
+
string memory baseUri,
|
|
90
|
+
IJB721TokenUriResolver tokenUriResolver,
|
|
91
|
+
string memory contractUri,
|
|
92
|
+
JB721InitTiersConfig memory tiersConfig,
|
|
93
|
+
JB721TiersHookFlags memory flags
|
|
94
|
+
)
|
|
95
|
+
external;
|
|
96
|
+
|
|
97
|
+
/// @notice Set the discount percent for a tier.
|
|
98
|
+
/// @param tierId The ID of the tier to set the discount of.
|
|
99
|
+
/// @param discountPercent The discount percent to set.
|
|
100
|
+
function setDiscountPercentOf(uint256 tierId, uint256 discountPercent) external;
|
|
101
|
+
|
|
102
|
+
/// @notice Set the discount percent for multiple tiers.
|
|
103
|
+
/// @param configs The configs to set the discount percent for.
|
|
104
|
+
function setDiscountPercentsOf(JB721TiersSetDiscountPercentConfig[] calldata configs) external;
|
|
105
|
+
|
|
106
|
+
/// @notice Manually mint NFTs from the provided tiers.
|
|
107
|
+
/// @param tierIds The IDs of the tiers to mint from.
|
|
108
|
+
/// @param beneficiary The address to mint to.
|
|
109
|
+
/// @return tokenIds The IDs of the newly minted tokens.
|
|
110
|
+
function mintFor(uint16[] calldata tierIds, address beneficiary) external returns (uint256[] memory tokenIds);
|
|
111
|
+
|
|
112
|
+
/// @notice Mint pending reserved NFTs based on the provided information.
|
|
113
|
+
/// @param reserveMintConfigs Contains information about how many reserved tokens to mint for each tier.
|
|
114
|
+
function mintPendingReservesFor(JB721TiersMintReservesConfig[] calldata reserveMintConfigs) external;
|
|
115
|
+
|
|
116
|
+
/// @notice Mint pending reserved NFTs for a specific tier.
|
|
117
|
+
/// @param tierId The ID of the tier to mint reserved NFTs from.
|
|
118
|
+
/// @param count The number of reserved NFTs to mint.
|
|
119
|
+
function mintPendingReservesFor(uint256 tierId, uint256 count) external;
|
|
120
|
+
|
|
121
|
+
/// @notice Update this hook's URI metadata properties.
|
|
122
|
+
/// @param baseUri The new base URI.
|
|
123
|
+
/// @param contractUri The new contract URI.
|
|
124
|
+
/// @param tokenUriResolver The new URI resolver.
|
|
125
|
+
/// @param encodedIPFSUriTierId The ID of the tier to set the encoded IPFS URI of.
|
|
126
|
+
/// @param encodedIPFSUri The encoded IPFS URI to set.
|
|
127
|
+
function setMetadata(
|
|
128
|
+
string calldata baseUri,
|
|
129
|
+
string calldata contractUri,
|
|
130
|
+
IJB721TokenUriResolver tokenUriResolver,
|
|
131
|
+
uint256 encodedIPFSUriTierId,
|
|
132
|
+
bytes32 encodedIPFSUri
|
|
133
|
+
)
|
|
134
|
+
external;
|
|
135
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import {IJB721TiersHook} from "./IJB721TiersHook.sol";
|
|
5
|
+
import {JBDeploy721TiersHookConfig} from "../structs/JBDeploy721TiersHookConfig.sol";
|
|
6
|
+
|
|
7
|
+
interface IJB721TiersHookDeployer {
|
|
8
|
+
event HookDeployed(uint256 indexed projectId, IJB721TiersHook hook, address caller);
|
|
9
|
+
|
|
10
|
+
/// @notice Deploys a 721 tiers hook for the specified project.
|
|
11
|
+
/// @param projectId The ID of the project to deploy the hook for.
|
|
12
|
+
/// @param deployTiersHookConfig The config to deploy the hook with.
|
|
13
|
+
/// @param salt A salt to use for the deterministic deployment.
|
|
14
|
+
/// @return newHook The address of the newly deployed hook.
|
|
15
|
+
function deployHookFor(
|
|
16
|
+
uint256 projectId,
|
|
17
|
+
JBDeploy721TiersHookConfig memory deployTiersHookConfig,
|
|
18
|
+
bytes32 salt
|
|
19
|
+
)
|
|
20
|
+
external
|
|
21
|
+
returns (IJB721TiersHook newHook);
|
|
22
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
5
|
+
import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
|
|
6
|
+
|
|
7
|
+
import {IJB721TiersHook} from "./IJB721TiersHook.sol";
|
|
8
|
+
import {IJB721TiersHookDeployer} from "./IJB721TiersHookDeployer.sol";
|
|
9
|
+
import {JBDeploy721TiersHookConfig} from "../structs/JBDeploy721TiersHookConfig.sol";
|
|
10
|
+
import {JBLaunchProjectConfig} from "../structs/JBLaunchProjectConfig.sol";
|
|
11
|
+
import {JBLaunchRulesetsConfig} from "../structs/JBLaunchRulesetsConfig.sol";
|
|
12
|
+
import {JBQueueRulesetsConfig} from "../structs/JBQueueRulesetsConfig.sol";
|
|
13
|
+
|
|
14
|
+
interface IJB721TiersHookProjectDeployer {
|
|
15
|
+
/// @notice The directory of terminals and controllers for projects.
|
|
16
|
+
/// @return The directory contract.
|
|
17
|
+
function DIRECTORY() external view returns (IJBDirectory);
|
|
18
|
+
|
|
19
|
+
/// @notice The 721 tiers hook deployer.
|
|
20
|
+
/// @return The hook deployer contract.
|
|
21
|
+
function HOOK_DEPLOYER() external view returns (IJB721TiersHookDeployer);
|
|
22
|
+
|
|
23
|
+
/// @notice Launches a new project with a 721 tiers hook attached.
|
|
24
|
+
/// @param owner The address to set as the owner of the project.
|
|
25
|
+
/// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook.
|
|
26
|
+
/// @param launchProjectConfig Configuration which dictates the behavior of the project.
|
|
27
|
+
/// @param controller The controller that the project's rulesets will be queued with.
|
|
28
|
+
/// @param salt A salt to use for the deterministic deployment.
|
|
29
|
+
/// @return projectId The ID of the newly launched project.
|
|
30
|
+
/// @return hook The 721 tiers hook that was deployed for the project.
|
|
31
|
+
function launchProjectFor(
|
|
32
|
+
address owner,
|
|
33
|
+
JBDeploy721TiersHookConfig memory deployTiersHookConfig,
|
|
34
|
+
JBLaunchProjectConfig memory launchProjectConfig,
|
|
35
|
+
IJBController controller,
|
|
36
|
+
bytes32 salt
|
|
37
|
+
)
|
|
38
|
+
external
|
|
39
|
+
returns (uint256 projectId, IJB721TiersHook hook);
|
|
40
|
+
|
|
41
|
+
/// @notice Launches rulesets for a project with an attached 721 tiers hook.
|
|
42
|
+
/// @param projectId The ID of the project that rulesets are being launched for.
|
|
43
|
+
/// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook.
|
|
44
|
+
/// @param launchRulesetsConfig Configuration which dictates the project's new rulesets.
|
|
45
|
+
/// @param controller The controller that the project's rulesets will be queued with.
|
|
46
|
+
/// @param salt A salt to use for the deterministic deployment.
|
|
47
|
+
/// @return rulesetId The ID of the successfully created ruleset.
|
|
48
|
+
/// @return hook The 721 tiers hook that was deployed for the project.
|
|
49
|
+
function launchRulesetsFor(
|
|
50
|
+
uint256 projectId,
|
|
51
|
+
JBDeploy721TiersHookConfig memory deployTiersHookConfig,
|
|
52
|
+
JBLaunchRulesetsConfig memory launchRulesetsConfig,
|
|
53
|
+
IJBController controller,
|
|
54
|
+
bytes32 salt
|
|
55
|
+
)
|
|
56
|
+
external
|
|
57
|
+
returns (uint256 rulesetId, IJB721TiersHook hook);
|
|
58
|
+
|
|
59
|
+
/// @notice Queues rulesets for a project with an attached 721 tiers hook.
|
|
60
|
+
/// @param projectId The ID of the project that rulesets are being queued for.
|
|
61
|
+
/// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook.
|
|
62
|
+
/// @param queueRulesetsConfig Configuration which dictates the project's newly queued rulesets.
|
|
63
|
+
/// @param controller The controller that the project's rulesets will be queued with.
|
|
64
|
+
/// @param salt A salt to use for the deterministic deployment.
|
|
65
|
+
/// @return rulesetId The ID of the successfully created ruleset.
|
|
66
|
+
/// @return hook The 721 tiers hook that was deployed for the project.
|
|
67
|
+
function queueRulesetsOf(
|
|
68
|
+
uint256 projectId,
|
|
69
|
+
JBDeploy721TiersHookConfig memory deployTiersHookConfig,
|
|
70
|
+
JBQueueRulesetsConfig memory queueRulesetsConfig,
|
|
71
|
+
IJBController controller,
|
|
72
|
+
bytes32 salt
|
|
73
|
+
)
|
|
74
|
+
external
|
|
75
|
+
returns (uint256 rulesetId, IJB721TiersHook hook);
|
|
76
|
+
}
|