import assert from 'assert' ; import { ContractDefinition , StructDefinition , EnumDefinition , TypeDescriptions } from 'solidity-ast' ; import { isNodeType , findAll } from 'solidity-ast/utils' ; import { StorageLayout , TypeItem } from './layout' ; import { normalizeTypeIdentifier } from '../utils/type-id' ; import { SrcDecoder } from '../src-decoder' ; import { ASTDereferencer } from '../ast-dereferencer' ; const currentLayoutVersion = '1.1' ; export function isCurrentLayoutVersion ( layout ) { return layout ?. layoutVersion === currentLayoutVersion ; } export function extractStorageLayout ( contractDef , decodeSrc , deref ) { const layout = { storage : [ ] , types : { } , layoutVersion : currentLayoutVersion } ; const derefUserDefinedType = deref ( [ 'StructDefinition' , 'EnumDefinition' ] ) ; for ( const varDecl of contractDef . nodes ) { if ( isNodeType ( 'VariableDeclaration' , varDecl ) ) { if ( ! varDecl . constant && varDecl . mutability !== 'immutable' ) { const type = normalizeTypeIdentifier ( typeDescriptions ( varDecl ) . typeIdentifier ) ; layout . storage . push ( { contract : contractDef . name , label : varDecl . name , type , src : decodeSrc ( varDecl ) , } ) ; assert ( varDecl . typeName != null ) ; const typeNames = new Map ( [ ... findTypeNames ( varDecl . typeName ) ] . map ( n => [ typeDescriptions ( n ) . typeIdentifier , n ] ) ) ; for ( const typeName of typeNames . values ( ) ) { const { typeIdentifier , typeString : label } = typeDescriptions ( typeName ) ; const type = normalizeTypeIdentifier ( typeIdentifier ) ; if ( type in layout . types ) { continue ; } let members ; if ( 'referencedDeclaration' in typeName && ! /^t_contract\b/ . test ( type ) ) { const typeDef = derefUserDefinedType ( typeName . referencedDeclaration ) ; members = getTypeMembers ( typeDef ) ; for ( const typeName of findTypeNames ( typeDef ) ) { const { typeIdentifier } = typeDescriptions ( typeName ) ; if ( ! typeNames . has ( typeIdentifier ) ) { typeNames . set ( typeIdentifier , typeName ) ; } } } layout . types [ type ] = { label , members } ; } } } } return layout ; } const findTypeNames = findAll ( [ 'ArrayTypeName' , 'ElementaryTypeName' , 'FunctionTypeName' , 'Mapping' , 'UserDefinedTypeName' , ] ) ; interface RequiredTypeDescriptions { typeIdentifier : string ; typeString : string ; } function typeDescriptions ( x ) { assert ( typeof x . typeDescriptions . typeIdentifier === 'string' ) ; assert ( typeof x . typeDescriptions . typeString === 'string' ) ; return x . typeDescriptions as RequiredTypeDescriptions ; } function getTypeMembers ( typeDef ) { if ( typeDef . nodeType === 'StructDefinition' ) { return typeDef . members . map ( m => { assert ( typeof m . typeDescriptions . typeIdentifier === 'string' ) ; return { label : m . name , type : normalizeTypeIdentifier ( m . typeDescriptions . typeIdentifier ) , } ; } ) ; } else { return typeDef . members . map ( m => m . name ) ; } }