@diamondslab/diamonds-hardhat-foundry 1.0.3 → 2.2.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/CHANGELOG.md +249 -0
- package/README.md +650 -4
- package/contracts/DiamondABILoader.sol +329 -0
- package/contracts/DiamondForgeHelpers.sol +309 -85
- package/contracts/DiamondFuzzBase.sol +322 -114
- package/dist/foundry.d.ts +28 -0
- package/dist/foundry.d.ts.map +1 -1
- package/dist/foundry.js +82 -1
- package/dist/foundry.js.map +1 -1
- package/dist/framework/DeploymentManager.d.ts +10 -11
- package/dist/framework/DeploymentManager.d.ts.map +1 -1
- package/dist/framework/DeploymentManager.js +56 -45
- package/dist/framework/DeploymentManager.js.map +1 -1
- package/dist/framework/ForgeFuzzingFramework.d.ts +4 -0
- package/dist/framework/ForgeFuzzingFramework.d.ts.map +1 -1
- package/dist/framework/ForgeFuzzingFramework.js +16 -2
- package/dist/framework/ForgeFuzzingFramework.js.map +1 -1
- package/dist/framework/HelperGenerator.d.ts.map +1 -1
- package/dist/framework/HelperGenerator.js +11 -0
- package/dist/framework/HelperGenerator.js.map +1 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -8
- package/dist/index.js.map +1 -1
- package/dist/tasks/deploy.js +11 -4
- package/dist/tasks/deploy.js.map +1 -1
- package/dist/tasks/generate-helpers.js +6 -4
- package/dist/tasks/generate-helpers.js.map +1 -1
- package/dist/tasks/init.js +3 -2
- package/dist/tasks/init.js.map +1 -1
- package/dist/tasks/test.js +13 -2
- package/dist/tasks/test.js.map +1 -1
- package/dist/types/hardhat.d.ts +1 -1
- package/dist/types/hardhat.d.ts.map +1 -1
- package/dist/types/hardhat.js +1 -0
- package/dist/types/hardhat.js.map +1 -1
- package/dist/utils/foundry.d.ts +1 -0
- package/dist/utils/foundry.d.ts.map +1 -1
- package/dist/utils/foundry.js +3 -0
- package/dist/utils/foundry.js.map +1 -1
- package/package.json +5 -3
- package/src/foundry.ts +104 -0
- package/src/framework/DeploymentManager.ts +74 -69
- package/src/framework/ForgeFuzzingFramework.ts +23 -1
- package/src/framework/HelperGenerator.ts +13 -0
- package/src/index.ts +0 -5
- package/src/tasks/deploy.ts +14 -3
- package/src/tasks/generate-helpers.ts +6 -2
- package/src/tasks/init.ts +3 -1
- package/src/tasks/test.ts +12 -1
- package/src/templates/ExampleFuzzTest.t.sol.template +26 -17
- package/src/templates/ExampleIntegrationTest.t.sol.template +9 -1
- package/src/templates/ExampleUnitTest.t.sol.template +7 -1
- package/src/types/hardhat.ts +1 -1
- package/src/utils/foundry.ts +5 -0
- package/dist/templates/DiamondDeployment.sol.template +0 -38
- package/dist/templates/ExampleFuzzTest.t.sol.template +0 -109
- package/dist/templates/ExampleIntegrationTest.t.sol.template +0 -79
- package/dist/templates/ExampleUnitTest.t.sol.template +0 -59
|
@@ -2,127 +2,335 @@
|
|
|
2
2
|
pragma solidity ^0.8.0;
|
|
3
3
|
|
|
4
4
|
import "forge-std/Test.sol";
|
|
5
|
-
import "
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
import "./DiamondABILoader.sol";
|
|
6
|
+
|
|
7
|
+
/// @title DiamondFuzzBase
|
|
8
|
+
/// @author DiamondsLab
|
|
9
|
+
/// @notice Abstract base contract for Diamond fuzzing and integration tests
|
|
10
|
+
/// @dev Inherit from this contract to create Diamond-based tests with built-in helpers for calling functions, measuring gas, and verifying routing
|
|
11
|
+
/// @custom:security This contract is intended for testing purposes only
|
|
12
|
+
/// @custom:usage
|
|
13
|
+
/// ```solidity
|
|
14
|
+
/// contract MyDiamondTest is DiamondFuzzBase {
|
|
15
|
+
/// function setUp() public override {
|
|
16
|
+
/// super.setUp();
|
|
17
|
+
/// // Additional setup
|
|
18
|
+
/// }
|
|
19
|
+
/// }
|
|
20
|
+
/// ```
|
|
13
21
|
abstract contract DiamondFuzzBase is Test {
|
|
14
|
-
using
|
|
22
|
+
using DiamondABILoader for string;
|
|
15
23
|
|
|
16
|
-
|
|
17
|
-
|
|
24
|
+
/// @notice Deployed Diamond contract address
|
|
25
|
+
/// @dev Loaded in setUp(), can be overridden by implementing _loadDiamondAddress()
|
|
26
|
+
address internal diamond;
|
|
18
27
|
|
|
19
|
-
|
|
20
|
-
|
|
28
|
+
/// @notice Diamond ABI JSON content
|
|
29
|
+
/// @dev Loaded from diamond-abi file in setUp()
|
|
30
|
+
string internal diamondABI;
|
|
21
31
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
32
|
+
/// @notice Array of all function selectors in the Diamond
|
|
33
|
+
bytes4[] internal diamondSelectors;
|
|
34
|
+
|
|
35
|
+
/// @notice Array of all function signatures in the Diamond
|
|
36
|
+
string[] internal diamondSignatures;
|
|
37
|
+
|
|
38
|
+
/// @notice Mapping of selector to function name for debugging
|
|
39
|
+
mapping(bytes4 => string) internal selectorToName;
|
|
40
|
+
|
|
41
|
+
/// @notice Error for failed Diamond calls
|
|
42
|
+
/// @param selector The function selector that was called
|
|
43
|
+
/// @param data The calldata that was sent
|
|
44
|
+
/// @param returnData The raw error data returned
|
|
45
|
+
error DiamondCallFailed(bytes4 selector, bytes data, bytes returnData);
|
|
46
|
+
|
|
47
|
+
/// @notice Error for unexpected Diamond success when expecting revert
|
|
48
|
+
/// @param selector The function selector that was called
|
|
49
|
+
/// @param data The calldata that was sent
|
|
50
|
+
/// @param returnData The return data from the unexpected success
|
|
51
|
+
error DiamondCallUnexpectedSuccess(bytes4 selector, bytes data, bytes returnData);
|
|
52
|
+
|
|
53
|
+
/// @notice Event emitted when Diamond is loaded in setUp
|
|
54
|
+
/// @param diamondAddress The address of the loaded Diamond
|
|
55
|
+
/// @param functionCount The number of functions found in the ABI
|
|
56
|
+
event DiamondLoaded(address indexed diamondAddress, uint256 functionCount);
|
|
57
|
+
|
|
58
|
+
/// @notice Event emitted when gas is measured for a function call
|
|
59
|
+
/// @param selector The function selector
|
|
60
|
+
/// @param functionName The function signature
|
|
61
|
+
/// @param gasUsed The gas consumed by the call
|
|
62
|
+
event GasMeasurement(bytes4 indexed selector, string functionName, uint256 gasUsed);
|
|
63
|
+
|
|
64
|
+
/// @notice Load deployed Diamond address
|
|
65
|
+
/// @dev Override this function to provide custom Diamond address loading logic
|
|
66
|
+
/// @dev Default implementation uses vm.envAddress("DIAMOND_ADDRESS") if available, otherwise uses a test deployment
|
|
67
|
+
/// @return diamondAddress The deployed Diamond contract address
|
|
68
|
+
function _loadDiamondAddress() internal view virtual returns (address diamondAddress) {
|
|
69
|
+
// Try to load from environment variable
|
|
70
|
+
try vm.envAddress("DIAMOND_ADDRESS") returns (address addr) {
|
|
71
|
+
return addr;
|
|
72
|
+
} catch {
|
|
73
|
+
// Fall back to expecting a deployment file or generated library
|
|
74
|
+
revert(
|
|
75
|
+
"DiamondFuzzBase: Override _loadDiamondAddress() or set DIAMOND_ADDRESS env var"
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/// @notice Get the path to the Diamond ABI file
|
|
81
|
+
/// @dev Override this to use a different ABI file path
|
|
82
|
+
/// @return abiPath The path to the Diamond ABI JSON file
|
|
83
|
+
function _getDiamondABIPath() internal view virtual returns (string memory abiPath) {
|
|
84
|
+
return "./diamond-abi/ExampleDiamond.json";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/// @notice Load and parse Diamond ABI file
|
|
88
|
+
/// @dev Uses DiamondABILoader to extract selectors and signatures
|
|
89
|
+
/// @dev Can be overridden to customize ABI loading behavior
|
|
90
|
+
function _loadDiamondABI() internal virtual {
|
|
91
|
+
string memory abiPath = _getDiamondABIPath();
|
|
92
|
+
|
|
93
|
+
// Load ABI JSON
|
|
94
|
+
diamondABI = DiamondABILoader.loadDiamondABI(abiPath);
|
|
95
|
+
|
|
96
|
+
// Extract selectors and signatures
|
|
97
|
+
diamondSelectors = DiamondABILoader.extractSelectors(diamondABI);
|
|
98
|
+
diamondSignatures = DiamondABILoader.extractSignatures(diamondABI);
|
|
99
|
+
|
|
100
|
+
require(diamondSelectors.length > 0, "DiamondFuzzBase: No selectors found");
|
|
101
|
+
require(
|
|
102
|
+
diamondSelectors.length == diamondSignatures.length,
|
|
103
|
+
"DiamondFuzzBase: Selector/signature count mismatch"
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
// Build selector to name mapping for debugging
|
|
107
|
+
for (uint256 i = 0; i < diamondSelectors.length; i++) {
|
|
108
|
+
selectorToName[diamondSelectors[i]] = diamondSignatures[i];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
emit DiamondLoaded(diamond, diamondSelectors.length);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/// @notice Call a Diamond function with selector and encoded arguments
|
|
115
|
+
/// @dev Low-level call helper for non-payable functions
|
|
116
|
+
/// @param selector The function selector (bytes4)
|
|
117
|
+
/// @param data The encoded function arguments (without selector)
|
|
118
|
+
/// @return success Whether the call succeeded
|
|
119
|
+
/// @return returnData The raw return data from the call
|
|
120
|
+
function _callDiamond(
|
|
121
|
+
bytes4 selector,
|
|
122
|
+
bytes memory data
|
|
123
|
+
) internal virtual returns (bool success, bytes memory returnData) {
|
|
124
|
+
bytes memory callData = abi.encodePacked(selector, data);
|
|
125
|
+
(success, returnData) = diamond.call(callData);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/// @notice Call a Diamond function with value (for payable functions)
|
|
129
|
+
/// @dev Low-level call helper for payable functions
|
|
130
|
+
/// @param selector The function selector (bytes4)
|
|
131
|
+
/// @param data The encoded function arguments (without selector)
|
|
132
|
+
/// @param value The ETH value to send with the call
|
|
133
|
+
/// @return success Whether the call succeeded
|
|
134
|
+
/// @return returnData The raw return data from the call
|
|
135
|
+
function _callDiamondWithValue(
|
|
136
|
+
bytes4 selector,
|
|
137
|
+
bytes memory data,
|
|
138
|
+
uint256 value
|
|
139
|
+
) internal virtual returns (bool success, bytes memory returnData) {
|
|
140
|
+
bytes memory callData = abi.encodePacked(selector, data);
|
|
141
|
+
(success, returnData) = diamond.call{value: value}(callData);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/// @notice Expect a Diamond call to revert with specific error
|
|
145
|
+
/// @dev Helper for testing expected reverts
|
|
146
|
+
/// @param selector The function selector to call
|
|
147
|
+
/// @param data The encoded function arguments
|
|
148
|
+
/// @param expectedError The expected error data (can be empty to expect any revert)
|
|
149
|
+
function _expectDiamondRevert(
|
|
150
|
+
bytes4 selector,
|
|
151
|
+
bytes memory data,
|
|
152
|
+
bytes memory expectedError
|
|
153
|
+
) internal virtual {
|
|
154
|
+
(bool success, bytes memory returnData) = _callDiamond(selector, data);
|
|
155
|
+
|
|
156
|
+
if (success) {
|
|
157
|
+
revert DiamondCallUnexpectedSuccess(selector, data, returnData);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (expectedError.length > 0) {
|
|
161
|
+
// Verify exact error match
|
|
162
|
+
assertEq(returnData, expectedError, "Unexpected error data");
|
|
163
|
+
}
|
|
164
|
+
// If expectedError is empty, just verify it reverted (which we already did)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/// @notice Verify function selector routes to expected facet address
|
|
168
|
+
/// @dev Uses DiamondLoupe to check facet routing
|
|
169
|
+
/// @param selector The function selector to check
|
|
170
|
+
/// @param expectedFacet The expected facet address (address(0) to just verify it exists)
|
|
171
|
+
/// @return facetAddress The actual facet address for this selector
|
|
172
|
+
function _verifyFacetRouting(
|
|
173
|
+
bytes4 selector,
|
|
174
|
+
address expectedFacet
|
|
175
|
+
) internal view virtual returns (address facetAddress) {
|
|
176
|
+
// Call facetAddress(bytes4) from DiamondLoupe
|
|
177
|
+
bytes memory callData = abi.encodeWithSignature("facetAddress(bytes4)", selector);
|
|
178
|
+
(bool success, bytes memory returnData) = diamond.staticcall(callData);
|
|
179
|
+
|
|
180
|
+
require(success, "DiamondFuzzBase: facetAddress call failed");
|
|
181
|
+
facetAddress = abi.decode(returnData, (address));
|
|
182
|
+
|
|
183
|
+
if (expectedFacet != address(0)) {
|
|
184
|
+
assertEq(facetAddress, expectedFacet, "Selector routes to unexpected facet");
|
|
185
|
+
} else {
|
|
186
|
+
assertTrue(facetAddress != address(0), "Selector has no facet");
|
|
187
|
+
}
|
|
27
188
|
|
|
28
|
-
/**
|
|
29
|
-
* @notice Setup function called before each test
|
|
30
|
-
* @dev Override this in child contracts to set up Diamond and facets
|
|
31
|
-
*/
|
|
32
|
-
function setUp() public virtual {
|
|
33
|
-
// Set up test accounts
|
|
34
|
-
deployer = address(this);
|
|
35
|
-
user1 = makeAddr("user1");
|
|
36
|
-
user2 = makeAddr("user2");
|
|
37
|
-
user3 = makeAddr("user3");
|
|
38
|
-
|
|
39
|
-
// Fund test accounts
|
|
40
|
-
vm.deal(user1, 100 ether);
|
|
41
|
-
vm.deal(user2, 100 ether);
|
|
42
|
-
vm.deal(user3, 100 ether);
|
|
43
|
-
|
|
44
|
-
// Child contracts should override and call setDiamondAddress()
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* @notice Set the Diamond contract address
|
|
49
|
-
* @param _diamond Address of the deployed Diamond
|
|
50
|
-
*/
|
|
51
|
-
function setDiamondAddress(address _diamond) internal {
|
|
52
|
-
require(_diamond != address(0), "Diamond address cannot be zero");
|
|
53
|
-
diamond = _diamond;
|
|
54
|
-
DiamondForgeHelpers.assertValidDiamond(_diamond);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* @notice Register a facet address
|
|
59
|
-
* @param name Name of the facet
|
|
60
|
-
* @param facetAddress Address of the facet
|
|
61
|
-
*/
|
|
62
|
-
function registerFacet(string memory name, address facetAddress) internal {
|
|
63
|
-
require(facetAddress != address(0), "Facet address cannot be zero");
|
|
64
|
-
facets[name] = facetAddress;
|
|
65
|
-
DiamondForgeHelpers.assertValidFacet(facetAddress, name);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* @notice Get a facet address by name
|
|
70
|
-
* @param name Name of the facet
|
|
71
|
-
* @return Address of the facet
|
|
72
|
-
*/
|
|
73
|
-
function getFacet(string memory name) internal view returns (address) {
|
|
74
|
-
address facetAddress = facets[name];
|
|
75
|
-
require(facetAddress != address(0), string(abi.encodePacked("Facet not found: ", name)));
|
|
76
189
|
return facetAddress;
|
|
77
190
|
}
|
|
78
191
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
function
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
192
|
+
/// @notice Measure gas consumption of a Diamond function call
|
|
193
|
+
/// @dev Gas profiling helper for optimization testing
|
|
194
|
+
/// @param selector The function selector to call
|
|
195
|
+
/// @param data The encoded function arguments
|
|
196
|
+
/// @return gasUsed The gas consumed by the call
|
|
197
|
+
function _measureDiamondGas(
|
|
198
|
+
bytes4 selector,
|
|
199
|
+
bytes memory data
|
|
200
|
+
) internal virtual returns (uint256 gasUsed) {
|
|
201
|
+
uint256 gasBefore = gasleft();
|
|
202
|
+
(bool success, bytes memory returnData) = _callDiamond(selector, data);
|
|
203
|
+
uint256 gasAfter = gasleft();
|
|
204
|
+
|
|
205
|
+
if (!success) {
|
|
206
|
+
revert DiamondCallFailed(selector, data, returnData);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
gasUsed = gasBefore - gasAfter;
|
|
210
|
+
emit GasMeasurement(selector, selectorToName[selector], gasUsed);
|
|
211
|
+
|
|
212
|
+
return gasUsed;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/// @notice Get the Diamond owner address
|
|
216
|
+
/// @dev Helper to get owner for ownership checks (assumes owner() function exists)
|
|
217
|
+
/// @return owner The current Diamond owner address
|
|
218
|
+
function _getDiamondOwner() internal view virtual returns (address owner) {
|
|
219
|
+
// Call owner() function - standard in most ownership facets
|
|
220
|
+
bytes memory callData = abi.encodeWithSignature("owner()");
|
|
221
|
+
(bool success, bytes memory returnData) = diamond.staticcall(callData);
|
|
222
|
+
|
|
223
|
+
require(success, "DiamondFuzzBase: owner() call failed");
|
|
224
|
+
owner = abi.decode(returnData, (address));
|
|
225
|
+
|
|
226
|
+
return owner;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/// @notice Check if an address has a specific role
|
|
230
|
+
/// @dev Helper for access control testing (assumes role-based access control)
|
|
231
|
+
/// @param role The role identifier (bytes32)
|
|
232
|
+
/// @param account The address to check
|
|
233
|
+
/// @return hasRole True if the account has the role
|
|
234
|
+
function _hasRole(bytes32 role, address account) internal view virtual returns (bool hasRole) {
|
|
235
|
+
bytes memory callData = abi.encodeWithSignature("hasRole(bytes32,address)", role, account);
|
|
236
|
+
(bool success, bytes memory returnData) = diamond.staticcall(callData);
|
|
237
|
+
|
|
238
|
+
if (!success) {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
hasRole = abi.decode(returnData, (bool));
|
|
243
|
+
return hasRole;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/// @notice Grant a role to an address (requires appropriate permissions)
|
|
247
|
+
/// @dev Helper for access control testing
|
|
248
|
+
/// @dev Note: The caller must have permission to grant the role (e.g., have DEFAULT_ADMIN_ROLE)
|
|
249
|
+
/// @dev For tests, use vm.prank() to call from an address with the appropriate permissions
|
|
250
|
+
/// @param role The role identifier
|
|
251
|
+
/// @param account The address to grant the role to
|
|
252
|
+
function _grantRole(bytes32 role, address account) internal virtual {
|
|
253
|
+
bytes4 selector = bytes4(keccak256("grantRole(bytes32,address)"));
|
|
254
|
+
bytes memory data = abi.encode(role, account);
|
|
255
|
+
(bool success, bytes memory returnData) = _callDiamond(selector, data);
|
|
256
|
+
|
|
257
|
+
if (!success) {
|
|
258
|
+
revert DiamondCallFailed(selector, data, returnData);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/// @notice Grant a role to the test contract itself
|
|
263
|
+
/// @dev Convenience helper that grants a role to address(this)
|
|
264
|
+
/// @dev The caller must have permission to grant the role - use vm.prank() as needed
|
|
265
|
+
/// @dev Common pattern in setUp(): vm.prank(owner); _grantRoleToSelf(DEFAULT_ADMIN_ROLE);
|
|
266
|
+
/// @param role The role identifier to grant to the test contract
|
|
267
|
+
function _grantRoleToSelf(bytes32 role) internal virtual {
|
|
268
|
+
_grantRole(role, address(this));
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/// @notice Revoke a role from an address (requires appropriate permissions)
|
|
272
|
+
/// @dev Helper for access control testing
|
|
273
|
+
/// @param role The role identifier
|
|
274
|
+
/// @param account The address to revoke the role from
|
|
275
|
+
function _revokeRole(bytes32 role, address account) internal virtual {
|
|
276
|
+
bytes4 selector = bytes4(keccak256("revokeRole(bytes32,address)"));
|
|
277
|
+
bytes memory data = abi.encode(role, account);
|
|
278
|
+
(bool success, bytes memory returnData) = _callDiamond(selector, data);
|
|
279
|
+
|
|
280
|
+
if (!success) {
|
|
281
|
+
revert DiamondCallFailed(selector, data, returnData);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/// @notice Get all function selectors in the Diamond
|
|
286
|
+
/// @dev Helper to access loaded selectors
|
|
287
|
+
/// @return selectors Array of all function selectors
|
|
288
|
+
function _getDiamondSelectors() internal view virtual returns (bytes4[] memory selectors) {
|
|
289
|
+
return diamondSelectors;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/// @notice Get all function signatures in the Diamond
|
|
293
|
+
/// @dev Helper to access loaded signatures
|
|
294
|
+
/// @return signatures Array of all function signatures
|
|
295
|
+
function _getDiamondSignatures() internal view virtual returns (string[] memory signatures) {
|
|
296
|
+
return diamondSignatures;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/// @notice Get function name/signature for a selector
|
|
300
|
+
/// @dev Helper for debugging and logging
|
|
301
|
+
/// @param selector The function selector
|
|
302
|
+
/// @return name The function signature/name
|
|
303
|
+
function _getFunctionName(bytes4 selector) internal view virtual returns (string memory name) {
|
|
304
|
+
return selectorToName[selector];
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/// @notice Setup function that loads Diamond address and ABI
|
|
308
|
+
/// @dev Override this to customize setup behavior, but call super.setUp() to load Diamond
|
|
309
|
+
/// @dev For access control tests, grant necessary roles in setUp() after calling super.setUp()
|
|
310
|
+
/// @custom:example
|
|
311
|
+
/// ```solidity
|
|
312
|
+
/// function setUp() public override {
|
|
313
|
+
/// super.setUp(); // Load Diamond and ABI
|
|
314
|
+
///
|
|
315
|
+
/// // Grant DEFAULT_ADMIN_ROLE to test contract for access control tests
|
|
316
|
+
/// vm.prank(_getDiamondOwner());
|
|
317
|
+
/// _grantRoleToSelf(DEFAULT_ADMIN_ROLE);
|
|
318
|
+
///
|
|
319
|
+
/// // Additional custom setup
|
|
320
|
+
/// }
|
|
321
|
+
/// ```
|
|
322
|
+
function setUp() public virtual {
|
|
323
|
+
// Load Diamond address
|
|
324
|
+
diamond = _loadDiamondAddress();
|
|
325
|
+
|
|
326
|
+
// Verify Diamond has code deployed
|
|
327
|
+
require(diamond.code.length > 0, "DiamondFuzzBase: Diamond has no code");
|
|
328
|
+
|
|
329
|
+
// Load and parse Diamond ABI
|
|
330
|
+
_loadDiamondABI();
|
|
331
|
+
|
|
332
|
+
// Log for debugging
|
|
333
|
+
console.log("Diamond loaded at:", diamond);
|
|
334
|
+
console.log("Functions found:", diamondSelectors.length);
|
|
127
335
|
}
|
|
128
336
|
}
|
package/dist/foundry.d.ts
CHANGED
|
@@ -7,5 +7,33 @@ export declare function getForgeConfig(): any;
|
|
|
7
7
|
export declare function parseRemappings(remappingsTxt: string): Remappings;
|
|
8
8
|
export declare function getRemappings(): Promise<Remappings>;
|
|
9
9
|
export declare function installDependency(dependency: string): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* Check if Foundry is installed
|
|
12
|
+
*/
|
|
13
|
+
export declare function isFoundryInstalled(): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Compile Forge contracts
|
|
16
|
+
*/
|
|
17
|
+
export declare function compileForge(options: {
|
|
18
|
+
cwd?: string;
|
|
19
|
+
verbose?: boolean;
|
|
20
|
+
}): Promise<{
|
|
21
|
+
success: boolean;
|
|
22
|
+
output?: string;
|
|
23
|
+
}>;
|
|
24
|
+
/**
|
|
25
|
+
* Run Forge tests with optional forking
|
|
26
|
+
*/
|
|
27
|
+
export declare function runForgeTest(options: {
|
|
28
|
+
matchTest?: string;
|
|
29
|
+
matchContract?: string;
|
|
30
|
+
verbosity?: number;
|
|
31
|
+
gasReport?: boolean;
|
|
32
|
+
forkUrl?: string;
|
|
33
|
+
cwd?: string;
|
|
34
|
+
}): Promise<{
|
|
35
|
+
success: boolean;
|
|
36
|
+
output?: string;
|
|
37
|
+
}>;
|
|
10
38
|
export {};
|
|
11
39
|
//# sourceMappingURL=foundry.d.ts.map
|
package/dist/foundry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"foundry.d.ts","sourceRoot":"","sources":["../src/foundry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,2BAA2B,EAAE,MAAM,8BAA8B,CAAC;AAM3E,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAIzC,qBAAa,mBAAoB,SAAQ,2BAA2B;gBACtD,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK;CAG5C;AAcD,wBAAgB,cAAc,QAE7B;AAED,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,UAAU,CA+BjE;AAED,wBAAsB,aAAa,wBAOlC;AAED,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,MAAM,iBAgBzD"}
|
|
1
|
+
{"version":3,"file":"foundry.d.ts","sourceRoot":"","sources":["../src/foundry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,2BAA2B,EAAE,MAAM,8BAA8B,CAAC;AAM3E,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAIzC,qBAAa,mBAAoB,SAAQ,2BAA2B;gBACtD,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK;CAG5C;AAcD,wBAAgB,cAAc,QAE7B;AAED,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,UAAU,CA+BjE;AAED,wBAAsB,aAAa,wBAOlC;AAED,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,MAAM,iBAgBzD;AA4CD;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAO5C;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE;IAC1C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAejD;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAyDjD"}
|
package/dist/foundry.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.installDependency = exports.getRemappings = exports.parseRemappings = exports.getForgeConfig = exports.HardhatFoundryError = void 0;
|
|
6
|
+
exports.runForgeTest = exports.compileForge = exports.isFoundryInstalled = exports.installDependency = exports.getRemappings = exports.parseRemappings = exports.getForgeConfig = exports.HardhatFoundryError = void 0;
|
|
7
7
|
const child_process_1 = require("child_process");
|
|
8
8
|
const errors_1 = require("hardhat/internal/core/errors");
|
|
9
9
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
@@ -101,4 +101,85 @@ function buildForgeExecutionError(exitCode, message) {
|
|
|
101
101
|
return new HardhatFoundryError(`Unexpected error while running \`forge\`: ${message}`);
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Check if Foundry is installed
|
|
106
|
+
*/
|
|
107
|
+
function isFoundryInstalled() {
|
|
108
|
+
try {
|
|
109
|
+
(0, child_process_1.execSync)("forge --version", { stdio: "pipe" });
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
exports.isFoundryInstalled = isFoundryInstalled;
|
|
117
|
+
/**
|
|
118
|
+
* Compile Forge contracts
|
|
119
|
+
*/
|
|
120
|
+
async function compileForge(options) {
|
|
121
|
+
const { cwd = process.cwd(), verbose = false } = options;
|
|
122
|
+
try {
|
|
123
|
+
const args = ["build"];
|
|
124
|
+
if (!verbose) {
|
|
125
|
+
args.push("--quiet");
|
|
126
|
+
}
|
|
127
|
+
const { stdout, stderr } = await exec(`forge ${args.join(" ")}`, { cwd });
|
|
128
|
+
return { success: true, output: stdout || stderr };
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
console.error("Forge build failed:", error.message);
|
|
132
|
+
return { success: false, output: error.message };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
exports.compileForge = compileForge;
|
|
136
|
+
/**
|
|
137
|
+
* Run Forge tests with optional forking
|
|
138
|
+
*/
|
|
139
|
+
async function runForgeTest(options) {
|
|
140
|
+
const { matchTest, matchContract, verbosity = 2, gasReport = false, forkUrl, cwd = process.cwd(), } = options;
|
|
141
|
+
try {
|
|
142
|
+
const args = ["test"];
|
|
143
|
+
// Add verbosity
|
|
144
|
+
if (verbosity > 0 && verbosity <= 5) {
|
|
145
|
+
args.push(`-${"v".repeat(verbosity)}`);
|
|
146
|
+
}
|
|
147
|
+
// Add test filters
|
|
148
|
+
if (matchTest) {
|
|
149
|
+
args.push("--match-test", matchTest);
|
|
150
|
+
}
|
|
151
|
+
if (matchContract) {
|
|
152
|
+
args.push("--match-contract", matchContract);
|
|
153
|
+
}
|
|
154
|
+
// Add gas reporting
|
|
155
|
+
if (gasReport) {
|
|
156
|
+
args.push("--gas-report");
|
|
157
|
+
}
|
|
158
|
+
// CRITICAL: Add fork URL to connect to deployed Diamond
|
|
159
|
+
// This allows Forge tests to access the Hardhat-deployed Diamond contract
|
|
160
|
+
if (forkUrl) {
|
|
161
|
+
args.push("--fork-url", forkUrl);
|
|
162
|
+
}
|
|
163
|
+
const cmd = `forge ${args.join(" ")}`;
|
|
164
|
+
console.log(`\nRunning: ${picocolors_1.default.blue(cmd)}\n`);
|
|
165
|
+
const { stdout, stderr } = await exec(cmd, { cwd, maxBuffer: 10 * 1024 * 1024 });
|
|
166
|
+
console.log(stdout);
|
|
167
|
+
if (stderr)
|
|
168
|
+
console.error(stderr);
|
|
169
|
+
return { success: true, output: stdout };
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
// Forge returns non-zero exit code when tests fail
|
|
173
|
+
// We still want to show the output
|
|
174
|
+
if (error.stdout)
|
|
175
|
+
console.log(error.stdout);
|
|
176
|
+
if (error.stderr)
|
|
177
|
+
console.error(error.stderr);
|
|
178
|
+
return {
|
|
179
|
+
success: false,
|
|
180
|
+
output: error.stdout || error.stderr || error.message,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
exports.runForgeTest = runForgeTest;
|
|
104
185
|
//# sourceMappingURL=foundry.js.map
|
package/dist/foundry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"foundry.js","sourceRoot":"","sources":["../src/foundry.ts"],"names":[],"mappings":"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","sourcesContent":["import { exec as execCallback, execSync } from \"child_process\";\nimport { NomicLabsHardhatPluginError } from \"hardhat/internal/core/errors\";\nimport picocolors from \"picocolors\";\nimport { promisify } from \"util\";\n\nconst exec = promisify(execCallback);\n\ntype Remappings = Record<string, string>;\n\nlet cachedRemappings: Promise<Remappings> | undefined;\n\nexport class HardhatFoundryError extends NomicLabsHardhatPluginError {\n constructor(message: string, parent?: Error) {\n super(\"diamonds-hardhat-foundry\", message, parent);\n }\n}\n\nclass ForgeInstallError extends HardhatFoundryError {\n constructor(dependency: string, parent: Error) {\n super(\n `Couldn't install '${dependency}', please install it manually.\n\n${parent.message}\n`,\n parent\n );\n }\n}\n\nexport function getForgeConfig() {\n return JSON.parse(runCmdSync(\"forge config --json\"));\n}\n\nexport function parseRemappings(remappingsTxt: string): Remappings {\n const remappings: Remappings = {};\n const remappingLines = remappingsTxt.split(/\\r\\n|\\r|\\n/);\n for (const remappingLine of remappingLines) {\n if (remappingLine.trim() === \"\") {\n continue;\n }\n\n if (remappingLine.includes(\":\")) {\n throw new HardhatFoundryError(\n `Invalid remapping '${remappingLine}', remapping contexts are not allowed`\n );\n }\n\n if (!remappingLine.includes(\"=\")) {\n throw new HardhatFoundryError(\n `Invalid remapping '${remappingLine}', remappings without a target are not allowed`\n );\n }\n\n const fromTo = remappingLine.split(\"=\");\n\n // if the remapping already exists, we ignore it because the first one wins\n if (remappings[fromTo[0]] !== undefined) {\n continue;\n }\n\n remappings[fromTo[0]] = fromTo[1];\n }\n\n return remappings;\n}\n\nexport async function getRemappings() {\n // Get remappings only once\n if (cachedRemappings === undefined) {\n cachedRemappings = runCmd(\"forge remappings\").then(parseRemappings);\n }\n\n return cachedRemappings;\n}\n\nexport async function installDependency(dependency: string) {\n // Check if --no-commit flag is supported. Best way is checking the help text\n const helpText = await runCmd(\"forge install --help\");\n const useNoCommitFlag = helpText.includes(\"--no-commit\");\n\n const cmd = `forge install ${\n useNoCommitFlag ? \"--no-commit\" : \"\"\n } ${dependency}`;\n\n console.log(`Running '${picocolors.blue(cmd)}'`);\n\n try {\n await exec(cmd);\n } catch (error: any) {\n throw new ForgeInstallError(dependency, error);\n }\n}\n\nfunction runCmdSync(cmd: string): string {\n try {\n return execSync(cmd, { stdio: \"pipe\" }).toString();\n } catch (error: any) {\n const pluginError = buildForgeExecutionError(\n error.status,\n error.stderr.toString()\n );\n\n throw pluginError;\n }\n}\n\nasync function runCmd(cmd: string): Promise<string> {\n try {\n const { stdout } = await exec(cmd);\n return stdout;\n } catch (error: any) {\n throw buildForgeExecutionError(error.code, error.message);\n }\n}\n\nfunction buildForgeExecutionError(\n exitCode: number | undefined,\n message: string\n) {\n switch (exitCode) {\n case 127:\n return new HardhatFoundryError(\n \"Couldn't run `forge`. Please check that your foundry installation is correct.\"\n );\n case 134:\n return new HardhatFoundryError(\n \"Running `forge` failed. Please check that your foundry.toml file is correct.\"\n );\n default:\n return new HardhatFoundryError(\n `Unexpected error while running \\`forge\\`: ${message}`\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"foundry.js","sourceRoot":"","sources":["../src/foundry.ts"],"names":[],"mappings":"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","sourcesContent":["import { exec as execCallback, execSync } from \"child_process\";\nimport { NomicLabsHardhatPluginError } from \"hardhat/internal/core/errors\";\nimport picocolors from \"picocolors\";\nimport { promisify } from \"util\";\n\nconst exec = promisify(execCallback);\n\ntype Remappings = Record<string, string>;\n\nlet cachedRemappings: Promise<Remappings> | undefined;\n\nexport class HardhatFoundryError extends NomicLabsHardhatPluginError {\n constructor(message: string, parent?: Error) {\n super(\"diamonds-hardhat-foundry\", message, parent);\n }\n}\n\nclass ForgeInstallError extends HardhatFoundryError {\n constructor(dependency: string, parent: Error) {\n super(\n `Couldn't install '${dependency}', please install it manually.\n\n${parent.message}\n`,\n parent\n );\n }\n}\n\nexport function getForgeConfig() {\n return JSON.parse(runCmdSync(\"forge config --json\"));\n}\n\nexport function parseRemappings(remappingsTxt: string): Remappings {\n const remappings: Remappings = {};\n const remappingLines = remappingsTxt.split(/\\r\\n|\\r|\\n/);\n for (const remappingLine of remappingLines) {\n if (remappingLine.trim() === \"\") {\n continue;\n }\n\n if (remappingLine.includes(\":\")) {\n throw new HardhatFoundryError(\n `Invalid remapping '${remappingLine}', remapping contexts are not allowed`\n );\n }\n\n if (!remappingLine.includes(\"=\")) {\n throw new HardhatFoundryError(\n `Invalid remapping '${remappingLine}', remappings without a target are not allowed`\n );\n }\n\n const fromTo = remappingLine.split(\"=\");\n\n // if the remapping already exists, we ignore it because the first one wins\n if (remappings[fromTo[0]] !== undefined) {\n continue;\n }\n\n remappings[fromTo[0]] = fromTo[1];\n }\n\n return remappings;\n}\n\nexport async function getRemappings() {\n // Get remappings only once\n if (cachedRemappings === undefined) {\n cachedRemappings = runCmd(\"forge remappings\").then(parseRemappings);\n }\n\n return cachedRemappings;\n}\n\nexport async function installDependency(dependency: string) {\n // Check if --no-commit flag is supported. Best way is checking the help text\n const helpText = await runCmd(\"forge install --help\");\n const useNoCommitFlag = helpText.includes(\"--no-commit\");\n\n const cmd = `forge install ${\n useNoCommitFlag ? \"--no-commit\" : \"\"\n } ${dependency}`;\n\n console.log(`Running '${picocolors.blue(cmd)}'`);\n\n try {\n await exec(cmd);\n } catch (error: any) {\n throw new ForgeInstallError(dependency, error);\n }\n}\n\nfunction runCmdSync(cmd: string): string {\n try {\n return execSync(cmd, { stdio: \"pipe\" }).toString();\n } catch (error: any) {\n const pluginError = buildForgeExecutionError(\n error.status,\n error.stderr.toString()\n );\n\n throw pluginError;\n }\n}\n\nasync function runCmd(cmd: string): Promise<string> {\n try {\n const { stdout } = await exec(cmd);\n return stdout;\n } catch (error: any) {\n throw buildForgeExecutionError(error.code, error.message);\n }\n}\n\nfunction buildForgeExecutionError(\n exitCode: number | undefined,\n message: string\n) {\n switch (exitCode) {\n case 127:\n return new HardhatFoundryError(\n \"Couldn't run `forge`. Please check that your foundry installation is correct.\"\n );\n case 134:\n return new HardhatFoundryError(\n \"Running `forge` failed. Please check that your foundry.toml file is correct.\"\n );\n default:\n return new HardhatFoundryError(\n `Unexpected error while running \\`forge\\`: ${message}`\n );\n }\n}\n\n/**\n * Check if Foundry is installed\n */\nexport function isFoundryInstalled(): boolean {\n try {\n execSync(\"forge --version\", { stdio: \"pipe\" });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Compile Forge contracts\n */\nexport async function compileForge(options: {\n cwd?: string;\n verbose?: boolean;\n}): Promise<{ success: boolean; output?: string }> {\n const { cwd = process.cwd(), verbose = false } = options;\n \n try {\n const args = [\"build\"];\n if (!verbose) {\n args.push(\"--quiet\");\n }\n \n const { stdout, stderr } = await exec(`forge ${args.join(\" \")}`, { cwd });\n return { success: true, output: stdout || stderr };\n } catch (error: any) {\n console.error(\"Forge build failed:\", error.message);\n return { success: false, output: error.message };\n }\n}\n\n/**\n * Run Forge tests with optional forking\n */\nexport async function runForgeTest(options: {\n matchTest?: string;\n matchContract?: string;\n verbosity?: number;\n gasReport?: boolean;\n forkUrl?: string;\n cwd?: string;\n}): Promise<{ success: boolean; output?: string }> {\n const {\n matchTest,\n matchContract,\n verbosity = 2,\n gasReport = false,\n forkUrl,\n cwd = process.cwd(),\n } = options;\n\n try {\n const args = [\"test\"];\n\n // Add verbosity\n if (verbosity > 0 && verbosity <= 5) {\n args.push(`-${\"v\".repeat(verbosity)}`);\n }\n\n // Add test filters\n if (matchTest) {\n args.push(\"--match-test\", matchTest);\n }\n\n if (matchContract) {\n args.push(\"--match-contract\", matchContract);\n }\n\n // Add gas reporting\n if (gasReport) {\n args.push(\"--gas-report\");\n }\n\n // CRITICAL: Add fork URL to connect to deployed Diamond\n // This allows Forge tests to access the Hardhat-deployed Diamond contract\n if (forkUrl) {\n args.push(\"--fork-url\", forkUrl);\n }\n\n const cmd = `forge ${args.join(\" \")}`;\n console.log(`\\nRunning: ${picocolors.blue(cmd)}\\n`);\n\n const { stdout, stderr } = await exec(cmd, { cwd, maxBuffer: 10 * 1024 * 1024 });\n console.log(stdout);\n if (stderr) console.error(stderr);\n\n return { success: true, output: stdout };\n } catch (error: any) {\n // Forge returns non-zero exit code when tests fail\n // We still want to show the output\n if (error.stdout) console.log(error.stdout);\n if (error.stderr) console.error(error.stderr);\n\n return {\n success: false,\n output: error.stdout || error.stderr || error.message,\n };\n }\n}\n"]}
|