Created
January 8, 2020 16:42
-
-
Save sydneyitguy/39b85cb9d67b434a0773067c50c9bdaf to your computer and use it in GitHub Desktop.
Ethereum address validation and normalization in Ruby
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Extracted from https://github.com/se3000/ruby-eth | |
# | |
# Dependencies: | |
# - gem 'digest-sha3' | |
# - gem 'rlp' | |
# | |
# Usage: | |
# - Eth::Utils.valid_address?('0x4Db7569F90bd836294B11c8b08B853d2de499Ced') | |
# => true | |
# - Eth::Utils.format_address('0x4db7569f90bd836294b11c8b08b853d2de499ced') | |
# => 0x4Db7569F90bd836294B11c8b08B853d2de499Ced | |
module Eth | |
module Utils | |
extend self | |
def prefix_hex(hex) | |
hex.match(/\A0x/) ? hex : "0x#{hex}" | |
end | |
def bin_to_hex(string) | |
RLP::Utils.encode_hex string | |
end | |
def remove_hex_prefix(s) | |
s[0,2].downcase == '0x' ? s[2..-1] : s | |
end | |
def keccak256(x) | |
Digest::SHA3.new(256).digest(x) | |
end | |
def valid_address?(address) | |
Address.new(address).valid? | |
end | |
def format_address(address) | |
Address.new(address).checksummed | |
end | |
end | |
class Address | |
def initialize(address) | |
@address = Utils.prefix_hex(address) | |
end | |
def valid? | |
if !matches_any_format? | |
false | |
elsif not_checksummed? | |
true | |
else | |
checksum_matches? | |
end | |
end | |
def checksummed | |
raise "Invalid address: #{address}" unless matches_any_format? | |
cased = unprefixed.chars.zip(checksum.chars).map do |char, check| | |
check.match(/[0-7]/) ? char.downcase : char.upcase | |
end | |
Utils.prefix_hex(cased.join) | |
end | |
private | |
attr_reader :address | |
def checksum_matches? | |
address == checksummed | |
end | |
def not_checksummed? | |
all_uppercase? || all_lowercase? | |
end | |
def all_uppercase? | |
address.match(/(?:0[xX])[A-F0-9]{40}/) | |
end | |
def all_lowercase? | |
address.match(/(?:0[xX])[a-f0-9]{40}/) | |
end | |
def matches_any_format? | |
address.match(/\A(?:0[xX])[a-fA-F0-9]{40}\z/) | |
end | |
def checksum | |
Utils.bin_to_hex(Utils.keccak256 unprefixed.downcase) | |
end | |
def unprefixed | |
Utils.remove_hex_prefix address | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment