@bananapus/721-hook-v6 0.0.50 → 0.0.52
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/package.json +4 -4
- package/script/Deploy.s.sol +36 -39
- package/script/helpers/Hook721DeploymentLib.sol +18 -24
- package/src/JB721Checkpoints.sol +11 -11
- package/src/JB721TiersHook.sol +33 -36
- package/src/JB721TiersHookDeployer.sol +1 -1
- package/src/JB721TiersHookProjectDeployer.sol +5 -8
- package/src/JB721TiersHookStore.sol +9 -9
- package/src/abstract/ERC721.sol +3 -3
- package/src/abstract/JB721Hook.sol +15 -13
- package/src/interfaces/IJB721Checkpoints.sol +3 -3
- package/src/interfaces/IJB721Hook.sol +1 -1
- package/src/interfaces/IJB721TiersHook.sol +8 -9
- package/src/interfaces/IJB721TiersHookStore.sol +3 -3
- package/src/libraries/JB721TiersHookLib.sol +182 -43
- package/src/structs/JB721Tier.sol +2 -2
- package/src/structs/JB721TierConfig.sol +2 -2
- package/src/structs/JBPayDataHookRulesetMetadata.sol +0 -3
- package/test/utils/ForTest_JB721TiersHook.sol +1 -1
- package/test/utils/UnitTestSetup.sol +8 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bananapus/721-hook-v6",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.52",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@bananapus/address-registry-v6": "^0.0.25",
|
|
32
|
-
"@bananapus/core-v6": "^0.0.
|
|
33
|
-
"@bananapus/ownable-v6": "^0.0.
|
|
34
|
-
"@bananapus/permission-ids-v6": "^0.0.
|
|
32
|
+
"@bananapus/core-v6": "^0.0.54",
|
|
33
|
+
"@bananapus/ownable-v6": "^0.0.25",
|
|
34
|
+
"@bananapus/permission-ids-v6": "^0.0.25",
|
|
35
35
|
"@openzeppelin/contracts": "5.6.1",
|
|
36
36
|
"@prb/math": "4.1.1",
|
|
37
37
|
"solady": "0.1.26"
|
package/script/Deploy.s.sol
CHANGED
|
@@ -24,20 +24,14 @@ contract DeployScript is Script, Sphinx {
|
|
|
24
24
|
AddressRegistryDeployment registry;
|
|
25
25
|
|
|
26
26
|
/// @notice The address that is allowed to forward calls to the terminal and controller on a users behalf.
|
|
27
|
-
|
|
28
|
-
address private TRUSTED_FORWARDER;
|
|
27
|
+
address private trustedForwarder;
|
|
29
28
|
|
|
30
29
|
/// @notice the salts that are used to deploy the contracts.
|
|
31
|
-
|
|
32
|
-
bytes32
|
|
33
|
-
|
|
34
|
-
bytes32
|
|
35
|
-
|
|
36
|
-
bytes32 HOOK_STORE_SALT = "JB721TiersHookStoreV6_";
|
|
37
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
38
|
-
bytes32 PROJECT_DEPLOYER_SALT = "JB721TiersHookProjectDeployerV6";
|
|
39
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
40
|
-
bytes32 CHECKPOINTS_DEPLOYER_SALT = "JB721CheckpointsDeployerV6";
|
|
30
|
+
bytes32 private constant _HOOK_SALT = "JB721TiersHookV6_";
|
|
31
|
+
bytes32 private constant _HOOK_DEPLOYER_SALT = "JB721TiersHookDeployerV6_";
|
|
32
|
+
bytes32 private constant _HOOK_STORE_SALT = "JB721TiersHookStoreV6_";
|
|
33
|
+
bytes32 private constant _PROJECT_DEPLOYER_SALT = "JB721TiersHookProjectDeployerV6";
|
|
34
|
+
bytes32 private constant _CHECKPOINTS_DEPLOYER_SALT = "JB721CheckpointsDeployerV6";
|
|
41
35
|
|
|
42
36
|
function configureSphinx() public override {
|
|
43
37
|
sphinxConfig.projectName = "nana-721-hook-v6";
|
|
@@ -53,7 +47,7 @@ contract DeployScript is Script, Sphinx {
|
|
|
53
47
|
);
|
|
54
48
|
|
|
55
49
|
// We use the same trusted forwarder as the core deployment.
|
|
56
|
-
|
|
50
|
+
trustedForwarder = core.permissions.trustedForwarder();
|
|
57
51
|
|
|
58
52
|
registry = AddressRegistryDeploymentLib.getDeployment(
|
|
59
53
|
vm.envOr(
|
|
@@ -74,25 +68,25 @@ contract DeployScript is Script, Sphinx {
|
|
|
74
68
|
{
|
|
75
69
|
// Perform the check for the store.
|
|
76
70
|
(address _store, bool _storeIsDeployed) = _isDeployed({
|
|
77
|
-
salt:
|
|
71
|
+
salt: _HOOK_STORE_SALT, creationCode: type(JB721TiersHookStore).creationCode, arguments: ""
|
|
78
72
|
});
|
|
79
73
|
|
|
80
74
|
// Deploy it if it has not been deployed yet.
|
|
81
|
-
store = !_storeIsDeployed ? new JB721TiersHookStore{salt:
|
|
75
|
+
store = !_storeIsDeployed ? new JB721TiersHookStore{salt: _HOOK_STORE_SALT}() : JB721TiersHookStore(_store);
|
|
82
76
|
}
|
|
83
77
|
|
|
84
78
|
JB721CheckpointsDeployer checkpointsDeployer;
|
|
85
79
|
{
|
|
86
80
|
// Perform the check for the deployer.
|
|
87
81
|
(address _deployer, bool _deployerIsDeployed) = _isDeployed({
|
|
88
|
-
salt:
|
|
82
|
+
salt: _CHECKPOINTS_DEPLOYER_SALT,
|
|
89
83
|
creationCode: type(JB721CheckpointsDeployer).creationCode,
|
|
90
84
|
arguments: abi.encode(store)
|
|
91
85
|
});
|
|
92
86
|
|
|
93
87
|
// Deploy it if it has not been deployed yet.
|
|
94
88
|
checkpointsDeployer = !_deployerIsDeployed
|
|
95
|
-
? new JB721CheckpointsDeployer{salt:
|
|
89
|
+
? new JB721CheckpointsDeployer{salt: _CHECKPOINTS_DEPLOYER_SALT}(store)
|
|
96
90
|
: JB721CheckpointsDeployer(_deployer);
|
|
97
91
|
}
|
|
98
92
|
|
|
@@ -100,7 +94,7 @@ contract DeployScript is Script, Sphinx {
|
|
|
100
94
|
{
|
|
101
95
|
// Perform the check for the registry.
|
|
102
96
|
(address _hook, bool _hookIsDeployed) = _isDeployed({
|
|
103
|
-
salt:
|
|
97
|
+
salt: _HOOK_SALT,
|
|
104
98
|
creationCode: type(JB721TiersHook).creationCode,
|
|
105
99
|
arguments: abi.encode(
|
|
106
100
|
core.directory,
|
|
@@ -110,22 +104,22 @@ contract DeployScript is Script, Sphinx {
|
|
|
110
104
|
store,
|
|
111
105
|
core.splits,
|
|
112
106
|
checkpointsDeployer,
|
|
113
|
-
|
|
107
|
+
trustedForwarder
|
|
114
108
|
)
|
|
115
109
|
});
|
|
116
110
|
|
|
117
111
|
// Deploy it if it has not been deployed yet.
|
|
118
112
|
hook = !_hookIsDeployed
|
|
119
|
-
? new JB721TiersHook{salt:
|
|
120
|
-
core.directory,
|
|
121
|
-
core.permissions,
|
|
122
|
-
core.prices,
|
|
123
|
-
core.rulesets,
|
|
124
|
-
store,
|
|
125
|
-
core.splits,
|
|
126
|
-
IJB721CheckpointsDeployer(address(checkpointsDeployer)),
|
|
127
|
-
|
|
128
|
-
)
|
|
113
|
+
? new JB721TiersHook{salt: _HOOK_SALT}({
|
|
114
|
+
directory: core.directory,
|
|
115
|
+
permissions: core.permissions,
|
|
116
|
+
prices: core.prices,
|
|
117
|
+
rulesets: core.rulesets,
|
|
118
|
+
store: store,
|
|
119
|
+
splits: core.splits,
|
|
120
|
+
checkpointsDeployer: IJB721CheckpointsDeployer(address(checkpointsDeployer)),
|
|
121
|
+
trustedForwarder: trustedForwarder
|
|
122
|
+
})
|
|
129
123
|
: JB721TiersHook(_hook);
|
|
130
124
|
}
|
|
131
125
|
|
|
@@ -133,15 +127,15 @@ contract DeployScript is Script, Sphinx {
|
|
|
133
127
|
{
|
|
134
128
|
// Perform the check for the registry.
|
|
135
129
|
(address _hookDeployer, bool _hookDeployerIsDeployed) = _isDeployed({
|
|
136
|
-
salt:
|
|
130
|
+
salt: _HOOK_DEPLOYER_SALT,
|
|
137
131
|
creationCode: type(JB721TiersHookDeployer).creationCode,
|
|
138
|
-
arguments: abi.encode(hook, store, registry.registry,
|
|
132
|
+
arguments: abi.encode(hook, store, registry.registry, trustedForwarder)
|
|
139
133
|
});
|
|
140
134
|
|
|
141
135
|
hookDeployer = !_hookDeployerIsDeployed
|
|
142
|
-
? new JB721TiersHookDeployer{salt:
|
|
143
|
-
hook, store, registry.registry,
|
|
144
|
-
)
|
|
136
|
+
? new JB721TiersHookDeployer{salt: _HOOK_DEPLOYER_SALT}({
|
|
137
|
+
hook: hook, store: store, addressRegistry: registry.registry, trustedForwarder: trustedForwarder
|
|
138
|
+
})
|
|
145
139
|
: JB721TiersHookDeployer(_hookDeployer);
|
|
146
140
|
}
|
|
147
141
|
|
|
@@ -149,15 +143,18 @@ contract DeployScript is Script, Sphinx {
|
|
|
149
143
|
{
|
|
150
144
|
// Perform the check for the registry.
|
|
151
145
|
(address _projectDeployer, bool _projectDeployerIsdeployed) = _isDeployed({
|
|
152
|
-
salt:
|
|
146
|
+
salt: _PROJECT_DEPLOYER_SALT,
|
|
153
147
|
creationCode: type(JB721TiersHookProjectDeployer).creationCode,
|
|
154
|
-
arguments: abi.encode(core.directory, core.permissions, hookDeployer,
|
|
148
|
+
arguments: abi.encode(core.directory, core.permissions, hookDeployer, trustedForwarder)
|
|
155
149
|
});
|
|
156
150
|
|
|
157
151
|
projectDeployer = !_projectDeployerIsdeployed
|
|
158
|
-
? new JB721TiersHookProjectDeployer{salt:
|
|
159
|
-
|
|
160
|
-
|
|
152
|
+
? new JB721TiersHookProjectDeployer{salt: _PROJECT_DEPLOYER_SALT}({
|
|
153
|
+
directory: core.directory,
|
|
154
|
+
permissions: core.permissions,
|
|
155
|
+
hookDeployer: hookDeployer,
|
|
156
|
+
trustedForwarder: trustedForwarder
|
|
157
|
+
})
|
|
161
158
|
: JB721TiersHookProjectDeployer(_projectDeployer);
|
|
162
159
|
}
|
|
163
160
|
}
|
|
@@ -11,10 +11,8 @@ import {IJB721TiersHookProjectDeployer} from "../../src/interfaces/IJB721TiersHo
|
|
|
11
11
|
import {IJB721TiersHookStore} from "../../src/interfaces/IJB721TiersHookStore.sol";
|
|
12
12
|
|
|
13
13
|
struct Hook721Deployment {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
17
|
-
IJB721TiersHookProjectDeployer project_deployer;
|
|
14
|
+
IJB721TiersHookDeployer hookDeployer;
|
|
15
|
+
IJB721TiersHookProjectDeployer projectDeployer;
|
|
18
16
|
IJB721TiersHookStore store;
|
|
19
17
|
}
|
|
20
18
|
|
|
@@ -25,17 +23,16 @@ library Hook721DeploymentLib {
|
|
|
25
23
|
Vm internal constant vm = Vm(VM_ADDRESS);
|
|
26
24
|
|
|
27
25
|
function getDeployment(string memory path) internal returns (Hook721Deployment memory deployment) {
|
|
28
|
-
//
|
|
26
|
+
// Match the current chain ID to the Sphinx network name used in deployment artifacts.
|
|
29
27
|
uint256 chainId = block.chainid;
|
|
30
28
|
|
|
31
|
-
//
|
|
32
|
-
// TODO: get constants without deploy.
|
|
29
|
+
// `SphinxConstants` exposes Sphinx's supported chain ID to network name mapping.
|
|
33
30
|
SphinxConstants sphinxConstants = new SphinxConstants();
|
|
34
31
|
NetworkInfo[] memory networks = sphinxConstants.getNetworkInfoArray();
|
|
35
32
|
|
|
36
33
|
for (uint256 _i; _i < networks.length; _i++) {
|
|
37
34
|
if (networks[_i].chainId == chainId) {
|
|
38
|
-
return getDeployment({path: path,
|
|
35
|
+
return getDeployment({path: path, networkName: networks[_i].name});
|
|
39
36
|
}
|
|
40
37
|
}
|
|
41
38
|
|
|
@@ -44,27 +41,26 @@ library Hook721DeploymentLib {
|
|
|
44
41
|
|
|
45
42
|
function getDeployment(
|
|
46
43
|
string memory path,
|
|
47
|
-
|
|
48
|
-
string memory network_name
|
|
44
|
+
string memory networkName
|
|
49
45
|
)
|
|
50
46
|
internal
|
|
51
47
|
view
|
|
52
48
|
returns (Hook721Deployment memory deployment)
|
|
53
49
|
{
|
|
54
|
-
deployment.
|
|
50
|
+
deployment.hookDeployer = IJB721TiersHookDeployer(
|
|
55
51
|
_getDeploymentAddress({
|
|
56
52
|
path: path,
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
projectName: "nana-721-hook-v6",
|
|
54
|
+
networkName: networkName,
|
|
59
55
|
contractName: "JB721TiersHookDeployer"
|
|
60
56
|
})
|
|
61
57
|
);
|
|
62
58
|
|
|
63
|
-
deployment.
|
|
59
|
+
deployment.projectDeployer = IJB721TiersHookProjectDeployer(
|
|
64
60
|
_getDeploymentAddress({
|
|
65
61
|
path: path,
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
projectName: "nana-721-hook-v6",
|
|
63
|
+
networkName: networkName,
|
|
68
64
|
contractName: "JB721TiersHookProjectDeployer"
|
|
69
65
|
})
|
|
70
66
|
);
|
|
@@ -72,8 +68,8 @@ library Hook721DeploymentLib {
|
|
|
72
68
|
deployment.store = IJB721TiersHookStore(
|
|
73
69
|
_getDeploymentAddress({
|
|
74
70
|
path: path,
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
projectName: "nana-721-hook-v6",
|
|
72
|
+
networkName: networkName,
|
|
77
73
|
contractName: "JB721TiersHookStore"
|
|
78
74
|
})
|
|
79
75
|
);
|
|
@@ -86,10 +82,8 @@ library Hook721DeploymentLib {
|
|
|
86
82
|
/// @return The address of the contract.
|
|
87
83
|
function _getDeploymentAddress(
|
|
88
84
|
string memory path,
|
|
89
|
-
|
|
90
|
-
string memory
|
|
91
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
92
|
-
string memory network_name,
|
|
85
|
+
string memory projectName,
|
|
86
|
+
string memory networkName,
|
|
93
87
|
string memory contractName
|
|
94
88
|
)
|
|
95
89
|
internal
|
|
@@ -98,7 +92,7 @@ library Hook721DeploymentLib {
|
|
|
98
92
|
{
|
|
99
93
|
string memory deploymentJson =
|
|
100
94
|
// forge-lint: disable-next-line(unsafe-cheatcode)
|
|
101
|
-
vm.readFile(string.concat(path,
|
|
102
|
-
return stdJson.readAddress(deploymentJson, ".address");
|
|
95
|
+
vm.readFile(string.concat(path, projectName, "/", networkName, "/", contractName, ".json"));
|
|
96
|
+
return stdJson.readAddress({json: deploymentJson, key: ".address"});
|
|
103
97
|
}
|
|
104
98
|
}
|
package/src/JB721Checkpoints.sol
CHANGED
|
@@ -40,7 +40,7 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
40
40
|
//*********************************************************************//
|
|
41
41
|
|
|
42
42
|
/// @notice The hook that this module tracks voting power for.
|
|
43
|
-
address public override
|
|
43
|
+
address public override hook;
|
|
44
44
|
|
|
45
45
|
//*********************************************************************//
|
|
46
46
|
// -------------------- internal stored properties ------------------- //
|
|
@@ -59,7 +59,7 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
59
59
|
/// @param store The store that holds tier data for each hook's NFTs.
|
|
60
60
|
constructor(IJB721TiersHookStore store) EIP712("JB721Checkpoints", "1") {
|
|
61
61
|
STORE = store;
|
|
62
|
-
|
|
62
|
+
hook = address(1);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
//*********************************************************************//
|
|
@@ -81,7 +81,7 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
81
81
|
uint256 tokenId = tokenIds[i];
|
|
82
82
|
|
|
83
83
|
// Only the current owner can enroll their tokens.
|
|
84
|
-
if (IERC721(
|
|
84
|
+
if (IERC721(hook).ownerOf(tokenId) != msg.sender) {
|
|
85
85
|
revert JB721Checkpoints_NotOwner({tokenId: tokenId, caller: msg.sender});
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -99,20 +99,20 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
99
99
|
|
|
100
100
|
/// @notice Initializes a cloned module with its hook reference.
|
|
101
101
|
/// @dev Can only be called once. Called by the deployer after cloning.
|
|
102
|
-
/// @param
|
|
103
|
-
function initialize(address
|
|
104
|
-
if (
|
|
102
|
+
/// @param hookAddress The hook this module serves.
|
|
103
|
+
function initialize(address hookAddress) external override {
|
|
104
|
+
if (hook != address(0)) revert JB721Checkpoints_AlreadyInitialized({hook: hook});
|
|
105
105
|
// `hook` cannot be zero when called through the deployer because `msg.sender` must equal `hook`.
|
|
106
|
-
|
|
106
|
+
hook = hookAddress;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
/// @notice Called by the hook after every NFT transfer to update checkpointed voting power.
|
|
110
|
-
/// @dev Only callable by the
|
|
110
|
+
/// @dev Only callable by the hook. Looks up the token's tier voting units from the store.
|
|
111
111
|
/// @param from The previous owner (address(0) on mint).
|
|
112
112
|
/// @param to The new owner (address(0) on burn).
|
|
113
113
|
/// @param tokenId The token ID to transfer.
|
|
114
114
|
function onTransfer(address from, address to, uint256 tokenId) external override {
|
|
115
|
-
if (msg.sender !=
|
|
115
|
+
if (msg.sender != hook) revert JB721Checkpoints_Unauthorized({caller: msg.sender, hook: hook});
|
|
116
116
|
|
|
117
117
|
if (from != address(0)) {
|
|
118
118
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
@@ -120,7 +120,7 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
// Look up this token's tier to get its voting units.
|
|
123
|
-
uint256 votingUnits = STORE.tierOfTokenId({hook:
|
|
123
|
+
uint256 votingUnits = STORE.tierOfTokenId({hook: hook, tokenId: tokenId, includeResolvedUri: false}).votingUnits;
|
|
124
124
|
|
|
125
125
|
// Move checkpointed voting power from the previous owner to the new owner.
|
|
126
126
|
_transferVotingUnits({from: from, to: to, amount: votingUnits});
|
|
@@ -161,6 +161,6 @@ contract JB721Checkpoints is Votes, IJB721Checkpoints {
|
|
|
161
161
|
/// @param account The address to get the voting units of.
|
|
162
162
|
/// @return The total voting units the account holds.
|
|
163
163
|
function _getVotingUnits(address account) internal view override returns (uint256) {
|
|
164
|
-
return STORE.votingUnitsOf({hook:
|
|
164
|
+
return STORE.votingUnitsOf({hook: hook, account: account});
|
|
165
165
|
}
|
|
166
166
|
}
|
package/src/JB721TiersHook.sol
CHANGED
|
@@ -107,7 +107,7 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
107
107
|
|
|
108
108
|
/// @notice The checkpoint module that manages IVotes-compatible checkpointed voting power for this hook's NFTs.
|
|
109
109
|
/// @dev Lazily deployed on the first transfer. Pass this to JBTokenDistributor as the IVotes token.
|
|
110
|
-
IJB721Checkpoints public override
|
|
110
|
+
IJB721Checkpoints public override checkpoints;
|
|
111
111
|
|
|
112
112
|
//*********************************************************************//
|
|
113
113
|
// -------------------------- constructor ---------------------------- //
|
|
@@ -232,9 +232,9 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
232
232
|
/// @notice Initialize a cloned copy of the hook. Sets the project association, ERC-721 name/symbol, pricing
|
|
233
233
|
/// context (currency + decimals), metadata URIs, initial tiers, and behavioral flags. Can only be called once
|
|
234
234
|
/// per clone — the implementation contract is pre-initialized in its constructor to prevent misuse.
|
|
235
|
-
/// @dev Called by `JB721TiersHookDeployer` immediately after cloning. Reverts
|
|
236
|
-
///
|
|
237
|
-
/// @param
|
|
235
|
+
/// @dev Called by `JB721TiersHookDeployer` immediately after cloning. Reverts if called more than once or if the
|
|
236
|
+
/// project ID is zero.
|
|
237
|
+
/// @param initialProjectId The ID of the project this hook is associated with.
|
|
238
238
|
/// @param name The name of the NFT collection.
|
|
239
239
|
/// @param symbol The symbol representing the NFT collection.
|
|
240
240
|
/// @param baseUri The URI to use as a base for full NFT `tokenUri`s.
|
|
@@ -244,7 +244,7 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
244
244
|
/// category (from least to greatest).
|
|
245
245
|
/// @param flags A set of additional options which dictate how the hook behaves.
|
|
246
246
|
function initialize(
|
|
247
|
-
uint256
|
|
247
|
+
uint256 initialProjectId,
|
|
248
248
|
string memory name,
|
|
249
249
|
string memory symbol,
|
|
250
250
|
string memory baseUri,
|
|
@@ -258,14 +258,14 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
258
258
|
{
|
|
259
259
|
// Stop re-initialization. This protects both the implementation contract (initialized in constructor)
|
|
260
260
|
// and clones (initialized via this function).
|
|
261
|
-
if (_initialized) revert JB721TiersHook_AlreadyInitialized(
|
|
261
|
+
if (_initialized) revert JB721TiersHook_AlreadyInitialized({projectId: projectId});
|
|
262
262
|
_initialized = true;
|
|
263
263
|
|
|
264
|
-
// Make sure a
|
|
265
|
-
if (
|
|
264
|
+
// Make sure a project ID is provided.
|
|
265
|
+
if (initialProjectId == 0) revert JB721TiersHook_NoProjectId({projectId: initialProjectId});
|
|
266
266
|
|
|
267
267
|
// Initialize the superclass.
|
|
268
|
-
JB721Hook._initialize({
|
|
268
|
+
JB721Hook._initialize({initialProjectId: initialProjectId, name: name, symbol: symbol});
|
|
269
269
|
|
|
270
270
|
// Validate pricing decimals are within a reasonable range.
|
|
271
271
|
if (tiersConfig.decimals > 18) revert JB721TiersHook_InvalidPricingDecimals(tiersConfig.decimals);
|
|
@@ -326,7 +326,8 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
326
326
|
/// @return The token URI from the `tokenUriResolver` if it is set. If it isn't set, the token URI for the NFT's
|
|
327
327
|
/// tier.
|
|
328
328
|
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
|
|
329
|
-
return
|
|
329
|
+
return
|
|
330
|
+
JB721TiersHookLib.resolveTokenURI({store: STORE, hook: address(this), baseUri: baseURI, tokenId: tokenId});
|
|
330
331
|
}
|
|
331
332
|
|
|
332
333
|
/// @notice The total cash-out weight across all outstanding NFTs and pending reserves. This is the denominator
|
|
@@ -348,15 +349,13 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
348
349
|
/// @param tierIdsToRemove The IDs of the tiers to remove.
|
|
349
350
|
function adjustTiers(JB721TierConfig[] calldata tiersToAdd, uint256[] calldata tierIdsToRemove) external override {
|
|
350
351
|
// Enforce permissions.
|
|
351
|
-
_requirePermissionFrom({
|
|
352
|
-
account: owner(), projectId: PROJECT_ID, permissionId: JBPermissionIds.ADJUST_721_TIERS
|
|
353
|
-
});
|
|
352
|
+
_requirePermissionFrom({account: owner(), projectId: projectId, permissionId: JBPermissionIds.ADJUST_721_TIERS});
|
|
354
353
|
|
|
355
354
|
// Delegate to the library (via DELEGATECALL) for tier removal, addition, event emission, and split setting.
|
|
356
355
|
JB721TiersHookLib.adjustTiersFor({
|
|
357
356
|
store: STORE,
|
|
358
357
|
splits: SPLITS,
|
|
359
|
-
projectId:
|
|
358
|
+
projectId: projectId,
|
|
360
359
|
hookAddress: address(this),
|
|
361
360
|
caller: _msgSender(),
|
|
362
361
|
tiersToAdd: tiersToAdd,
|
|
@@ -379,7 +378,7 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
379
378
|
returns (uint256[] memory tokenIds)
|
|
380
379
|
{
|
|
381
380
|
// Enforce permissions.
|
|
382
|
-
_requirePermissionFrom({account: owner(), projectId:
|
|
381
|
+
_requirePermissionFrom({account: owner(), projectId: projectId, permissionId: JBPermissionIds.MINT_721});
|
|
383
382
|
|
|
384
383
|
// Record the mint. The token IDs returned correspond to the tiers passed in.
|
|
385
384
|
(tokenIds,,) = STORE.recordMint({
|
|
@@ -417,7 +416,7 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
417
416
|
function setDiscountPercentOf(uint256 tierId, uint256 discountPercent) external override {
|
|
418
417
|
// Enforce permissions.
|
|
419
418
|
_requirePermissionFrom({
|
|
420
|
-
account: owner(), projectId:
|
|
419
|
+
account: owner(), projectId: projectId, permissionId: JBPermissionIds.SET_721_DISCOUNT_PERCENT
|
|
421
420
|
});
|
|
422
421
|
_setDiscountPercentOf({tierId: tierId, discountPercent: discountPercent});
|
|
423
422
|
}
|
|
@@ -428,7 +427,7 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
428
427
|
function setDiscountPercentsOf(JB721TiersSetDiscountPercentConfig[] calldata configs) external override {
|
|
429
428
|
// Enforce permissions.
|
|
430
429
|
_requirePermissionFrom({
|
|
431
|
-
account: owner(), projectId:
|
|
430
|
+
account: owner(), projectId: projectId, permissionId: JBPermissionIds.SET_721_DISCOUNT_PERCENT
|
|
432
431
|
});
|
|
433
432
|
|
|
434
433
|
for (uint256 i; i < configs.length;) {
|
|
@@ -453,24 +452,22 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
453
452
|
/// @param tokenUriResolver The new URI resolver. Pass `IJB721TokenUriResolver(address(this))` as a sentinel value
|
|
454
453
|
/// to leave unchanged. `address(this)` is used instead of `address(0)` because `address(0)` is a valid value that
|
|
455
454
|
/// clears the resolver.
|
|
456
|
-
/// @param
|
|
457
|
-
/// @param
|
|
455
|
+
/// @param encodedIpfsUriTierId The ID of the tier to set the encoded IPFS URI of.
|
|
456
|
+
/// @param encodedIpfsUri The encoded IPFS URI to set.
|
|
458
457
|
function setMetadata(
|
|
459
458
|
string calldata name,
|
|
460
459
|
string calldata symbol,
|
|
461
460
|
string calldata baseUri,
|
|
462
461
|
string calldata contractUri,
|
|
463
462
|
IJB721TokenUriResolver tokenUriResolver,
|
|
464
|
-
uint256
|
|
465
|
-
bytes32
|
|
463
|
+
uint256 encodedIpfsUriTierId,
|
|
464
|
+
bytes32 encodedIpfsUri
|
|
466
465
|
)
|
|
467
466
|
external
|
|
468
467
|
override
|
|
469
468
|
{
|
|
470
469
|
// Enforce permissions.
|
|
471
|
-
_requirePermissionFrom({
|
|
472
|
-
account: owner(), projectId: PROJECT_ID, permissionId: JBPermissionIds.SET_721_METADATA
|
|
473
|
-
});
|
|
470
|
+
_requirePermissionFrom({account: owner(), projectId: projectId, permissionId: JBPermissionIds.SET_721_METADATA});
|
|
474
471
|
|
|
475
472
|
// Cache _msgSender() at function entry to avoid repeated calls.
|
|
476
473
|
address caller = _msgSender();
|
|
@@ -501,11 +498,11 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
501
498
|
// Store the new URI resolver.
|
|
502
499
|
_recordSetTokenUriResolver(tokenUriResolver);
|
|
503
500
|
}
|
|
504
|
-
if (
|
|
505
|
-
emit
|
|
501
|
+
if (encodedIpfsUriTierId != 0 && encodedIpfsUri != bytes32(0)) {
|
|
502
|
+
emit SetEncodedIpfsUri({tierId: encodedIpfsUriTierId, encodedUri: encodedIpfsUri, caller: caller});
|
|
506
503
|
|
|
507
504
|
// Store the new encoded IPFS URI.
|
|
508
|
-
STORE.
|
|
505
|
+
STORE.recordSetEncodedIpfsUriOf({tierId: encodedIpfsUriTierId, encodedIpfsUri: encodedIpfsUri});
|
|
509
506
|
}
|
|
510
507
|
}
|
|
511
508
|
|
|
@@ -519,12 +516,12 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
519
516
|
/// @param count The number of reserved NFTs to mint.
|
|
520
517
|
function mintPendingReservesFor(uint256 tierId, uint256 count) public override {
|
|
521
518
|
// Get a reference to the project's current ruleset.
|
|
522
|
-
JBRuleset memory ruleset = _currentRulesetOf(
|
|
519
|
+
JBRuleset memory ruleset = _currentRulesetOf(projectId);
|
|
523
520
|
|
|
524
521
|
// Pending reserve mints must not be paused.
|
|
525
522
|
if (JB721TiersRulesetMetadataResolver.mintPendingReservesPaused((JBRulesetMetadataResolver.metadata(ruleset))))
|
|
526
523
|
{
|
|
527
|
-
revert JB721TiersHook_MintReserveNftsPaused({projectId:
|
|
524
|
+
revert JB721TiersHook_MintReserveNftsPaused({projectId: projectId, tierId: tierId});
|
|
528
525
|
}
|
|
529
526
|
|
|
530
527
|
// Record the reserved mint for the tier.
|
|
@@ -694,7 +691,7 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
694
691
|
(value, valid) = JB721TiersHookLib.normalizePaymentValue({
|
|
695
692
|
packedPricingContext: _packedPricingContext,
|
|
696
693
|
prices: PRICES,
|
|
697
|
-
projectId:
|
|
694
|
+
projectId: projectId,
|
|
698
695
|
amountValue: context.amount.value,
|
|
699
696
|
amountCurrency: context.amount.currency,
|
|
700
697
|
amountDecimals: context.amount.decimals
|
|
@@ -722,7 +719,7 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
722
719
|
JB721TiersHookLib.distributeAll({
|
|
723
720
|
directory: DIRECTORY,
|
|
724
721
|
splits: SPLITS,
|
|
725
|
-
projectId:
|
|
722
|
+
projectId: projectId,
|
|
726
723
|
hookAddress: address(this),
|
|
727
724
|
token: context.forwardedAmount.token,
|
|
728
725
|
amount: context.forwardedAmount.value,
|
|
@@ -766,7 +763,7 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
766
763
|
// If transfers are pausable, check if they're paused.
|
|
767
764
|
if (transfersPausable) {
|
|
768
765
|
// Get a reference to the project's current ruleset.
|
|
769
|
-
JBRuleset memory ruleset = _currentRulesetOf(
|
|
766
|
+
JBRuleset memory ruleset = _currentRulesetOf(projectId);
|
|
770
767
|
|
|
771
768
|
// If transfers are paused and the NFT isn't being transferred to the zero address, revert.
|
|
772
769
|
if (
|
|
@@ -776,7 +773,7 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
776
773
|
)
|
|
777
774
|
) {
|
|
778
775
|
revert JB721TiersHook_TierTransfersPaused({
|
|
779
|
-
projectId:
|
|
776
|
+
projectId: projectId, tokenId: tokenId, from: from, to: to
|
|
780
777
|
});
|
|
781
778
|
}
|
|
782
779
|
}
|
|
@@ -789,11 +786,11 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, JB721Hook, IJB721TiersHook
|
|
|
789
786
|
STORE.recordTransferForTier({tierId: tierId, from: from, to: to});
|
|
790
787
|
|
|
791
788
|
// Deploy the checkpoint module lazily on the first transfer.
|
|
792
|
-
if (address(
|
|
793
|
-
|
|
789
|
+
if (address(checkpoints) == address(0)) {
|
|
790
|
+
checkpoints = CHECKPOINTS_DEPLOYER.deploy(address(this));
|
|
794
791
|
}
|
|
795
792
|
|
|
796
793
|
// Notify the checkpoint module to update checkpointed voting power.
|
|
797
|
-
|
|
794
|
+
checkpoints.onTransfer({from: from, to: to, tokenId: tokenId});
|
|
798
795
|
}
|
|
799
796
|
}
|
|
@@ -89,7 +89,7 @@ contract JB721TiersHookDeployer is ERC2771Context, IJB721TiersHookDeployer {
|
|
|
89
89
|
emit HookDeployed({projectId: projectId, hook: newHook, caller: _msgSender()});
|
|
90
90
|
|
|
91
91
|
newHook.initialize({
|
|
92
|
-
|
|
92
|
+
initialProjectId: projectId,
|
|
93
93
|
name: deployTiersHookConfig.name,
|
|
94
94
|
symbol: deployTiersHookConfig.symbol,
|
|
95
95
|
baseUri: deployTiersHookConfig.baseUri,
|
|
@@ -89,8 +89,8 @@ contract JB721TiersHookProjectDeployer is
|
|
|
89
89
|
returns (uint256 projectId, IJB721TiersHook hook)
|
|
90
90
|
{
|
|
91
91
|
// Reserve the project ID up front so permissionless project creations cannot invalidate hook deployment.
|
|
92
|
-
IJBProjects
|
|
93
|
-
projectId =
|
|
92
|
+
IJBProjects projects = DIRECTORY.PROJECTS();
|
|
93
|
+
projectId = projects.createFor(address(this));
|
|
94
94
|
|
|
95
95
|
// Deploy the hook.
|
|
96
96
|
hook = HOOK_DEPLOYER.deployHookFor({
|
|
@@ -108,7 +108,7 @@ contract JB721TiersHookProjectDeployer is
|
|
|
108
108
|
JBOwnable(address(hook)).transferOwnershipToProject(projectId);
|
|
109
109
|
|
|
110
110
|
// Transfer the project NFT to its intended owner.
|
|
111
|
-
|
|
111
|
+
projects.safeTransferFrom({from: address(this), to: owner, tokenId: projectId});
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
/// @notice Launches rulesets for a project with an attached 721 tiers hook.
|
|
@@ -135,8 +135,8 @@ contract JB721TiersHookProjectDeployer is
|
|
|
135
135
|
returns (uint256 rulesetId, IJB721TiersHook hook)
|
|
136
136
|
{
|
|
137
137
|
// Get the project's projects contract.
|
|
138
|
-
IJBProjects
|
|
139
|
-
address projectOwner =
|
|
138
|
+
IJBProjects projects = DIRECTORY.PROJECTS();
|
|
139
|
+
address projectOwner = projects.ownerOf(projectId);
|
|
140
140
|
|
|
141
141
|
// Enforce permissions.
|
|
142
142
|
_requirePermissionFrom({
|
|
@@ -277,7 +277,6 @@ contract JB721TiersHookProjectDeployer is
|
|
|
277
277
|
ownerMustSendPayouts: payDataRulesetConfig.metadata.ownerMustSendPayouts,
|
|
278
278
|
holdFees: payDataRulesetConfig.metadata.holdFees,
|
|
279
279
|
scopeCashOutsToLocalBalances: payDataRulesetConfig.metadata.scopeCashOutsToLocalBalances,
|
|
280
|
-
pauseCrossProjectFeeFreeInflows: payDataRulesetConfig.metadata.pauseCrossProjectFeeFreeInflows,
|
|
281
280
|
useDataHookForPay: true,
|
|
282
281
|
useDataHookForCashOut: payDataRulesetConfig.metadata.useDataHookForCashOut,
|
|
283
282
|
dataHook: address(dataHook),
|
|
@@ -352,7 +351,6 @@ contract JB721TiersHookProjectDeployer is
|
|
|
352
351
|
ownerMustSendPayouts: payDataRulesetConfig.metadata.ownerMustSendPayouts,
|
|
353
352
|
holdFees: payDataRulesetConfig.metadata.holdFees,
|
|
354
353
|
scopeCashOutsToLocalBalances: payDataRulesetConfig.metadata.scopeCashOutsToLocalBalances,
|
|
355
|
-
pauseCrossProjectFeeFreeInflows: payDataRulesetConfig.metadata.pauseCrossProjectFeeFreeInflows,
|
|
356
354
|
useDataHookForPay: true,
|
|
357
355
|
useDataHookForCashOut: payDataRulesetConfig.metadata.useDataHookForCashOut,
|
|
358
356
|
dataHook: address(dataHook),
|
|
@@ -428,7 +426,6 @@ contract JB721TiersHookProjectDeployer is
|
|
|
428
426
|
ownerMustSendPayouts: payDataRulesetConfig.metadata.ownerMustSendPayouts,
|
|
429
427
|
holdFees: payDataRulesetConfig.metadata.holdFees,
|
|
430
428
|
scopeCashOutsToLocalBalances: payDataRulesetConfig.metadata.scopeCashOutsToLocalBalances,
|
|
431
|
-
pauseCrossProjectFeeFreeInflows: payDataRulesetConfig.metadata.pauseCrossProjectFeeFreeInflows,
|
|
432
429
|
useDataHookForPay: true,
|
|
433
430
|
useDataHookForCashOut: payDataRulesetConfig.metadata.useDataHookForCashOut,
|
|
434
431
|
dataHook: address(dataHook),
|