@@ -84,18 +84,17 @@ fn clob_auth_eip712_hash(address: &Address, timestamp: &str) -> Result<[u8; 32]>
8484
8585 // Struct hash
8686 let type_hash = ethers:: utils:: keccak256 (
87- "ClobAuth(address address,string timestamp,string nonce,string message)" ,
87+ "ClobAuth(address address,string timestamp,uint256 nonce,string message)" ,
8888 ) ;
8989 let timestamp_hash = ethers:: utils:: keccak256 ( timestamp. as_bytes ( ) ) ;
90- let nonce_hash = ethers:: utils:: keccak256 ( "0" . as_bytes ( ) ) ;
9190 let message_hash =
9291 ethers:: utils:: keccak256 ( "This message attests that I control the given wallet" . as_bytes ( ) ) ;
9392
9493 let struct_hash = ethers:: utils:: keccak256 ( ethers:: abi:: encode ( & [
9594 ethers:: abi:: Token :: FixedBytes ( type_hash. to_vec ( ) ) ,
9695 ethers:: abi:: Token :: Address ( * address) ,
9796 ethers:: abi:: Token :: FixedBytes ( timestamp_hash. to_vec ( ) ) ,
98- ethers:: abi:: Token :: FixedBytes ( nonce_hash . to_vec ( ) ) ,
97+ ethers:: abi:: Token :: Uint ( U256 :: zero ( ) ) ,
9998 ethers:: abi:: Token :: FixedBytes ( message_hash. to_vec ( ) ) ,
10099 ] ) ) ;
101100
@@ -118,7 +117,7 @@ pub async fn derive_api_credentials(
118117 let wallet: LocalWallet = private_key
119118 . parse ( )
120119 . context ( "Invalid private key for Polymarket" ) ?;
121- let address = format ! ( "{:?}" , wallet. address( ) ) ;
120+ let address = ethers :: utils :: to_checksum ( & wallet. address ( ) , None ) ;
122121
123122 // Check cache first
124123 if let Some ( ( api_key, secret, passphrase) ) = load_cached_credentials ( & address) {
@@ -135,10 +134,32 @@ pub async fn derive_api_credentials(
135134 let signature = wallet. sign_hash ( hash. into ( ) ) ?;
136135 let sig_hex = format ! ( "0x{}" , hex:: encode( signature. to_vec( ) ) ) ;
137136
138- // POST to derive-api- key (or create new)
139- let url = format ! ( "{}/auth/derive- api-key" , CLOB_BASE ) ;
137+ // Try to create API key first (for new wallets), fall back to derive
138+ let create_url = format ! ( "{}/auth/api-key" , CLOB_BASE ) ;
140139 let resp = client
141- . get ( & url)
140+ . post ( & create_url)
141+ . header ( "POLY_ADDRESS" , & address)
142+ . header ( "POLY_SIGNATURE" , & sig_hex)
143+ . header ( "POLY_TIMESTAMP" , & timestamp)
144+ . header ( "POLY_NONCE" , "0" )
145+ . send ( )
146+ . await ?;
147+
148+ if resp. status ( ) . is_success ( ) {
149+ let body: serde_json:: Value = resp. json ( ) . await ?;
150+ let api_key = body[ "apiKey" ] . as_str ( ) . unwrap_or_default ( ) . to_string ( ) ;
151+ let secret = body[ "secret" ] . as_str ( ) . unwrap_or_default ( ) . to_string ( ) ;
152+ let passphrase = body[ "passphrase" ] . as_str ( ) . unwrap_or_default ( ) . to_string ( ) ;
153+ if !api_key. is_empty ( ) {
154+ let _ = save_cached_credentials ( & address, & api_key, & secret, & passphrase) ;
155+ return Ok ( ( api_key, secret, passphrase) ) ;
156+ }
157+ }
158+
159+ // Fall back to derive existing credentials
160+ let derive_url = format ! ( "{}/auth/derive-api-key" , CLOB_BASE ) ;
161+ let resp = client
162+ . get ( & derive_url)
142163 . header ( "POLY_ADDRESS" , & address)
143164 . header ( "POLY_SIGNATURE" , & sig_hex)
144165 . header ( "POLY_TIMESTAMP" , & timestamp)
@@ -284,7 +305,7 @@ pub async fn sign_order(
284305#[ allow( dead_code) ]
285306struct GammaMarket {
286307 #[ serde( rename = "clobTokenIds" ) ]
287- clob_token_ids : Option < Vec < String > > ,
308+ clob_token_ids : Option < serde_json :: Value > ,
288309 slug : Option < String > ,
289310 #[ serde( rename = "negRisk" ) ]
290311 neg_risk : Option < bool > ,
@@ -295,22 +316,41 @@ pub async fn get_market_info(client: &reqwest::Client, slug: &str) -> Result<(Ve
295316 let url = format ! ( "{}/markets?slug={}" , GAMMA_BASE , urlencoding:: encode( slug) ) ;
296317 let markets: Vec < GammaMarket > = client. get ( & url) . send ( ) . await ?. json ( ) . await ?;
297318 let market = markets. first ( ) . context ( "Market not found" ) ?;
298- let token_ids = market. clob_token_ids . clone ( ) . context ( "No CLOB token IDs" ) ?;
299319 let neg_risk = market. neg_risk . unwrap_or ( false ) ;
320+
321+ // clobTokenIds can be a JSON array or a JSON string containing an array
322+ let token_ids = match & market. clob_token_ids {
323+ Some ( serde_json:: Value :: Array ( arr) ) => arr
324+ . iter ( )
325+ . filter_map ( |v| v. as_str ( ) . map ( String :: from) )
326+ . collect ( ) ,
327+ Some ( serde_json:: Value :: String ( s) ) => {
328+ serde_json:: from_str :: < Vec < String > > ( s) . unwrap_or_default ( )
329+ }
330+ _ => Vec :: new ( ) ,
331+ } ;
332+ if token_ids. is_empty ( ) {
333+ bail ! ( "No CLOB token IDs found for market" ) ;
334+ }
300335 Ok ( ( token_ids, neg_risk) )
301336}
302337
303338/// Get tick size for a token
304339pub async fn get_tick_size ( client : & reqwest:: Client , token_id : & str ) -> Result < f64 > {
305340 let url = format ! ( "{}/tick-size?token_id={}" , CLOB_BASE , token_id) ;
306- #[ derive( Deserialize ) ]
307- struct TickResponse {
308- #[ serde( rename = "tickSize" ) ]
309- tick_size : Option < String > ,
310- }
311- let resp: TickResponse = client. get ( & url) . send ( ) . await ?. json ( ) . await ?;
312- resp. tick_size
313- . and_then ( |s| s. parse :: < f64 > ( ) . ok ( ) )
341+ let resp: serde_json:: Value = client. get ( & url) . send ( ) . await ?. json ( ) . await ?;
342+ // Handle both "minimum_tick_size" (number) and "tickSize" (string)
343+ resp. get ( "minimum_tick_size" )
344+ . and_then ( |v| {
345+ v. as_f64 ( )
346+ . or_else ( || v. as_str ( ) . and_then ( |s| s. parse ( ) . ok ( ) ) )
347+ } )
348+ . or_else ( || {
349+ resp. get ( "tickSize" ) . and_then ( |v| {
350+ v. as_f64 ( )
351+ . or_else ( || v. as_str ( ) . and_then ( |s| s. parse ( ) . ok ( ) ) )
352+ } )
353+ } )
314354 . context ( "Failed to get tick size" )
315355}
316356
0 commit comments