Skip to content

Commit 13b6c12

Browse files
authored
Fix Hardhat compile error when public variables are used to implement interface functions (#1055)
1 parent 742415c commit 13b6c12

File tree

5 files changed

+66
-14
lines changed

5 files changed

+66
-14
lines changed

packages/core/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
- Fix Hardhat compile error when public variables are used to implement interface functions. ([#1055](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1055))
6+
37
## 1.34.4 (2024-07-22)
48

59
- Fix Hardhat compile error when return parameter names are documented as param. ([#1050](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1050))

packages/core/contracts/test/NamespacedToModify.sol

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,22 @@ contract HasNatSpecWithMultipleReturns {
260260
function hasReturnsDocumentedAsParamsInContract() public pure returns (uint a, uint b) {
261261
}
262262
}
263+
264+
interface IHasExternalViewFunction {
265+
function foo() external view returns (uint256);
266+
}
267+
268+
contract HasExternalViewFunction is IHasExternalViewFunction {
269+
// This generates a getter function that conforms to the interface
270+
uint256 public foo;
271+
272+
// References a selector in an interface
273+
bytes4 constant USING_INTERFACE_FUNCTION_SELECTOR = IHasExternalViewFunction.foo.selector;
274+
275+
// References a getter generated for a public variable
276+
bytes4 immutable IMMUTABLE_USING_GETTER = this.foo.selector;
277+
}
278+
279+
contract DeploysContractToImmutable {
280+
HasFunction public immutable example = new HasFunction(1);
281+
}

packages/core/src/utils/make-namespaced.test.ts.md

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Generated by [AVA](https://avajs.dev).
3232
content: `// SPDX-License-Identifier: MIT␊
3333
pragma solidity ^0.8.20;␊
3434
35-
contract Example {␊
35+
abstract contract Example {␊
3636
/// @custom:storage-location erc7201:example.main␊
3737
struct MainStorage {␊
3838
uint256 x;␊
@@ -98,12 +98,12 @@ Generated by [AVA](https://avajs.dev).
9898
enum $astId_id_random { dummy }␊
9999
}␊
100100
101-
contract HasFunction {␊
101+
abstract contract HasFunction {␊
102102
103103
function foo() pure public returns (bool) {}␊
104104
}␊
105105
106-
contract UsingFunction is HasFunction {␊
106+
abstract contract UsingFunction is HasFunction {␊
107107
enum $astId_id_random { dummy }␊
108108
}␊
109109
@@ -117,7 +117,7 @@ Generated by [AVA](https://avajs.dev).
117117
function plusOne(uint x) pure public returns (bool) {}␊
118118
}␊
119119
120-
contract Consumer {␊
120+
abstract contract Consumer {␊
121121
enum $astId_id_random { dummy }␊
122122
123123
function usingFreeFunction() pure public returns (bool) {}␊
@@ -127,7 +127,7 @@ Generated by [AVA](https://avajs.dev).
127127
function usingLibraryFunction() pure public returns (bool) {}␊
128128
}␊
129129
130-
contract HasConstantWithSelector {␊
130+
abstract contract HasConstantWithSelector {␊
131131
bytes4 constant CONTRACT_CONSTANT_USING_SELECTOR = HasFunction.foo.selector;␊
132132
}␊
133133
@@ -159,7 +159,7 @@ Generated by [AVA](https://avajs.dev).
159159
*/␊
160160
enum $astId_id_random { dummy }␊
161161
162-
contract UsingForDirectives {␊
162+
abstract contract UsingForDirectives {␊
163163
enum $astId_id_random { dummy }␊
164164
165165
function usingFor(uint x) pure public returns (bool) {}␊
@@ -185,7 +185,7 @@ Generated by [AVA](https://avajs.dev).
185185
186186
int8 constant MAX_SIZE_C = 2;␊
187187
188-
contract StructArrayUsesConstant {␊
188+
abstract contract StructArrayUsesConstant {␊
189189
uint16 private constant MAX_SIZE = 10;␊
190190
191191
struct NotNamespaced {␊
@@ -208,11 +208,11 @@ Generated by [AVA](https://avajs.dev).
208208
interface IDummy {␊
209209
}␊
210210
211-
contract UsesAddress {␊
211+
abstract contract UsesAddress {␊
212212
IDummy public constant MY_CONTRACT = IDummy(MY_ADDRESS);␊
213213
}␊
214214
215-
contract HasFunctionWithRequiredReturn {␊
215+
abstract contract HasFunctionWithRequiredReturn {␊
216216
struct S { uint x; }␊
217217
function foo(S calldata s) internal pure returns (bool) {}␊
218218
}␊
@@ -235,7 +235,7 @@ Generated by [AVA](https://avajs.dev).
235235
*/␊
236236
function hasReturnsDocumentedAsParams() pure returns (bool a, bool b) {}␊
237237
238-
contract HasNatSpecWithMultipleReturns {␊
238+
abstract contract HasNatSpecWithMultipleReturns {␊
239239
/**␊
240240
* @return uint 1␊
241241
* @return uint 2␊
@@ -254,15 +254,33 @@ Generated by [AVA](https://avajs.dev).
254254
*/␊
255255
function hasReturnsDocumentedAsParamsInContract() public pure returns (bool a, bool b) {}␊
256256
}␊
257-
`,
257+
258+
interface IHasExternalViewFunction {␊
259+
function foo() external view returns (bool);␊
260+
}␊
261+
262+
abstract contract HasExternalViewFunction is IHasExternalViewFunction {␊
263+
// This generates a getter function that conforms to the interface␊
264+
enum $astId_id_random { dummy }␊
265+
266+
// References a selector in an interface␊
267+
bytes4 constant USING_INTERFACE_FUNCTION_SELECTOR = IHasExternalViewFunction.foo.selector;␊
268+
269+
// References a getter generated for a public variable␊
270+
enum $astId_id_random { dummy }␊
271+
}␊
272+
273+
abstract contract DeploysContractToImmutable {␊
274+
enum $astId_id_random { dummy }␊
275+
}`,
258276
},
259277
'contracts/test/NamespacedToModifyImported.sol': {
260278
content: `// SPDX-License-Identifier: MIT␊
261279
pragma solidity ^0.8.20;␊
262280
263281
import {CONSTANT_USING_SELECTOR, plusTwo, plusThree, CustomErrorOutsideContract} from "./NamespacedToModify.sol";␊
264282
265-
contract Example {}␊
283+
abstract contract Example {}␊
266284
`,
267285
},
268286
},
@@ -295,12 +313,12 @@ Generated by [AVA](https://avajs.dev).
295313
content: `// SPDX-License-Identifier: MIT␊
296314
pragma solidity 0.7.6;␊
297315
298-
contract HasFunction {␊
316+
abstract contract HasFunction {␊
299317
300318
function foo() pure public returns (bool) {}␊
301319
}␊
302320
303-
contract UsingFunction is HasFunction {␊
321+
abstract contract UsingFunction is HasFunction {␊
304322
enum $astId_id_random { dummy }␊
305323
}␊
306324
`,
160 Bytes
Binary file not shown.

packages/core/src/utils/make-namespaced.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ export function makeNamespacedInput(input: SolcInput, output: SolcOutput): SolcI
4848
case 'ContractDefinition': {
4949
const contractDef = node;
5050

51+
// Mark contracts as abstract, since state variables are converted to dummy enums
52+
// which would not generate public getters for inherited interface functions
53+
if (contractDef.contractKind === 'contract' && !contractDef.abstract) {
54+
modifications.push(makeInsertBefore(contractDef, 'abstract '));
55+
}
56+
5157
// Remove any calls to parent constructors from the inheritance list
5258
const inherits = contractDef.baseContracts;
5359
for (const inherit of inherits) {
@@ -210,6 +216,11 @@ function makeReplace(node: Node, orig: Buffer, text: string): Modification {
210216
return { start, end, text };
211217
}
212218

219+
function makeInsertBefore(node: Node, text: string): Modification {
220+
const { start } = getPositions(node);
221+
return { start: start, end: start, text };
222+
}
223+
213224
function makeInsertAfter(node: Node, text: string): Modification {
214225
const { end } = getPositions(node);
215226
return { start: end, end, text };

0 commit comments

Comments
 (0)