Polymarket.Tx (Polymarket v0.2.0)

Copy Markdown View Source

Pure EIP-155 legacy (type-0) Ethereum transaction serialization and signing.

This module covers exactly the surface needed to produce signed raw transaction bytes from caller-provided fields. There is no JSON-RPC, no nonce/gas fetching, and no submission. Callers are expected to broadcast the bytes through their own client.

Scope

  • Legacy type-0 transactions only (RLP-encoded list of 9 fields).
  • EIP-155 chain-id-mixed signature (v = chain_id * 2 + 35 + yParity).
  • No EIP-1559 type-2 (max_priority_fee_per_gas / max_fee_per_gas). Polymarket on Polygon transactions today are routinely sent as type-0 with gasPrice, so this is sufficient for the planned CTF / pUSD on-chain helpers. Type-2 support is deferred until a real consumer needs it.
  • Keccak-256 message hashing via ExKeccak and signing via ExSecp256k1.sign_compact/2 — matches polymarket_clob's EIP-712 signing path.

Output contract

serialize/1 and hash/1 operate on a Polymarket.Tx struct that has its :v, :r, :s fields populated. Call sign/2 to fill them; calling serialize/1 on an unsigned tx raises ArgumentError.

Output is byte-for-byte verified against ethers.Transaction in test/fixtures/tx.json § legacy_transactions.

Fields

  • :nonce — non-negative integer.
  • :gas_price — non-negative integer (wei).
  • :gas_limit — non-negative integer (units of gas).
  • :to0x-prefixed 20-byte hex address, or nil for contract creation.
  • :value — non-negative integer (wei).
  • :data — raw bytes (binary). Pass <<>> (or nil) for empty data.
  • :chain_id — positive integer (137 for Polygon, 1 for Ethereum mainnet).
  • :v, :r, :s — populated by sign/2. nil on an unsigned tx.

Summary

Functions

Returns keccak256(serialize(tx)) — the on-chain transaction hash.

Builds a Polymarket.Tx from required keyword fields.

Returns the RLP-encoded signed raw transaction bytes — the 9-field list [nonce, gas_price, gas_limit, to, value, data, v, r, s].

Signs the transaction using a 32-byte private key (raw binary or 0x-prefixed hex). Returns a new Polymarket.Tx with :v, :r, :s populated.

Returns keccak256(signing_payload(tx)) — the 32-byte digest that is signed under EIP-155.

Returns the RLP-encoded unsigned signing payload — the 9-field list [nonce, gas_price, gas_limit, to, value, data, chain_id, 0, 0]. This is the input to signing_hash/1.

Types

t()

@type t() :: %Polymarket.Tx{
  chain_id: pos_integer(),
  data: binary(),
  gas_limit: non_neg_integer(),
  gas_price: non_neg_integer(),
  nonce: non_neg_integer(),
  r: non_neg_integer() | nil,
  s: non_neg_integer() | nil,
  to: String.t() | nil,
  v: non_neg_integer() | nil,
  value: non_neg_integer()
}

Functions

hash(tx)

@spec hash(t()) :: <<_::256>>

Returns keccak256(serialize(tx)) — the on-chain transaction hash.

new(opts)

@spec new(keyword()) :: t()

Builds a Polymarket.Tx from required keyword fields.

Required: :nonce, :gas_price, :gas_limit, :chain_id. Optional: :to (default nil — contract creation), :value (default 0), :data (default <<>>).

Raises KeyError if a required key is missing, and ArgumentError if an unknown key is passed. Unknown keys are rejected — rather than silently ignored — because a typo in :to (e.g. too: / recipient:) would otherwise leave :to at its nil default and sign an unintended contract-creation transaction.

serialize(tx)

@spec serialize(t()) :: binary()

Returns the RLP-encoded signed raw transaction bytes — the 9-field list [nonce, gas_price, gas_limit, to, value, data, v, r, s].

Raises ArgumentError if tx has not been signed.

sign(tx, private_key)

@spec sign(t(), binary() | String.t()) :: t()

Signs the transaction using a 32-byte private key (raw binary or 0x-prefixed hex). Returns a new Polymarket.Tx with :v, :r, :s populated.

:v follows EIP-155: chain_id * 2 + 35 + yParity, where yParity is the secp256k1 recovery id (0 or 1).

signing_hash(tx)

@spec signing_hash(t()) :: <<_::256>>

Returns keccak256(signing_payload(tx)) — the 32-byte digest that is signed under EIP-155.

signing_payload(tx)

@spec signing_payload(t()) :: binary()

Returns the RLP-encoded unsigned signing payload — the 9-field list [nonce, gas_price, gas_limit, to, value, data, chain_id, 0, 0]. This is the input to signing_hash/1.