@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.
- package/LICENSE +21 -0
- package/README.md +422 -0
- package/SECURITY.md +55 -0
- package/SKILLS.md +163 -0
- package/deployments/nana-suckers-v5/arbitrum/JBArbitrumSucker.json +1425 -0
- package/deployments/nana-suckers-v5/arbitrum/JBArbitrumSuckerDeployer.json +391 -0
- package/deployments/nana-suckers-v5/arbitrum/JBCCIPSucker.json +1479 -0
- package/deployments/nana-suckers-v5/arbitrum/JBCCIPSuckerDeployer.json +433 -0
- package/deployments/nana-suckers-v5/arbitrum/JBCCIPSuckerDeployer_1.json +433 -0
- package/deployments/nana-suckers-v5/arbitrum/JBCCIPSuckerDeployer_2.json +433 -0
- package/deployments/nana-suckers-v5/arbitrum/JBCCIPSucker_1.json +1479 -0
- package/deployments/nana-suckers-v5/arbitrum/JBCCIPSucker_2.json +1479 -0
- package/deployments/nana-suckers-v5/arbitrum/JBSuckerRegistry.json +690 -0
- package/deployments/nana-suckers-v5/arbitrum_sepolia/JBArbitrumSucker.json +1425 -0
- package/deployments/nana-suckers-v5/arbitrum_sepolia/JBArbitrumSuckerDeployer.json +391 -0
- package/deployments/nana-suckers-v5/arbitrum_sepolia/JBCCIPSucker.json +1479 -0
- package/deployments/nana-suckers-v5/arbitrum_sepolia/JBCCIPSuckerDeployer.json +433 -0
- package/deployments/nana-suckers-v5/arbitrum_sepolia/JBCCIPSuckerDeployer_1.json +433 -0
- package/deployments/nana-suckers-v5/arbitrum_sepolia/JBCCIPSuckerDeployer_2.json +433 -0
- package/deployments/nana-suckers-v5/arbitrum_sepolia/JBCCIPSucker_1.json +1479 -0
- package/deployments/nana-suckers-v5/arbitrum_sepolia/JBCCIPSucker_2.json +1479 -0
- package/deployments/nana-suckers-v5/arbitrum_sepolia/JBSuckerRegistry.json +690 -0
- package/deployments/nana-suckers-v5/base/JBBaseSucker.json +1389 -0
- package/deployments/nana-suckers-v5/base/JBBaseSuckerDeployer.json +376 -0
- package/deployments/nana-suckers-v5/base/JBCCIPSucker.json +1483 -0
- package/deployments/nana-suckers-v5/base/JBCCIPSuckerDeployer.json +436 -0
- package/deployments/nana-suckers-v5/base/JBCCIPSuckerDeployer_1.json +436 -0
- package/deployments/nana-suckers-v5/base/JBCCIPSuckerDeployer_2.json +436 -0
- package/deployments/nana-suckers-v5/base/JBCCIPSucker_1.json +1483 -0
- package/deployments/nana-suckers-v5/base/JBCCIPSucker_2.json +1483 -0
- package/deployments/nana-suckers-v5/base/JBSuckerRegistry.json +694 -0
- package/deployments/nana-suckers-v5/base_sepolia/JBBaseSucker.json +1389 -0
- package/deployments/nana-suckers-v5/base_sepolia/JBBaseSuckerDeployer.json +376 -0
- package/deployments/nana-suckers-v5/base_sepolia/JBCCIPSucker.json +1483 -0
- package/deployments/nana-suckers-v5/base_sepolia/JBCCIPSuckerDeployer.json +436 -0
- package/deployments/nana-suckers-v5/base_sepolia/JBCCIPSuckerDeployer_1.json +436 -0
- package/deployments/nana-suckers-v5/base_sepolia/JBCCIPSuckerDeployer_2.json +436 -0
- package/deployments/nana-suckers-v5/base_sepolia/JBCCIPSucker_1.json +1483 -0
- package/deployments/nana-suckers-v5/base_sepolia/JBCCIPSucker_2.json +1483 -0
- package/deployments/nana-suckers-v5/base_sepolia/JBSuckerRegistry.json +694 -0
- package/deployments/nana-suckers-v5/ethereum/JBArbitrumSucker.json +1429 -0
- package/deployments/nana-suckers-v5/ethereum/JBArbitrumSuckerDeployer.json +394 -0
- package/deployments/nana-suckers-v5/ethereum/JBBaseSucker.json +1389 -0
- package/deployments/nana-suckers-v5/ethereum/JBBaseSuckerDeployer.json +376 -0
- package/deployments/nana-suckers-v5/ethereum/JBCCIPSucker.json +1483 -0
- package/deployments/nana-suckers-v5/ethereum/JBCCIPSuckerDeployer.json +436 -0
- package/deployments/nana-suckers-v5/ethereum/JBCCIPSuckerDeployer_1.json +436 -0
- package/deployments/nana-suckers-v5/ethereum/JBCCIPSuckerDeployer_2.json +436 -0
- package/deployments/nana-suckers-v5/ethereum/JBCCIPSucker_1.json +1483 -0
- package/deployments/nana-suckers-v5/ethereum/JBCCIPSucker_2.json +1483 -0
- package/deployments/nana-suckers-v5/ethereum/JBOptimismSucker.json +1389 -0
- package/deployments/nana-suckers-v5/ethereum/JBOptimismSuckerDeployer.json +376 -0
- package/deployments/nana-suckers-v5/ethereum/JBSuckerRegistry.json +694 -0
- package/deployments/nana-suckers-v5/optimism/JBCCIPSucker.json +1479 -0
- package/deployments/nana-suckers-v5/optimism/JBCCIPSuckerDeployer.json +433 -0
- package/deployments/nana-suckers-v5/optimism/JBCCIPSuckerDeployer_1.json +433 -0
- package/deployments/nana-suckers-v5/optimism/JBCCIPSuckerDeployer_2.json +433 -0
- package/deployments/nana-suckers-v5/optimism/JBCCIPSucker_1.json +1479 -0
- package/deployments/nana-suckers-v5/optimism/JBCCIPSucker_2.json +1479 -0
- package/deployments/nana-suckers-v5/optimism/JBOptimismSucker.json +1385 -0
- package/deployments/nana-suckers-v5/optimism/JBOptimismSuckerDeployer.json +373 -0
- package/deployments/nana-suckers-v5/optimism/JBSuckerRegistry.json +690 -0
- package/deployments/nana-suckers-v5/optimism_sepolia/JBCCIPSucker.json +1483 -0
- package/deployments/nana-suckers-v5/optimism_sepolia/JBCCIPSuckerDeployer.json +436 -0
- package/deployments/nana-suckers-v5/optimism_sepolia/JBCCIPSuckerDeployer_1.json +436 -0
- package/deployments/nana-suckers-v5/optimism_sepolia/JBCCIPSuckerDeployer_2.json +436 -0
- package/deployments/nana-suckers-v5/optimism_sepolia/JBCCIPSucker_1.json +1483 -0
- package/deployments/nana-suckers-v5/optimism_sepolia/JBCCIPSucker_2.json +1483 -0
- package/deployments/nana-suckers-v5/optimism_sepolia/JBOptimismSucker.json +1389 -0
- package/deployments/nana-suckers-v5/optimism_sepolia/JBOptimismSuckerDeployer.json +376 -0
- package/deployments/nana-suckers-v5/optimism_sepolia/JBSuckerRegistry.json +694 -0
- package/deployments/nana-suckers-v5/sepolia/JBArbitrumSucker.json +1429 -0
- package/deployments/nana-suckers-v5/sepolia/JBArbitrumSuckerDeployer.json +394 -0
- package/deployments/nana-suckers-v5/sepolia/JBBaseSucker.json +1389 -0
- package/deployments/nana-suckers-v5/sepolia/JBBaseSuckerDeployer.json +376 -0
- package/deployments/nana-suckers-v5/sepolia/JBCCIPSucker.json +1483 -0
- package/deployments/nana-suckers-v5/sepolia/JBCCIPSuckerDeployer.json +436 -0
- package/deployments/nana-suckers-v5/sepolia/JBCCIPSuckerDeployer_1.json +436 -0
- package/deployments/nana-suckers-v5/sepolia/JBCCIPSuckerDeployer_2.json +436 -0
- package/deployments/nana-suckers-v5/sepolia/JBCCIPSucker_1.json +1483 -0
- package/deployments/nana-suckers-v5/sepolia/JBCCIPSucker_2.json +1483 -0
- package/deployments/nana-suckers-v5/sepolia/JBOptimismSucker.json +1389 -0
- package/deployments/nana-suckers-v5/sepolia/JBOptimismSuckerDeployer.json +376 -0
- package/deployments/nana-suckers-v5/sepolia/JBSuckerRegistry.json +694 -0
- package/foundry.lock +11 -0
- package/foundry.toml +22 -0
- package/package.json +33 -0
- package/remappings.txt +1 -0
- package/script/Deploy.s.sol +506 -0
- package/script/helpers/SuckerDeploymentLib.sol +97 -0
- package/slither-ci.config.json +10 -0
- package/sphinx.lock +476 -0
- package/src/JBArbitrumSucker.sol +311 -0
- package/src/JBBaseSucker.sol +41 -0
- package/src/JBCCIPSucker.sol +303 -0
- package/src/JBOptimismSucker.sol +143 -0
- package/src/JBSucker.sol +1159 -0
- package/src/JBSuckerRegistry.sol +262 -0
- package/src/deployers/JBArbitrumSuckerDeployer.sol +86 -0
- package/src/deployers/JBBaseSuckerDeployer.sol +26 -0
- package/src/deployers/JBCCIPSuckerDeployer.sol +88 -0
- package/src/deployers/JBOptimismSuckerDeployer.sol +82 -0
- package/src/deployers/JBSuckerDeployer.sol +147 -0
- package/src/enums/JBAddToBalanceMode.sol +11 -0
- package/src/enums/JBLayer.sol +8 -0
- package/src/enums/JBSuckerState.sol +14 -0
- package/src/interfaces/IArbGatewayRouter.sol +11 -0
- package/src/interfaces/IArbL1GatewayRouter.sol +17 -0
- package/src/interfaces/IArbL2GatewayRouter.sol +14 -0
- package/src/interfaces/ICCIPRouter.sol +11 -0
- package/src/interfaces/IJBArbitrumSucker.sol +13 -0
- package/src/interfaces/IJBArbitrumSuckerDeployer.sol +12 -0
- package/src/interfaces/IJBCCIPSuckerDeployer.sol +15 -0
- package/src/interfaces/IJBOpSuckerDeployer.sol +11 -0
- package/src/interfaces/IJBOptimismSucker.sol +10 -0
- package/src/interfaces/IJBSucker.sol +144 -0
- package/src/interfaces/IJBSuckerDeployer.sol +40 -0
- package/src/interfaces/IJBSuckerExtended.sol +22 -0
- package/src/interfaces/IJBSuckerRegistry.sol +75 -0
- package/src/interfaces/IOPMessenger.sol +18 -0
- package/src/interfaces/IOPStandardBridge.sol +29 -0
- package/src/interfaces/IWrappedNativeToken.sol +13 -0
- package/src/libraries/ARBAddresses.sol +17 -0
- package/src/libraries/ARBChains.sol +11 -0
- package/src/libraries/CCIPHelper.sol +136 -0
- package/src/structs/JBClaim.sol +13 -0
- package/src/structs/JBInboxTreeRoot.sol +12 -0
- package/src/structs/JBLeaf.sol +14 -0
- package/src/structs/JBMessageRoot.sol +16 -0
- package/src/structs/JBOutboxTree.sol +18 -0
- package/src/structs/JBRemoteToken.sol +17 -0
- package/src/structs/JBSuckerDeployerConfig.sol +12 -0
- package/src/structs/JBSuckersPair.sol +11 -0
- package/src/structs/JBTokenMapping.sol +13 -0
- package/src/utils/MerkleLib.sol +1020 -0
- package/test/Fork.t.sol +514 -0
- package/test/InteropCompat.t.sol +676 -0
- package/test/SuckerAttacks.t.sol +509 -0
- package/test/SuckerDeepAttacks.t.sol +1563 -0
- package/test/mocks/ERC20Mock.sol +36 -0
- package/test/mocks/MockMessenger.sol +42 -0
- package/test/unit/arb.t.sol +28 -0
- package/test/unit/ccip_native_interop.t.sol +719 -0
- package/test/unit/ccip_refund.t.sol +234 -0
- package/test/unit/deployer.t.sol +475 -0
- package/test/unit/emergency.t.sol +305 -0
- package/test/unit/merkle.t.sol +212 -0
- package/test/unit/multi_chain_evolution.t.sol +622 -0
- 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
|
+
}
|