@bananapus/suckers-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.
Files changed (149) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +422 -0
  3. package/SECURITY.md +55 -0
  4. package/SKILLS.md +163 -0
  5. package/deployments/nana-suckers-v5/arbitrum/JBArbitrumSucker.json +1425 -0
  6. package/deployments/nana-suckers-v5/arbitrum/JBArbitrumSuckerDeployer.json +391 -0
  7. package/deployments/nana-suckers-v5/arbitrum/JBCCIPSucker.json +1479 -0
  8. package/deployments/nana-suckers-v5/arbitrum/JBCCIPSuckerDeployer.json +433 -0
  9. package/deployments/nana-suckers-v5/arbitrum/JBCCIPSuckerDeployer_1.json +433 -0
  10. package/deployments/nana-suckers-v5/arbitrum/JBCCIPSuckerDeployer_2.json +433 -0
  11. package/deployments/nana-suckers-v5/arbitrum/JBCCIPSucker_1.json +1479 -0
  12. package/deployments/nana-suckers-v5/arbitrum/JBCCIPSucker_2.json +1479 -0
  13. package/deployments/nana-suckers-v5/arbitrum/JBSuckerRegistry.json +690 -0
  14. package/deployments/nana-suckers-v5/arbitrum_sepolia/JBArbitrumSucker.json +1425 -0
  15. package/deployments/nana-suckers-v5/arbitrum_sepolia/JBArbitrumSuckerDeployer.json +391 -0
  16. package/deployments/nana-suckers-v5/arbitrum_sepolia/JBCCIPSucker.json +1479 -0
  17. package/deployments/nana-suckers-v5/arbitrum_sepolia/JBCCIPSuckerDeployer.json +433 -0
  18. package/deployments/nana-suckers-v5/arbitrum_sepolia/JBCCIPSuckerDeployer_1.json +433 -0
  19. package/deployments/nana-suckers-v5/arbitrum_sepolia/JBCCIPSuckerDeployer_2.json +433 -0
  20. package/deployments/nana-suckers-v5/arbitrum_sepolia/JBCCIPSucker_1.json +1479 -0
  21. package/deployments/nana-suckers-v5/arbitrum_sepolia/JBCCIPSucker_2.json +1479 -0
  22. package/deployments/nana-suckers-v5/arbitrum_sepolia/JBSuckerRegistry.json +690 -0
  23. package/deployments/nana-suckers-v5/base/JBBaseSucker.json +1389 -0
  24. package/deployments/nana-suckers-v5/base/JBBaseSuckerDeployer.json +376 -0
  25. package/deployments/nana-suckers-v5/base/JBCCIPSucker.json +1483 -0
  26. package/deployments/nana-suckers-v5/base/JBCCIPSuckerDeployer.json +436 -0
  27. package/deployments/nana-suckers-v5/base/JBCCIPSuckerDeployer_1.json +436 -0
  28. package/deployments/nana-suckers-v5/base/JBCCIPSuckerDeployer_2.json +436 -0
  29. package/deployments/nana-suckers-v5/base/JBCCIPSucker_1.json +1483 -0
  30. package/deployments/nana-suckers-v5/base/JBCCIPSucker_2.json +1483 -0
  31. package/deployments/nana-suckers-v5/base/JBSuckerRegistry.json +694 -0
  32. package/deployments/nana-suckers-v5/base_sepolia/JBBaseSucker.json +1389 -0
  33. package/deployments/nana-suckers-v5/base_sepolia/JBBaseSuckerDeployer.json +376 -0
  34. package/deployments/nana-suckers-v5/base_sepolia/JBCCIPSucker.json +1483 -0
  35. package/deployments/nana-suckers-v5/base_sepolia/JBCCIPSuckerDeployer.json +436 -0
  36. package/deployments/nana-suckers-v5/base_sepolia/JBCCIPSuckerDeployer_1.json +436 -0
  37. package/deployments/nana-suckers-v5/base_sepolia/JBCCIPSuckerDeployer_2.json +436 -0
  38. package/deployments/nana-suckers-v5/base_sepolia/JBCCIPSucker_1.json +1483 -0
  39. package/deployments/nana-suckers-v5/base_sepolia/JBCCIPSucker_2.json +1483 -0
  40. package/deployments/nana-suckers-v5/base_sepolia/JBSuckerRegistry.json +694 -0
  41. package/deployments/nana-suckers-v5/ethereum/JBArbitrumSucker.json +1429 -0
  42. package/deployments/nana-suckers-v5/ethereum/JBArbitrumSuckerDeployer.json +394 -0
  43. package/deployments/nana-suckers-v5/ethereum/JBBaseSucker.json +1389 -0
  44. package/deployments/nana-suckers-v5/ethereum/JBBaseSuckerDeployer.json +376 -0
  45. package/deployments/nana-suckers-v5/ethereum/JBCCIPSucker.json +1483 -0
  46. package/deployments/nana-suckers-v5/ethereum/JBCCIPSuckerDeployer.json +436 -0
  47. package/deployments/nana-suckers-v5/ethereum/JBCCIPSuckerDeployer_1.json +436 -0
  48. package/deployments/nana-suckers-v5/ethereum/JBCCIPSuckerDeployer_2.json +436 -0
  49. package/deployments/nana-suckers-v5/ethereum/JBCCIPSucker_1.json +1483 -0
  50. package/deployments/nana-suckers-v5/ethereum/JBCCIPSucker_2.json +1483 -0
  51. package/deployments/nana-suckers-v5/ethereum/JBOptimismSucker.json +1389 -0
  52. package/deployments/nana-suckers-v5/ethereum/JBOptimismSuckerDeployer.json +376 -0
  53. package/deployments/nana-suckers-v5/ethereum/JBSuckerRegistry.json +694 -0
  54. package/deployments/nana-suckers-v5/optimism/JBCCIPSucker.json +1479 -0
  55. package/deployments/nana-suckers-v5/optimism/JBCCIPSuckerDeployer.json +433 -0
  56. package/deployments/nana-suckers-v5/optimism/JBCCIPSuckerDeployer_1.json +433 -0
  57. package/deployments/nana-suckers-v5/optimism/JBCCIPSuckerDeployer_2.json +433 -0
  58. package/deployments/nana-suckers-v5/optimism/JBCCIPSucker_1.json +1479 -0
  59. package/deployments/nana-suckers-v5/optimism/JBCCIPSucker_2.json +1479 -0
  60. package/deployments/nana-suckers-v5/optimism/JBOptimismSucker.json +1385 -0
  61. package/deployments/nana-suckers-v5/optimism/JBOptimismSuckerDeployer.json +373 -0
  62. package/deployments/nana-suckers-v5/optimism/JBSuckerRegistry.json +690 -0
  63. package/deployments/nana-suckers-v5/optimism_sepolia/JBCCIPSucker.json +1483 -0
  64. package/deployments/nana-suckers-v5/optimism_sepolia/JBCCIPSuckerDeployer.json +436 -0
  65. package/deployments/nana-suckers-v5/optimism_sepolia/JBCCIPSuckerDeployer_1.json +436 -0
  66. package/deployments/nana-suckers-v5/optimism_sepolia/JBCCIPSuckerDeployer_2.json +436 -0
  67. package/deployments/nana-suckers-v5/optimism_sepolia/JBCCIPSucker_1.json +1483 -0
  68. package/deployments/nana-suckers-v5/optimism_sepolia/JBCCIPSucker_2.json +1483 -0
  69. package/deployments/nana-suckers-v5/optimism_sepolia/JBOptimismSucker.json +1389 -0
  70. package/deployments/nana-suckers-v5/optimism_sepolia/JBOptimismSuckerDeployer.json +376 -0
  71. package/deployments/nana-suckers-v5/optimism_sepolia/JBSuckerRegistry.json +694 -0
  72. package/deployments/nana-suckers-v5/sepolia/JBArbitrumSucker.json +1429 -0
  73. package/deployments/nana-suckers-v5/sepolia/JBArbitrumSuckerDeployer.json +394 -0
  74. package/deployments/nana-suckers-v5/sepolia/JBBaseSucker.json +1389 -0
  75. package/deployments/nana-suckers-v5/sepolia/JBBaseSuckerDeployer.json +376 -0
  76. package/deployments/nana-suckers-v5/sepolia/JBCCIPSucker.json +1483 -0
  77. package/deployments/nana-suckers-v5/sepolia/JBCCIPSuckerDeployer.json +436 -0
  78. package/deployments/nana-suckers-v5/sepolia/JBCCIPSuckerDeployer_1.json +436 -0
  79. package/deployments/nana-suckers-v5/sepolia/JBCCIPSuckerDeployer_2.json +436 -0
  80. package/deployments/nana-suckers-v5/sepolia/JBCCIPSucker_1.json +1483 -0
  81. package/deployments/nana-suckers-v5/sepolia/JBCCIPSucker_2.json +1483 -0
  82. package/deployments/nana-suckers-v5/sepolia/JBOptimismSucker.json +1389 -0
  83. package/deployments/nana-suckers-v5/sepolia/JBOptimismSuckerDeployer.json +376 -0
  84. package/deployments/nana-suckers-v5/sepolia/JBSuckerRegistry.json +694 -0
  85. package/foundry.lock +11 -0
  86. package/foundry.toml +22 -0
  87. package/package.json +33 -0
  88. package/remappings.txt +1 -0
  89. package/script/Deploy.s.sol +506 -0
  90. package/script/helpers/SuckerDeploymentLib.sol +97 -0
  91. package/slither-ci.config.json +10 -0
  92. package/sphinx.lock +476 -0
  93. package/src/JBArbitrumSucker.sol +311 -0
  94. package/src/JBBaseSucker.sol +41 -0
  95. package/src/JBCCIPSucker.sol +303 -0
  96. package/src/JBOptimismSucker.sol +143 -0
  97. package/src/JBSucker.sol +1159 -0
  98. package/src/JBSuckerRegistry.sol +262 -0
  99. package/src/deployers/JBArbitrumSuckerDeployer.sol +86 -0
  100. package/src/deployers/JBBaseSuckerDeployer.sol +26 -0
  101. package/src/deployers/JBCCIPSuckerDeployer.sol +88 -0
  102. package/src/deployers/JBOptimismSuckerDeployer.sol +82 -0
  103. package/src/deployers/JBSuckerDeployer.sol +147 -0
  104. package/src/enums/JBAddToBalanceMode.sol +11 -0
  105. package/src/enums/JBLayer.sol +8 -0
  106. package/src/enums/JBSuckerState.sol +14 -0
  107. package/src/interfaces/IArbGatewayRouter.sol +11 -0
  108. package/src/interfaces/IArbL1GatewayRouter.sol +17 -0
  109. package/src/interfaces/IArbL2GatewayRouter.sol +14 -0
  110. package/src/interfaces/ICCIPRouter.sol +11 -0
  111. package/src/interfaces/IJBArbitrumSucker.sol +13 -0
  112. package/src/interfaces/IJBArbitrumSuckerDeployer.sol +12 -0
  113. package/src/interfaces/IJBCCIPSuckerDeployer.sol +15 -0
  114. package/src/interfaces/IJBOpSuckerDeployer.sol +11 -0
  115. package/src/interfaces/IJBOptimismSucker.sol +10 -0
  116. package/src/interfaces/IJBSucker.sol +144 -0
  117. package/src/interfaces/IJBSuckerDeployer.sol +40 -0
  118. package/src/interfaces/IJBSuckerExtended.sol +22 -0
  119. package/src/interfaces/IJBSuckerRegistry.sol +75 -0
  120. package/src/interfaces/IOPMessenger.sol +18 -0
  121. package/src/interfaces/IOPStandardBridge.sol +29 -0
  122. package/src/interfaces/IWrappedNativeToken.sol +13 -0
  123. package/src/libraries/ARBAddresses.sol +17 -0
  124. package/src/libraries/ARBChains.sol +11 -0
  125. package/src/libraries/CCIPHelper.sol +136 -0
  126. package/src/structs/JBClaim.sol +13 -0
  127. package/src/structs/JBInboxTreeRoot.sol +12 -0
  128. package/src/structs/JBLeaf.sol +14 -0
  129. package/src/structs/JBMessageRoot.sol +16 -0
  130. package/src/structs/JBOutboxTree.sol +18 -0
  131. package/src/structs/JBRemoteToken.sol +17 -0
  132. package/src/structs/JBSuckerDeployerConfig.sol +12 -0
  133. package/src/structs/JBSuckersPair.sol +11 -0
  134. package/src/structs/JBTokenMapping.sol +13 -0
  135. package/src/utils/MerkleLib.sol +1020 -0
  136. package/test/Fork.t.sol +514 -0
  137. package/test/InteropCompat.t.sol +676 -0
  138. package/test/SuckerAttacks.t.sol +509 -0
  139. package/test/SuckerDeepAttacks.t.sol +1563 -0
  140. package/test/mocks/ERC20Mock.sol +36 -0
  141. package/test/mocks/MockMessenger.sol +42 -0
  142. package/test/unit/arb.t.sol +28 -0
  143. package/test/unit/ccip_native_interop.t.sol +719 -0
  144. package/test/unit/ccip_refund.t.sol +234 -0
  145. package/test/unit/deployer.t.sol +475 -0
  146. package/test/unit/emergency.t.sol +305 -0
  147. package/test/unit/merkle.t.sol +212 -0
  148. package/test/unit/multi_chain_evolution.t.sol +622 -0
  149. package/test/unit/registry.t.sol +26 -0
