Cartouche.Hex (Cartouche v0.2.0)

Copy Markdown View Source

Helper module for parsing and encoding hex values.

If you use Cartouche.Hex, then you can use the ~h sigil for compile-time hex-to-binary compilation.

Summary

Functions

Checksums an Ethereum address per EIP-55.

Parses an Ethereum 20-bytes hex string.

Parses a hex string, but returns :error instead of raising if hex is invalid.

Parses a hex string and raises if invalid.

Decodes hex, allowing it to either be "0x..." or a raw binary.

Parses hex value as a big-endian integer.

Parses hex value as a big-endian integer. Raises if invalid.

Parses hex is value is not nil, otherwise returns nil.

Parses a hex value as a big-endian integer if not nil, otherwise returns nil.

Parses an Ethereum x-bytes hex string.

Parses an Ethereum 32-bytes hex string.

Encodes a binary as a checksummed Ethereum address.

Encodes hex, in CAPITALS.

Encodes a number as a binary of a fixed byte length, left-padded with zeros.

Encodes a given value as a lowercase hex string, starting with 0x.

If input is a tuple {:ok, x} then returns a tuple {:ok, hex} where hex = encode(x). Otherwise, returns its input unchanged.

Encodes a non-negative integer as a JSON-RPC "quantity" string.

Encodes hex, striping any leading zeros.

Alias for decode_hex.

Alias for decode_hex!.

Similar non-sigil compile-time hex parser.

If input is non-nil, returns input encoded as a hex string. Otherwise, returns nil.

Returns the nibbles of a binary as a list.

Pads a binary to a given length.

Handles the sigil ~h for list of words.

Alias for encode_address.

Alias for encode_hex.

Types

t()

@type t() :: binary()

Functions

checksum_address(address)

@spec checksum_address(String.t() | <<_::160>>) :: String.t()

Checksums an Ethereum address per EIP-55.

The result is a string-encoded (mixed-case) version of the address.

Examples

iex> Cartouche.Hex.checksum_address("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed")
"0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"

iex> Cartouche.Hex.checksum_address("0xFB6916095CA1DF60BB79CE92CE3EA74C37C5D359")
"0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"

iex> Cartouche.Hex.checksum_address("0xdbf03b407c01e7cd3cbea99509d93f8dddc8c6fb")
"0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB"

iex> Cartouche.Hex.checksum_address("0xd1220a0cf47c7b9be7a2e6ba89f429762e7b9adb")
"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb"

decode_address!(hex)

@spec decode_address!(String.t()) :: t() | no_return()

Parses an Ethereum 20-bytes hex string.

Identical to decode_hex!/1 except fails if string is not exactly 20-bytes.

Examples

iex> Cartouche.Hex.decode_address!("0x0000000000000000000000000000000000000001") <<1::160>>

iex> Cartouche.Hex.decode_address!("0xaabb") ** (Cartouche.Hex.InvalidHex) invalid hex address: "0xaabb"

decode_hex(b)

@spec decode_hex(String.t()) :: {:ok, t()} | :invalid_hex

Parses a hex string, but returns :error instead of raising if hex is invalid.

Examples

iex> Cartouche.Hex.decode_hex("0xaabb")
{:ok, <<170, 187>>}

iex> Cartouche.Hex.decode_hex("aabb")
{:ok, <<170, 187>>}

iex> Cartouche.Hex.decode_hex("0xgggg")
:invalid_hex

decode_hex!(b)

@spec decode_hex!(String.t()) :: t()

Parses a hex string and raises if invalid.

Examples

iex> Cartouche.Hex.decode_hex!("aabb") <<170, 187>>

iex> Cartouche.Hex.decode_hex!("0xggaabb") ** (Cartouche.Hex.InvalidHex) invalid hex: "0xggaabb"

decode_hex_input!(hex)

@spec decode_hex_input!(String.t() | binary()) :: t()

Decodes hex, allowing it to either be "0x..." or a raw binary.

Note: a hex-printed string, in this case, must start with 0x,

  otherwise it will be interpreted as its ASCII values.

Examples

iex> Cartouche.Hex.decode_hex_input!("0x55")
<<0x55>>

iex> Cartouche.Hex.decode_hex_input!(<<0x55>>)
<<0x55>>

decode_hex_number(b)

