Packages
polymarket_sdk
0.2.0
Higher-level Elixir SDK for Polymarket: Gamma market discovery, Data API, CTF/pUSD helpers, and convenience facades. Depends on polymarket_clob for the low-level CLOB surface.
Current section
Files
Jump to
Current section
Files
lib/polymarket/erc20.ex
defmodule Polymarket.ERC20 do
@moduledoc """
ERC-20 helpers for the SDK's on-chain surface.
The SDK currently needs only one ERC-20 function — `approve` — to
authorize the CollateralOnramp / CollateralOfframp and the CTF
contract to spend tokens on the caller's behalf.
## Two surfaces
* **Calldata builder** — `approve_calldata/2` produces the raw
function-call bytes for `approve(spender, amount)`. Use this when
you want the bytes for inspection, off-line signing, or submission
through your own Polygon RPC client.
* **Submit wrapper** — `approve/2` builds, signs, and broadcasts the
transaction in one call. Composes `approve_calldata/2` +
`Polymarket.Tx` + `Polymarket.RPC`. Caller provides every signing
field (`:nonce`, `:gas_price`, `:gas_limit`, `:chain_id`,
`:private_key`); the wrapper does not auto-fetch from RPC.
Calldata bytes are validated against the canonical ethers.js v6
encoder in `test/fixtures/calldata.json`. The submit wrapper's
composition (calldata → tx → signed bytes) is verified against a
manually-composed reference in
`test/polymarket/submit_test.exs`.
No transaction-submission policy is baked in. Confirmation polling,
retry on `nonce too low`, and EIP-1559 type-2 fees are out of scope
here.
"""
alias Polymarket.ABI
@approve_signature "approve(address,uint256)"
@doc """
Builds calldata for `approve(address spender, uint256 amount)`.
## Examples
iex> Polymarket.ERC20.approve_calldata(
...> "0x0000000000000000000000000000000000000001",
...> 1_000
...> )
...> |> Polymarket.ABI.to_hex()
"0x095ea7b3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000003e8"
"""
@spec approve_calldata(String.t(), non_neg_integer()) :: binary()
def approve_calldata(spender, amount)
when is_binary(spender) and is_integer(amount) and amount >= 0 do
ABI.function_selector(@approve_signature) <>
ABI.encode_address(spender) <>
ABI.encode_uint256(amount)
end
@doc """
Builds, signs, and broadcasts an `approve(spender, amount)` transaction
to the ERC-20 token at `:contract`. Returns the on-chain transaction
hash on success.
## Required `opts`
* `:contract` — the ERC-20 token address being approved (e.g. pUSD).
* `:spender` — the address being authorized (e.g. CTF or onramp).
* `:amount` — non-negative integer, in the token's smallest unit.
* `:nonce`, `:gas_price`, `:gas_limit`, `:chain_id` — caller-provided
transaction fields. The wrapper does not auto-fetch from RPC; use
`Polymarket.RPC.nonce/2`, `gas_price/1`, and `estimate_gas/2`
first if you need that.
* `:private_key` — 32-byte private key (raw binary or `0x`-prefixed
hex) for the sender.
Returns `{:error, {:missing_option, key}}` when a required option is
absent, honoring the `{:ok, _} | {:error, _}` contract instead of
raising.
"""
@spec approve(Polymarket.RPC.t(), keyword()) :: {:ok, String.t()} | {:error, term()}
def approve(%Polymarket.RPC{} = rpc, opts) when is_list(opts) do
with :ok <- Polymarket.Submit.require_opts(opts, [:contract, :spender, :amount]) do
contract = Keyword.fetch!(opts, :contract)
calldata = approve_calldata(Keyword.fetch!(opts, :spender), Keyword.fetch!(opts, :amount))
Polymarket.Submit.send_call(rpc, contract, calldata, opts)
end
end
end