diff --git a/pkg/evm/key.go b/pkg/evm/key.go index d6adad1..da5398c 100644 --- a/pkg/evm/key.go +++ b/pkg/evm/key.go @@ -327,10 +327,8 @@ func saveEVMSigner(path string, s *EVMSigner) error { Address: s.addr.String(), CreatedAt: time.Now().UTC().Format(time.RFC3339), } - body, err := json.MarshalIndent(&f, "", " ") - if err != nil { - return err - } + // identityFile is all strings — json.MarshalIndent cannot fail. + body, _ := json.MarshalIndent(&f, "", " ") tmp := path + ".tmp" if err := os.WriteFile(tmp, body, 0o600); err != nil { return err diff --git a/pkg/evm/rpc_test.go b/pkg/evm/rpc_test.go index 9406345..ebb652c 100644 --- a/pkg/evm/rpc_test.go +++ b/pkg/evm/rpc_test.go @@ -182,3 +182,33 @@ func TestHexHelpersRoundtrip(t *testing.T) { } } } + +// TestHexHelpers_ErrorPaths covers each rejection branch of hexToUint64 +// and hexToBigInt: missing 0x prefix, non-hex characters, value too +// large for uint64. +func TestHexHelpers_ErrorPaths(t *testing.T) { + t.Parallel() + if _, err := hexToUint64("no-prefix"); err == nil { + t.Error("hexToUint64 should reject input without 0x prefix") + } + if _, err := hexToUint64("0xZZZZ"); err == nil { + t.Error("hexToUint64 should reject non-hex characters") + } + // 17-digit hex value overflows uint64. + if _, err := hexToUint64("0x10000000000000000"); err == nil { + t.Error("hexToUint64 should reject value too large for uint64") + } + if _, err := hexToBigInt("no-prefix"); err == nil { + t.Error("hexToBigInt should reject input without 0x prefix") + } + if _, err := hexToBigInt("0xZZZZ"); err == nil { + t.Error("hexToBigInt should reject non-hex characters") + } + // Empty-after-prefix returns 0 cleanly for both. + if got, err := hexToUint64("0x"); err != nil || got != 0 { + t.Errorf("hexToUint64(0x) = (%d, %v), want (0, nil)", got, err) + } + if got, err := hexToBigInt("0x"); err != nil || got == nil || got.Sign() != 0 { + t.Errorf("hexToBigInt(0x) = (%v, %v), want (0, nil)", got, err) + } +} diff --git a/pkg/wallet/signer.go b/pkg/wallet/signer.go index db2135c..13ab2b1 100644 --- a/pkg/wallet/signer.go +++ b/pkg/wallet/signer.go @@ -139,10 +139,8 @@ func saveLocalSigner(path string, s *LocalSigner) error { Pubkey: hex.EncodeToString(s.pub), CreatedAt: time.Now().UTC().Format(time.RFC3339), } - body, err := json.MarshalIndent(&f, "", " ") - if err != nil { - return fmt.Errorf("marshal: %w", err) - } + // identityFile is all strings — json.MarshalIndent cannot fail. + body, _ := json.MarshalIndent(&f, "", " ") // Write to a tmp then rename — never leave a half-written identity file. tmp := path + ".tmp" if err := os.WriteFile(tmp, body, 0o600); err != nil { diff --git a/pkg/wallet/zz_helpers_test.go b/pkg/wallet/zz_helpers_test.go index 30f50ea..6f3d717 100644 --- a/pkg/wallet/zz_helpers_test.go +++ b/pkg/wallet/zz_helpers_test.go @@ -172,3 +172,25 @@ func TestMemoryStore_IsSettled_DefaultFalse(t *testing.T) { t.Error("IsSettled fresh: want false") } } + +// TestEVMAccessors_WithBinding drives the non-nil-evm branch of +// EVMAddress / EVMChainID / EVMToken so coverage isn't pinned at the +// nil-binding shortcut path alone. +func TestEVMAccessors_WithBinding(t *testing.T) { + w := newDualWallet(t) // binds an EVM signer + chain id + defer w.Close() + addr := w.EVMAddress() + zero := [20]byte{} + if addr == zero { + t.Errorf("EVMAddress with binding should be non-zero, got zero") + } + if got := w.EVMChainID(); got == 0 { + t.Errorf("EVMChainID with binding should be non-zero, got 0") + } + // EVMToken on a dual wallet may legitimately be zero (mock method), + // just confirm the call doesn't panic and returns 20 bytes. + tok := w.EVMToken() + if len(tok) != 20 { + t.Errorf("EVMToken: got %d bytes, want 20", len(tok)) + } +}