Skip to content

Commit 58652bc

Browse files
authored
Add EIP-1191 checksum encoding (#174)
* Add EIP-1191 checksum encoding * cleanup
1 parent 0a99433 commit 58652bc

File tree

2 files changed

+69
-7
lines changed

2 files changed

+69
-7
lines changed

lib/ethers/utils.ex

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -237,26 +237,47 @@ defmodule Ethers.Utils do
237237
@doc """
238238
Will convert an upper or lowercase Ethereum address to a checksum address.
239239
240+
If `chain_id` is specified, ERC-1191 checksum encoding will be used.
241+
NOTE: ERC-1191 is generally NOT backwards compatible with ERC-55 encoding
242+
(encoding without `chain_id`).
243+
240244
## Examples
241245
242246
iex> Ethers.Utils.to_checksum_address("0xc1912fee45d61c87cc5ea59dae31190fffff232d")
243247
"0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d"
244248
245249
iex> Ethers.Utils.to_checksum_address("0XC1912FEE45D61C87CC5EA59DAE31190FFFFF232D")
246250
"0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d"
251+
252+
iex> Ethers.Utils.to_checksum_address("0xde709f2102306220921060314715629080e2fb77", 31)
253+
"0xDE709F2102306220921060314715629080e2Fb77"
254+
255+
iex> Ethers.Utils.to_checksum_address("0XDE709F2102306220921060314715629080e2Fb77", 30)
256+
"0xDe709F2102306220921060314715629080e2FB77"
247257
"""
248-
@spec to_checksum_address(Ethers.Types.t_address()) :: Ethers.Types.t_address()
249-
def to_checksum_address("0x" <> address), do: to_checksum_address(address)
250-
def to_checksum_address("0X" <> address), do: to_checksum_address(address)
258+
@spec to_checksum_address(Ethers.Types.t_address(), pos_integer() | nil) ::
259+
Ethers.Types.t_address()
260+
def to_checksum_address(address, chain_id \\ nil)
261+
262+
def to_checksum_address("0x" <> address, chain_id), do: to_checksum_address(address, chain_id)
263+
def to_checksum_address("0X" <> address, chain_id), do: to_checksum_address(address, chain_id)
264+
265+
def to_checksum_address(<<address_bin::binary-20>>, chain_id),
266+
do: hex_encode(address_bin, false) |> to_checksum_address(chain_id)
267+
268+
def to_checksum_address(address, nil), do: calculate_checksum_address(address, address)
251269

252-
def to_checksum_address(<<address_bin::binary-20>>),
253-
do: hex_encode(address_bin) |> to_checksum_address()
270+
def to_checksum_address(address, chain_id) when is_integer(chain_id),
271+
do: calculate_checksum_address(address, "#{chain_id}0x#{address}")
254272

255-
def to_checksum_address(address) do
273+
defp calculate_checksum_address(address, hash_input) do
256274
address = String.downcase(address)
257275

258276
hashed_address =
259-
address |> Ethers.keccak_module().hash_256() |> Base.encode16(case: :lower)
277+
hash_input
278+
|> String.downcase()
279+
|> Ethers.keccak_module().hash_256()
280+
|> Base.encode16(case: :lower)
260281

261282
checksum_address =
262283
address

test/ethers/utils_test.exs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,38 @@ defmodule Ethers.UtilsTest do
33
alias Ethers.Utils
44
doctest Ethers.Utils
55

6+
@rsk_mainnet_addresses [
7+
"0x27b1FdB04752BBc536007A920D24ACB045561c26",
8+
"0x3599689E6292B81B2D85451025146515070129Bb",
9+
"0x42712D45473476B98452f434E72461577d686318",
10+
"0x52908400098527886E0F7030069857D2E4169ee7",
11+
"0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD",
12+
"0x6549F4939460DE12611948B3F82B88C3C8975323",
13+
"0x66F9664f97f2B50F62d13EA064982F936de76657",
14+
"0x8617E340b3D01Fa5f11f306f4090fd50E238070D",
15+
"0x88021160c5C792225E4E5452585947470010289d",
16+
"0xD1220A0Cf47c7B9BE7a2e6ba89F429762E7B9adB",
17+
"0xDBF03B407c01E7CD3cBea99509D93F8Dddc8C6FB",
18+
"0xDe709F2102306220921060314715629080e2FB77",
19+
"0xFb6916095cA1Df60bb79ce92cE3EA74c37c5d359"
20+
]
21+
22+
@rsk_testnet_addresses [
23+
"0x27B1FdB04752BbC536007a920D24acB045561C26",
24+
"0x3599689e6292b81b2D85451025146515070129Bb",
25+
"0x42712D45473476B98452F434E72461577D686318",
26+
"0x52908400098527886E0F7030069857D2e4169EE7",
27+
"0x5aAeb6053F3e94c9b9A09F33669435E7EF1BEaEd",
28+
"0x6549f4939460dE12611948b3f82b88C3c8975323",
29+
"0x66f9664F97F2b50f62d13eA064982F936DE76657",
30+
"0x8617e340b3D01fa5F11f306F4090Fd50e238070d",
31+
"0x88021160c5C792225E4E5452585947470010289d",
32+
"0xd1220a0CF47c7B9Be7A2E6Ba89f429762E7b9adB",
33+
"0xdbF03B407C01E7cd3cbEa99509D93f8dDDc8C6fB",
34+
"0xDE709F2102306220921060314715629080e2Fb77",
35+
"0xFb6916095CA1dF60bb79CE92ce3Ea74C37c5D359"
36+
]
37+
638
describe "get_block_timestamp" do
739
test "returns the block timestamp" do
840
assert {:ok, n} = Ethers.current_block_number()
@@ -101,6 +133,15 @@ defmodule Ethers.UtilsTest do
101133
assert Ethers.Utils.to_checksum_address(bin_address) ==
102134
"0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1"
103135
end
136+
137+
test "does erc-1191 checksum" do
138+
%{30 => @rsk_mainnet_addresses, 31 => @rsk_testnet_addresses}
139+
|> Enum.each(fn {chain_id, addresses} ->
140+
Enum.each(addresses, fn address ->
141+
assert Ethers.Utils.to_checksum_address(address, chain_id) == address
142+
end)
143+
end)
144+
end
104145
end
105146

106147
describe "public_key_to_address/2" do

0 commit comments

Comments
 (0)