@@ -4,18 +4,28 @@ pragma solidity ^0.8.20;
44import {Script} from "forge-std/Script.sol " ;
55import {console2} from "forge-std/console2.sol " ;
66import {IDiamondCut} from "../src/facets/Diamond/IDiamondCut.sol " ; // Adjust to your imports
7- import {DiamondCutFacet} from "../src/facets/Diamond/DiamondCutFacet.sol " ; // Example; not used directly
87
98contract DeployDiamond is Script {
109 // Configurable list: Add new facet names here (e.g., "MarketplaceFacet")
1110 string [] public facetNames = [
1211 "DiamondCutFacet " ,
1312 "DiamondLoupeFacet " ,
1413 "OwnershipFacet " ,
15- "ERC20Facet "
14+ "ERC20Facet " ,
15+ "ERC2771Recipient "
1616 // Add "MarketplaceFacet" here for auto-inclusion
1717 ];
1818
19+ // Parallel list: Subdirectories for each facet (in same order as facetNames)
20+ string [] public subdirs = [
21+ "Diamond " ,
22+ "Diamond " ,
23+ "Ownership " ,
24+ "ERC20 " ,
25+ "ERC2771Recipient "
26+ // Add corresponding subdir for new facets
27+ ];
28+
1929 function setUp () public {} // Optional setup
2030
2131 function run () public {
@@ -30,7 +40,8 @@ contract DeployDiamond is Script {
3040
3141 // Step 3: Deploy Diamond (adapt to your DiamondDeploy logic)
3242 // address diamond = address(new DiamondDeploy(facetCuts));
33- // ... (e.g., cut the diamond, transfer ownership)
43+ // IDiamondCut(address(diamond)).diamondCut(facetCuts, IDiamondCut.DiamondCutFunctionType.Add, ""); // Initial cut
44+ // OwnershipFacet(address(diamond)).transferOwnership(msg.sender); // Example
3445
3546 // Optional: Persist selectors to JSON for reuse
3647 _persistSelectors (facetCuts);
@@ -39,51 +50,19 @@ contract DeployDiamond is Script {
3950 }
4051
4152 /// @dev Builds FacetCut[] by dynamically fetching selectors and deploying facets.
53+ /// Now stack-safe: Loop has minimal locals; heavy work delegated.
4254 function _buildFacetCuts () internal returns (IDiamondCut.FacetCut[] memory cuts ) {
4355 uint256 numFacets = facetNames.length ;
4456 cuts = new IDiamondCut.FacetCut [](numFacets);
4557
4658 for (uint256 i = 0 ; i < numFacets; ++ i) {
4759 string memory name = facetNames[i];
48- string memory contractId = string .concat ("src/facets/ " , name, ".sol: " , name);
49-
50- // Fetch methodIdentifiers JSON
51- string [] memory inspectCmd = new string [](5 );
52- inspectCmd[0 ] = "forge " ;
53- inspectCmd[1 ] = "inspect " ;
54- inspectCmd[2 ] = contractId;
55- inspectCmd[3 ] = "methodIdentifiers " ;
56- inspectCmd[4 ] = "--json " ;
57-
58- bytes memory outputBytes = vm.ffi (inspectCmd);
59- string memory json = string (outputBytes);
60-
61- // Parse all selector hex strings (e.g., ["0xa9059cbb", ...])
62- bytes memory selBytes = vm.parseJson (json, ".methodIdentifiers.[] " );
63- string [] memory selStrings = abi.decode (selBytes, (string []));
64-
65- // Convert to bytes4[]
66- bytes4 [] memory selectors = new bytes4 [](selStrings.length );
67- for (uint256 j = 0 ; j < selStrings.length ; ++ j) {
68- selectors[j] = bytes4 (vm.parseBytes32 (selStrings[j]));
69- }
70-
71- // Fetch bytecode JSON for deployment
72- string [] memory bytecodeCmd = new string [](5 );
73- bytecodeCmd[0 ] = "forge " ;
74- bytecodeCmd[1 ] = "inspect " ;
75- bytecodeCmd[2 ] = contractId;
76- bytecodeCmd[3 ] = "bytecode " ;
77- bytecodeCmd[4 ] = "--json " ;
78-
79- bytes memory bytecodeOutputBytes = vm.ffi (bytecodeCmd);
80- string memory bytecodeJson = string (bytecodeOutputBytes);
60+ string memory subdir = subdirs[i]; // Fetch corresponding subdir
61+ string memory contractId = string .concat ("src/facets/ " , subdir, "/ " , name, ".sol: " , name);
8162
82- // Parse bytecode hex string (e.g., "0x6080604052...")
83- string memory bytecodeStr = abi.decode (vm.parseJson (bytecodeJson, ".bytecode " ), (string ));
84- bytes memory bytecode = vm.parseBytes (bytecodeStr); // Handles full hex
85-
86- // Deploy facet
63+ // Delegate to sub-functions to avoid stack bloat
64+ bytes4 [] memory selectors = _getSelectors (contractId);
65+ bytes memory bytecode = _getBytecode (contractId);
8766 address facetAddr = _deployCode (bytecode);
8867
8968 // Build cut (Add action; adjust if Replace/Remove needed)
@@ -97,6 +76,55 @@ contract DeployDiamond is Script {
9776 }
9877 }
9978
79+ /// @dev Fetches and parses method selectors via FFI.
80+ function _getSelectors (string memory contractId ) internal returns (bytes4 [] memory selectors ) {
81+ string [] memory inspectCmd = new string [](5 );
82+ inspectCmd[0 ] = "forge " ;
83+ inspectCmd[1 ] = "inspect " ;
84+ inspectCmd[2 ] = contractId;
85+ inspectCmd[3 ] = "methodIdentifiers " ;
86+ inspectCmd[4 ] = "--json " ;
87+
88+ bytes memory outputBytes = vm.ffi (inspectCmd);
89+ string memory json = string (outputBytes);
90+
91+ // Parse JSON: Root is { "sig": "sel", ... } → ".*" selects all values as string[] of hex selectors (no "0x")
92+ bytes memory selBytes = vm.parseJson (json, ".* " );
93+ string [] memory selStrings = abi.decode (selBytes, (string []));
94+
95+ // Edge case: No selectors (e.g., abstract contract)
96+ if (selStrings.length == 0 ) {
97+ selectors = new bytes4 [](0 );
98+ return selectors;
99+ }
100+
101+ selectors = new bytes4 [](selStrings.length );
102+ for (uint256 j = 0 ; j < selStrings.length ; ++ j) {
103+ // Prepend "0x" since forge inspect outputs raw hex without prefix
104+ string memory hexWithPrefix = string .concat ("0x " , selStrings[j]);
105+ console2.log ("Parsed selector hex: %s " , hexWithPrefix); // Debug: Confirm prepending
106+ // vm.parseBytes32 converts "0x..." hex string to bytes32; cast to bytes4 (first 4 bytes)
107+ selectors[j] = bytes4 (vm.parseBytes32 (hexWithPrefix));
108+ console2.log ("Selector %d: %s " , j, hexWithPrefix); // Debug: Log each selector
109+ }
110+ }
111+
112+ /// @dev Fetches bytecode via FFI.
113+ function _getBytecode (string memory contractId ) internal returns (bytes memory bytecode ) {
114+ string [] memory bytecodeCmd = new string [](5 );
115+ bytecodeCmd[0 ] = "forge " ;
116+ bytecodeCmd[1 ] = "inspect " ;
117+ bytecodeCmd[2 ] = contractId;
118+ bytecodeCmd[3 ] = "bytecode " ;
119+ bytecodeCmd[4 ] = "--json " ;
120+
121+ bytes memory outputBytes = vm.ffi (bytecodeCmd);
122+ string memory bytecodeJson = string (outputBytes);
123+
124+ string memory bytecodeStr = abi.decode (vm.parseJson (bytecodeJson, ".bytecode " ), (string ));
125+ bytecode = vm.parseBytes (bytecodeStr);
126+ }
127+
100128 /// @dev Deploys raw creation bytecode (assumes no constructor args).
101129 function _deployCode (bytes memory code ) internal returns (address addr ) {
102130 assembly {
@@ -112,22 +140,17 @@ contract DeployDiamond is Script {
112140 string memory root = vm.projectRoot ();
113141 string memory filePath = string .concat (root, "/facet-selectors.json " );
114142
115- // Manual JSON build (simple; use forge-std/StdJson.sol for complex structs )
143+ // Build JSON manually (simple array of objects )
116144 string memory jsonContent = '{"facets": [ ' ;
117145 for (uint256 i = 0 ; i < cuts.length ; ++ i) {
118- string memory name = facetNames[i]; // Map back via index
146+ string memory name = facetNames[i]; // Map back via index
119147 jsonContent = string .concat (jsonContent, '{"name":" ' , name, '","selectors":[ ' );
120- for (uint256 j = 0 ; j < cuts[i].functionSelectors.length ; ++ j) {
121- // Convert bytes4 back to hex string for JSON
122- bytes memory selHex = new bytes (10 ); // "0x" + 8 hex chars
123- selHex[0 ] = bytes1 (hex "30 " ); // '0'
124- selHex[1 ] = bytes1 (hex "78 " ); // 'x'
125- // ... (assembly or loop to append hex; simplified here)
126- // Full impl: Use toHexString from forge-std/Test.sol
127- jsonContent = string .concat (jsonContent, '"0x ' , _toHexString (cuts[i].functionSelectors[j]), '" ' );
128- if (j < cuts[i].functionSelectors.length - 1 ) jsonContent = string .concat (jsonContent, ", " );
148+ bytes4 [] memory selectors = cuts[i].functionSelectors;
149+ for (uint256 j = 0 ; j < selectors.length ; ++ j) {
150+ jsonContent = string .concat (jsonContent, '" ' , _toHexString (selectors[j]), '" ' );
151+ if (j < selectors.length - 1 ) jsonContent = string .concat (jsonContent, ", " );
129152 }
130- jsonContent = string .concat (jsonContent, ']}} ' );
153+ jsonContent = string .concat (jsonContent, ']} ' );
131154 if (i < cuts.length - 1 ) jsonContent = string .concat (jsonContent, ", " );
132155 }
133156 jsonContent = string .concat (jsonContent, ']} ' );
@@ -136,25 +159,22 @@ contract DeployDiamond is Script {
136159 console2.log ("Persisted selectors to %s " , filePath);
137160 }
138161
139- // Helper: bytes4 to hex string (adapt from forge-std/Test.sol )
162+ // Helper: bytes4 to hex string (e.g., "0xa9059cbb" )
140163 function _toHexString (bytes4 value ) internal pure returns (string memory ) {
141- bytes memory str = new bytes (10 );
142- str[0 ] = "0 " ;
143- str[1 ] = "x " ;
164+ bytes memory str = new bytes (10 ); // "0x" + 8 hex chars
165+ str[0 ] = bytes1 (uint8 (48 )); // '0'
166+ str[1 ] = bytes1 (uint8 (120 )); // 'x'
167+ uint256 temp = uint256 (uint32 (value));
144168 for (uint256 i = 0 ; i < 4 ; ++ i) {
145- uint8 _byte = uint8 (uint32 (value) >> (8 * (3 - i)));
146- str[2 + i * 2 ] = _toHexDigit (_byte >> 4 );
147- str[3 + i * 2 ] = _toHexDigit (_byte & 0x0f );
169+ uint8 byteVal = uint8 (temp >> (8 * (3 - i)));
170+ str[2 + i * 2 ] = _toHexDigit (uint8 (byteVal >> 4 ) );
171+ str[3 + i * 2 ] = _toHexDigit (uint8 (byteVal & 0x0f ) );
148172 }
149173 return string (str);
150174 }
151175
152176 function _toHexDigit (uint8 digit ) internal pure returns (bytes1 ) {
153- if (digit < 10 ) return bytes1 (char (uint8 (48 + digit)));
154- return bytes1 (char (uint8 (97 + digit - 10 )));
155- }
156-
157- function char (uint8 b ) internal pure returns (bytes1 ) {
158- return bytes1 (b);
177+ uint8 c = digit < 10 ? uint8 (48 + digit) : uint8 (97 + digit - 10 ); // '0'-'9', 'a'-'f'
178+ return bytes1 (c);
159179 }
160180}
0 commit comments