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/collateral.ex
defmodule Polymarket.Collateral do
@moduledoc """
Polymarket CollateralOnramp / CollateralOfframp pUSD operations.
Polymarket's v2 collateral lifecycle:
* `wrap(_asset, _to, _amount)` on the CollateralOnramp converts an
approved asset (typically USDC.e on Polygon) into pUSD credited
to `_to`. The caller must `approve` the onramp for `_amount` of
`_asset` first; see `Polymarket.ERC20.approve/2` (or
`approve_calldata/2`).
* `unwrap(_asset, _to, _amount)` on the CollateralOfframp does the
reverse: burns `_amount` of pUSD from the caller and credits the
equivalent `_asset` to `_to`. The caller must `approve` the
offramp for `_amount` of pUSD first.
## Two surfaces
* **Calldata builders** — `wrap_calldata/3` and `unwrap_calldata/3`
produce the raw function-call bytes. Use them when you want the
bytes for inspection, off-line signing, or submission through
your own Polygon RPC client.
* **Submit wrappers** — `wrap/2` and `unwrap/2` build, sign, and
broadcast in one call. Compose the calldata builder +
`Polymarket.Tx` + `Polymarket.RPC`. Caller provides every signing
field (`:nonce`, `:gas_price`, `:gas_limit`, `:chain_id`,
`:private_key`); the wrappers do not auto-fetch from RPC.
## Confirmed v2 contract addresses
Per `V2_HANDOFF.md`, re-verified 2026-04-27:
* pUSD CollateralToken proxy: `0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB`
* CollateralOnramp: `0x93070a847efEf7F70739046A929D47a521F5B8ee`
* CollateralOfframp: `0x2957922Eb93258b93368531d39fAcCA3B4dC5854`
* USDC.e on Polygon: `0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174`
Calldata bytes are validated against the canonical ethers.js v6
encoder in `test/fixtures/calldata.json`. The submit-wrapper
composition (calldata → tx → signed bytes) is verified against a
manually-composed reference in
`test/polymarket/submit_test.exs`.
"""
alias Polymarket.ABI
@wrap_signature "wrap(address,address,uint256)"
@unwrap_signature "unwrap(address,address,uint256)"
@doc """
Builds calldata for `wrap(address _asset, address _to, uint256 _amount)`
on the CollateralOnramp.
"""
@spec wrap_calldata(String.t(), String.t(), non_neg_integer()) :: binary()
def wrap_calldata(asset, to, amount)
when is_binary(asset) and is_binary(to) and is_integer(amount) and amount >= 0 do
ABI.function_selector(@wrap_signature) <>
ABI.encode_address(asset) <>
ABI.encode_address(to) <>
ABI.encode_uint256(amount)
end
@doc """
Builds calldata for `unwrap(address _asset, address _to, uint256 _amount)`
on the CollateralOfframp.
"""
@spec unwrap_calldata(String.t(), String.t(), non_neg_integer()) :: binary()
def unwrap_calldata(asset, to, amount)
when is_binary(asset) and is_binary(to) and is_integer(amount) and amount >= 0 do
ABI.function_selector(@unwrap_signature) <>
ABI.encode_address(asset) <>
ABI.encode_address(to) <>
ABI.encode_uint256(amount)
end
# ── Submit wrappers ──
@doc """
Builds, signs, and broadcasts a `wrap(_asset, _to, _amount)`
transaction to the CollateralOnramp at `:contract`. Returns the
on-chain transaction hash on success.
The caller must have already approved the onramp to spend `:_amount`
of `:_asset` (use `Polymarket.ERC20.approve/2`).
Required `opts`: `:contract`, `:asset`, `:to`, `:amount`, plus
`:nonce`, `:gas_price`, `:gas_limit`, `:chain_id`, `:private_key`.
"""
@spec wrap(Polymarket.RPC.t(), keyword()) :: {:ok, String.t()} | {:error, term()}
def wrap(%Polymarket.RPC{} = rpc, opts) when is_list(opts) do
with :ok <- Polymarket.Submit.require_opts(opts, [:contract, :asset, :to, :amount]) do
calldata =
wrap_calldata(
Keyword.fetch!(opts, :asset),
Keyword.fetch!(opts, :to),
Keyword.fetch!(opts, :amount)
)
Polymarket.Submit.send_call(rpc, Keyword.fetch!(opts, :contract), calldata, opts)
end
end
@doc """
Builds, signs, and broadcasts an `unwrap(_asset, _to, _amount)`
transaction to the CollateralOfframp at `:contract`. Returns the
on-chain transaction hash on success.
The caller must have already approved the offramp to spend `:_amount`
of pUSD.
Required `opts`: same as `wrap/2`.
"""
@spec unwrap(Polymarket.RPC.t(), keyword()) :: {:ok, String.t()} | {:error, term()}
def unwrap(%Polymarket.RPC{} = rpc, opts) when is_list(opts) do
with :ok <- Polymarket.Submit.require_opts(opts, [:contract, :asset, :to, :amount]) do
calldata =
unwrap_calldata(
Keyword.fetch!(opts, :asset),
Keyword.fetch!(opts, :to),
Keyword.fetch!(opts, :amount)
)
Polymarket.Submit.send_call(rpc, Keyword.fetch!(opts, :contract), calldata, opts)
end
end
end