@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.
Files changed (59) hide show
  1. package/CHANGELOG.md +249 -0
  2. package/README.md +650 -4
  3. package/contracts/DiamondABILoader.sol +329 -0
  4. package/contracts/DiamondForgeHelpers.sol +309 -85
  5. package/contracts/DiamondFuzzBase.sol +322 -114
  6. package/dist/foundry.d.ts +28 -0
  7. package/dist/foundry.d.ts.map +1 -1
  8. package/dist/foundry.js +82 -1
  9. package/dist/foundry.js.map +1 -1
  10. package/dist/framework/DeploymentManager.d.ts +10 -11
  11. package/dist/framework/DeploymentManager.d.ts.map +1 -1
  12. package/dist/framework/DeploymentManager.js +56 -45
  13. package/dist/framework/DeploymentManager.js.map +1 -1
  14. package/dist/framework/ForgeFuzzingFramework.d.ts +4 -0
  15. package/dist/framework/ForgeFuzzingFramework.d.ts.map +1 -1
  16. package/dist/framework/ForgeFuzzingFramework.js +16 -2
  17. package/dist/framework/ForgeFuzzingFramework.js.map +1 -1
  18. package/dist/framework/HelperGenerator.d.ts.map +1 -1
  19. package/dist/framework/HelperGenerator.js +11 -0
  20. package/dist/framework/HelperGenerator.js.map +1 -1
  21. package/dist/index.d.ts +0 -3
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +0 -8
  24. package/dist/index.js.map +1 -1
  25. package/dist/tasks/deploy.js +11 -4
  26. package/dist/tasks/deploy.js.map +1 -1
  27. package/dist/tasks/generate-helpers.js +6 -4
  28. package/dist/tasks/generate-helpers.js.map +1 -1
  29. package/dist/tasks/init.js +3 -2
  30. package/dist/tasks/init.js.map +1 -1
  31. package/dist/tasks/test.js +13 -2
  32. package/dist/tasks/test.js.map +1 -1
  33. package/dist/types/hardhat.d.ts +1 -1
  34. package/dist/types/hardhat.d.ts.map +1 -1
  35. package/dist/types/hardhat.js +1 -0
  36. package/dist/types/hardhat.js.map +1 -1
  37. package/dist/utils/foundry.d.ts +1 -0
  38. package/dist/utils/foundry.d.ts.map +1 -1
  39. package/dist/utils/foundry.js +3 -0
  40. package/dist/utils/foundry.js.map +1 -1
  41. package/package.json +5 -3
  42. package/src/foundry.ts +104 -0
  43. package/src/framework/DeploymentManager.ts +74 -69
  44. package/src/framework/ForgeFuzzingFramework.ts +23 -1
  45. package/src/framework/HelperGenerator.ts +13 -0
  46. package/src/index.ts +0 -5
  47. package/src/tasks/deploy.ts +14 -3
  48. package/src/tasks/generate-helpers.ts +6 -2
  49. package/src/tasks/init.ts +3 -1
  50. package/src/tasks/test.ts +12 -1
  51. package/src/templates/ExampleFuzzTest.t.sol.template +26 -17
  52. package/src/templates/ExampleIntegrationTest.t.sol.template +9 -1
  53. package/src/templates/ExampleUnitTest.t.sol.template +7 -1
  54. package/src/types/hardhat.ts +1 -1
  55. package/src/utils/foundry.ts +5 -0
  56. package/dist/templates/DiamondDeployment.sol.template +0 -38
  57. package/dist/templates/ExampleFuzzTest.t.sol.template +0 -109
  58. package/dist/templates/ExampleIntegrationTest.t.sol.template +0 -79
  59. 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 "forge-std/console.sol";
6
- import "./DiamondForgeHelpers.sol";
7
-
8
- /**
9
- * @title DiamondFuzzBase
10
- * @notice Base contract for Diamond fuzz testing with Forge
11
- * @dev Provides setup methods and utilities for fuzz testing Diamond contracts
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 DiamondForgeHelpers for address;
22
+ using DiamondABILoader for string;
15
23
 
16
- // Diamond contract address (set by child contracts)
17
- address public diamond;
24
+ /// @notice Deployed Diamond contract address
25
+ /// @dev Loaded in setUp(), can be overridden by implementing _loadDiamondAddress()
26
+ address internal diamond;
18
27
 
19
- // Facet addresses (set by child contracts)
20
- mapping(string => address) public facets;
28
+ /// @notice Diamond ABI JSON content
29
+ /// @dev Loaded from diamond-abi file in setUp()
30
+ string internal diamondABI;
21
31
 
22
- // Test accounts
23
- address public deployer;
24
- address public user1;
25
- address public user2;
26
- address public user3;
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
- * @notice Assume valid Ethereum address for fuzzing
81
- * @param addr Address to validate
82
- */
83
- function assumeValidAddress(address addr) internal pure {
84
- vm.assume(addr != address(0));
85
- vm.assume(addr != address(0xdead));
86
- vm.assume(uint160(addr) > 0xFF); // Avoid precompiles
87
- }
88
-
89
- /**
90
- * @notice Assume valid amount for fuzzing (not zero, not unreasonably large)
91
- * @param amount Amount to validate
92
- */
93
- function assumeValidAmount(uint256 amount) internal pure {
94
- vm.assume(amount > 0);
95
- vm.assume(amount < type(uint128).max); // Reasonable upper bound
96
- }
97
-
98
- /**
99
- * @notice Bound a fuzzed value to a specific range
100
- * @param value The fuzzed value
101
- * @param min Minimum value (inclusive)
102
- * @param max Maximum value (inclusive)
103
- * @return Bounded value
104
- */
105
- function boundValue(uint256 value, uint256 min, uint256 max) internal pure returns (uint256) {
106
- return bound(value, min, max);
107
- }
108
-
109
- /**
110
- * @notice Log test context for debugging
111
- * @param testName Name of the test
112
- */
113
- function logTestContext(string memory testName) internal view {
114
- console.log("=== Test:", testName, "===");
115
- console.log("Diamond:", diamond);
116
- console.log("Deployer:", deployer);
117
- console.log("Block number:", block.number);
118
- console.log("Block timestamp:", block.timestamp);
119
- }
120
-
121
- /**
122
- * @notice Expect a revert with specific error message
123
- * @param errorMessage Expected error message
124
- */
125
- function expectRevertWithMessage(string memory errorMessage) internal {
126
- vm.expectRevert(bytes(errorMessage));
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
@@ -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
@@ -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"]}