package polymarket
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
OCaml client library for the Polymarket prediction market API
Install
dune-project
Dependency
Authors
Maintainers
Sources
0.2.0.tar.gz
md5=4eb4c5d2f63ff081c9713d90be5a51b2
sha512=0e3de0c9b40683e09ab8f9f966a44784ef1b9b482c3eefef84104a7e8042c92f1d79893ee9588b24fa3d0decaed7f365509f4d1c23c66ce8328efb64e721f276
doc/src/polymarket.common/crypto.ml.html
Source file crypto.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172(** Cryptographic utilities for Polymarket API authentication. This module provides EIP-712 signing, HMAC-SHA256, and Ethereum address derivation for API authentication. *) type private_key = string let private_key_of_string s = s let private_key_to_string s = s (** {1 Hashing} *) let keccak256 data = let hash = Digestif.KECCAK_256.digest_string data in "0x" ^ Digestif.KECCAK_256.to_hex hash let keccak256_hex hex_str = let bytes = Hex.to_string (`Hex hex_str) in keccak256 bytes (** {1 HMAC-SHA256} *) let hmac_sha256 ~key message = Digestif.SHA256.(to_raw_string (hmac_string ~key message)) let sign_l2_request ~secret ~timestamp ~method_ ~path ~body = (* Decode base64 secret *) let key = Base64.decode_exn secret in (* Construct message: timestamp + method + path + body *) let message = timestamp ^ method_ ^ path ^ body in (* Compute HMAC-SHA256 *) let signature = hmac_sha256 ~key message in (* Encode result as base64 *) Base64.encode_exn signature (** {1 EIP-712 Helpers} *) (** Pad hex string to 64 chars (32 bytes) with leading zeros *) let pad_hex_32 hex = let len = String.length hex in if len >= 64 then hex else String.make (64 - len) '0' ^ hex (** Encode uint256 as 32-byte hex *) let encode_uint256 n = let hex = Printf.sprintf "%x" n in pad_hex_32 hex (** EIP-712 type hash for domain: keccak256("EIP712Domain(string name,string version,uint256 chainId)") *) let domain_type_hash = let type_string = "EIP712Domain(string name,string version,uint256 chainId)" in let hash = Digestif.KECCAK_256.digest_string type_string in Digestif.KECCAK_256.to_hex hash (** EIP-712 type hash for ClobAuth: keccak256("ClobAuth(address address,string timestamp,uint256 nonce,string message)") *) let clob_auth_type_hash = let type_string = "ClobAuth(address address,string timestamp,uint256 nonce,string message)" in let hash = Digestif.KECCAK_256.digest_string type_string in Digestif.KECCAK_256.to_hex hash (** Compute EIP-712 domain separator *) let compute_domain_separator () = (* Encode: typeHash + keccak256(name) + keccak256(version) + chainId *) let name_hash = Digestif.KECCAK_256.(to_hex (digest_string Constants.clob_domain_name)) in let version_hash = Digestif.KECCAK_256.(to_hex (digest_string Constants.clob_domain_version)) in let chain_id_hex = encode_uint256 Constants.polygon_chain_id in let data = domain_type_hash ^ name_hash ^ version_hash ^ chain_id_hex in let bytes = Hex.to_string (`Hex data) in Digestif.KECCAK_256.(to_hex (digest_string bytes)) (** Compute struct hash for ClobAuth message *) let compute_clob_auth_hash ~address ~timestamp ~nonce = (* Remove 0x prefix from address if present *) let addr_hex = if String.length address > 2 && String.sub address 0 2 = "0x" then String.sub address 2 (String.length address - 2) else address in (* Pad address to 32 bytes (left-padded with zeros) *) let addr_padded = pad_hex_32 addr_hex in (* Hash the string fields *) let timestamp_hash = Digestif.KECCAK_256.(to_hex (digest_string timestamp)) in let message_hash = Digestif.KECCAK_256.(to_hex (digest_string Constants.auth_message_text)) in let nonce_hex = encode_uint256 nonce in (* Encode: typeHash + address + keccak256(timestamp) + nonce + keccak256(message) *) let data = clob_auth_type_hash ^ addr_padded ^ timestamp_hash ^ nonce_hex ^ message_hash in let bytes = Hex.to_string (`Hex data) in Digestif.KECCAK_256.(to_hex (digest_string bytes)) (** Compute EIP-712 hash to sign *) let compute_eip712_hash ~address ~timestamp ~nonce = let domain_separator = compute_domain_separator () in let struct_hash = compute_clob_auth_hash ~address ~timestamp ~nonce in (* Final hash: keccak256("\x19\x01" + domainSeparator + structHash) *) let prefix = "\x19\x01" in let domain_bytes = Hex.to_string (`Hex domain_separator) in let struct_bytes = Hex.to_string (`Hex struct_hash) in let data = prefix ^ domain_bytes ^ struct_bytes in Digestif.KECCAK_256.(to_hex (digest_string data)) (** {1 Secp256k1 Signing} *) (** Sign a 32-byte hash with private key, returns signature with recovery id *) let sign_hash ~private_key hash_hex = let open Libsecp256k1.External in (* Create signing context *) let ctx = Context.create ~sign:true ~verify:true () in (* Parse private key (hex string to bytes) *) let sk_bytes = Bigstring.of_string (Hex.to_string (`Hex private_key)) in let sk = Key.read_sk_exn ctx sk_bytes in (* Parse hash (hex string to bytes) *) let hash_bytes = Bigstring.of_string (Hex.to_string (`Hex hash_hex)) in (* Sign with recoverable signature *) let signature = Sign.sign_recoverable_exn ctx ~sk hash_bytes in (* Serialize signature (65 bytes: 64 bytes r+s + 1 byte recovery id) *) let sig_bytes = Sign.to_bytes ctx signature in let sig_str = Bigstring.to_string sig_bytes in (* First 64 bytes are r+s, last byte is recovery id *) let rs_hex = Hex.of_string (String.sub sig_str 0 64) in let recid = Char.code sig_str.[64] in let v = recid + 27 in let (`Hex rs_str) = rs_hex in Printf.sprintf "0x%s%02x" rs_str v (** {1 Public API} *) let sign_clob_auth_message ~private_key ~address ~timestamp ~nonce = let hash = compute_eip712_hash ~address ~timestamp ~nonce in sign_hash ~private_key hash let private_key_to_address private_key = let open Libsecp256k1.External in (* Create context *) let ctx = Context.create ~sign:true ~verify:true () in (* Parse private key *) let sk_bytes = Bigstring.of_string (Hex.to_string (`Hex private_key)) in let sk = Key.read_sk_exn ctx sk_bytes in (* Derive public key *) let pk = Key.neuterize_exn ctx sk in (* Serialize uncompressed public key (65 bytes: 0x04 + 64 bytes) *) let pk_bytes = Key.to_bytes ~compress:false ctx pk in let pk_str = Bigstring.to_string pk_bytes in (* Take last 64 bytes (skip 0x04 prefix), hash with keccak256, take last 20 bytes *) let pk_data = String.sub pk_str 1 64 in let hash = Digestif.KECCAK_256.digest_string pk_data in let hash_hex = Digestif.KECCAK_256.to_hex hash in (* Last 40 chars (20 bytes) of hash = address *) "0x" ^ String.sub hash_hex (String.length hash_hex - 40) 40 let current_timestamp_ms () = let t = Unix.gettimeofday () in Printf.sprintf "%.0f" (t *. 1000.0) module Private = struct let pad_hex_32 = pad_hex_32 let encode_uint256 = encode_uint256 let sign_hash = sign_hash end
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>