@spec decode_hex_number(String.t()) :: {:ok, integer()} | :invalid_hex

Parses hex value as a big-endian integer.

Examples

iex> Cartouche.Hex.decode_hex_number("0xaabb")
{:ok, 0xaabb}

iex> Cartouche.Hex.decode_hex_number("0xgggg")
:invalid_hex

decode_hex_number!(b)

@spec decode_hex_number!(String.t()) :: integer() | no_return()

Parses hex value as a big-endian integer. Raises if invalid.

Examples

iex> Cartouche.Hex.decode_hex_number!("0xaabb") 0xaabb

iex> Cartouche.Hex.decode_hex_number!("0xgggg") ** (Cartouche.Hex.InvalidHex) invalid hex number: "0xgggg"

decode_maybe_hex!(h)

@spec decode_maybe_hex!(String.t() | nil) :: t() | nil

Parses hex is value is not nil, otherwise returns nil.

Examples

iex> Cartouche.Hex.decode_maybe_hex!("0xaabb") <<170, 187>>

iex> Cartouche.Hex.decode_maybe_hex!(nil) nil

decode_maybe_hex_number!(b)

@spec decode_maybe_hex_number!(String.t() | nil) :: integer() | nil | no_return()

Parses a hex value as a big-endian integer if not nil, otherwise returns nil.

Useful for JSON-RPC fields that are absent on pre-fork blocks (e.g. blobGasUsed / blobGasPrice on pre-Cancun receipts).

Examples

iex> Cartouche.Hex.decode_maybe_hex_number!("0xaabb") 0xaabb

iex> Cartouche.Hex.decode_maybe_hex_number!(nil) nil

decode_sized!(hex, sz, msg \\ nil)

@spec decode_sized!(String.t(), integer(), String.t() | nil) :: t() | no_return()

Parses an Ethereum x-bytes hex string.

Identical to decode_hex!/1 except fails if string is not exactly x-bytes.

Examples

iex> Cartouche.Hex.decode_sized!("0x001122", 3) <<0x00, 0x11, 0x22>>

iex> Cartouche.Hex.decode_sized!("0xaabb", 3) ** (Cartouche.Hex.InvalidHex) invalid 3-byte sized hex: "0xaabb"

decode_word!(hex)

@spec decode_word!(String.t()) :: t() | no_return()

Parses an Ethereum 32-bytes hex string.

Identical to decode_hex!/1 except fails if string is not exactly 32-bytes.

Examples

iex> Cartouche.Hex.decode_word!("0x0000000000000000000000000000000000000000000000000000000000000001") <<1::256>>

iex> Cartouche.Hex.decode_word!("0xaabb") ** (Cartouche.Hex.InvalidHex) invalid hex word: "0xaabb"

encode_address(b)

@spec encode_address(t()) :: String.t()

Encodes a binary as a checksummed Ethereum address.

Examples

iex> Cartouche.Hex.encode_address(<<0xaa, 0xbb, 0xcc, 0::136>>) "0xaABbcC0000000000000000000000000000000000"

iex> Cartouche.Hex.encode_address(<<55>>) ** (Cartouche.Hex.InvalidHex) Expected 20-byte address for in Cartouche.Hex.encode_address/1

encode_big_hex(hex)

@spec encode_big_hex(binary()) :: String.t()

Encodes hex, in CAPITALS.

Examples

iex> Cartouche.Hex.encode_big_hex(<<0xcc, 0xdd>>) "0xCCDD"

encode_bytes(b, size)

@spec encode_bytes(integer() | nil, pos_integer()) :: binary() | nil

Encodes a number as a binary of a fixed byte length, left-padded with zeros.

Examples

iex> Cartouche.Hex.encode_bytes(257, 4)
<<0, 0, 1, 1>>

iex> Cartouche.Hex.encode_bytes(nil, 4)
nil

encode_hex(b)

@spec encode_hex(t()) :: String.t()

Encodes a given value as a lowercase hex string, starting with 0x.

Examples

iex> Cartouche.Hex.encode_hex(<<0xaa, 0xbb>>) "0xaabb"

encode_hex_result(els)

@spec encode_hex_result({:ok, t()} | term()) :: {:ok, String.t()} | term()

If input is a tuple {:ok, x} then returns a tuple {:ok, hex} where hex = encode(x). Otherwise, returns its input unchanged.