@@ -0,0 +1,305 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.13;
3
+
4
+ import "forge-std/Test.sol";
5
+
6
+ import "../../src/JBSucker.sol";
7
+ import {LibClone} from "solady/src/utils/LibClone.sol";
8
+ import /* {*} from */ "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
9
+
10
+ contract SuckerEmergencyTest is Test {
11
+ using stdStorage for StdStorage;
12
+
13
+ address constant DIRECTORY = address(600);
14
+ address constant PERMISSIONS = address(800);
15
+ address constant TOKENS = address(700);
16
+ address constant CONTROLLER = address(900);
17
+ address constant PROJECT = address(1000);
18
+ address constant FORWARDER = address(1100);
19
+
20
+ function setUp() public {
21
+ vm.label(DIRECTORY, "MOCK_DIRECTORY");
22
+ vm.label(PERMISSIONS, "MOCK_PERMISSIONS");
23
+ vm.label(TOKENS, "MOCK_TOKENS");
24
+ vm.label(CONTROLLER, "MOCK_CONTROLLER");
25
+ vm.label(PROJECT, "MOCK_PROJECT");
26
+ }
27
+
28
+ function testHelloWorld() external {
29
+ _createTestSucker(1, "");
30
+ }
31
+
32
+ /// @notice Ensures that if a sucker is deprecated and a claim is valid that a user can withdraw their deposit.
33
+ function testEmergencyExitWhenDeprecated(bool setAsDeprecated, bool isValidClaim, JBClaim memory claim) external {
34
+ uint256 projectId = 1;
35
+ TestSucker sucker = _createTestSucker(projectId, "");
36
+
37
+ // Mock the Directory.
38
+ vm.mockCall(DIRECTORY, abi.encodeCall(IJBDirectory.PROJECTS, ()), abi.encode(PROJECT));
39
+ // Mock the owner of the project.
40
+ vm.mockCall(PROJECT, abi.encodeCall(IERC721.ownerOf, (projectId)), abi.encode(address(this)));
41
+
42
+ // Set the outbox balance to be atleast the same as the attempted exit amount.
43
+ sucker.test_setOutboxBalance(claim.token, claim.leaf.terminalTokenAmount);
44
+
45
+ // Set the state of the sucker to be deprecated.
46
+ if (setAsDeprecated) {
47
+ uint256 deprecationTimestamp = block.timestamp + 14 days;
48
+ sucker.setDeprecation(uint40(deprecationTimestamp));
49
+
50
+ // Foward until its deprecated.
51
+ vm.warp(deprecationTimestamp);
52
+ }
53
+
54
+ // Mock the calls the sucker does to mint the tokens to the user.
55
+ vm.mockCall(DIRECTORY, abi.encodeCall(IJBDirectory.controllerOf, (projectId)), abi.encode(CONTROLLER));
56
+ vm.mockCall(
57
+ CONTROLLER,
58
+ abi.encodeCall(
59
+ IJBController.mintTokensOf,
60
+ (projectId, claim.leaf.projectTokenCount, address(uint160(uint256(claim.leaf.beneficiary))), "", false)
61
+ ),
62
+ abi.encode(claim.leaf.projectTokenCount)
63
+ );
64
+
65
+ // This ensures that if either the claim is considered invalid or that if the sucker was not deprecated that the
66
+ // emergency exit would not work.
67
+ sucker.test_setNextMerkleCheckToBe(isValidClaim);
68
+ if (!isValidClaim || !setAsDeprecated) {
69
+ vm.expectRevert();
70
+ }
71
+
72
+ // Attempt to emergency exit.
73
+ sucker.exitThroughEmergencyHatch(claim);
74
+
75
+ // Attempt to double exit.
76
+ vm.expectRevert();
77
+ sucker.exitThroughEmergencyHatch(claim);
78
+ }
79
+
80
+ /// @notice Ensures that if a sucker is send disabled and a claim is valid that a user can withdraw their deposit.
81
+ function testEmergencyExitWhenSendingDisabled(bool sendDisabled, bool isValidClaim, JBClaim memory claim) external {
82
+ uint256 projectId = 1;
83
+ TestSucker sucker = _createTestSucker(projectId, "");
84
+
85
+ // Mock the Directory.
86
+ vm.mockCall(DIRECTORY, abi.encodeCall(IJBDirectory.PROJECTS, ()), abi.encode(PROJECT));
87
+ // Mock the owner of the project.
88
+ vm.mockCall(PROJECT, abi.encodeCall(IERC721.ownerOf, (projectId)), abi.encode(address(this)));
89
+
90
+ // Set the outbox balance to be atleast the same as the attempted exit amount.
91
+ sucker.test_setOutboxBalance(claim.token, claim.leaf.terminalTokenAmount);
92
+
93
+ // Set the state of the sucker to be deprecated.
94
+ if (sendDisabled) {
95
+ uint256 deprecationTimestamp = block.timestamp + 14 days;
96
+ sucker.setDeprecation(uint40(deprecationTimestamp));
97
+
98
+ // Foward until sending is disabled, which is the next block.
99
+ vm.warp(block.timestamp + 1);
100
+ }
101
+
102
+ // Mock the calls the sucker does to mint the tokens to the user.
103
+ vm.mockCall(DIRECTORY, abi.encodeCall(IJBDirectory.controllerOf, (projectId)), abi.encode(CONTROLLER));
104
+ vm.mockCall(
105
+ CONTROLLER,
106
+ abi.encodeCall(
107
+ IJBController.mintTokensOf,
108
+ (projectId, claim.leaf.projectTokenCount, address(uint160(uint256(claim.leaf.beneficiary))), "", false)
109
+ ),
110
+ abi.encode(claim.leaf.projectTokenCount)
111
+ );
112
+
113
+ // This ensures that if either the claim is considered invalid or that if the sucker was not deprecated that the
114
+ // emergency exit would not work.
115
+ sucker.test_setNextMerkleCheckToBe(isValidClaim);
116
+ if (!isValidClaim || !sendDisabled) {
117
+ vm.expectRevert();
118
+ }
119
+
120
+ // Attempt to emergency exit.
121
+ sucker.exitThroughEmergencyHatch(claim);
122
+
123
+ // Attempt to double exit.
124
+ vm.expectRevert();
125
+ sucker.exitThroughEmergencyHatch(claim);
126
+ }
127
+
128
+ /// @notice Ensures that users can exit on the local chain if the emergency hatch is opened for the token.
129
+ function testEmergencyExitWhenEmergencyHatchOpenForToken(
130
+ bool tokenAllowEmergencyHatch,
131
+ bool isValidClaim,
132
+ JBClaim memory claim
133
+ )
134
+ external
135
+ {
136
+ uint256 projectId = 1;
137
+ TestSucker sucker = _createTestSucker(projectId, "");
138
+
139
+ // Mock the Directory.
140
+ vm.mockCall(DIRECTORY, abi.encodeCall(IJBDirectory.PROJECTS, ()), abi.encode(PROJECT));
141
+ // Mock the owner of the project.
142
+ vm.mockCall(PROJECT, abi.encodeCall(IERC721.ownerOf, (projectId)), abi.encode(address(this)));
143
+
144
+ // Set the outbox balance to be atleast the same as the attempted exit amount.
145
+ sucker.test_setOutboxBalance(claim.token, claim.leaf.terminalTokenAmount);
146
+
147
+ // Open the emergency hatch for the token if set for this test.
148
+ if (tokenAllowEmergencyHatch) {
149
+ address[] memory tokens = new address[](1);
150
+ tokens[0] = claim.token;
151
+
152
+ sucker.enableEmergencyHatchFor(tokens);
153
+ }
154
+
155
+ // Mock the calls the sucker does to mint the tokens to the user.
156
+ vm.mockCall(DIRECTORY, abi.encodeCall(IJBDirectory.controllerOf, (projectId)), abi.encode(CONTROLLER));
157
+ vm.mockCall(
158
+ CONTROLLER,
159
+ abi.encodeCall(
160
+ IJBController.mintTokensOf,
161
+ (projectId, claim.leaf.projectTokenCount, address(uint160(uint256(claim.leaf.beneficiary))), "", false)
162
+ ),
163
+ abi.encode(claim.leaf.projectTokenCount)
164
+ );
165
+
166
+ // This ensures that if either the claim is considered invalid or that if the sucker was not deprecated that the
167
+ // emergency exit would not work.
168
+ sucker.test_setNextMerkleCheckToBe(isValidClaim);
169
+ if (!isValidClaim || !tokenAllowEmergencyHatch) {
170
+ vm.expectRevert();
171
+ }
172
+
173
+ // Attempt to emergency exit.
174
+ sucker.exitThroughEmergencyHatch(claim);
175
+
176
+ // Attempt to double exit.
177
+ vm.expectRevert();
178
+ sucker.exitThroughEmergencyHatch(claim);
179
+ }
180
+
181
+ /// @notice tests that the deprecation can be set, changed and cancelled.
182
+ function testCancelDeprecation(uint40 currentTime, uint40 deprecateAt, uint40 changeDeprecationTo) external {
183
+ uint40 messagingDelay = 14 days;
184
+
185
+ // Use bound() instead of vm.assume() to avoid excessive fuzz rejection.
186
+ uint40 maxSafe = type(uint40).max - 3 * messagingDelay;
187
+ currentTime = uint40(bound(currentTime, 0, maxSafe));
188
+ deprecateAt = uint40(bound(deprecateAt, currentTime + messagingDelay + 1, maxSafe + messagingDelay));
189
+ changeDeprecationTo =
190
+ uint40(bound(changeDeprecationTo, deprecateAt + messagingDelay + 1, maxSafe + 2 * messagingDelay));
191
+
192
+ // The time that we have to change the deprecation.
193
+ uint40 bufferTime;
194
+ bufferTime = deprecateAt - messagingDelay - currentTime;
195
+
196
+ uint256 projectId = 1;
197
+ TestSucker sucker = _createTestSucker(projectId, "");
198
+
199
+ // Mock the Directory.
200
+ vm.mockCall(DIRECTORY, abi.encodeCall(IJBDirectory.PROJECTS, ()), abi.encode(PROJECT));
201
+ // Mock the owner of the project.
202
+ vm.mockCall(PROJECT, abi.encodeCall(IERC721.ownerOf, (projectId)), abi.encode(address(this)));
203
+
204
+ // Set the time at which the deprecation call is done.
205
+ vm.warp(currentTime);
206
+ // Set the deprecation to be at a future time.
207
+ sucker.setDeprecation(uint40(deprecateAt));
208
+
209
+ // Foward to a time before its fully deprecated..
210
+ vm.warp(currentTime + bufferTime - 1);
211
+ // The state should be `DEPRECATION_PENDING`.
212
+ assertEq(uint8(sucker.state()), 1);
213
+ // Change the time at which it deprecates
214
+ sucker.setDeprecation(changeDeprecationTo);
215
+ }
216
+
217
+ function _createTestSucker(uint256 projectId, bytes32 salt) internal returns (TestSucker) {
218
+ // Singleton.
219
+ TestSucker singleton = new TestSucker(
220
+ IJBDirectory(DIRECTORY),
221
+ IJBPermissions(PERMISSIONS),
222
+ IJBTokens(TOKENS),
223
+ JBAddToBalanceMode.MANUAL,
224
+ FORWARDER
225
+ );
226
+ vm.label(address(singleton), "SUCKER_SINGLETON");
227
+
228
+ // Clone the singleton and initialize the clone.
229
+ TestSucker sucker = TestSucker(payable(address(LibClone.cloneDeterministic(address(singleton), salt))));
230
+ vm.label(address(sucker), "SUCKER");
231
+ sucker.initialize(projectId);
232
+
233
+ return TestSucker(sucker);
234
+ }
235
+ }
236
+
237
+ contract TestSucker is JBSucker {
238
+ bool nextCheckShouldPass;
239
+
240
+ /// @param directory A contract storing directories of terminals and controllers for each project.
241
+ /// @param tokens A contract that manages token minting and burning.
242
+ /// @param permissions A contract storing permissions.
243
+ /// @param addToBalanceMode The mode of adding tokens to balance.
244
+ constructor(
245
+ IJBDirectory directory,
246
+ IJBPermissions permissions,
247
+ IJBTokens tokens,
248
+ JBAddToBalanceMode addToBalanceMode,
249
+ address forwarder
250
+ )
251
+ JBSucker(directory, permissions, tokens, addToBalanceMode, forwarder)
252
+ {}
253
+
254
+ function _sendRootOverAMB(
255
+ uint256 transportPayment,
256
+ uint256,
257
+ address token,
258
+ uint256 amount,
259
+ JBRemoteToken memory remoteToken,
260
+ JBMessageRoot memory message
261
+ )
262
+ internal
263
+ override
264
+ {}
265
+
266
+ function _isRemotePeer(address sender) internal view override returns (bool valid) {
267
+ return sender == _toAddress(peer());
268
+ }
269
+
270
+ function peerChainId() external view virtual override returns (uint256) {
271
+ return block.chainid;
272
+ }
273
+
274
+ /// @notice Validates a branch root against the expected root.
275
+ /// @dev This is a virtual function to allow tests to override the behavior, it should never be overwritten
276
+ /// otherwise.
277
+ function _validateBranchRoot(
278
+ bytes32 expectedRoot,
279
+ uint256 projectTokenCount,
280
+ uint256 terminalTokenAmount,
281
+ bytes32 beneficiary,
282
+ uint256 index,
283
+ bytes32[_TREE_DEPTH] calldata leaves
284
+ )
285
+ internal
286
+ virtual
287
+ override
288
+ {
289
+ // If the next check should fail, then we forward the call.
290
+ if (!nextCheckShouldPass) {
291
+ super._validateBranchRoot(expectedRoot, projectTokenCount, terminalTokenAmount, beneficiary, index, leaves);
292
+ }
293
+
294
+ // Set it to be false again.
295
+ nextCheckShouldPass = false;
296
+ }
297
+
298
+ function test_setNextMerkleCheckToBe(bool _nextCheckShouldPass) external {
299
+ nextCheckShouldPass = _nextCheckShouldPass;
300
+ }
301
+
302
+ function test_setOutboxBalance(address token, uint256 amount) external {
303
+ _outboxOf[token].balance = amount;
304
+ }
305
+ }
@@ -0,0 +1,212 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.13;
3
+
4
+ import "forge-std/Test.sol";
5
+
6
+ import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
7
+ import "../../src/JBSucker.sol";
8
+ import "../../src/deployers/JBOptimismSuckerDeployer.sol";
9
+
10
+ import {JBLeaf} from "../../src/structs/JBLeaf.sol";
11
+ import {JBClaim} from "../../src/structs/JBClaim.sol";
12
+
13
+ contract MerkleUnitTest is JBSucker, Test {
14
+ using MerkleLib for MerkleLib.Tree;
15
+
16
+ bytes32[32] _proof;
17
+
18
+ constructor()
19
+ // OPMessenger(address(500)),
20
+ // OPStandardBridge(address(550)),
21
+ JBSucker(
22
+ IJBDirectory(address(600)),
23
+ IJBPermissions(address(800)),
24
+ IJBTokens(address(700)),
25
+ JBAddToBalanceMode.MANUAL,
26
+ address(0)
27
+ )
28
+ // self.initialize(.NATIVE_TOKEN, JBConstants.NATIVE_TOKEN, JBConstants.NATIVE_TOKEN)
29
+
30
+ {
31
+ // initialize({peer: address(this), projectId: 1});
32
+ }
33
+
34
+ function setUp() public {
35
+ // Insert some items into the queue
36
+ // Index 0
37
+ _insertIntoTree(8 ether, JBConstants.NATIVE_TOKEN, 15 ether, bytes32(uint256(uint160(address(1000)))));
38
+ // Index 1
39
+ _insertIntoTree(0.1 ether, JBConstants.NATIVE_TOKEN, 200 ether, bytes32(uint256(uint160(address(999)))));
40
+ // Index 2
41
+ _insertIntoTree(5 ether, JBConstants.NATIVE_TOKEN, 5 ether, bytes32(uint256(uint160(address(120)))));
42
+
43
+ // Pre-computed proof thats valid for the above data.
44
+ _proof[0] = 0x0000000000000000000000000000000000000000000000000000000000000000;
45
+ _proof[1] = 0x15d682413b76d69d3a9f37321d80938e54900b74e3f119c027ed87dcec1a935b;
46
+ _proof[2] = 0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30;
47
+ _proof[3] = 0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85;
48
+ _proof[4] = 0xe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344;
49
+ _proof[5] = 0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d;
50
+ _proof[6] = 0x887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968;
51
+ _proof[7] = 0xffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f83;
52
+ _proof[8] = 0x9867cc5f7f196b93bae1e27e6320742445d290f2263827498b54fec539f756af;
53
+ _proof[9] = 0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0;
54
+ _proof[10] = 0xf9dc3e7fe016e050eff260334f18a5d4fe391d82092319f5964f2e2eb7c1c3a5;
55
+ _proof[11] = 0xf8b13a49e282f609c317a833fb8d976d11517c571d1221a265d25af778ecf892;
56
+ _proof[12] = 0x3490c6ceeb450aecdc82e28293031d10c7d73bf85e57bf041a97360aa2c5d99c;
57
+ _proof[13] = 0xc1df82d9c4b87413eae2ef048f94b4d3554cea73d92b0f7af96e0271c691e2bb;
58
+ _proof[14] = 0x5c67add7c6caf302256adedf7ab114da0acfe870d449a3a489f781d659e8becc;
59
+ _proof[15] = 0xda7bce9f4e8618b6bd2f4132ce798cdc7a60e7e1460a7299e3c6342a579626d2;
60
+ _proof[16] = 0x2733e50f526ec2fa19a22b31e8ed50f23cd1fdf94c9154ed3a7609a2f1ff981f;
61
+ _proof[17] = 0xe1d3b5c807b281e4683cc6d6315cf95b9ade8641defcb32372f1c126e398ef7a;
62
+ _proof[18] = 0x5a2dce0a8a7f68bb74560f8f71837c2c2ebbcbf7fffb42ae1896f13f7c7479a0;
63
+ _proof[19] = 0xb46a28b6f55540f89444f63de0378e3d121be09e06cc9ded1c20e65876d36aa0;
64
+ _proof[20] = 0xc65e9645644786b620e2dd2ad648ddfcbf4a7e5b1a3a4ecfe7f64667a3f0b7e2;
65
+ _proof[21] = 0xf4418588ed35a2458cffeb39b93d26f18d2ab13bdce6aee58e7b99359ec2dfd9;
66
+ _proof[22] = 0x5a9c16dc00d6ef18b7933a6f8dc65ccb55667138776f7dea101070dc8796e377;
67
+ _proof[23] = 0x4df84f40ae0c8229d0d6069e5c8f39a7c299677a09d367fc7b05e3bc380ee652;
68
+ _proof[24] = 0xcdc72595f74c7b1043d0e1ffbab734648c838dfb0527d971b602bc216c9619ef;
69
+ _proof[25] = 0x0abf5ac974a1ed57f4050aa510dd9c74f508277b39d7973bb2dfccc5eeb0618d;
70
+ _proof[26] = 0xb8cd74046ff337f0a7bf2c8e03e10f642c1886798d71806ab1e888d9e5ee87d0;
71
+ _proof[27] = 0x838c5655cb21c6cb83313b5a631175dff4963772cce9108188b34ac87c81c41e;
72
+ _proof[28] = 0x662ee4dd2dd7b2bc707961b1e646c4047669dcb6584f0d8d770daf5d7e7deb2e;
73
+ _proof[29] = 0x388ab20e2573d171a88108e79d820e98f26c0b84aa8b2f4aa4968dbb818ea322;
74
+ _proof[30] = 0x93237c50ba75ee485f4c22adf2f741400bdf8d6a9cc7df7ecae576221665d735;
75
+ _proof[31] = 0x8448818bb4ae4562849e949e17ac16e0be16688e156b5cf15e098c627c0056a9;
76
+ }
77
+
78
+ function test_insertIntoTree() public {
79
+ // Queue the item.
80
+ _insertIntoTree(10 ether, JBConstants.NATIVE_TOKEN, 10 ether, bytes32(uint256(uint160(address(1337)))));
81
+ }
82
+
83
+ function test_validate() public {
84
+ // Move outbound root to inbound root.
85
+ _inboxOf[JBConstants.NATIVE_TOKEN] =
86
+ JBInboxTreeRoot({nonce: 0, root: _outboxOf[JBConstants.NATIVE_TOKEN].tree.root()});
87
+
88
+ bytes32[32] memory __proof = _proof;
89
+
90
+ // Mock the token minting.
91
+ address _mockController = address(900);
92
+ vm.mockCall(
93
+ address(DIRECTORY), abi.encodeCall(IJBDirectory.controllerOf, (projectId())), abi.encode(_mockController)
94
+ );
95
+ vm.mockCall(
96
+ _mockController,
97
+ abi.encodeCall(IJBController.mintTokensOf, (projectId(), 5 ether, address(120), "", false)),
98
+ abi.encode(0)
99
+ );
100
+
101
+ // Attempt to validate proof.
102
+ JBSucker(this)
103
+ .claim(
104
+ JBClaim({
105
+ token: JBConstants.NATIVE_TOKEN,
106
+ leaf: JBLeaf({
107
+ index: 2,
108
+ beneficiary: bytes32(uint256(uint160(address(120)))),
109
+ projectTokenCount: 5 ether,
110
+ terminalTokenAmount: 5 ether
111
+ }),
112
+ proof: __proof
113
+ })
114
+ );
115
+ }
116
+
117
+ function test_validate_only_once() public {
118
+ // Move outbound root to inbound root.
119
+ _inboxOf[JBConstants.NATIVE_TOKEN] =
120
+ JBInboxTreeRoot({nonce: 0, root: _outboxOf[JBConstants.NATIVE_TOKEN].tree.root()});
121
+
122
+ bytes32[32] memory __proof = _proof;
123
+
124
+ // Mock the token minting.
125
+ address _mockController = address(900);
126
+ vm.mockCall(
127
+ address(DIRECTORY), abi.encodeCall(IJBDirectory.controllerOf, (projectId())), abi.encode(_mockController)
128
+ );
129
+ vm.mockCall(
130
+ _mockController,
131
+ abi.encodeCall(IJBController.mintTokensOf, (projectId(), 5 ether, address(120), "", false)),
132
+ abi.encode(0)
133
+ );
134
+
135
+ // Attempt to validate proof.
136
+ JBSucker(this)
137
+ .claim(
138
+ JBClaim({
139
+ token: JBConstants.NATIVE_TOKEN,
140
+ leaf: JBLeaf({
141
+ index: 2,
142
+ beneficiary: bytes32(uint256(uint160(address(120)))),
143
+ projectTokenCount: 5 ether,
144
+ terminalTokenAmount: 5 ether
145
+ }),
146
+ proof: __proof
147
+ })
148
+ );
149
+
150
+ // Attempt to do it again.
151
+ vm.expectRevert();
152
+ JBSucker(this)
153
+ .claim(
154
+ JBClaim({
155
+ token: JBConstants.NATIVE_TOKEN,
156
+ leaf: JBLeaf({
157
+ index: 2,
158
+ beneficiary: bytes32(uint256(uint160(address(120)))),
159
+ projectTokenCount: 5 ether,
160
+ terminalTokenAmount: 5 ether
161
+ }),
162
+ proof: __proof
163
+ })
164
+ );
165
+ }
166
+
167
+ function _isRemotePeer(address) internal view virtual override returns (bool valid) {
168
+ return false;
169
+ }
170
+
171
+ function _sendRootOverAMB(
172
+ uint256 transportPayment,
173
+ uint256 index,
174
+ address token,
175
+ uint256 amount,
176
+ JBRemoteToken memory remoteToken,
177
+ JBMessageRoot memory message
178
+ )
179
+ internal
180
+ virtual
181
+ override
182
+ {}
183
+ function peerChainId() external view override returns (uint256 chainId) {}
184
+ }
185
+
186
+ contract DeployerUnitTest is Test {
187
+ function testDoesntRevert() public {
188
+ // Deploy the deployer.
189
+ JBOptimismSuckerDeployer _deployer = new JBOptimismSuckerDeployer(
190
+ IJBDirectory(address(0)), IJBPermissions(address(0)), IJBTokens(address(0)), address(this), address(0)
191
+ );
192
+
193
+ // Configure the chain specific contstants.
194
+ _deployer.setChainSpecificConstants(IOPMessenger(address(1)), IOPStandardBridge(address(1)));
195
+
196
+ // Deploy the singleton.
197
+ JBOptimismSucker _sucker = new JBOptimismSucker(
198
+ _deployer,
199
+ IJBDirectory(address(0)),
200
+ IJBPermissions(address(0)),
201
+ IJBTokens(address(0)),
202
+ JBAddToBalanceMode.MANUAL,
203
+ address(0)
204
+ );
205
+
206
+ // Configure the singleton on the deployer.
207
+ _deployer.configureSingleton(_sucker);
208
+
209
+ // Create a sucker for a project.
210
+ _deployer.createForSender(1, bytes32(0));
211
+ }
212
+ }