# `Cartouche.Typed`
[🔗](https://github.com/zenhive/cartouche/blob/main/lib/cartouche/typed.ex#L1)

Module to build EIP-712 typed data, which can then be signed or recovered from.

# `t`

```elixir
@type t() :: %Cartouche.Typed{
  domain: Cartouche.Typed.Domain.t(),
  types: type_map(),
  value: value_map()
}
```

# `type_map`

```elixir
@type type_map() :: %{required(String.t()) =&gt; Cartouche.Typed.Type.t()}
```

# `value_map`

```elixir
@type value_map() :: %{required(String.t()) =&gt; term()}
```

# `deserialize`

```elixir
@spec deserialize(%{}) :: t()
```

Deserializes a Typed value from JSON or a map into a struct.

## Examples
    iex> %{
    ...>   "domain" => %{
    ...>     "name" => "Ether Mail",
    ...>     "version" => "1",
    ...>     "chainId" => 1,
    ...>     "verifyingContract" => "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
    ...>   },
    ...>   "types" => %{
    ...>     "Person" => [
    ...>       %{
    ...>         "name" => "name",
    ...>         "type" => "string"
    ...>       },
    ...>       %{
    ...>         "name" => "wallet",
    ...>         "type" => "address"
    ...>       },
    ...>     ],
    ...>     "Mail" => [
    ...>       %{
    ...>         "name" => "from",
    ...>         "type" => "Person"
    ...>       },
    ...>       %{
    ...>         "name" => "to",
    ...>         "type" => "Person"
    ...>       },
    ...>       %{
    ...>         "name" => "contents",
    ...>         "type" => "string"
    ...>       },
    ...>     ]
    ...>   },
    ...>   "value" => %{
    ...>     "from" => %{ "name" => "Cow", "wallet" => "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" },
    ...>     "to" => %{ "name" => "Bob", "wallet" => "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" },
    ...>     "contents" => "Hello, Bob!"
    ...>   }
    ...> }
    ...> |> Cartouche.Typed.deserialize()
    %Cartouche.Typed{
      domain: %Cartouche.Typed.Domain{
        chain_id: 1,
        name: "Ether Mail",
        verifying_contract: ~h[CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC],
        version: "1"
      },
      types: %{
        "Mail" => %Cartouche.Typed.Type{fields: [{"from", "Person"}, {"to", "Person"}, {"contents", :string}]},
        "Person" => %Cartouche.Typed.Type{fields: [{"name", :string}, {"wallet", :address}]}
      },
      value: %{
        "contents" => "Hello, Bob!",
        "from" => %{
          "name" => "Cow",
          "wallet" => ~h[CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826]
        },
        "to" => %{
          "name" => "Bob",
          "wallet" => ~h[BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB]
        }
      }
    }

# `domain_seperator`

```elixir
@spec domain_seperator(t()) :: binary()
```

Builds a domain struct for a given type, per the EIP-712 spec.

## Examples

    iex> %Cartouche.Typed{
    ...>   domain: %Cartouche.Typed.Domain{
    ...>     chain_id: 1,
    ...>     name: "Ether Mail",
    ...>     verifying_contract: ~h[0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC],
    ...>     version: "1"
    ...>   },
    ...>   types: %{
    ...>     "Mail" => %Cartouche.Typed.Type{fields: [{"from", "Person"}, {"to", "Person"}, {"contents", :string}]},
    ...>     "Person" => %Cartouche.Typed.Type{fields: [{"name", :string}, {"wallet", :address}]}
    ...>   },
    ...>   value: %{
    ...>     "contents" => "Hello, Bob!",
    ...>     "from" => %{
    ...>       "name" => "Cow",
    ...>       "wallet" => ~h[0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826]
    ...>     },
    ...>     "to" => %{
    ...>       "name" => "Bob",
    ...>       "wallet" => ~h[0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB]
    ...>     }
    ...>   }
    ...> }
    ...> |> Cartouche.Typed.domain_seperator()
    ...> |> to_hex()
    "0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f"

# `encode`

```elixir
@spec encode(t()) :: binary()
```

Encodes a given typed value such that it can be signed or recovered.

## Examples

    iex> %Cartouche.Typed{
    ...>   domain: %Cartouche.Typed.Domain{
    ...>     chain_id: 1,
    ...>     name: "Ether Mail",
    ...>     verifying_contract: ~h[0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC],
    ...>     version: "1"
    ...>   },
    ...>   types: %{
    ...>     "Mail" => %Cartouche.Typed.Type{fields: [{"from", "Person"}, {"to", "Person"}, {"contents", :string}]},
    ...>     "Person" => %Cartouche.Typed.Type{fields: [{"name", :string}, {"wallet", :address}]}
    ...>   },
    ...>   value: %{
    ...>     "contents" => "Hello, Bob!",
    ...>     "from" => %{
    ...>       "name" => "Cow",
    ...>       "wallet" => ~h[0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826]
    ...>     },
    ...>     "to" => %{
    ...>       "name" => "Bob",
    ...>       "wallet" => ~h[0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB]
    ...>     }
    ...>   }
    ...> }
    ...> |> Cartouche.Typed.encode()
    ...> |> to_hex()
    "0x1901f2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090fc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e"

    iex> %Cartouche.Typed{
    ...>   domain: %Cartouche.Typed.Domain{
    ...>     chain_id: 1,
    ...>     name: "Complex Array",
    ...>     verifying_contract: ~h[0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC],
    ...>     version: "1"
    ...>   },
    ...>   types: %{
    ...>     "Array" => %Cartouche.Typed.Type{fields: [{"a", {:uint, 256}}, {"b", {:uint, 256}}, {"c", :string}, {"d", {:array, :bytes}}]}
    ...>   },
    ...>   value: %{
    ...>     "a" => 55,
    ...>     "b" => 66,
    ...>     "c" => "Hello",
    ...>     "d" => [<<0x11, 0x22>>, <<0x33, 0x44>>]
    ...>   }
    ...> }
    ...> |> Cartouche.Typed.encode()
    ...> |> to_hex()
    "0x190103bd1627b4c5f7540c63d7ee347524dcef247eed29c833dd3b1455b8dec4009fcc95538bfc3f979ca59d9ef7de5ed402a4e403857b3de87d1fc8ed4a2a7cddd9"

    iex> %Cartouche.Typed{
    ...>   domain: %Cartouche.Typed.Domain{
    ...>     name: "Complex Array",
    ...>     version: "1"
    ...>   },
    ...>   types: %{
    ...>     "Array" => %Cartouche.Typed.Type{fields: [{"a", {:uint, 256}}, {"b", {:uint, 256}}, {"c", :string}, {"d", {:array, :bytes}}]}
    ...>   },
    ...>   value: %{
    ...>     "a" => 55,
    ...>     "b" => 66,
    ...>     "c" => "Hello",
    ...>     "d" => [<<0x11, 0x22>>, <<0x33, 0x44>>]
    ...>   }
    ...> }
    ...> |> Cartouche.Typed.encode()
    ...> |> to_hex()
    "0x1901f4806c1a9dae718712eca4906bfca239a3a4a6dea2e9b9a1284fee5ff4df4b1ccc95538bfc3f979ca59d9ef7de5ed402a4e403857b3de87d1fc8ed4a2a7cddd9"

    iex> %Cartouche.Typed{
    ...>   domain: %Cartouche.Typed.Domain{
    ...>     name: "Complex Array",
    ...>     version: "1"
    ...>   },
    ...>   types: %{
    ...>     "Array" => %Cartouche.Typed.Type{fields: [{"a", {:uint, 256}}, {"b", {:uint, 256}}, {"c", :string}, {"d", :bool}]}
    ...>   },
    ...>   value: %{
    ...>     "a" => 55,
    ...>     "b" => 66,
    ...>     "c" => "Hello",
    ...>     "d" => true
    ...>   }
    ...> }
    ...> |> Cartouche.Typed.encode()
    ...> |> to_hex()
    "0x1901f4806c1a9dae718712eca4906bfca239a3a4a6dea2e9b9a1284fee5ff4df4b1c8c56315a01fe3937526fe8c2b472b7e9e1c21728f6c14d5ffb0e0c156f74aca0"

# `encode_type`

```elixir
@spec encode_type(String.t(), type_map()) :: String.t()
```

Encodes the struct type per EIP-712. For this, we basically build an ABI-style value
like `Mail(Person from,Person to,string contents)`, but then to that we need to append
any other types we've seen, like:

`Mail(Person from,Person to,string contents)Person(string name,address wallet)`.

This is a tail-call optimized implementation to build the types then track and append types that need to be added.

## Examples

    iex> Cartouche.Typed.encode_type("Mail", %{
    ...>   "Mail" => %Cartouche.Typed.Type{fields: [{"from", "Person"}, {"to", "Person"}, {"contents", :string}]},
    ...>   "Person" => %Cartouche.Typed.Type{fields: [{"name", :string}, {"wallet", :address}]}
    ...> })
    "Mail(Person from,Person to,string contents)Person(string name,address wallet)"

# `hash_struct`

```elixir
@spec hash_struct(String.t(), value_map(), type_map()) :: binary()
```

Hashes a struct value, per the EIP-712 spec.

## Examples

    iex> types = %{
    ...>   "Mail" => %Cartouche.Typed.Type{fields: [{"from", "Person"}, {"to", "Person"}, {"contents", :string}]},
    ...>   "Person" => %Cartouche.Typed.Type{fields: [{"name", :string}, {"wallet", :address}]}
    ...> }
    ...> value = %{
    ...>   "contents" => "Hello, Bob!",
    ...>   "from" => %{
    ...>     "name" => "Cow",
    ...>     "wallet" => ~h[0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826]
    ...>   },
    ...>   "to" => %{
    ...>     "name" => "Bob",
    ...>     "wallet" => ~h[0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB]
    ...>   }
    ...> }
    iex> to_hex(Cartouche.Typed.hash_struct("Mail", value, types))
    "0xc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e"

# `serialize`

```elixir
@spec serialize(t()) :: %{}
```

Serializes a Typed value, such that it can be passed to JSON or JavaScript.

## Examples
    iex> %Cartouche.Typed{
    ...>   domain: %Cartouche.Typed.Domain{
    ...>     chain_id: 1,
    ...>     name: "Ether Mail",
    ...>     verifying_contract: ~h[CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC],
    ...>     version: "1"
    ...>   },
    ...>   types: %{
    ...>     "Mail" => %Cartouche.Typed.Type{fields: [{"from", "Person"}, {"to", "Person"}, {"contents", :string}]},
    ...>     "Person" => %Cartouche.Typed.Type{fields: [{"name", :string}, {"wallet", :address}]}
    ...>   },
    ...>   value: %{
    ...>     "contents" => "Hello, Bob!",
    ...>     "from" => %{
    ...>       "name" => "Cow",
    ...>       "wallet" => ~h[CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826]
    ...>     },
    ...>     "to" => %{
    ...>       "name" => "Bob",
    ...>       "wallet" => ~h[BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB]
    ...>     }
    ...>   }
    ...> }
    ...> |> Cartouche.Typed.serialize()
    %{
      "domain" => %{
        "name" => "Ether Mail",
        "version" => "1",
        "chainId" => 1,
        "verifyingContract" => "0xcccccccccccccccccccccccccccccccccccccccc"
      },
      "types" => %{
        "Person" => [
          %{
            "name" => "name",
            "type" => "string"
          },
          %{
            "name" => "wallet",
            "type" => "address"
          },
        ],
        "Mail" => [
          %{
            "name" => "from",
            "type" => "Person"
          },
          %{
            "name" => "to",
            "type" => "Person"
          },
          %{
            "name" => "contents",
            "type" => "string"
          },
        ]
      },
      "value" => %{
        "from" => %{ "name" => "Cow", "wallet" => "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826" },
        "to" => %{ "name" => "Bob", "wallet" => "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" },
        "contents" => "Hello, Bob!"
      }
    }

---

*Consult [api-reference.md](api-reference.md) for complete listing*
