@@ -22,6 +22,8 @@ private def abiTypeString : ParamType → String
2222 | .tuple _ => "tuple"
2323 | .array t => abiTypeString t ++ "[]"
2424 | .fixedArray t n => abiTypeString t ++ "[" ++ toString n ++ "]"
25+ | .adt _ _ => "tuple" -- ADTs are ABI-encoded as static tuples
26+ | .newtypeOf _ baseType => abiTypeString baseType -- Erased to base type
2527
2628-- Uses `fieldTypeToParamType` from CompilationModel (shared, not duplicated).
2729-- Uses `isInteropEntrypointName` from CompilationModel for consistent filtering.
@@ -39,6 +41,13 @@ mutual
3941 | .tuple elems =>
4042 let rendered := elems.map (fun ty => renderParam "" ty none)
4143 some ("[" ++ String.intercalate ", " rendered ++ "]" )
44+ | .adt _ maxFields =>
45+ -- ADTs encode as (uint8 tag, uint256 field₀, ..., uint256 fieldₙ)
46+ let tagComponent := renderParam "tag" .uint8 none
47+ let fieldComponents := List.range maxFields |>.map fun i =>
48+ renderParam s! "field{ i} " .uint256 none
49+ some ("[" ++ String.intercalate ", " (tagComponent :: fieldComponents) ++ "]" )
50+ | .newtypeOf _ baseType => abiComponents? baseType
4251 | .array t => abiComponents? t
4352 | .fixedArray t _ => abiComponents? t
4453 | _ => none
@@ -126,4 +135,50 @@ def writeContractABIFile (outDir : String) (spec : CompilationModel) : IO Unit :
126135 let path := s! "{ outDir} /{ spec.name} .abi.json"
127136 IO.FS.writeFile path (emitContractABIJson spec)
128137
138+ /-- Render the storage layout for a contract as a JSON object.
139+ Includes EIP-7201 namespace when present (#1730, Axis 4 Step 4d).
140+ The output is a JSON object with `"contract"`, `"storageNamespace"`,
141+ and `"fields"` keys. -/
142+ def emitContractStorageLayoutJson (spec : CompilationModel) : String :=
143+ let nsTerm := match spec.storageNamespace with
144+ | some ns => jsonString (toString ns)
145+ | none => "null"
146+ let fieldEntries := renderFields spec.fields 0
147+ "{" ++ joinJsonFields [
148+ s! "\" contract\" : { jsonString spec.name} " ,
149+ s! "\" storageNamespace\" : { nsTerm} " ,
150+ s! "\" fields\" : [{ String.intercalate ", " fieldEntries} ]"
151+ ] ++ "}\n "
152+ where
153+ renderFieldType : FieldType → String
154+ | .uint256 => "uint256"
155+ | .address => "address"
156+ | .adt name maxFields => s! "adt({ name} ,{ maxFields} )"
157+ | .dynamicArray elemType => renderStorageArrayElemType elemType ++ "[]"
158+ | .mappingTyped _ => "mapping"
159+ | .mappingStruct _ _ => "mapping"
160+ | .mappingStruct2 _ _ _ => "mapping"
161+ renderStorageArrayElemType : StorageArrayElemType → String
162+ | .uint256 => "uint256"
163+ | .address => "address"
164+ | .bool => "bool"
165+ | .uint8 => "uint8"
166+ | .bytes32 => "bytes32"
167+ renderFields (fields : List Field) (idx : Nat) : List String :=
168+ match fields with
169+ | [] => []
170+ | f :: rest =>
171+ let slot := f.slot.getD idx
172+ let entry := "{" ++ joinJsonFields [
173+ s! "\" name\" : { jsonString f.name} " ,
174+ s! "\" slot\" : { jsonString (toString slot)} " ,
175+ s! "\" type\" : { jsonString (renderFieldType f.ty)} "
176+ ] ++ "}"
177+ entry :: renderFields rest (idx + 1 )
178+
179+ def writeContractStorageLayoutFile (outDir : String) (spec : CompilationModel) : IO Unit := do
180+ IO.FS.createDirAll outDir
181+ let path := s! "{ outDir} /{ spec.name} .storage.json"
182+ IO.FS.writeFile path (emitContractStorageLayoutJson spec)
183+
129184end Compiler.ABI
0 commit comments