@zoralabs/coins 2.1.2 → 2.3.0
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/.turbo/turbo-build$colon$js.log +152 -0
- package/CHANGELOG.md +93 -0
- package/README.md +4 -0
- package/abis/BaseCoin.json +26 -5
- package/abis/BaseTest.json +2 -7
- package/abis/ContentCoin.json +26 -5
- package/abis/CreatorCoin.json +30 -9
- package/abis/FeeEstimatorHook.json +94 -6
- package/abis/ICoin.json +26 -0
- package/abis/ICoinV3.json +26 -0
- package/abis/ICreatorCoin.json +39 -0
- package/abis/IERC721.json +36 -36
- package/abis/IHasCoinType.json +15 -0
- package/abis/IHasTotalSupplyForPositions.json +15 -0
- package/abis/{LiquidityMigrationReceiver.json → IUpgradeableDestinationV4HookWithUpdateableFee.json} +10 -18
- package/abis/IZoraFactory.json +121 -0
- package/abis/IZoraHookRegistry.json +188 -0
- package/abis/VmContractHelper226.json +233 -0
- package/abis/ZoraFactoryImpl.json +101 -6
- package/abis/ZoraHookRegistry.json +375 -0
- package/abis/{CreatorCoinHook.json → ZoraV4CoinHook.json} +95 -2
- package/addresses/8453.json +6 -5
- package/audits/report-cantinacode-zora-0827.pdf +3498 -4
- package/dist/index.cjs +93 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +93 -13
- package/dist/index.js.map +1 -1
- package/dist/wagmiGenerated.d.ts +144 -22
- package/dist/wagmiGenerated.d.ts.map +1 -1
- package/foundry.toml +4 -1
- package/package/wagmiGenerated.ts +93 -13
- package/package.json +6 -4
- package/script/PrintRegisterUpgradePath.s.sol +0 -7
- package/script/TestBackingCoinSwap.s.sol +0 -3
- package/script/TestV4Swap.s.sol +0 -3
- package/script/UpgradeFactoryImpl.s.sol +1 -1
- package/src/BaseCoin.sol +19 -24
- package/src/ContentCoin.sol +11 -2
- package/src/CreatorCoin.sol +34 -15
- package/src/ZoraFactoryImpl.sol +163 -92
- package/src/deployment/CoinsDeployerBase.sol +24 -58
- package/src/hook-registry/ZoraHookRegistry.sol +97 -0
- package/src/hooks/{BaseZoraV4CoinHook.sol → ZoraV4CoinHook.sol} +77 -15
- package/src/interfaces/ICoin.sol +19 -1
- package/src/interfaces/ICreatorCoin.sol +4 -0
- package/src/interfaces/IUpgradeableV4Hook.sol +18 -0
- package/src/interfaces/IZoraFactory.sol +51 -10
- package/src/interfaces/IZoraHookRegistry.sol +47 -0
- package/src/libs/CoinConstants.sol +43 -32
- package/src/libs/CoinDopplerMultiCurve.sol +11 -11
- package/src/libs/CoinRewardsV4.sol +68 -37
- package/src/libs/CoinSetup.sol +2 -9
- package/src/libs/DopplerMath.sol +2 -2
- package/src/libs/HooksDeployment.sol +13 -65
- package/src/libs/V4Liquidity.sol +109 -15
- package/src/version/ContractVersionBase.sol +1 -1
- package/test/Coin.t.sol +5 -5
- package/test/CoinRewardsV4.t.sol +33 -0
- package/test/CoinUniV4.t.sol +32 -30
- package/test/ContentCoinRewards.t.sol +363 -0
- package/test/CreatorCoin.t.sol +53 -29
- package/test/CreatorCoinRewards.t.sol +375 -0
- package/test/DeploymentHooks.t.sol +64 -12
- package/test/Factory.t.sol +24 -7
- package/test/HooksDeployment.t.sol +4 -4
- package/test/LiquidityMigration.t.sol +149 -16
- package/test/Upgrades.t.sol +44 -48
- package/test/V4Liquidity.t.sol +178 -0
- package/test/ZoraHookRegistry.t.sol +266 -0
- package/test/utils/BaseTest.sol +25 -43
- package/test/utils/FeeEstimatorHook.sol +4 -6
- package/test/utils/RewardTestHelpers.sol +106 -0
- package/.turbo/turbo-build.log +0 -199
- package/abis/AutoSwapperTest.json +0 -618
- package/abis/BadImpl.json +0 -15
- package/abis/BaseZoraV4CoinHook.json +0 -1664
- package/abis/CoinConstants.json +0 -158
- package/abis/CoinRewardsV4.json +0 -67
- package/abis/CoinTest.json +0 -819
- package/abis/CoinUniV4Test.json +0 -1128
- package/abis/ContentCoinHook.json +0 -1733
- package/abis/CreatorCoinTest.json +0 -887
- package/abis/Deploy.json +0 -9
- package/abis/DeployHooks.json +0 -9
- package/abis/DeployScript.json +0 -35
- package/abis/DeployedCoinVersionLookupTest.json +0 -740
- package/abis/DifferentNamespaceVersionLookup.json +0 -39
- package/abis/FactoryTest.json +0 -748
- package/abis/FakeHookNoInterface.json +0 -21
- package/abis/GenerateDeterministicParams.json +0 -9
- package/abis/HooksDeploymentTest.json +0 -645
- package/abis/HooksTest.json +0 -709
- package/abis/InvalidLiquidityMigrationReceiver.json +0 -21
- package/abis/LiquidityMigrationTest.json +0 -889
- package/abis/MockBadFactory.json +0 -15
- package/abis/MultiOwnableTest.json +0 -766
- package/abis/PrintUpgradeCommand.json +0 -9
- package/abis/TestDeployedCoinVersionLookupImplementation.json +0 -39
- package/abis/TestV4Swap.json +0 -9
- package/abis/UpgradeFactoryImpl.json +0 -9
- package/abis/UpgradeHooks.json +0 -35
- package/abis/UpgradesTest.json +0 -723
- package/src/hooks/ContentCoinHook.sol +0 -27
- package/src/hooks/CreatorCoinHook.sol +0 -27
- package/src/libs/CreatorCoinConstants.sol +0 -16
- package/src/libs/CreatorCoinRewards.sol +0 -34
- package/src/libs/MarketConstants.sol +0 -15
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
// SPDX-License-Identifier: UNLICENSED
|
|
2
|
+
pragma solidity ^0.8.28;
|
|
3
|
+
|
|
4
|
+
import "forge-std/Test.sol";
|
|
5
|
+
|
|
6
|
+
import {IZoraHookRegistry} from "../src/interfaces/IZoraHookRegistry.sol";
|
|
7
|
+
import {ZoraHookRegistry} from "../src/hook-registry/ZoraHookRegistry.sol";
|
|
8
|
+
|
|
9
|
+
contract MockHook {
|
|
10
|
+
function contractVersion() public pure returns (string memory) {
|
|
11
|
+
return "0.0.0";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
contract ZoraHookRegistryTest is Test {
|
|
16
|
+
uint256 internal forkId;
|
|
17
|
+
address internal owner;
|
|
18
|
+
|
|
19
|
+
ZoraHookRegistry internal zoraHookRegistry;
|
|
20
|
+
MockHook internal mockHook;
|
|
21
|
+
|
|
22
|
+
function setUp() public {
|
|
23
|
+
forkId = vm.createSelectFork("base", 34509280);
|
|
24
|
+
owner = makeAddr("owner");
|
|
25
|
+
|
|
26
|
+
address[] memory initialOwners = new address[](1);
|
|
27
|
+
initialOwners[0] = owner;
|
|
28
|
+
|
|
29
|
+
zoraHookRegistry = new ZoraHookRegistry();
|
|
30
|
+
zoraHookRegistry.initialize(initialOwners);
|
|
31
|
+
|
|
32
|
+
mockHook = new MockHook();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function test_register_hooks() public {
|
|
36
|
+
address[] memory hooks = new address[](1);
|
|
37
|
+
string[] memory tags = new string[](1);
|
|
38
|
+
|
|
39
|
+
hooks[0] = address(mockHook);
|
|
40
|
+
tags[0] = "ZoraHook";
|
|
41
|
+
|
|
42
|
+
vm.prank(owner);
|
|
43
|
+
zoraHookRegistry.registerHooks(hooks, tags);
|
|
44
|
+
|
|
45
|
+
assertEq(zoraHookRegistry.isRegisteredHook(hooks[0]), true);
|
|
46
|
+
|
|
47
|
+
address[] memory addrs = zoraHookRegistry.getHookAddresses();
|
|
48
|
+
assertEq(addrs.length, 1);
|
|
49
|
+
assertEq(addrs[0], hooks[0]);
|
|
50
|
+
|
|
51
|
+
assertEq(zoraHookRegistry.getHookTag(hooks[0]), tags[0]);
|
|
52
|
+
|
|
53
|
+
IZoraHookRegistry.ZoraHook[] memory zoraHooks = zoraHookRegistry.getHooks();
|
|
54
|
+
assertEq(zoraHooks.length, 1);
|
|
55
|
+
assertEq(zoraHooks[0].hook, hooks[0]);
|
|
56
|
+
assertEq(zoraHooks[0].tag, "ZoraHook");
|
|
57
|
+
assertEq(zoraHooks[0].version, "0.0.0");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function test_revert_register_hooks_only_owner() public {
|
|
61
|
+
address[] memory hooks = new address[](1);
|
|
62
|
+
string[] memory tags = new string[](1);
|
|
63
|
+
|
|
64
|
+
hooks[0] = address(mockHook);
|
|
65
|
+
tags[0] = "ZoraHook";
|
|
66
|
+
|
|
67
|
+
vm.expectRevert(abi.encodeWithSignature("OnlyOwner()"));
|
|
68
|
+
vm.prank(makeAddr("notOwner"));
|
|
69
|
+
zoraHookRegistry.registerHooks(hooks, tags);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function test_revert_register_hooks_array_length_mismatch() public {
|
|
73
|
+
address[] memory hooks = new address[](2);
|
|
74
|
+
string[] memory tags = new string[](1);
|
|
75
|
+
|
|
76
|
+
hooks[0] = address(mockHook);
|
|
77
|
+
hooks[1] = makeAddr("anotherHook");
|
|
78
|
+
tags[0] = "Tag0";
|
|
79
|
+
|
|
80
|
+
vm.prank(owner);
|
|
81
|
+
vm.expectRevert(abi.encodeWithSignature("ArrayLengthMismatch()"));
|
|
82
|
+
zoraHookRegistry.registerHooks(hooks, tags);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function test_register_duplicate_is_idempotent() public {
|
|
86
|
+
address[] memory hooks = new address[](1);
|
|
87
|
+
string[] memory tags = new string[](1);
|
|
88
|
+
|
|
89
|
+
hooks[0] = address(mockHook);
|
|
90
|
+
tags[0] = "ZoraHook";
|
|
91
|
+
|
|
92
|
+
vm.startPrank(owner);
|
|
93
|
+
zoraHookRegistry.registerHooks(hooks, tags);
|
|
94
|
+
zoraHookRegistry.registerHooks(hooks, tags);
|
|
95
|
+
vm.stopPrank();
|
|
96
|
+
|
|
97
|
+
assertEq(zoraHookRegistry.getHookAddresses().length, 1);
|
|
98
|
+
assertEq(zoraHookRegistry.isRegisteredHook(hooks[0]), true);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function test_remove_hooks() public {
|
|
102
|
+
address[] memory hooks = new address[](1);
|
|
103
|
+
string[] memory tags = new string[](1);
|
|
104
|
+
|
|
105
|
+
hooks[0] = address(mockHook);
|
|
106
|
+
tags[0] = "ZoraHook";
|
|
107
|
+
|
|
108
|
+
vm.prank(owner);
|
|
109
|
+
zoraHookRegistry.registerHooks(hooks, tags);
|
|
110
|
+
|
|
111
|
+
vm.prank(owner);
|
|
112
|
+
zoraHookRegistry.removeHooks(hooks);
|
|
113
|
+
|
|
114
|
+
assertEq(zoraHookRegistry.isRegisteredHook(hooks[0]), false);
|
|
115
|
+
assertEq(zoraHookRegistry.getHookAddresses().length, 0);
|
|
116
|
+
assertEq(zoraHookRegistry.getHookTag(hooks[0]), "");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function test_revert_remove_hooks_only_owner() public {
|
|
120
|
+
address[] memory hooks = new address[](1);
|
|
121
|
+
string[] memory tags = new string[](1);
|
|
122
|
+
|
|
123
|
+
hooks[0] = address(mockHook);
|
|
124
|
+
tags[0] = "ZoraHook";
|
|
125
|
+
|
|
126
|
+
vm.prank(owner);
|
|
127
|
+
zoraHookRegistry.registerHooks(hooks, tags);
|
|
128
|
+
|
|
129
|
+
vm.expectRevert(abi.encodeWithSignature("OnlyOwner()"));
|
|
130
|
+
vm.prank(makeAddr("notOwner"));
|
|
131
|
+
zoraHookRegistry.removeHooks(hooks);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function test_remove_unregistered_noop() public {
|
|
135
|
+
address[] memory hooks = new address[](1);
|
|
136
|
+
hooks[0] = makeAddr("unregistered");
|
|
137
|
+
|
|
138
|
+
uint256 beforeLen = zoraHookRegistry.getHookAddresses().length;
|
|
139
|
+
|
|
140
|
+
vm.prank(owner);
|
|
141
|
+
zoraHookRegistry.removeHooks(hooks);
|
|
142
|
+
|
|
143
|
+
assertEq(zoraHookRegistry.getHookAddresses().length, beforeLen);
|
|
144
|
+
assertEq(zoraHookRegistry.isRegisteredHook(hooks[0]), false);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function test_emit_register_hooks_event() public {
|
|
148
|
+
address[] memory hooks = new address[](1);
|
|
149
|
+
string[] memory tags = new string[](1);
|
|
150
|
+
|
|
151
|
+
hooks[0] = address(mockHook);
|
|
152
|
+
tags[0] = "ZoraHook";
|
|
153
|
+
|
|
154
|
+
vm.expectEmit(true, true, true, false, address(zoraHookRegistry));
|
|
155
|
+
emit IZoraHookRegistry.ZoraHookRegistered(hooks[0], tags[0], "0.0.0");
|
|
156
|
+
|
|
157
|
+
vm.prank(owner);
|
|
158
|
+
zoraHookRegistry.registerHooks(hooks, tags);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function test_emit_remove_hooks_event() public {
|
|
162
|
+
address[] memory hooks = new address[](1);
|
|
163
|
+
string[] memory tags = new string[](1);
|
|
164
|
+
|
|
165
|
+
hooks[0] = address(mockHook);
|
|
166
|
+
tags[0] = "ZoraHook";
|
|
167
|
+
|
|
168
|
+
vm.prank(owner);
|
|
169
|
+
zoraHookRegistry.registerHooks(hooks, tags);
|
|
170
|
+
|
|
171
|
+
vm.expectEmit(true, true, true, false, address(zoraHookRegistry));
|
|
172
|
+
emit IZoraHookRegistry.ZoraHookRemoved(hooks[0], tags[0], "0.0.0");
|
|
173
|
+
|
|
174
|
+
vm.prank(owner);
|
|
175
|
+
zoraHookRegistry.removeHooks(hooks);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function test_hook_version() public {
|
|
179
|
+
address[] memory hooks = new address[](1);
|
|
180
|
+
string[] memory tags = new string[](1);
|
|
181
|
+
|
|
182
|
+
hooks[0] = 0x81542dC43Aff247eff4a0eceFC286A2973aE1040;
|
|
183
|
+
tags[0] = "CONTENT";
|
|
184
|
+
|
|
185
|
+
vm.prank(owner);
|
|
186
|
+
zoraHookRegistry.registerHooks(hooks, tags);
|
|
187
|
+
|
|
188
|
+
assertEq(zoraHookRegistry.getHookVersion(hooks[0]), "1.1.1");
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function test_hook_version_not_found() public {
|
|
192
|
+
address[] memory hooks = new address[](1);
|
|
193
|
+
string[] memory tags = new string[](1);
|
|
194
|
+
|
|
195
|
+
hooks[0] = 0xA1eBdD5cA6470Bbd67114331387f2dDa7bfad040;
|
|
196
|
+
tags[0] = "CONTENT";
|
|
197
|
+
|
|
198
|
+
vm.prank(owner);
|
|
199
|
+
zoraHookRegistry.registerHooks(hooks, tags);
|
|
200
|
+
|
|
201
|
+
assertEq(zoraHookRegistry.getHookVersion(hooks[0]), "");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function test_is_registered_hook_false_when_never_registered() public view {
|
|
205
|
+
assertEq(zoraHookRegistry.isRegisteredHook(address(this)), false);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function test_get_hook_addresses_multiple_and_remove_middle() public {
|
|
209
|
+
address a = address(mockHook);
|
|
210
|
+
address b = address(new MockHook());
|
|
211
|
+
address c = address(new MockHook());
|
|
212
|
+
|
|
213
|
+
address[] memory hooks = new address[](3);
|
|
214
|
+
string[] memory tags = new string[](3);
|
|
215
|
+
hooks[0] = a;
|
|
216
|
+
tags[0] = "A";
|
|
217
|
+
hooks[1] = b;
|
|
218
|
+
tags[1] = "B";
|
|
219
|
+
hooks[2] = c;
|
|
220
|
+
tags[2] = "C";
|
|
221
|
+
|
|
222
|
+
vm.prank(owner);
|
|
223
|
+
zoraHookRegistry.registerHooks(hooks, tags);
|
|
224
|
+
|
|
225
|
+
address[] memory removeB = new address[](1);
|
|
226
|
+
removeB[0] = b;
|
|
227
|
+
|
|
228
|
+
vm.prank(owner);
|
|
229
|
+
zoraHookRegistry.removeHooks(removeB);
|
|
230
|
+
|
|
231
|
+
address[] memory addrs = zoraHookRegistry.getHookAddresses();
|
|
232
|
+
assertEq(addrs.length, 2);
|
|
233
|
+
|
|
234
|
+
bool hasA;
|
|
235
|
+
bool hasC;
|
|
236
|
+
for (uint256 i = 0; i < addrs.length; i++) {
|
|
237
|
+
if (addrs[i] == a) hasA = true;
|
|
238
|
+
if (addrs[i] == c) hasC = true;
|
|
239
|
+
assertTrue(addrs[i] != b);
|
|
240
|
+
}
|
|
241
|
+
assertTrue(hasA);
|
|
242
|
+
assertTrue(hasC);
|
|
243
|
+
|
|
244
|
+
assertEq(zoraHookRegistry.getHookTag(a), "A");
|
|
245
|
+
assertEq(zoraHookRegistry.getHookTag(b), "");
|
|
246
|
+
assertEq(zoraHookRegistry.getHookTag(c), "C");
|
|
247
|
+
|
|
248
|
+
IZoraHookRegistry.ZoraHook[] memory full = zoraHookRegistry.getHooks();
|
|
249
|
+
assertEq(full.length, 2);
|
|
250
|
+
|
|
251
|
+
bool okA;
|
|
252
|
+
bool okC;
|
|
253
|
+
for (uint256 i = 0; i < full.length; i++) {
|
|
254
|
+
if (full[i].hook == a) {
|
|
255
|
+
assertEq(full[i].tag, "A");
|
|
256
|
+
assertEq(full[i].version, "0.0.0");
|
|
257
|
+
okA = true;
|
|
258
|
+
} else if (full[i].hook == c) {
|
|
259
|
+
assertEq(full[i].tag, "C");
|
|
260
|
+
assertEq(full[i].version, "0.0.0");
|
|
261
|
+
okC = true;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
assertTrue(okA && okC);
|
|
265
|
+
}
|
|
266
|
+
}
|
package/test/utils/BaseTest.sol
CHANGED
|
@@ -24,10 +24,9 @@ import {IUniswapV3Factory} from "../../src/interfaces/IUniswapV3Factory.sol";
|
|
|
24
24
|
import {IUniswapV3Pool} from "../../src/interfaces/IUniswapV3Pool.sol";
|
|
25
25
|
import {IProtocolRewards} from "../../src/interfaces/IProtocolRewards.sol";
|
|
26
26
|
import {ProtocolRewards} from "../utils/ProtocolRewards.sol";
|
|
27
|
-
import {MarketConstants} from "../../src/libs/MarketConstants.sol";
|
|
28
27
|
import {CoinConfigurationVersions} from "../../src/libs/CoinConfigurationVersions.sol";
|
|
29
28
|
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
|
|
30
|
-
import {
|
|
29
|
+
import {ZoraV4CoinHook} from "../../src/hooks/ZoraV4CoinHook.sol";
|
|
31
30
|
import {HooksDeployment} from "../../src/libs/HooksDeployment.sol";
|
|
32
31
|
import {CoinConstants} from "../../src/libs/CoinConstants.sol";
|
|
33
32
|
import {ProxyShim} from "./ProxyShim.sol";
|
|
@@ -41,15 +40,19 @@ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
|
|
|
41
40
|
import {Actions} from "@uniswap/v4-periphery/src/libraries/Actions.sol";
|
|
42
41
|
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|
43
42
|
import {CreatorCoin} from "../../src/CreatorCoin.sol";
|
|
44
|
-
import {CreatorCoinHook} from "../../src/hooks/CreatorCoinHook.sol";
|
|
45
43
|
import {ContractAddresses} from "./ContractAddresses.sol";
|
|
46
44
|
import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
|
|
47
45
|
import {HookUpgradeGate} from "../../src/hooks/HookUpgradeGate.sol";
|
|
46
|
+
import {ZoraHookRegistry} from "../../src/hook-registry/ZoraHookRegistry.sol";
|
|
48
47
|
|
|
49
48
|
contract BaseTest is Test, ContractAddresses {
|
|
50
49
|
using stdStorage for StdStorage;
|
|
51
50
|
|
|
52
51
|
int24 internal constant USDC_TICK_LOWER = 57200;
|
|
52
|
+
int24 internal constant DEFAULT_DISCOVERY_TICK_LOWER = CoinConstants.DEFAULT_DISCOVERY_TICK_LOWER;
|
|
53
|
+
int24 internal constant DEFAULT_DISCOVERY_TICK_UPPER = CoinConstants.DEFAULT_DISCOVERY_TICK_UPPER;
|
|
54
|
+
uint16 internal constant DEFAULT_NUM_DISCOVERY_POSITIONS = CoinConstants.DEFAULT_NUM_DISCOVERY_POSITIONS;
|
|
55
|
+
uint256 internal constant DEFAULT_DISCOVERY_SUPPLY_SHARE = CoinConstants.DEFAULT_DISCOVERY_SUPPLY_SHARE;
|
|
53
56
|
|
|
54
57
|
struct Users {
|
|
55
58
|
address factoryOwner;
|
|
@@ -85,13 +88,9 @@ contract BaseTest is Test, ContractAddresses {
|
|
|
85
88
|
CreatorCoin internal creatorCoinImpl;
|
|
86
89
|
ZoraFactoryImpl internal factoryImpl;
|
|
87
90
|
IZoraFactory internal factory;
|
|
88
|
-
|
|
89
|
-
CreatorCoinHook internal creatorCoinHook;
|
|
91
|
+
ZoraV4CoinHook internal hook;
|
|
90
92
|
HookUpgradeGate internal hookUpgradeGate;
|
|
91
|
-
|
|
92
|
-
int24 internal constant DEFAULT_DISCOVERY_TICK_UPPER = CoinConstants.DEFAULT_DISCOVERY_TICK_UPPER;
|
|
93
|
-
uint16 internal constant DEFAULT_NUM_DISCOVERY_POSITIONS = CoinConstants.DEFAULT_NUM_DISCOVERY_POSITIONS;
|
|
94
|
-
uint256 internal constant DEFAULT_DISCOVERY_SUPPLY_SHARE = CoinConstants.DEFAULT_DISCOVERY_SUPPLY_SHARE;
|
|
93
|
+
ZoraHookRegistry internal zoraHookRegistry;
|
|
95
94
|
|
|
96
95
|
function _defaultPoolConfig(address currency) internal pure returns (bytes memory) {
|
|
97
96
|
return CoinConfigurationVersions.defaultDopplerMultiCurveUniV4(currency);
|
|
@@ -203,28 +202,14 @@ contract BaseTest is Test, ContractAddresses {
|
|
|
203
202
|
vm.stopPrank();
|
|
204
203
|
}
|
|
205
204
|
|
|
206
|
-
function _deployFeeEstimatorHook(
|
|
207
|
-
deployCodeTo("FeeEstimatorHook.sol", abi.encode(V4_POOL_MANAGER, address(factory), hookUpgradeGate
|
|
205
|
+
function _deployFeeEstimatorHook(address hooks) internal {
|
|
206
|
+
deployCodeTo("FeeEstimatorHook.sol", abi.encode(V4_POOL_MANAGER, address(factory), hookUpgradeGate), hooks);
|
|
208
207
|
}
|
|
209
208
|
|
|
210
|
-
function
|
|
209
|
+
function getSalt(address[] memory trustedMessageSenders) public returns (bytes32 hookSalt) {
|
|
211
210
|
address deployer = address(this);
|
|
212
211
|
|
|
213
|
-
(,
|
|
214
|
-
deployer,
|
|
215
|
-
V4_POOL_MANAGER,
|
|
216
|
-
address(factory),
|
|
217
|
-
trustedMessageSenders,
|
|
218
|
-
address(hookUpgradeGate)
|
|
219
|
-
);
|
|
220
|
-
|
|
221
|
-
(, creatorCoinSalt) = HooksDeployment.mineForCreatorCoinSalt(
|
|
222
|
-
deployer,
|
|
223
|
-
V4_POOL_MANAGER,
|
|
224
|
-
address(factory),
|
|
225
|
-
trustedMessageSenders,
|
|
226
|
-
address(hookUpgradeGate)
|
|
227
|
-
);
|
|
212
|
+
(, hookSalt) = HooksDeployment.mineForCoinSalt(deployer, V4_POOL_MANAGER, address(factory), trustedMessageSenders, address(hookUpgradeGate));
|
|
228
213
|
}
|
|
229
214
|
|
|
230
215
|
function _deployHooks() internal {
|
|
@@ -232,24 +217,14 @@ contract BaseTest is Test, ContractAddresses {
|
|
|
232
217
|
trustedMessageSenders[0] = UNIVERSAL_ROUTER;
|
|
233
218
|
trustedMessageSenders[1] = V4_POSITION_MANAGER;
|
|
234
219
|
|
|
235
|
-
|
|
220
|
+
bytes32 hookSalt = getSalt(trustedMessageSenders);
|
|
236
221
|
|
|
237
|
-
|
|
238
|
-
payable(
|
|
239
|
-
address(
|
|
240
|
-
HooksDeployment.deployHookWithSalt(
|
|
241
|
-
HooksDeployment.contentCoinCreationCode(V4_POOL_MANAGER, address(factory), trustedMessageSenders, address(hookUpgradeGate)),
|
|
242
|
-
contentCoinSalt
|
|
243
|
-
)
|
|
244
|
-
)
|
|
245
|
-
)
|
|
246
|
-
);
|
|
247
|
-
creatorCoinHook = CreatorCoinHook(
|
|
222
|
+
hook = ZoraV4CoinHook(
|
|
248
223
|
payable(
|
|
249
224
|
address(
|
|
250
225
|
HooksDeployment.deployHookWithSalt(
|
|
251
|
-
HooksDeployment.
|
|
252
|
-
|
|
226
|
+
HooksDeployment.makeHookCreationCode(V4_POOL_MANAGER, address(factory), trustedMessageSenders, address(hookUpgradeGate)),
|
|
227
|
+
hookSalt
|
|
253
228
|
)
|
|
254
229
|
)
|
|
255
230
|
)
|
|
@@ -291,13 +266,20 @@ contract BaseTest is Test, ContractAddresses {
|
|
|
291
266
|
|
|
292
267
|
hookUpgradeGate = new HookUpgradeGate(users.factoryOwner);
|
|
293
268
|
|
|
269
|
+
zoraHookRegistry = new ZoraHookRegistry();
|
|
270
|
+
|
|
271
|
+
address[] memory initialOwners = new address[](2);
|
|
272
|
+
initialOwners[0] = users.factoryOwner;
|
|
273
|
+
initialOwners[1] = address(factory);
|
|
274
|
+
zoraHookRegistry.initialize(initialOwners);
|
|
275
|
+
|
|
294
276
|
_deployHooks();
|
|
295
277
|
|
|
296
278
|
coinV4Impl = new ContentCoin(users.feeRecipient, address(protocolRewards), IPoolManager(V4_POOL_MANAGER), DOPPLER_AIRLOCK);
|
|
297
279
|
|
|
298
280
|
creatorCoinImpl = new CreatorCoin(users.feeRecipient, address(protocolRewards), IPoolManager(V4_POOL_MANAGER), DOPPLER_AIRLOCK);
|
|
299
281
|
|
|
300
|
-
factoryImpl = new ZoraFactoryImpl(address(coinV4Impl), address(creatorCoinImpl), address(
|
|
282
|
+
factoryImpl = new ZoraFactoryImpl(address(coinV4Impl), address(creatorCoinImpl), address(hook), address(zoraHookRegistry));
|
|
301
283
|
UUPSUpgradeable(address(factory)).upgradeToAndCall(address(factoryImpl), "");
|
|
302
284
|
factory = IZoraFactory(address(factory));
|
|
303
285
|
|
|
@@ -317,7 +299,7 @@ contract BaseTest is Test, ContractAddresses {
|
|
|
317
299
|
vm.label(address(V4_QUOTER), "V4_QUOTER");
|
|
318
300
|
vm.label(address(V4_PERMIT2), "V4_PERMIT2");
|
|
319
301
|
vm.label(address(UNIVERSAL_ROUTER), "UNIVERSAL_ROUTER");
|
|
320
|
-
vm.label(address(
|
|
302
|
+
vm.label(address(hook), "HOOK");
|
|
321
303
|
}
|
|
322
304
|
|
|
323
305
|
struct TradeRewards {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity ^0.8.13;
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {ZoraV4CoinHook} from "../../src/hooks/ZoraV4CoinHook.sol";
|
|
5
5
|
import {IPoolManager, PoolKey} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
|
|
6
6
|
import {IDeployedCoinVersionLookup} from "../../src/interfaces/IDeployedCoinVersionLookup.sol";
|
|
7
7
|
import {IHasRewardsRecipients} from "../../src/interfaces/IHasRewardsRecipients.sol";
|
|
@@ -15,11 +15,10 @@ import {ICoin, IHasSwapPath} from "../../src/interfaces/ICoin.sol";
|
|
|
15
15
|
import {UniV4SwapToCurrency} from "../../src/libs/UniV4SwapToCurrency.sol";
|
|
16
16
|
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
17
17
|
import {CoinRewardsV4} from "../../src/libs/CoinRewardsV4.sol";
|
|
18
|
-
import {BaseZoraV4CoinHook} from "../../src/hooks/BaseZoraV4CoinHook.sol";
|
|
19
18
|
import {IHooksUpgradeGate} from "../../src/interfaces/IHooksUpgradeGate.sol";
|
|
20
19
|
|
|
21
20
|
/// @dev Test util - meant to be able to etched where a normal zora hook is, to gather the fees from swaps but not distribute them
|
|
22
|
-
contract FeeEstimatorHook is
|
|
21
|
+
contract FeeEstimatorHook is ZoraV4CoinHook {
|
|
23
22
|
struct FeeEstimatorState {
|
|
24
23
|
uint128 fees0;
|
|
25
24
|
uint128 fees1;
|
|
@@ -34,9 +33,8 @@ contract FeeEstimatorHook is BaseZoraV4CoinHook {
|
|
|
34
33
|
constructor(
|
|
35
34
|
IPoolManager _poolManager,
|
|
36
35
|
IDeployedCoinVersionLookup _coinVersionLookup,
|
|
37
|
-
IHooksUpgradeGate upgradeGate
|
|
38
|
-
|
|
39
|
-
) BaseZoraV4CoinHook(_poolManager, _coinVersionLookup, new address[](0), upgradeGate, initialSupplyForPositions) {}
|
|
36
|
+
IHooksUpgradeGate upgradeGate
|
|
37
|
+
) ZoraV4CoinHook(_poolManager, _coinVersionLookup, new address[](0), upgradeGate) {}
|
|
40
38
|
|
|
41
39
|
FeeEstimatorState public feeState;
|
|
42
40
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.13;
|
|
3
|
+
|
|
4
|
+
import {CoinRewardsV4} from "../../src/libs/CoinRewardsV4.sol";
|
|
5
|
+
import {FeeEstimatorHook} from "./FeeEstimatorHook.sol";
|
|
6
|
+
import {CoinConstants} from "../../src/libs/CoinConstants.sol";
|
|
7
|
+
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
8
|
+
import {Vm} from "forge-std/Vm.sol";
|
|
9
|
+
|
|
10
|
+
// Shared struct to track balance changes for reward recipients
|
|
11
|
+
struct RewardBalances {
|
|
12
|
+
uint256 creator;
|
|
13
|
+
uint256 platformReferrer;
|
|
14
|
+
uint256 tradeReferrer;
|
|
15
|
+
uint256 protocol;
|
|
16
|
+
uint256 doppler;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
library RewardTestHelpers {
|
|
20
|
+
Vm private constant vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
|
|
21
|
+
|
|
22
|
+
// Helper function to calculate reward based on BPS
|
|
23
|
+
function calculateReward(uint256 total, uint256 bps) internal pure returns (uint256) {
|
|
24
|
+
return (total * bps) / 10_000;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function calculateExpectedRewards(
|
|
28
|
+
uint256 marketRewards,
|
|
29
|
+
bool hasPlatformReferrer,
|
|
30
|
+
bool hasTradeReferrer
|
|
31
|
+
) internal pure returns (RewardBalances memory rewards) {
|
|
32
|
+
rewards.creator = calculateReward(marketRewards, CoinConstants.CREATOR_REWARD_BPS);
|
|
33
|
+
rewards.platformReferrer = hasPlatformReferrer ? calculateReward(marketRewards, CoinConstants.CREATE_REFERRAL_REWARD_BPS) : 0;
|
|
34
|
+
rewards.tradeReferrer = hasTradeReferrer ? calculateReward(marketRewards, CoinConstants.TRADE_REFERRAL_REWARD_BPS) : 0;
|
|
35
|
+
rewards.doppler = calculateReward(marketRewards, CoinConstants.DOPPLER_REWARD_BPS);
|
|
36
|
+
rewards.protocol = marketRewards - rewards.creator - rewards.platformReferrer - rewards.tradeReferrer - rewards.doppler;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Helper function to assert reward distributions match expected RewardBalances
|
|
40
|
+
function assertRewardsApproxEqRel(RewardBalances memory actual, RewardBalances memory expected) internal pure {
|
|
41
|
+
vm.assertApproxEqRel(actual.creator, expected.creator, 0.25e18, "Creator reward amount");
|
|
42
|
+
vm.assertApproxEqRel(actual.platformReferrer, expected.platformReferrer, 0.25e18, "Platform referrer reward amount");
|
|
43
|
+
vm.assertApproxEqRel(actual.tradeReferrer, expected.tradeReferrer, 0.25e18, "Trade referrer reward amount");
|
|
44
|
+
vm.assertApproxEqRel(actual.doppler, expected.doppler, 0.25e18, "Doppler reward amount");
|
|
45
|
+
vm.assertApproxEqRel(actual.protocol, expected.protocol, 0.25e18, "Protocol reward amount");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Helper function to assert with higher tolerance for complex conversions
|
|
49
|
+
function assertRewardsApproxEqRelWithTolerance(RewardBalances memory actual, RewardBalances memory expected, uint256 tolerance) internal pure {
|
|
50
|
+
vm.assertApproxEqRel(actual.creator, expected.creator, tolerance, "Creator reward amount");
|
|
51
|
+
vm.assertApproxEqRel(actual.platformReferrer, expected.platformReferrer, tolerance, "Platform referrer reward amount");
|
|
52
|
+
vm.assertApproxEqRel(actual.tradeReferrer, expected.tradeReferrer, tolerance, "Trade referrer reward amount");
|
|
53
|
+
vm.assertApproxEqRel(actual.doppler, expected.doppler, tolerance, "Doppler reward amount");
|
|
54
|
+
vm.assertApproxEqRel(actual.protocol, expected.protocol, tolerance, "Protocol reward amount");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Generic function to record token balances for all reward recipients
|
|
58
|
+
function recordBalances(
|
|
59
|
+
IERC20 token,
|
|
60
|
+
address creator,
|
|
61
|
+
address platformReferrer,
|
|
62
|
+
address tradeReferrer,
|
|
63
|
+
address protocolRecipient,
|
|
64
|
+
address dopplerRecipient
|
|
65
|
+
) internal view returns (RewardBalances memory balances) {
|
|
66
|
+
balances.creator = token.balanceOf(creator);
|
|
67
|
+
balances.platformReferrer = token.balanceOf(platformReferrer);
|
|
68
|
+
balances.tradeReferrer = token.balanceOf(tradeReferrer);
|
|
69
|
+
balances.protocol = token.balanceOf(protocolRecipient);
|
|
70
|
+
balances.doppler = token.balanceOf(dopplerRecipient);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Helper function to calculate reward deltas between two balance snapshots
|
|
74
|
+
function calculateRewardDeltas(
|
|
75
|
+
RewardBalances memory initialBalances,
|
|
76
|
+
RewardBalances memory finalBalances
|
|
77
|
+
) internal pure returns (RewardBalances memory deltas) {
|
|
78
|
+
deltas.creator = finalBalances.creator - initialBalances.creator;
|
|
79
|
+
deltas.platformReferrer = finalBalances.platformReferrer - initialBalances.platformReferrer;
|
|
80
|
+
deltas.tradeReferrer = finalBalances.tradeReferrer - initialBalances.tradeReferrer;
|
|
81
|
+
deltas.protocol = finalBalances.protocol - initialBalances.protocol;
|
|
82
|
+
deltas.doppler = finalBalances.doppler - initialBalances.doppler;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Helper function to sum total rewards
|
|
86
|
+
function getTotalRewards(RewardBalances memory rewards) internal pure returns (uint256) {
|
|
87
|
+
return rewards.creator + rewards.platformReferrer + rewards.tradeReferrer + rewards.protocol + rewards.doppler;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Helper function to calculate reward deltas after trade for any token
|
|
91
|
+
function calculateTokenRewardDeltas(
|
|
92
|
+
RewardBalances memory initialBalances,
|
|
93
|
+
IERC20 token,
|
|
94
|
+
address creator,
|
|
95
|
+
address platformReferrer,
|
|
96
|
+
address tradeReferrer,
|
|
97
|
+
address protocolRecipient,
|
|
98
|
+
address dopplerRecipient
|
|
99
|
+
) internal view returns (RewardBalances memory deltas) {
|
|
100
|
+
deltas.creator = token.balanceOf(creator) - initialBalances.creator;
|
|
101
|
+
deltas.platformReferrer = token.balanceOf(platformReferrer) - initialBalances.platformReferrer;
|
|
102
|
+
deltas.tradeReferrer = token.balanceOf(tradeReferrer) - initialBalances.tradeReferrer;
|
|
103
|
+
deltas.protocol = token.balanceOf(protocolRecipient) - initialBalances.protocol;
|
|
104
|
+
deltas.doppler = token.balanceOf(dopplerRecipient) - initialBalances.doppler;
|
|
105
|
+
}
|
|
106
|
+
}
|