Skip to content

Instantly share code, notes, and snippets.

@0x19
Last active June 27, 2023 14:42
Show Gist options
  • Select an option

  • Save 0x19/1a0635d35bcb42f331f7692a424038d5 to your computer and use it in GitHub Desktop.

Select an option

Save 0x19/1a0635d35bcb42f331f7692a424038d5 to your computer and use it in GitHub Desktop.
Golang IsEIP55
package main
import (
"fmt"
"strings"
"testing"
"golang.org/x/crypto/sha3"
)
// IsEIP55 checks if the given Ethereum address is EIP-55 compliant.
// It does this by hashing the lowercase address and then checking if each character in the original address is correctly cased according to the EIP-55 specification.
// If the address is EIP-55 compliant, the function returns nil. Otherwise, it returns an error that provides more information about why the address is not compliant.
func IsEIP55(address string) error {
// Check if the address has a '0x' prefix
if !strings.HasPrefix(address, "0x") {
return fmt.Errorf("address does not start with '0x'")
}
// Remove '0x' prefix
address = address[2:]
// Check if the address is the correct length
if len(address) != 40 {
return fmt.Errorf("address is not the correct length")
}
// Hash the lowercase address
hash := sha3.NewLegacyKeccak256()
hash.Write([]byte(strings.ToLower(address)))
hashedAddress := hash.Sum(nil)
// Iterate over each character in the address
for i := 0; i < len(address); i++ {
if address[i] >= '0' && address[i] <= '9' {
// We can't upper-case the decimal digits
continue
}
// Check if the corresponding hex digit (nibble) in the hash is 8 or higher
hashedValue := hashedAddress[i/2] >> uint(4*(1-i%2)) & 0xf
if hashedValue >= 8 {
if address[i] < 'A' || address[i] > 'F' {
// The character is not upper-case
return fmt.Errorf("character at position %d should be upper-case", i)
}
} else {
if address[i] < 'a' || address[i] > 'f' {
// The character is not lower-case
return fmt.Errorf("character at position %d should be lower-case", i)
}
}
}
return nil
}
func TestIsEIP55(t *testing.T) {
// Define test cases
testCases := []struct {
name string
address string
wantErr bool
}{
{
name: "Valid EIP55 address",
address: "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
wantErr: false,
},
{
name: "Invalid EIP55 address",
address: "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed",
wantErr: true,
},
{
name: "Address without 0x prefix",
address: "5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
wantErr: true,
},
{
name: "Address of incorrect length",
address: "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1Be",
wantErr: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := isEIP55(tc.address)
if (err != nil) != tc.wantErr {
t.Errorf("isEIP55() error = %v, wantErr %v", err, tc.wantErr)
}
})
}
}
func main() {
fmt.Println(IsEIP55("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed")) // Should print: <nil>
fmt.Println(IsEIP55("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed")) // Should print: character at position 1 should be upper-case
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment