Current section
Files
Jump to
Current section
Files
lib/exw3/utils.ex
defmodule ExW3.Utils do
@type invalid_hex_string :: :invalid_hex_string
@type negative_integer :: :negative_integer
@type non_integer :: :non_integer
@type eth_hex :: String.t()
@doc "Convert eth hex string to integer"
@spec hex_to_integer(eth_hex) ::
{:ok, non_neg_integer} | {:error, invalid_hex_string}
def hex_to_integer(hex) do
case hex do
"0x" <> hex -> {:ok, String.to_integer(hex, 16)}
_ -> {:error, :invalid_hex_string}
end
rescue
ArgumentError ->
{:error, :invalid_hex_string}
end
@doc "Convert an integer to eth hex string"
@spec integer_to_hex(non_neg_integer) ::
{:ok, eth_hex} | {:error, negative_integer | non_integer}
def integer_to_hex(i) do
case i do
i when i < 0 -> {:error, :negative_integer}
i -> {:ok, "0x" <> Integer.to_string(i, 16)}
end
rescue
ArgumentError ->
{:error, :non_integer}
end
@doc "Returns a 0x prepended 32 byte hash of the input string"
@spec keccak256(String.t()) :: String.t()
def keccak256(str) do
"0x#{str |> ExKeccak.hash_256() |> Base.encode16(case: :lower)}"
end
@unit_map %{
:noether => 0,
:wei => 1,
:kwei => 1_000,
:Kwei => 1_000,
:babbage => 1_000,
:femtoether => 1_000,
:mwei => 1_000_000,
:Mwei => 1_000_000,
:lovelace => 1_000_000,
:picoether => 1_000_000,
:gwei => 1_000_000_000,
:Gwei => 1_000_000_000,
:shannon => 1_000_000_000,
:nanoether => 1_000_000_000,
:nano => 1_000_000_000,
:szabo => 1_000_000_000_000,
:microether => 1_000_000_000_000,
:micro => 1_000_000_000_000,
:finney => 1_000_000_000_000_000,
:milliether => 1_000_000_000_000_000,
:milli => 1_000_000_000_000_000,
:ether => 1_000_000_000_000_000_000,
:kether => 1_000_000_000_000_000_000_000,
:grand => 1_000_000_000_000_000_000_000,
:mether => 1_000_000_000_000_000_000_000_000,
:gether => 1_000_000_000_000_000_000_000_000_000,
:tether => 1_000_000_000_000_000_000_000_000_000_000
}
@doc "Converts the value to whatever unit key is provided. See unit map for details."
@spec to_wei(integer, atom) :: integer
def to_wei(num, key) do
if @unit_map[key] do
num * @unit_map[key]
else
throw("#{key} not valid unit")
end
end
@doc "Converts the value to whatever unit key is provided. See unit map for details."
@spec from_wei(integer, atom) :: integer | float | no_return
def from_wei(num, key) do
if @unit_map[key] do
num / @unit_map[key]
else
throw("#{key} not valid unit")
end
end
@doc "Returns a checksummed address conforming to EIP-55"
@spec to_checksum_address(String.t()) :: String.t()
def to_checksum_address(address) do
address = address |> String.downcase() |> String.replace(~r/^0x/, "")
hash_bin = ExKeccak.hash_256(address)
hash =
hash_bin
|> Base.encode16(case: :lower)
|> String.replace(~r/^0x/, "")
keccak_hash_list =
hash
|> String.split("", trim: true)
|> Enum.map(fn x -> elem(Integer.parse(x, 16), 0) end)
list_arr =
for n <- 0..(String.length(address) - 1) do
number = Enum.at(keccak_hash_list, n)
cond do
number >= 8 -> String.upcase(String.at(address, n))
true -> String.downcase(String.at(address, n))
end
end
"0x" <> List.to_string(list_arr)
end
@doc "Checks if the address is a valid checksummed address"
@spec is_valid_checksum_address(String.t()) :: boolean
def is_valid_checksum_address(address) do
ExW3.Utils.to_checksum_address(address) == address
end
@doc "converts Ethereum style bytes to string"
@spec bytes_to_string(binary()) :: binary()
def bytes_to_string(bytes) do
bytes
|> Base.encode16(case: :lower)
|> String.replace_trailing("0", "")
|> Base.decode16!(case: :lower)
end
@doc "Converts an Ethereum address into a form that can be used by the ABI encoder"
@spec format_address(binary()) :: integer()
def format_address(address) do
address
|> String.slice(2..-1)
|> Base.decode16!(case: :lower)
|> :binary.decode_unsigned()
end
@doc "Converts bytes to Ethereum address"
@spec to_address(binary()) :: binary()
def to_address(bytes) do
Enum.join(["0x", bytes |> Base.encode16(case: :lower)], "")
end
end