@@ -5,6 +5,7 @@ import { formatLines, Lines, spaceBetween } from './utils/format-lines';
5
5
import type { Visibility , SourceUnit , ContractDefinition , FunctionDefinition , VariableDeclaration , StorageLocation , TypeDescriptions , TypeName , InheritanceSpecifier , ModifierInvocation , FunctionCall } from 'solidity-ast' ;
6
6
import type { FileContent , ProjectPathsConfig , ResolvedFile } from 'hardhat/types' ;
7
7
import type { ExposedConfig } from './config' ;
8
+ import assert from 'assert' ;
8
9
9
10
export interface SolcOutput {
10
11
sources : {
@@ -105,7 +106,7 @@ function getExposedContent(ast: SourceUnit, relativizePath: (p: string) => strin
105
106
106
107
const clashingFunctions : Record < string , number > = { } ;
107
108
for ( const fn of externalizableFunctions ) {
108
- const id = getFunctionId ( fn ) ;
109
+ const id = getFunctionId ( fn , c , deref ) ;
109
110
clashingFunctions [ id ] ??= 0 ;
110
111
clashingFunctions [ id ] += 1 ;
111
112
}
@@ -121,13 +122,13 @@ function getExposedContent(ast: SourceUnit, relativizePath: (p: string) => strin
121
122
[ `bytes32 public constant __hh_exposed_bytecode_marker = "hardhat-exposed";\n` ] ,
122
123
spaceBetween (
123
124
// slots for storage function parameters
124
- ...getAllStorageArguments ( externalizableFunctions ) . map ( a => [
125
+ ...getAllStorageArguments ( externalizableFunctions , c , deref ) . map ( a => [
125
126
`mapping(uint256 => ${ a . storageType } ) internal ${ prefix } ${ a . storageVar } ;` ,
126
127
] ) ,
127
128
// events for internal returns
128
129
...returnedEventFunctions . map ( fn => {
129
- const evName = clashingEvents [ fn . name ] === 1 ? fn . name : getFunctionNameQualified ( fn , false ) ;
130
- const params = getFunctionReturnParameters ( fn , null ) ;
130
+ const evName = clashingEvents [ fn . name ] === 1 ? fn . name : getFunctionNameQualified ( fn , c , deref , false ) ;
131
+ const params = getFunctionReturnParameters ( fn , c , deref , null ) ;
131
132
return [
132
133
`event return${ prefix } ${ evName } (${ params . map ( printArgument ) . join ( ', ' ) } );`
133
134
]
@@ -138,24 +139,24 @@ function getExposedContent(ast: SourceUnit, relativizePath: (p: string) => strin
138
139
...externalizableVariables . map ( v => [
139
140
[
140
141
'function' ,
141
- `${ prefix } ${ v . name } (${ getVarGetterArgs ( v ) . map ( printArgument ) . join ( ', ' ) } )` ,
142
+ `${ prefix } ${ v . name } (${ getVarGetterArgs ( v , c , deref ) . map ( printArgument ) . join ( ', ' ) } )` ,
142
143
'external' ,
143
144
v . mutability === 'mutable' || ( v . mutability === 'immutable' && ! v . value ) ? 'view' : 'pure' ,
144
145
'returns' ,
145
- `(${ getVarGetterReturnType ( v ) } )` ,
146
+ `(${ getVarGetterReturnType ( v , c , deref ) } )` ,
146
147
'{'
147
148
] . join ( ' ' ) ,
148
149
[
149
- `return ${ isLibrary ? c . name + '.' : '' } ${ v . name } ${ getVarGetterArgs ( v ) . map ( a => `[${ a . name } ]` ) . join ( '' ) } ;` ,
150
+ `return ${ isLibrary ? c . name + '.' : '' } ${ v . name } ${ getVarGetterArgs ( v , c , deref ) . map ( a => `[${ a . name } ]` ) . join ( '' ) } ;` ,
150
151
] ,
151
152
'}' ,
152
153
] ) ,
153
154
// external functions
154
155
...externalizableFunctions . map ( fn => {
155
- const fnName = clashingFunctions [ getFunctionId ( fn ) ] === 1 ? fn . name : getFunctionNameQualified ( fn ) ;
156
- const fnArgs = getFunctionArguments ( fn ) ;
157
- const fnRets = getFunctionReturnParameters ( fn ) ;
158
- const evName = isNonViewWithReturns ( fn ) && ( clashingEvents [ fn . name ] === 1 ? fn . name : getFunctionNameQualified ( fn , false ) ) ;
156
+ const fnName = clashingFunctions [ getFunctionId ( fn , c , deref ) ] === 1 ? fn . name : getFunctionNameQualified ( fn , c , deref ) ;
157
+ const fnArgs = getFunctionArguments ( fn , c , deref ) ;
158
+ const fnRets = getFunctionReturnParameters ( fn , c , deref ) ;
159
+ const evName = isNonViewWithReturns ( fn ) && ( clashingEvents [ fn . name ] === 1 ? fn . name : getFunctionNameQualified ( fn , c , deref , false ) ) ;
159
160
160
161
// function header
161
162
const header = [
@@ -228,14 +229,14 @@ function areFunctionsFullyImplemented(contract: ContractDefinition, deref: ASTDe
228
229
return abstractFunctionIds . size === 0 ;
229
230
}
230
231
231
- function getFunctionId ( fn : FunctionDefinition ) : string {
232
- const storageArgs = new Set < Argument > ( getStorageArguments ( fn ) ) ;
233
- const nonStorageArgs = getFunctionArguments ( fn ) . filter ( a => ! storageArgs . has ( a ) ) ;
232
+ function getFunctionId ( fn : FunctionDefinition , context : ContractDefinition , deref : ASTDereferencer ) : string {
233
+ const storageArgs = new Set < Argument > ( getStorageArguments ( fn , context , deref ) ) ;
234
+ const nonStorageArgs = getFunctionArguments ( fn , context , deref ) . filter ( a => ! storageArgs . has ( a ) ) ;
234
235
return fn . name + nonStorageArgs . map ( a => a . type ) . join ( '' ) ;
235
236
}
236
237
237
- function getFunctionNameQualified ( fn : FunctionDefinition , onlyStorage : boolean = true ) : string {
238
- return fn . name + ( onlyStorage ? getStorageArguments ( fn ) : getFunctionArguments ( fn ) )
238
+ function getFunctionNameQualified ( fn : FunctionDefinition , context : ContractDefinition , deref : ASTDereferencer , onlyStorage : boolean = true ) : string {
239
+ return fn . name + ( onlyStorage ? getStorageArguments ( fn , context , deref ) : getFunctionArguments ( fn , context , deref ) )
239
240
. map ( arg => arg . storageType ?? arg . type )
240
241
. map ( type => type . replace ( / .* / , '' ) . replace ( / [ ^ 0 - 9 a - z A - Z $ _ ] + / g, '_' ) ) // sanitize
241
242
. join ( '_' )
@@ -289,7 +290,7 @@ function makeConstructor(contract: ContractDefinition, deref: ASTDereferencer, i
289
290
const args = [ ] ;
290
291
for ( const a of constructors . get ( c . id ) ?. parameters . parameters ?? [ ] ) {
291
292
const name = missingArguments . has ( a . name ) ? `${ c . name } _${ a . name } ` : a . name ;
292
- const type = getVarType ( a , 'memory' ) ;
293
+ const type = getVarType ( a , c , deref , 'memory' ) ;
293
294
missingArguments . set ( name , type ) ;
294
295
args . push ( name ) ;
295
296
}
@@ -390,55 +391,68 @@ interface Argument {
390
391
391
392
const printArgument = ( arg : Argument ) => `${ arg . type } ${ arg . name } ` ;
392
393
393
- function getFunctionArguments ( fnDef : FunctionDefinition ) : Argument [ ] {
394
+ function getFunctionArguments ( fnDef : FunctionDefinition , context : ContractDefinition , deref : ASTDereferencer ) : Argument [ ] {
394
395
return fnDef . parameters . parameters . map ( ( p , i ) => {
395
396
const name = p . name || `arg${ i } ` ;
396
397
if ( p . storageLocation === 'storage' ) {
397
- const storageType = getVarType ( p , null ) ;
398
+ const storageType = getVarType ( p , context , deref , null ) ;
398
399
const storageVar = 'v_' + storageType . replace ( / [ ^ 0 - 9 a - z A - Z $ _ ] + / g, '_' ) ;
399
400
// The argument is an index to an array in storage.
400
401
return { name, type : 'uint256' , storageVar, storageType } ;
401
402
} else {
402
- const type = getVarType ( p , 'calldata' ) ;
403
+ const type = getVarType ( p , context , deref , 'calldata' ) ;
403
404
return { name, type } ;
404
405
}
405
406
} ) ;
406
407
}
407
408
408
- function getStorageArguments ( fn : FunctionDefinition ) : Required < Argument > [ ] {
409
- return getFunctionArguments ( fn )
409
+ function getStorageArguments ( fn : FunctionDefinition , context : ContractDefinition , deref : ASTDereferencer ) : Required < Argument > [ ] {
410
+ return getFunctionArguments ( fn , context , deref )
410
411
. filter ( ( a ) : a is Required < Argument > => ! ! ( a . storageVar && a . storageType ) ) ;
411
412
}
412
413
413
- function getAllStorageArguments ( fns : FunctionDefinition [ ] ) : Required < Argument > [ ] {
414
+ function getAllStorageArguments ( fns : FunctionDefinition [ ] , context : ContractDefinition , deref : ASTDereferencer ) : Required < Argument > [ ] {
414
415
return [
415
416
...new Map (
416
- fns . flatMap ( getStorageArguments ) . map ( a => [ a . storageVar , a ] ) ,
417
+ fns . flatMap ( fn => getStorageArguments ( fn , context , deref ) ) . map ( a => [ a . storageVar , a ] ) ,
417
418
) . values ( ) ,
418
419
] ;
419
420
}
420
421
421
- function getFunctionReturnParameters ( fnDef : FunctionDefinition , location : StorageLocation | null = 'memory' ) : Argument [ ] {
422
+ function getFunctionReturnParameters ( fnDef : FunctionDefinition , context : ContractDefinition , deref : ASTDereferencer , location : StorageLocation | null = 'memory' ) : Argument [ ] {
422
423
return fnDef . returnParameters . parameters . map ( ( p , i ) => {
423
424
const name = p . name || `ret${ i } ` ;
424
- const type = getVarType ( p , location ) ;
425
+ const type = getVarType ( p , context , deref , location ) ;
425
426
return { name, type } ;
426
427
} ) ;
427
428
}
428
429
429
- function getVarType ( varDecl : VariableDeclaration , location : StorageLocation | null = varDecl . storageLocation ) : string {
430
+ function getVarType ( varDecl : VariableDeclaration , context : ContractDefinition , deref : ASTDereferencer , location : StorageLocation | null = varDecl . storageLocation ) : string {
430
431
if ( ! varDecl . typeName ) {
431
432
throw new Error ( 'Missing type information' ) ;
432
433
}
433
- return getType ( varDecl . typeName , location ) ;
434
+ return getType ( varDecl . typeName , context , deref , location ) ;
434
435
}
435
436
436
- function getType ( typeName : TypeName , location : StorageLocation | null ) : string {
437
+ function getType ( typeName : TypeName , context : ContractDefinition , deref : ASTDereferencer , location : StorageLocation | null ) : string {
437
438
const { typeString, typeIdentifier } = typeName . typeDescriptions ;
438
439
if ( typeof typeString !== 'string' || typeof typeIdentifier !== 'string' ) {
439
440
throw new Error ( 'Missing type information' ) ;
440
441
}
441
- const type = typeString . replace ( / ^ ( s t r u c t | e n u m | c o n t r a c t ) / , '' ) + ( typeIdentifier . endsWith ( '_ptr' ) && location ? ` ${ location } ` : '' ) ;
442
+
443
+ let type = typeString . replace ( / ^ ( s t r u c t | e n u m | c o n t r a c t ) / , '' ) + ( typeIdentifier . endsWith ( '_ptr' ) && location ? ` ${ location } ` : '' ) ;
444
+
445
+ const typeScopeMatch = type . match ( / ^ ( [ a - z A - Z 0 - 9 _ $ ] + ) \. / ) ;
446
+ if ( typeScopeMatch ) {
447
+ const [ , typeScope ] = typeScopeMatch ;
448
+
449
+ const isScopeImplicit = context . linearizedBaseContracts . some ( c => deref ( 'ContractDefinition' , c ) . name === typeScope ) ;
450
+
451
+ if ( isScopeImplicit ) {
452
+ type = type . replace ( `${ typeScope } .` , '' ) ;
453
+ }
454
+ }
455
+
442
456
return type ;
443
457
}
444
458
@@ -458,26 +472,26 @@ function getVariables(contract: ContractDefinition, deref: ASTDereferencer, subs
458
472
return res ;
459
473
}
460
474
461
- function getVarGetterArgs ( v : VariableDeclaration ) : Argument [ ] {
475
+ function getVarGetterArgs ( v : VariableDeclaration , context : ContractDefinition , deref : ASTDereferencer ) : Argument [ ] {
462
476
if ( ! v . typeName ) {
463
477
throw new Error ( 'missing typenName' ) ;
464
478
}
465
479
const types = [ ] ;
466
480
for ( let t = v . typeName ; t . nodeType === 'Mapping' ; t = t . valueType ) {
467
- types . push ( { name : `arg${ types . length } ` , type : getType ( t . keyType , 'memory' ) } )
481
+ types . push ( { name : `arg${ types . length } ` , type : getType ( t . keyType , context , deref , 'memory' ) } )
468
482
}
469
483
return types ;
470
484
}
471
485
472
- function getVarGetterReturnType ( v : VariableDeclaration ) : string {
486
+ function getVarGetterReturnType ( v : VariableDeclaration , context : ContractDefinition , deref : ASTDereferencer ) : string {
473
487
if ( ! v . typeName ) {
474
488
throw new Error ( 'missing typenName' ) ;
475
489
}
476
490
let t = v . typeName ;
477
491
while ( t . nodeType === 'Mapping' ) {
478
492
t = t . valueType ;
479
493
}
480
- return getType ( t , 'memory' ) ;
494
+ return getType ( t , context , deref , 'memory' ) ;
481
495
}
482
496
483
497
function getFunctions ( contract : ContractDefinition , deref : ASTDereferencer , subset ?: Visibility [ ] ) : FunctionDefinition [ ] {
0 commit comments