@@ -7,11 +7,9 @@ import (
77 "log"
88 "os"
99 "path/filepath"
10- "sort"
1110 "strings"
1211
1312 "github.com/ethereum-optimism/optimism/packages/contracts-bedrock/scripts/checks/common"
14- "github.com/google/go-cmp/cmp"
1513)
1614
1715var excludeContracts = []string {
@@ -130,7 +128,7 @@ func processFile(artifactPath string) (*common.Void, []error) {
130128 return nil , []error {fmt .Errorf ("failed to compare ABIs: %w" , err )}
131129 }
132130 if ! match {
133- return nil , []error {fmt .Errorf ("%s: Differences found in ABI between interface and actual contract" , contractName )}
131+ return nil , []error {fmt .Errorf ("differences found" )}
134132 }
135133
136134 return nil , nil
@@ -229,53 +227,115 @@ func normalizeInternalType(internalType string) string {
229227}
230228
231229func compareABIs (abi1 , abi2 json.RawMessage ) (bool , error ) {
232- var data1 , data2 []map [string ]interface {}
230+ var interfaceABI , contractABI []map [string ]interface {}
233231
234- if err := json .Unmarshal (abi1 , & data1 ); err != nil {
235- return false , fmt .Errorf ("error unmarshalling first ABI: %w" , err )
232+ if err := json .Unmarshal (abi1 , & interfaceABI ); err != nil {
233+ return false , fmt .Errorf ("error unmarshalling interface ABI: %w" , err )
236234 }
237235
238- if err := json .Unmarshal (abi2 , & data2 ); err != nil {
239- return false , fmt .Errorf ("error unmarshalling second ABI: %w" , err )
236+ if err := json .Unmarshal (abi2 , & contractABI ); err != nil {
237+ return false , fmt .Errorf ("error unmarshalling contract ABI: %w" , err )
240238 }
241239
242- // Sort the ABI data
243- sort .Slice (data1 , func (i , j int ) bool {
244- return abiItemLess (data1 [i ], data1 [j ])
245- })
246- sort .Slice (data2 , func (i , j int ) bool {
247- return abiItemLess (data2 [i ], data2 [j ])
248- })
240+ // Create maps for easier lookup
241+ interfaceItems := make (map [string ]map [string ]interface {})
242+ contractItems := make (map [string ]map [string ]interface {})
243+
244+ // Helper to create a unique key for each ABI item
245+ makeKey := func (item map [string ]interface {}) string {
246+ itemType := getString (item , "type" )
247+ itemName := getString (item , "name" )
248+ inputs , _ := json .Marshal (item ["inputs" ])
249+ outputs , _ := json .Marshal (item ["outputs" ])
250+ return fmt .Sprintf ("%s_%s_%s_%s" , itemType , itemName , inputs , outputs )
251+ }
249252
250- // Compare using go-cmp
251- diff := cmp .Diff (data1 , data2 )
252- if diff != "" {
253- log .Printf ("ABI diff: %s" , diff )
254- return false , nil
253+ // Build lookup maps
254+ for _ , item := range interfaceABI {
255+ key := makeKey (item )
256+ interfaceItems [key ] = item
257+ }
258+ for _ , item := range contractABI {
259+ key := makeKey (item )
260+ contractItems [key ] = item
255261 }
256- return true , nil
257- }
258262
259- func abiItemLess (a , b map [string ]interface {}) bool {
260- aType := getString (a , "type" )
261- bType := getString (b , "type" )
263+ // Check for missing items in both directions
264+ isMatch := true
265+
266+ // Check interface items exist in contract
267+ for key , item := range interfaceItems {
268+ if _ , exists := contractItems [key ]; ! exists {
269+ itemType := getString (item , "type" )
270+ signature := formatABIItem (item )
271+ log .Printf ("REMOVE %s from interface: %s" , itemType , signature )
272+ isMatch = false
273+ }
274+ }
262275
263- if aType != bType {
264- return aType < bType
276+ // Check contract items exist in interface
277+ for key , item := range contractItems {
278+ if _ , exists := interfaceItems [key ]; ! exists {
279+ itemType := getString (item , "type" )
280+ signature := formatABIItem (item )
281+ log .Printf ("ADD %s to interface: %s" , itemType , signature )
282+ isMatch = false
283+ }
265284 }
266285
267- aName := getString (a , "name" )
268- bName := getString (b , "name" )
269- return aName < bName
286+ return isMatch , nil
270287}
271288
272- func getString (m map [string ]interface {}, key string ) string {
273- if v , ok := m [key ]; ok {
274- if s , ok := v .(string ); ok {
275- return s
289+ // Helper function to format ABI item into a readable signature
290+ func formatABIItem (item map [string ]interface {}) string {
291+ itemType := getString (item , "type" )
292+ itemName := getString (item , "name" )
293+
294+ // Format inputs
295+ inputs , _ := item ["inputs" ].([]interface {})
296+ inputStr := make ([]string , 0 , len (inputs ))
297+ for _ , input := range inputs {
298+ if inputMap , ok := input .(map [string ]interface {}); ok {
299+ paramType := getString (inputMap , "type" )
300+ paramName := getString (inputMap , "name" )
301+ if paramName != "" {
302+ inputStr = append (inputStr , fmt .Sprintf ("%s %s" , paramType , paramName ))
303+ } else {
304+ inputStr = append (inputStr , paramType )
305+ }
276306 }
277307 }
278- return ""
308+
309+ // Format outputs
310+ outputs , _ := item ["outputs" ].([]interface {})
311+ outputStr := make ([]string , 0 , len (outputs ))
312+ for _ , output := range outputs {
313+ if outputMap , ok := output .(map [string ]interface {}); ok {
314+ paramType := getString (outputMap , "type" )
315+ paramName := getString (outputMap , "name" )
316+ if paramName != "" {
317+ outputStr = append (outputStr , fmt .Sprintf ("%s %s" , paramType , paramName ))
318+ } else {
319+ outputStr = append (outputStr , paramType )
320+ }
321+ }
322+ }
323+
324+ // Build the signature based on the item type
325+ switch itemType {
326+ case "function" :
327+ returnStr := ""
328+ if len (outputStr ) > 0 {
329+ returnStr = fmt .Sprintf (" returns (%s)" , strings .Join (outputStr , ", " ))
330+ }
331+ return fmt .Sprintf ("function %s(%s)%s" , itemName , strings .Join (inputStr , ", " ), returnStr )
332+ case "event" :
333+ return fmt .Sprintf ("event %s(%s)" , itemName , strings .Join (inputStr , ", " ))
334+ case "constructor" :
335+ return fmt .Sprintf ("constructor(%s)" , strings .Join (inputStr , ", " ))
336+ default :
337+ return fmt .Sprintf ("%s %s(%s)" , itemType , itemName , strings .Join (inputStr , ", " ))
338+ }
279339}
280340
281341func isExcluded (contractName string ) bool {
@@ -286,3 +346,13 @@ func isExcluded(contractName string) bool {
286346 }
287347 return false
288348}
349+
350+ // getString safely retrieves a string value from a map[string]interface{}
351+ func getString (m map [string ]interface {}, key string ) string {
352+ if val , ok := m [key ]; ok {
353+ if str , ok := val .(string ); ok {
354+ return str
355+ }
356+ }
357+ return ""
358+ }
0 commit comments