@@ -182,22 +182,47 @@ def get_input_dict_contracts(input_dict: Dict) -> ContractCodes:
182182
183183def get_input_dict_interfaces (input_dict : Dict ) -> Dict :
184184 interface_sources : Dict = {}
185+
185186 for path , value in input_dict .get ("interfaces" , {}).items ():
186187 key = _standardize_path (path )
188+
187189 if key .endswith (".json" ):
188- if "abi" not in value :
190+ # EthPM Manifest v3 (EIP-2678)
191+ if "contractTypes" in value :
192+ for name , ct in value ["contractTypes" ].items ():
193+ if name in interface_sources :
194+ raise JSONError (f"Interface namespace collision: { name } " )
195+
196+ interface_sources [name ] = {"type" : "json" , "code" : ct ["abi" ]}
197+
198+ continue # Skip to next interface
199+
200+ # ABI JSON file (`{"abi": List[ABI]}`)
201+ elif "abi" in value :
202+ interface = {"type" : "json" , "code" : value ["abi" ]}
203+
204+ # ABI JSON file (`List[ABI]`)
205+ elif isinstance (value , list ):
206+ interface = {"type" : "json" , "code" : value }
207+
208+ else :
189209 raise JSONError (f"Interface '{ path } ' must have 'abi' field" )
190- interface = { "type" : "json" , "code" : value [ "abi" ]}
210+
191211 elif key .endswith (".vy" ):
192212 if "content" not in value :
193213 raise JSONError (f"Interface '{ path } ' must have 'content' field" )
214+
194215 interface = {"type" : "vyper" , "code" : value ["content" ]}
216+
195217 else :
196218 raise JSONError (f"Interface '{ path } ' must have suffix '.vy' or '.json'" )
219+
197220 key = key .rsplit ("." , maxsplit = 1 )[0 ]
198221 if key in interface_sources :
199222 raise JSONError (f"Interface namespace collision: { key } " )
223+
200224 interface_sources [key ] = interface
225+
201226 return interface_sources
202227
203228
@@ -248,6 +273,11 @@ def get_interface_codes(
248273 code = contract_sources [contract_path ]
249274 interface_codes = extract_file_interface_imports (code )
250275 for interface_name , interface_path in interface_codes .items ():
276+ # If we know the interfaces already (e.g. EthPM Manifest file)
277+ if interface_name in interface_sources :
278+ interfaces [interface_name ] = interface_sources [interface_name ]
279+ continue
280+
251281 path = Path (contract_path ).parent .joinpath (interface_path ).as_posix ()
252282 keys = [_standardize_path (path )]
253283 if not interface_path .startswith ("." ):
@@ -281,7 +311,29 @@ def get_interface_codes(
281311 with valid_path .open () as fh :
282312 code = fh .read ()
283313 if valid_path .suffix == ".json" :
284- interfaces [interface_name ] = {"type" : "json" , "code" : json .loads (code .encode ())}
314+ code_dict = json .loads (code .encode ())
315+ # EthPM Manifest v3 (EIP-2678)
316+ if "contractTypes" in code_dict :
317+ if interface_name not in code_dict ["contractTypes" ]:
318+ raise JSONError (f"'{ interface_name } ' not found in '{ valid_path } '" )
319+
320+ if "abi" not in code_dict ["contractTypes" ][interface_name ]:
321+ raise JSONError (f"Missing abi for '{ interface_name } ' in '{ valid_path } '" )
322+
323+ abi = code_dict ["contractTypes" ][interface_name ]["abi" ]
324+ interfaces [interface_name ] = {"type" : "json" , "code" : abi }
325+
326+ # ABI JSON (`{"abi": List[ABI]}`)
327+ elif "abi" in code_dict :
328+ interfaces [interface_name ] = {"type" : "json" , "code" : code_dict ["abi" ]}
329+
330+ # ABI JSON (`List[ABI]`)
331+ elif isinstance (code_dict , list ):
332+ interfaces [interface_name ] = {"type" : "json" , "code" : code_dict }
333+
334+ else :
335+ raise JSONError (f"Unexpected type in file: '{ valid_path } '" )
336+
285337 else :
286338 interfaces [interface_name ] = {"type" : "vyper" , "code" : code }
287339
0 commit comments