Current section
Files
Jump to
Current section
Files
lib/clob/deposit_wallet.ex
defmodule Polymarket.Clob.DepositWallet do
@moduledoc """
Derives a wallet's **deposit-wallet** address — the contract wallet Polymarket
trades through on a user's behalf.
Polymarket does not let a bare EOA be an order's `maker`; orders are made by a
deposit wallet deterministically derived from the EOA via `CREATE2`. Two kinds
exist, matching how the account was created:
* a **proxy** wallet (EIP-1167 minimal proxy) for Magic/email accounts —
`signatureType` `:proxy`; and
* a **Gnosis Safe** (1-of-1) for browser-wallet accounts — `:gnosis_safe`.
To place such an order, set the order's `maker` to the derived address, keep
`signer` as the EOA, and sign with the matching `signature_type`.
Addresses are derived as `keccak256(0xff ‖ factory ‖ salt ‖ initCodeHash)[12..]`,
with the factory contracts and init-code hashes taken from the reference
`rs-clob-client-v2`. Proxy wallets exist only on Polygon; Safe wallets on both
Polygon and Amoy.
"""
alias Polymarket.Crypto
@polygon 137
@amoy 80_002
@proxy_factory %{
@polygon => Base.decode16!("aB45c5A4B0c941a2F231C04C3f49182e1A254052", case: :mixed)
}
@safe_factory Base.decode16!("aacFeEa03eb1561C4e67d661e40682Bd20E3541b", case: :mixed)
@proxy_init_code_hash Base.decode16!(
"d21df8dc65880a8606f09fe0ce3df9b8869287ab0b058be05aa9e8af6330a00b",
case: :lower
)
@safe_init_code_hash Base.decode16!(
"2bce2127ff07fb632d16c8347c4ebf501f4841168bed00d9e6ef715ddb6fcecf",
case: :lower
)
@doc """
Derives the Polymarket **proxy** wallet (`signatureType` `:proxy`) for `eoa` on
`chain_id`.
The CREATE2 salt is `keccak256(eoa)` (the 20 address bytes, unpadded). Proxy
wallets are only deployed on Polygon (137); other chains return `:error`.
"""
@spec derive_proxy_wallet(<<_::160>>, integer()) :: {:ok, <<_::160>>} | :error
def derive_proxy_wallet(<<_::160>> = eoa, chain_id) when is_integer(chain_id) do
case Map.fetch(@proxy_factory, chain_id) do
{:ok, factory} -> {:ok, create2(factory, Crypto.keccak256(eoa), @proxy_init_code_hash)}
:error -> :error
end
end
@doc """
Derives the Polymarket **Gnosis Safe** wallet (`signatureType` `:gnosis_safe`)
for `eoa` on `chain_id`.
The CREATE2 salt is `keccak256(eoa left-padded to 32 bytes)`. Safe wallets are
configured on Polygon (137) and Amoy (80002); other chains return `:error`.
"""
@spec derive_safe_wallet(<<_::160>>, integer()) :: {:ok, <<_::160>>} | :error
def derive_safe_wallet(<<_::160>> = eoa, chain_id) when chain_id in [@polygon, @amoy] do
salt = Crypto.keccak256(<<0::96, eoa::binary>>)
{:ok, create2(@safe_factory, salt, @safe_init_code_hash)}
end
def derive_safe_wallet(<<_::160>>, _chain_id), do: :error
# CREATE2 address: the low 20 bytes of keccak256(0xff ‖ factory ‖ salt ‖ hash).
@spec create2(<<_::160>>, <<_::256>>, <<_::256>>) :: <<_::160>>
defp create2(factory, salt, init_code_hash) do
<<_::binary-size(12), address::binary-size(20)>> =
Crypto.keccak256([<<0xFF>>, factory, salt, init_code_hash])
address
end
end