Skip to content

Commit

Permalink
Bring in checksum eth addresses (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
tzumby authored Dec 19, 2023
1 parent 44d81d1 commit cd86643
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 7 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## v1.0.1

* Brings in Ethereum EIP-55 checksum addresses, credit goes to @wchenNL

## v1.0.0

**Breaking Changes**
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Add the dependency to your `mix.exs`:
```
def deps do
[
{:block_keys, "~> 1.0.0"}
{:block_keys, "~> 1.0.1"}
]
end
```
Expand Down
48 changes: 44 additions & 4 deletions lib/block_keys/ethereum/address.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
defmodule BlockKeys.Ethereum.Address do
@moduledoc """
Converts a public extended key into an Ethereum Address
Follows EIP-55 checksum address encoding: https://eips.ethereum.org/EIPS/eip-55
"""

alias BlockKeys.{Encoding, Crypto}
Expand All @@ -9,10 +10,19 @@ defmodule BlockKeys.Ethereum.Address do
xpub
|> maybe_decode()
|> decompress()
|> keccak256()
|> ExKeccak.hash_256()
|> to_address()
end

def valid_address?("0x" <> address = full_address) when byte_size(address) == 40 do
case to_checksum_address(full_address) do
{:ok, checksum} -> checksum == full_address
_ -> false
end
end

def valid_address?(_address), do: false

defp maybe_decode(<<"xpub", _rest::binary>> = encoded_key) do
decoded_key =
encoded_key
Expand All @@ -31,9 +41,39 @@ defmodule BlockKeys.Ethereum.Address do
end

defp to_address(<<_::binary-12, address::binary-20>>) do
"0x"
|> Kernel.<>(address |> Base.encode16(case: :lower))
{:ok, checksum_address} =
"0x"
|> Kernel.<>(address |> Base.encode16(case: :lower))
|> to_checksum_address()

checksum_address
end

defp keccak256(data), do: ExKeccak.hash_256(data)
defp to_checksum_address("0x" <> address) when byte_size(address) == 40 do
address = String.downcase(address)

checksum =
Enum.zip(
String.graphemes(address),
String.graphemes(address |> ExKeccak.hash_256() |> Base.encode16(case: :lower))
)
|> Enum.map_join(fn {each_address, each_hash} ->
cond do
String.match?(each_address, ~r/[0-9]/) ->
each_address

String.match?(each_address, ~r/[a-f]/) and elem(Integer.parse(each_hash, 16), 0) >= 8 ->
String.upcase(each_address)

String.match?(each_address, ~r/[a-f]/) ->
each_address
end
end)

{:ok, "0x" <> checksum}
end

defp to_checksum_address(_address) do
{:error, "unrecognized address"}
end
end
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule BlockKeys.MixProject do
def project do
[
app: :block_keys,
version: "1.0.0",
version: "1.0.1",
elixir: "~> 1.7",
description: description(),
start_permanent: Mix.env() == :prod,
Expand Down
2 changes: 1 addition & 1 deletion test/block_keys/ethereum/address_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ defmodule EthereumAddressTest do

assert CKD.derive(root_key, "M/44'/60'/0'/0/0")
|> Address.from_xpub() ==
"0x73bb50c828fd325c011d740fde78d02528826156"
"0x73Bb50c828fD325c011d740fDe78d02528826156"
end
end

0 comments on commit cd86643

Please sign in to comment.