Examples

iex> Cartouche.Hex.encode_hex_result({:ok, <<0xaa, 0xbb>>})
{:ok, "0xaabb"}

iex> Cartouche.Hex.encode_hex_result({:error, 55})
{:error, 55}

encode_quantity(n)

@spec encode_quantity(non_neg_integer()) :: String.t()

Encodes a non-negative integer as a JSON-RPC "quantity" string.

Lowercase hex with 0x prefix and no leading zeros. 0 becomes "0x0".

This matches the JSON-RPC spec for the QUANTITY type used in eth_getBlockByNumber, eth_getBalance, eth_call block params, etc.

Examples

iex> Cartouche.Hex.encode_quantity(0)
"0x0"

iex> Cartouche.Hex.encode_quantity(55)
"0x37"

iex> Cartouche.Hex.encode_quantity(24_975_978)
"0x17d1a6a"

encode_short_hex(hex)

@spec encode_short_hex(binary() | integer()) :: String.t()

Encodes hex, striping any leading zeros.

Examples

iex> Cartouche.Hex.encode_short_hex(<<0xc>>) "0xC"

iex> Cartouche.Hex.encode_short_hex(12) "0xC"

iex> Cartouche.Hex.encode_short_hex(<<0x0>>) "0x0"

from_hex(b)

@spec from_hex(String.t()) :: {:ok, t()} | :invalid_hex

Alias for decode_hex.

Examples

iex> Cartouche.Hex.from_hex("0xaabb")
{:ok, <<0xaa, 0xbb>>}

iex> Cartouche.Hex.from_hex("0xgggg")
:invalid_hex

from_hex!(b)

@spec from_hex!(String.t()) :: t()

Alias for decode_hex!.

Examples

iex> Cartouche.Hex.from_hex!("0xaabb") <<0xaa, 0xbb>>

iex> Cartouche.Hex.from_hex!("0xggaabb") ** (Cartouche.Hex.InvalidHex) invalid hex: "0xggaabb"

hex!(hex_str)

(macro)

Similar non-sigil compile-time hex parser.

Examples

iex> use Cartouche.Hex
iex> hex!("0x22")
<<0x22>>

iex> use Cartouche.Hex
iex> hex!("0x2244")
<<0x22, 0x44>>

maybe_encode_hex(b)

@spec maybe_encode_hex(t() | nil) :: String.t() | nil

If input is non-nil, returns input encoded as a hex string. Otherwise, returns nil.

Examples

iex> Cartouche.Hex.maybe_encode_hex(<<0xaa, 0xbb>>) "0xaabb"

iex> Cartouche.Hex.maybe_encode_hex(nil) nil

nibbles(v)

@spec nibbles(binary()) :: [0..15]

Returns the nibbles of a binary as a list.

Examples

iex> Cartouche.Hex.nibbles(<<0xF5, 0xE6, 0xD0>>)
[0xF, 0x5, 0xE, 0x6, 0xD, 0x0]

pad(bin, size)

@spec pad(binary(), pos_integer()) :: binary()

Pads a binary to a given length.

Examples

iex> Cartouche.Hex.pad(<<1, 2>>, 2)
<<1, 2>>

iex> Cartouche.Hex.pad(<<1, 2>>, 4)
<<0, 0, 1, 2>>

iex> Cartouche.Hex.pad(<<1, 2>>, 1)
** (FunctionClauseError) no function clause matching in Cartouche.Hex.pad/2

sigil_h(term, modifiers)

(macro)

Handles the sigil ~h for list of words.

Parses a hex string at compile-time.

Examples

iex> use Cartouche.Hex
iex> ~h[0x22]
<<0x22>>

iex> use Cartouche.Hex
iex> ~h[0x2244]
<<0x22, 0x44>>

to_address(b)

@spec to_address(t()) :: String.t()

Alias for encode_address.

Examples

iex> Cartouche.Hex.to_address(<<0xaa, 0xbb, 0xcc, 0::136>>) "0xaABbcC0000000000000000000000000000000000"

to_hex(b)

@spec to_hex(t()) :: String.t()

Alias for encode_hex.

Examples

iex> Cartouche.Hex.to_hex(<<0xaa, 0xbb>>) "0xaabb"