Hand-rolled Solidity ABI primitives for the fixed on-chain surfaces in this SDK.
This module covers exactly what Polymarket.ERC20, Polymarket.CTF,
and Polymarket.Collateral need: function selectors, fixed-size types
(address, uint256, bytes32), and the uint256[] dynamic-array
tail. It is intentionally narrow — there is no general-purpose ABI
decoder, no support for bytes/string/nested tuples, and no
ex_abi / ethereumex / ethers framework dep (per DECISIONS.md
#4 — extra dep weight previously cost the live bot ~10 seconds of
startup latency).
Calldata produced by these functions is validated byte-for-byte
against ethers.js v6 fixtures in test/fixtures/calldata.json.
Encoding rules summary
- Function selector — first 4 bytes of
keccak256(signature). uint256— 32-byte big-endian unsigned.address— 20-byte address right-aligned in a 32-byte word (zero-padded on the left).bytes32— 32 bytes inline.uint256[]— dynamic. The function head emits a 32-byte offset pointer (the byte position of the array's tail relative to the start of the args, post-selector). The tail at that offset is[length :: uint256][element0 :: uint256]…. The caller is responsible for placing the offset in the head; this module providesencode_uint256_array_tail/1for the tail bytes.
Summary
Functions
Encodes a 20-byte hex Ethereum address as a 32-byte right-aligned word (12 bytes of zero padding + 20 address bytes).
Encodes a 32-byte hex value as 32 bytes inline.
Encodes a non-negative integer as 32-byte big-endian unsigned.
Returns the dynamic tail of a uint256[] argument: a 32-byte length
word followed by each element as 32-byte big-endian unsigned.
Returns the 4-byte function selector for a Solidity signature like
"approve(address,uint256)".
Hex-encodes raw calldata for human display or fixture comparison.
Always lowercase, always 0x-prefixed.
Functions
@spec encode_address(String.t()) :: <<_::256>>
Encodes a 20-byte hex Ethereum address as a 32-byte right-aligned word (12 bytes of zero padding + 20 address bytes).
Accepts the address with or without a leading 0x and in any case.
EIP-55 checksum is preserved on input but the output is the canonical
Solidity representation (lowercase nibbles, byte-equivalent regardless
of input case).
@spec encode_bytes32(String.t()) :: <<_::256>>
Encodes a 32-byte hex value as 32 bytes inline.
Accepts 0x-prefixed or bare hex (any case). Used for bytes32
parameters such as parentCollectionId and conditionId.
@spec encode_uint256(non_neg_integer()) :: <<_::256>>
Encodes a non-negative integer as 32-byte big-endian unsigned.
@spec encode_uint256_array_tail([non_neg_integer()]) :: binary()
Returns the dynamic tail of a uint256[] argument: a 32-byte length
word followed by each element as 32-byte big-endian unsigned.
The caller must separately place the head offset — typically
head_size bytes (i.e. number of static head words × 32) — using
encode_uint256/1.
@spec function_selector(String.t()) :: <<_::32>>
Returns the 4-byte function selector for a Solidity signature like
"approve(address,uint256)".
Hex-encodes raw calldata for human display or fixture comparison.
Always lowercase, always 0x-prefixed.