@@ -174,6 +174,10 @@ function ensureBigInt(
174174 } ) ;
175175}
176176
177+ function isRecord ( x : unknown ) : x is Record < string , unknown > {
178+ return ! ! x && typeof x === 'object' && ! Array . isArray ( x ) ;
179+ }
180+
177181function normalizeContractTuple ( tuple : unknown , index : number ) : GenesisContractDeployment {
178182 if ( ! Array . isArray ( tuple ) || tuple . length < 2 ) {
179183 throw createError ( 'RPC' , {
@@ -191,7 +195,11 @@ function normalizeContractTuple(tuple: unknown, index: number): GenesisContractD
191195 } ;
192196}
193197
194- function normalizeStorageTuple ( tuple : unknown , index : number ) : GenesisStorageEntry {
198+ // Normalizes a "raw" storage entry tuple: [key, value]
199+ function normalizeRawStorageTuple (
200+ tuple : unknown ,
201+ index : number ,
202+ ) : Extract < GenesisStorageEntry , { format : 'raw' } > {
195203 if ( ! Array . isArray ( tuple ) || tuple . length < 2 ) {
196204 throw createError ( 'RPC' , {
197205 resource : 'zksrpc' as Resource ,
@@ -203,11 +211,67 @@ function normalizeStorageTuple(tuple: unknown, index: number): GenesisStorageEnt
203211
204212 const [ keyRaw , valueRaw ] = tuple as [ unknown , unknown ] ;
205213 return {
214+ format : 'raw' as const ,
206215 key : ensureHex ( keyRaw , 'additional_storage.key' , { index } ) ,
207216 value : ensureHex ( valueRaw , 'additional_storage.value' , { index } ) ,
208217 } ;
209218}
210219
220+ // Normalizes additional storage entries from either "raw" or "pretty" format.
221+ function normalizeAdditionalStorage (
222+ value : unknown ,
223+ record : Record < string , unknown > ,
224+ ) : GenesisStorageEntry [ ] {
225+ const effective = value ?? record [ 'additional_storage_raw' ] ;
226+
227+ // Raw tuple format: [[key, value], ...]
228+ if ( Array . isArray ( effective ) ) {
229+ return effective . map ( ( entry , index ) => {
230+ const kv = normalizeRawStorageTuple ( entry , index ) ;
231+ return { format : 'raw' as const , key : kv . key , value : kv . value } ;
232+ } ) ;
233+ }
234+
235+ // Pretty format: { [address]: { [slot]: value } }
236+ if ( isRecord ( effective ) ) {
237+ const out : GenesisStorageEntry [ ] = [ ] ;
238+ for ( const [ addrRaw , slotsRaw ] of Object . entries ( effective ) ) {
239+ const address = ensureHex ( addrRaw , 'additional_storage.address' , { } ) ;
240+
241+ if ( ! isRecord ( slotsRaw ) ) {
242+ throw createError ( 'RPC' , {
243+ resource : 'zksrpc' as Resource ,
244+ operation : 'zksrpc.normalizeGenesis' ,
245+ message : 'Malformed genesis response: additional_storage[address] must be an object map.' ,
246+ context : { address, valueType : typeof slotsRaw } ,
247+ } ) ;
248+ }
249+
250+ for ( const [ slotRaw , valRaw ] of Object . entries ( slotsRaw ) ) {
251+ out . push ( {
252+ format : 'pretty' as const ,
253+ address,
254+ key : ensureHex ( slotRaw , 'additional_storage.key' , { address } ) ,
255+ value : ensureHex ( valRaw , 'additional_storage.value' , { address, key : slotRaw } ) ,
256+ } ) ;
257+ }
258+ }
259+ return out ;
260+ }
261+
262+ throw createError ( 'RPC' , {
263+ resource : 'zksrpc' as Resource ,
264+ operation : 'zksrpc.normalizeGenesis' ,
265+ message :
266+ 'Malformed genesis response: additional_storage must be an array (raw) or an object map (pretty).' ,
267+ context : {
268+ valueType : typeof effective ,
269+ hasAdditionalStorage : 'additional_storage' in record ,
270+ hasAdditionalStorageRaw : 'additional_storage_raw' in record ,
271+ } ,
272+ } ) ;
273+ }
274+
211275// Normalizes the genesis response into camel-cased fields and typed entries.
212276export function normalizeGenesis ( raw : unknown ) : GenesisInput {
213277 try {
@@ -232,23 +296,14 @@ export function normalizeGenesis(raw: unknown): GenesisInput {
232296 } ) ;
233297 }
234298
235- const storageRaw = record [ 'additional_storage' ] ;
236- if ( ! Array . isArray ( storageRaw ) ) {
237- throw createError ( 'RPC' , {
238- resource : 'zksrpc' as Resource ,
239- operation : 'zksrpc.normalizeGenesis' ,
240- message : 'Malformed genesis response: additional_storage must be an array.' ,
241- context : { valueType : typeof storageRaw } ,
242- } ) ;
243- }
244-
245299 const executionVersion = ensureNumber ( record [ 'execution_version' ] , 'execution_version' ) ;
246300 const genesisRoot = ensureHex ( record [ 'genesis_root' ] , 'genesis_root' , { } ) ;
247301
248302 const initialContracts = contractsRaw . map ( ( entry , index ) =>
249303 normalizeContractTuple ( entry , index ) ,
250304 ) ;
251- const additionalStorage = storageRaw . map ( ( entry , index ) => normalizeStorageTuple ( entry , index ) ) ;
305+
306+ const additionalStorage = normalizeAdditionalStorage ( record [ 'additional_storage' ] , record ) ;
252307
253308 return {
254309 initialContracts,
0 commit comments