From a72ca3b4f69ee35472a6679cab708230b208619d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dobaczewski?= Date: Mon, 20 Nov 2023 16:36:53 +0100 Subject: [PATCH] ParseSignatures should add a number to duplicated names --- abi/contract.go | 22 +++++++++++++++++++--- abi/contract_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/abi/contract.go b/abi/contract.go index 7ba0604..34b0e86 100644 --- a/abi/contract.go +++ b/abi/contract.go @@ -208,6 +208,9 @@ func (a *ABI) MustParseJSON(data []byte) *Contract { // ParseSignatures parses list of signatures and returns a Contract instance. // Signatures must be prefixed with the kind, e.g. "constructor" or "event". // For functions, the "function" prefix can be omitted. +// +// In case of duplicate function, event or error names, a counter will be +// appended to the name starting from 2. func (a *ABI) ParseSignatures(signatures ...string) (*Contract, error) { c := &Contract{ Methods: make(map[string]*Method), @@ -268,7 +271,7 @@ func (a *ABI) ParseSignatures(signatures ...string) (*Contract, error) { if err != nil { return nil, err } - c.Methods[method.Name()] = method + appendWithCounter(c.Methods, method.Name(), method) c.MethodsBySignature[method.Signature()] = method case sigparser.EventSignatureInput: sig, err := sigparser.ParseSignatureAs(sigparser.EventKind, s) @@ -279,7 +282,7 @@ func (a *ABI) ParseSignatures(signatures ...string) (*Contract, error) { if err != nil { return nil, err } - c.Events[event.Name()] = event + appendWithCounter(c.Events, event.Name(), event) case sigparser.ErrorSignatureInput: sig, err := sigparser.ParseSignatureAs(sigparser.ErrorKind, s) if err != nil { @@ -289,7 +292,7 @@ func (a *ABI) ParseSignatures(signatures ...string) (*Contract, error) { if err != nil { return nil, err } - c.Errors[errsig.Name()] = errsig + appendWithCounter(c.Errors, errsig.Name(), errsig) default: return nil, fmt.Errorf("invalid signature: %s", s) } @@ -553,3 +556,16 @@ func parseInternalType(typ string) (int, string, string) { } return kind, intName, intNamespace } + +// appendWithCounter appends a value to a map. If the key already exists, it +// will be suffixed with a number. +func appendWithCounter[T any](m map[string]T, key string, value T) { + nextKey := key + for i := 0; ; i++ { + if _, ok := m[nextKey]; !ok { + m[nextKey] = value + return + } + nextKey = key + strconv.Itoa(i+2) + } +} diff --git a/abi/contract_test.go b/abi/contract_test.go index c2a8f24..69f2e15 100644 --- a/abi/contract_test.go +++ b/abi/contract_test.go @@ -59,6 +59,7 @@ func TestABI_ParseSignatures(t *testing.T) { `constructor(CustomUint a)`, `function Foo(CustomUint a) nonpayable returns (CustomUint)`, `function Bar(Struct[2][2] a) nonpayable returns (uint8[2][2])`, + `function Bar()`, // Should be added as Bar2 because Bar already exists ) require.NoError(t, err) @@ -72,6 +73,7 @@ func TestABI_ParseSignatures(t *testing.T) { require.NotNil(t, abi.Constructor) require.NotNil(t, abi.Methods["Foo"]) require.NotNil(t, abi.Methods["Bar"]) + require.NotNil(t, abi.Methods["Bar2"]) assert.Equal(t, "Status", abi.Types["Status"].String()) assert.Equal(t, "Struct", abi.Types["Struct"].String()) @@ -277,3 +279,29 @@ func Fuzz_parseArrays(f *testing.F) { _, _, _ = parseArrays(typ) }) } + +func Test_appendWithCounter(t *testing.T) { + t.Run("add same key", func(t *testing.T) { + m := make(map[string]int) + + appendWithCounter(m, "test", 1) + appendWithCounter(m, "test", 2) + appendWithCounter(m, "test", 3) + assert.Equal(t, 3, len(m)) + assert.Equal(t, 1, m["test"]) + assert.Equal(t, 2, m["test2"]) + assert.Equal(t, 3, m["test3"]) + }) + + t.Run("add same key with number", func(t *testing.T) { + m := make(map[string]int) + + appendWithCounter(m, "test", 1) + appendWithCounter(m, "test", 2) + appendWithCounter(m, "test2", 3) + assert.Equal(t, 3, len(m)) + assert.Equal(t, 1, m["test"]) + assert.Equal(t, 2, m["test2"]) + assert.Equal(t, 3, m["test22"]) + }) +}