diff --git a/.gitignore b/.gitignore index dcc8992..b465764 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ rs-clob-client/ +rs-clob-client-v2-main/ /target/ **/target/ @@ -17,5 +18,10 @@ rs-clob-client/ *.local .env +.env.e2e.* +.e2e/ +e2e-output/ +e2e-*.log +scripts/e2e.sh -CLAUDE.md \ No newline at end of file +CLAUDE.md diff --git a/Cargo.lock b/Cargo.lock index 26a3032..b60d968 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,6 +41,7 @@ dependencies = [ "alloy-network", "alloy-provider", "alloy-rpc-client", + "alloy-rpc-types", "alloy-serde", "alloy-signer", "alloy-signer-local", @@ -403,6 +404,18 @@ dependencies = [ "wasmtimer", ] +[[package]] +name = "alloy-rpc-types" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bdcbf9dfd5eea8bfeb078b1d906da8cd3a39c4d4dbe7a628025648e323611f6" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", +] + [[package]] name = "alloy-rpc-types-any" version = "1.7.3" @@ -1064,9 +1077,9 @@ dependencies = [ [[package]] name = "bon" -version = "3.9.0" +version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d13a61f2963b88eef9c1be03df65d42f6996dfeac1054870d950fcf66686f83" +checksum = "f47dbe92550676ee653353c310dfb9cf6ba17ee70396e1f7cf0a2020ad49b2fe" dependencies = [ "bon-macros", "rustversion", @@ -1074,9 +1087,9 @@ dependencies = [ [[package]] name = "bon-macros" -version = "3.9.0" +version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d314cc62af2b6b0c65780555abb4d02a03dd3b799cd42419044f0c38d99738c0" +checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" dependencies = [ "darling 0.23.0", "ident_case", @@ -1221,11 +1234,22 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + [[package]] name = "chrono" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "js-sys", @@ -1316,7 +1340,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bb320cac8a0750d7f25280aa97b09c26edfe161164238ecbbb31092b079e735" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "proptest", "serde_core", ] @@ -1391,6 +1415,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc" version = "3.4.0" @@ -1798,7 +1831,7 @@ checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2018,6 +2051,7 @@ dependencies = [ "cfg-if", "libc", "r-efi", + "rand_core 0.10.1", "wasip2", "wasip3", ] @@ -2547,7 +2581,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" dependencies = [ - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] @@ -2974,7 +3008,7 @@ dependencies = [ "chrono", "clap", "dirs", - "polymarket-client-sdk", + "polymarket_client_sdk_v2", "predicates", "rust_decimal", "rust_decimal_macros", @@ -2986,10 +3020,10 @@ dependencies = [ ] [[package]] -name = "polymarket-client-sdk" -version = "0.4.2" +name = "polymarket_client_sdk_v2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "827f8ed9308deb61c96e6ff6df6137742a038e3dd27aa62f78c06dea763951c3" +checksum = "594de0bf0e129e266946f02acbf579253951afbbee92fc1564da3c9179303aea" dependencies = [ "alloy", "async-stream", @@ -3001,7 +3035,7 @@ dependencies = [ "futures", "hmac", "phf", - "rand 0.9.2", + "rand 0.10.1", "reqwest 0.13.2", "rust_decimal", "rust_decimal_macros", @@ -3011,8 +3045,9 @@ dependencies = [ "serde_json", "serde_repr", "serde_with", + "sha1", "sha2", - "strum_macros", + "strum_macros 0.28.0", "url", "uuid", ] @@ -3287,6 +3322,17 @@ dependencies = [ "serde", ] +[[package]] +name = "rand" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +dependencies = [ + "chacha20", + "getrandom 0.4.1", + "rand_core 0.10.1", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -3326,6 +3372,12 @@ dependencies = [ "serde", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "rand_xorshift" version = "0.4.0" @@ -3600,9 +3652,9 @@ checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "rust_decimal" -version = "1.40.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61f703d19852dbf87cbc513643fa81428361eb6940f1ac14fd58155d295a3eb0" +checksum = "2ce901f9a19d251159075a4c37af514c3b8ef99c22e02dd8c19161cf397ee94a" dependencies = [ "arrayvec", "borsh", @@ -3612,6 +3664,7 @@ dependencies = [ "rkyv", "serde", "serde_json", + "wasm-bindgen", ] [[package]] @@ -4017,9 +4070,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.16.1" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" +checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" dependencies = [ "base64", "chrono", @@ -4036,11 +4089,11 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.16.1" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" +checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" dependencies = [ - "darling 0.21.3", + "darling 0.23.0", "proc-macro2", "quote", "syn 2.0.117", @@ -4056,6 +4109,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + [[package]] name = "sha2" version = "0.10.9" @@ -4063,7 +4127,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.10.7", ] @@ -4174,7 +4238,7 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "strum_macros", + "strum_macros 0.27.2", ] [[package]] @@ -4189,6 +4253,18 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "subtle" version = "2.6.1" @@ -4683,9 +4759,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.21.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" dependencies = [ "getrandom 0.4.1", "js-sys", @@ -4766,6 +4842,7 @@ dependencies = [ "cfg-if", "once_cell", "rustversion", + "serde", "wasm-bindgen-macro", "wasm-bindgen-shared", ] diff --git a/Cargo.toml b/Cargo.toml index a01bf05..6e4755f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,8 @@ name = "polymarket" path = "src/main.rs" [dependencies] -polymarket-client-sdk = { version = "0.4", features = ["gamma", "data", "bridge", "clob", "ctf"] } -alloy = { version = "1.6.3", default-features = false, features = ["providers", "sol-types", "contract", "reqwest", "reqwest-rustls-tls", "signer-local", "signers"] } +polymarket_client_sdk_v2 = { version = "0.5.1", features = ["gamma", "data", "bridge", "clob", "ctf"] } +alloy = { version = "1.6.3", default-features = false, features = ["providers", "rpc-types", "sol-types", "contract", "reqwest", "reqwest-rustls-tls", "signer-local", "signers"] } clap = { version = "4", features = ["derive"] } tokio = { version = "1", features = ["rt-multi-thread", "macros"] } serde_json = "1" diff --git a/README.md b/README.md index e9a2eee..7575d59 100644 --- a/README.md +++ b/README.md @@ -332,14 +332,14 @@ polymarket data builder-volume --period month ### Contract Approvals -Before trading, Polymarket contracts need ERC-20 (USDC) and ERC-1155 (CTF token) approvals. +Before trading, Polymarket contracts need ERC-20 (pUSD) and ERC-1155 (CTF token) approvals. ```bash # Check current approvals (read-only) polymarket approve check polymarket approve check 0xSOME_ADDRESS -# Approve all contracts (sends 6 on-chain transactions, needs MATIC for gas) +# Approve all contracts (sends on-chain transactions, needs MATIC for gas) polymarket approve set ``` @@ -348,10 +348,10 @@ polymarket approve set Split, merge, and redeem conditional tokens directly on-chain. ```bash -# Split $10 USDC into YES/NO tokens +# Split $10 pUSD into YES/NO tokens polymarket ctf split --condition 0xCONDITION... --amount 10 -# Merge tokens back to USDC +# Merge tokens back to pUSD polymarket ctf merge --condition 0xCONDITION... --amount 10 # Redeem winning tokens after resolution @@ -366,7 +366,7 @@ polymarket ctf collection-id --condition 0xCONDITION... --index-set 1 polymarket ctf position-id --collection 0xCOLLECTION... ``` -`--amount` is in USDC (e.g., `10` = $10). The `--partition` flag defaults to binary (`1,2`). On-chain operations require MATIC for gas on Polygon. +`--amount` is in pUSD (e.g., `10` = $10). The `--partition` flag defaults to binary (`1,2`). On-chain operations require MATIC for gas on Polygon. ### Bridge diff --git a/src/auth.rs b/src/auth.rs index a5cf34d..66c1a60 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -2,15 +2,20 @@ use std::str::FromStr; use alloy::providers::ProviderBuilder; use anyhow::{Context, Result}; -use polymarket_client_sdk::auth::state::Authenticated; -use polymarket_client_sdk::auth::{LocalSigner, Normal, Signer as _}; -use polymarket_client_sdk::clob::types::SignatureType; -use polymarket_client_sdk::{POLYGON, clob}; +use polymarket_client_sdk_v2::auth::state::Authenticated; +use polymarket_client_sdk_v2::auth::{LocalSigner, Normal, Signer as _}; +use polymarket_client_sdk_v2::clob::types::SignatureType; +use polymarket_client_sdk_v2::{POLYGON, clob}; use crate::config; +const DEFAULT_CLOB_HOST: &str = "https://clob.polymarket.com"; const DEFAULT_RPC_URL: &str = "https://polygon.drpc.org"; +fn clob_host() -> String { + std::env::var("POLYMARKET_CLOB_HOST").unwrap_or_else(|_| DEFAULT_CLOB_HOST.to_string()) +} + fn rpc_url() -> String { std::env::var("POLYMARKET_RPC_URL").unwrap_or_else(|_| DEFAULT_RPC_URL.to_string()) } @@ -25,7 +30,7 @@ fn parse_signature_type(s: &str) -> SignatureType { pub fn resolve_signer( private_key: Option<&str>, -) -> Result { +) -> Result { let (key, _) = config::resolve_key(private_key)?; let key = key.ok_or_else(|| anyhow::anyhow!("{}", config::NO_WALLET_MSG))?; LocalSigner::from_str(&key) @@ -42,12 +47,12 @@ pub async fn authenticated_clob_client( } pub async fn authenticate_with_signer( - signer: &(impl polymarket_client_sdk::auth::Signer + Sync), + signer: &(impl polymarket_client_sdk_v2::auth::Signer + Sync), signature_type_flag: Option<&str>, ) -> Result>> { let sig_type = parse_signature_type(&config::resolve_signature_type(signature_type_flag)?); - clob::Client::default() + unauthenticated_clob_client()? .authentication_builder(signer) .signature_type(sig_type) .authenticate() @@ -55,6 +60,11 @@ pub async fn authenticate_with_signer( .context("Failed to authenticate with Polymarket CLOB") } +pub fn unauthenticated_clob_client() -> Result { + clob::Client::new(&clob_host(), clob::Config::default()) + .context("Failed to create Polymarket CLOB client") +} + pub async fn create_readonly_provider() -> Result { ProviderBuilder::new() .connect(&rpc_url()) diff --git a/src/commands/approve.rs b/src/commands/approve.rs index b52dc0c..6de94be 100644 --- a/src/commands/approve.rs +++ b/src/commands/approve.rs @@ -3,17 +3,19 @@ use alloy::primitives::U256; use alloy::sol; +use alloy::sol_types::SolCall; use anyhow::{Context, Result}; use clap::{Args, Subcommand}; -use polymarket_client_sdk::types::{Address, address}; -use polymarket_client_sdk::{POLYGON, contract_config}; +use polymarket_client_sdk_v2::types::Address; use crate::auth; use crate::output::OutputFormat; use crate::output::approve::{ApprovalStatus, print_approval_status, print_tx_result}; -/// Polygon USDC (same address as `USDC_ADDRESS_STR`; `address!` requires a literal). -const USDC_ADDRESS: Address = address!("0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"); +use super::{ + COLLATERAL_SYMBOL, CONDITIONAL_TOKENS, CTF_COLLATERAL_ADAPTER, CTF_EXCHANGE, NEG_RISK_ADAPTER, + NEG_RISK_CTF_COLLATERAL_ADAPTER, NEG_RISK_CTF_EXCHANGE, proxy, +}; sol! { #[sol(rpc)] @@ -49,85 +51,112 @@ pub enum ApproveCommand { struct ApprovalTarget { name: &'static str, address: Address, + needs_collateral_allowance: bool, + needs_ctf_operator: bool, } fn approval_targets() -> Result> { - let config = contract_config(POLYGON, false).context("No contract config for Polygon")?; - let neg_risk_config = - contract_config(POLYGON, true).context("No neg-risk contract config for Polygon")?; - - let mut targets = vec![ + Ok(vec![ ApprovalTarget { name: "CTF Exchange", - address: config.exchange, + address: CTF_EXCHANGE, + needs_collateral_allowance: true, + needs_ctf_operator: true, }, ApprovalTarget { name: "Neg Risk Exchange", - address: neg_risk_config.exchange, + address: NEG_RISK_CTF_EXCHANGE, + needs_collateral_allowance: true, + needs_ctf_operator: true, }, - ]; - - if let Some(adapter) = neg_risk_config.neg_risk_adapter { - targets.push(ApprovalTarget { + ApprovalTarget { name: "Neg Risk Adapter", - address: adapter, - }); - } - - Ok(targets) + address: NEG_RISK_ADAPTER, + needs_collateral_allowance: true, + needs_ctf_operator: true, + }, + ApprovalTarget { + name: "Conditional Tokens", + address: CONDITIONAL_TOKENS, + needs_collateral_allowance: true, + needs_ctf_operator: false, + }, + ApprovalTarget { + name: "CTF Collateral Adapter", + address: CTF_COLLATERAL_ADAPTER, + needs_collateral_allowance: true, + needs_ctf_operator: true, + }, + ApprovalTarget { + name: "Neg Risk CTF Collateral Adapter", + address: NEG_RISK_CTF_COLLATERAL_ADAPTER, + needs_collateral_allowance: true, + needs_ctf_operator: true, + }, + ]) } pub async fn execute( args: ApproveArgs, output: OutputFormat, private_key: Option<&str>, + signature_type: Option<&str>, ) -> Result<()> { match args.command { - ApproveCommand::Check { address } => check(address, private_key, output).await, - ApproveCommand::Set => set(private_key, output).await, + ApproveCommand::Check { address } => { + check(address, private_key, signature_type, output).await + } + ApproveCommand::Set => set(private_key, signature_type, output).await, } } async fn check( address_arg: Option
, private_key: Option<&str>, + signature_type: Option<&str>, output: OutputFormat, ) -> Result<()> { let owner: Address = if let Some(addr) = address_arg { addr + } else if proxy::is_proxy_mode(signature_type)? { + proxy::derive_proxy_address(private_key)? } else { let signer = auth::resolve_signer(private_key)?; - polymarket_client_sdk::auth::Signer::address(&signer) + polymarket_client_sdk_v2::auth::Signer::address(&signer) }; let provider = auth::create_readonly_provider().await?; - let config = contract_config(POLYGON, false).context("No contract config for Polygon")?; - - let usdc = IERC20::new(USDC_ADDRESS, provider.clone()); - let ctf = IERC1155::new(config.conditional_tokens, provider.clone()); + let collateral = IERC20::new(super::COLLATERAL_ADDRESS, provider.clone()); + let ctf = IERC1155::new(CONDITIONAL_TOKENS, provider.clone()); let targets = approval_targets()?; let mut statuses = Vec::new(); for target in &targets { - let (usdc_allowance, usdc_error) = match usdc.allowance(owner, target.address).call().await - { - Ok(val) => (val, None), - Err(e) => (U256::ZERO, Some(e.to_string())), + let (collateral_allowance, collateral_error) = if target.needs_collateral_allowance { + match collateral.allowance(owner, target.address).call().await { + Ok(val) => (val, None), + Err(e) => (U256::ZERO, Some(e.to_string())), + } + } else { + (U256::ZERO, None) }; - let (ctf_approved, ctf_error) = + let (ctf_approved, ctf_error) = if target.needs_ctf_operator { match ctf.isApprovedForAll(owner, target.address).call().await { - Ok(val) => (val, None), - Err(e) => (false, Some(e.to_string())), - }; + Ok(val) => (Some(val), None), + Err(e) => (Some(false), Some(e.to_string())), + } + } else { + (None, None) + }; statuses.push(ApprovalStatus { contract_name: target.name.to_string(), contract_address: format!("{}", target.address), - usdc_allowance, + collateral_allowance, ctf_approved, - usdc_error, + collateral_error, ctf_error, }); } @@ -135,15 +164,19 @@ async fn check( print_approval_status(&statuses, &output) } -async fn set(private_key: Option<&str>, output: OutputFormat) -> Result<()> { - let provider = auth::create_provider(private_key).await?; - let config = contract_config(POLYGON, false).context("No contract config for Polygon")?; - - let usdc = IERC20::new(USDC_ADDRESS, provider.clone()); - let ctf = IERC1155::new(config.conditional_tokens, provider.clone()); - +async fn set( + private_key: Option<&str>, + signature_type: Option<&str>, + output: OutputFormat, +) -> Result<()> { + let use_proxy = proxy::is_proxy_mode(signature_type)?; let targets = approval_targets()?; - let total = targets.len() * 2; + let total = targets + .iter() + .map(|target| { + usize::from(target.needs_collateral_allowance) + usize::from(target.needs_ctf_operator) + }) + .sum(); if matches!(output, OutputFormat::Table) { println!("Approving contracts...\n"); @@ -153,52 +186,56 @@ async fn set(private_key: Option<&str>, output: OutputFormat) -> Result<()> { let mut step = 0; for target in &targets { - step += 1; - let label = format!("USDC \u{2192} {}", target.name); - let tx_hash = usdc - .approve(target.address, U256::MAX) - .send() - .await - .context(format!("Failed to send USDC approval for {}", target.name))? - .watch() - .await - .context(format!( - "Failed to confirm USDC approval for {}", - target.name - ))?; - - match output { - OutputFormat::Table => print_tx_result(step, total, &label, tx_hash), - OutputFormat::Json => results.push(serde_json::json!({ - "step": step, - "type": "erc20", - "contract": target.name, - "tx_hash": format!("{tx_hash}"), - })), + if target.needs_collateral_allowance { + step += 1; + let label = format!("{COLLATERAL_SYMBOL} \u{2192} {}", target.name); + let calldata = IERC20::approveCall { + spender: target.address, + value: U256::MAX, + } + .abi_encode(); + let (tx_hash, _) = + proxy::send_call(private_key, use_proxy, super::COLLATERAL_ADDRESS, calldata) + .await + .context(format!( + "Failed {COLLATERAL_SYMBOL} approval for {}", + target.name + ))?; + + match output { + OutputFormat::Table => print_tx_result(step, total, &label, tx_hash), + OutputFormat::Json => results.push(serde_json::json!({ + "step": step, + "type": "erc20", + "asset": COLLATERAL_SYMBOL, + "contract": target.name, + "tx_hash": format!("{tx_hash}"), + })), + } } - step += 1; - let label = format!("CTF \u{2192} {}", target.name); - let tx_hash = ctf - .setApprovalForAll(target.address, true) - .send() - .await - .context(format!("Failed to send CTF approval for {}", target.name))? - .watch() - .await - .context(format!( - "Failed to confirm CTF approval for {}", - target.name - ))?; - - match output { - OutputFormat::Table => print_tx_result(step, total, &label, tx_hash), - OutputFormat::Json => results.push(serde_json::json!({ - "step": step, - "type": "erc1155", - "contract": target.name, - "tx_hash": format!("{tx_hash}"), - })), + if target.needs_ctf_operator { + step += 1; + let label = format!("CTF \u{2192} {}", target.name); + let calldata = IERC1155::setApprovalForAllCall { + operator: target.address, + approved: true, + } + .abi_encode(); + let (tx_hash, _) = + proxy::send_call(private_key, use_proxy, CONDITIONAL_TOKENS, calldata) + .await + .context(format!("Failed CTF approval for {}", target.name))?; + + match output { + OutputFormat::Table => print_tx_result(step, total, &label, tx_hash), + OutputFormat::Json => results.push(serde_json::json!({ + "step": step, + "type": "erc1155", + "contract": target.name, + "tx_hash": format!("{tx_hash}"), + })), + } } } diff --git a/src/commands/bridge.rs b/src/commands/bridge.rs index 9c9d859..869cefa 100644 --- a/src/commands/bridge.rs +++ b/src/commands/bridge.rs @@ -1,6 +1,6 @@ use anyhow::Result; use clap::{Args, Subcommand}; -use polymarket_client_sdk::bridge::{ +use polymarket_client_sdk_v2::bridge::{ self, types::{DepositRequest, StatusRequest}, }; @@ -19,7 +19,7 @@ pub enum BridgeCommand { /// Get deposit addresses for a wallet (EVM, Solana, Bitcoin) Deposit { /// Polymarket wallet address (0x...) - address: polymarket_client_sdk::types::Address, + address: polymarket_client_sdk_v2::types::Address, }, /// List supported chains and tokens for deposits diff --git a/src/commands/clob.rs b/src/commands/clob.rs index 9ee089f..b073f88 100644 --- a/src/commands/clob.rs +++ b/src/commands/clob.rs @@ -16,8 +16,7 @@ use crate::output::clob::{ use anyhow::Result; use chrono::NaiveDate; use clap::{Args, Subcommand}; -use polymarket_client_sdk::clob; -use polymarket_client_sdk::clob::types::{ +use polymarket_client_sdk_v2::clob::types::{ Amount, AssetType, Interval, OrderType, Side, TimeRange, request::{ BalanceAllowanceRequest, CancelMarketOrderRequest, DeleteNotificationsRequest, @@ -25,7 +24,7 @@ use polymarket_client_sdk::clob::types::{ PriceHistoryRequest, PriceRequest, SpreadRequest, TradesRequest, UserRewardsEarningRequest, }, }; -use polymarket_client_sdk::types::{B256, Decimal, U256}; +use polymarket_client_sdk_v2::types::{B256, Decimal, U256}; #[derive(Args)] pub struct ClobArgs { @@ -245,7 +244,7 @@ pub enum ClobCommand { /// Side: buy or sell #[arg(long)] side: CliSide, - /// Amount (USDC for buys, shares for sells) + /// Amount (pUSD for buys, shares for sells) #[arg(long)] amount: String, /// Order type: FOK or FAK (default: FOK) @@ -493,7 +492,7 @@ pub async fn execute( signature_type: Option<&str>, ) -> Result<()> { // Unauthenticated client — cheap to construct, used by read commands and CreateApiKey. - let unauth = clob::Client::default(); + let unauth = auth::unauthenticated_clob_client()?; let output = &output; match args.command { @@ -686,8 +685,11 @@ pub async fn execute( .post_only(post_only) .build() .await?; - let order = client.sign(&signer, order).await?; - let result = client.post_order(order).await?; + let signed_order = client.sign(&signer, order).await?; + let mut results = client.post_orders(vec![signed_order]).await?; + let result = results + .pop() + .ok_or_else(|| anyhow::anyhow!("Order submission returned no result"))?; print_post_order_result(&result, output)?; } @@ -765,8 +767,11 @@ pub async fn execute( .order_type(OrderType::from(order_type)) .build() .await?; - let order = client.sign(&signer, order).await?; - let result = client.post_order(order).await?; + let signed_order = client.sign(&signer, order).await?; + let mut results = client.post_orders(vec![signed_order]).await?; + let result = results + .pop() + .ok_or_else(|| anyhow::anyhow!("Order submission returned no result"))?; print_post_order_result(&result, output)?; } diff --git a/src/commands/comments.rs b/src/commands/comments.rs index 4003911..3a91b28 100644 --- a/src/commands/comments.rs +++ b/src/commands/comments.rs @@ -1,6 +1,6 @@ use anyhow::Result; use clap::{Args, Subcommand}; -use polymarket_client_sdk::gamma::{ +use polymarket_client_sdk_v2::gamma::{ self, types::{ ParentEntityType, @@ -55,7 +55,7 @@ pub enum CommentsCommand { /// List comments by a user's wallet address ByUser { /// Wallet address (0x...) - address: polymarket_client_sdk::types::Address, + address: polymarket_client_sdk_v2::types::Address, /// Max results #[arg(long, default_value = "25")] diff --git a/src/commands/ctf.rs b/src/commands/ctf.rs index 5d06ea5..e09bbd2 100644 --- a/src/commands/ctf.rs +++ b/src/commands/ctf.rs @@ -1,19 +1,72 @@ use alloy::primitives::U256; +use alloy::sol; +use alloy::sol_types::SolCall; use anyhow::{Context, Result}; use clap::{Args, Subcommand}; -use polymarket_client_sdk::ctf::types::{ - CollectionIdRequest, ConditionIdRequest, MergePositionsRequest, PositionIdRequest, - RedeemNegRiskRequest, RedeemPositionsRequest, SplitPositionRequest, -}; -use polymarket_client_sdk::types::{Address, B256}; -use polymarket_client_sdk::{POLYGON, ctf}; +use polymarket_client_sdk_v2::types::{Address, B256}; use rust_decimal::Decimal; use crate::auth; use crate::output::OutputFormat; use crate::output::ctf as ctf_output; -use super::{USDC_ADDRESS_STR, USDC_DECIMALS}; +use super::proxy; +use super::{ + COLLATERAL_ADDRESS_STR, COLLATERAL_DECIMALS, COLLATERAL_SYMBOL, CONDITIONAL_TOKENS, + NEG_RISK_ADAPTER, +}; + +sol! { + #[sol(rpc)] + interface IConditionalTokens { + function getConditionId( + address oracle, + bytes32 questionId, + uint256 outcomeSlotCount + ) external pure returns (bytes32); + + function getCollectionId( + bytes32 parentCollectionId, + bytes32 conditionId, + uint256 indexSet + ) external view returns (bytes32); + + function getPositionId( + address collateralToken, + bytes32 collectionId + ) external view returns (uint256); + + function splitPosition( + address collateralToken, + bytes32 parentCollectionId, + bytes32 conditionId, + uint256[] calldata partition, + uint256 amount + ) external; + + function mergePositions( + address collateralToken, + bytes32 parentCollectionId, + bytes32 conditionId, + uint256[] calldata partition, + uint256 amount + ) external; + + function redeemPositions( + address collateralToken, + bytes32 parentCollectionId, + bytes32 conditionId, + uint256[] calldata indexSets + ) external; + } + + interface INegRiskAdapter { + function redeemPositions( + bytes32 conditionId, + uint256[] calldata amounts + ) external; + } +} #[derive(Args)] pub struct CtfArgs { @@ -28,11 +81,11 @@ pub enum CtfCommand { /// Condition ID (0x-prefixed 32-byte hex) #[arg(long)] condition: B256, - /// Amount in USDC (e.g. 10 for $10) + /// Amount in pUSD (e.g. 10 for $10) #[arg(long)] amount: String, - /// Collateral token address (defaults to USDC) - #[arg(long, default_value = USDC_ADDRESS_STR)] + /// Collateral token address (defaults to pUSD) + #[arg(long, default_value = COLLATERAL_ADDRESS_STR)] collateral: Address, /// Custom partition as comma-separated index sets (e.g. "1,2" for binary, "1,2,4" for 3-outcome) #[arg(long)] @@ -46,11 +99,11 @@ pub enum CtfCommand { /// Condition ID (0x-prefixed 32-byte hex) #[arg(long)] condition: B256, - /// Amount in USDC (e.g. 10 for $10) + /// Amount in pUSD (e.g. 10 for $10) #[arg(long)] amount: String, - /// Collateral token address (defaults to USDC) - #[arg(long, default_value = USDC_ADDRESS_STR)] + /// Collateral token address (defaults to pUSD) + #[arg(long, default_value = COLLATERAL_ADDRESS_STR)] collateral: Address, /// Custom partition as comma-separated index sets (e.g. "1,2" for binary, "1,2,4" for 3-outcome) #[arg(long)] @@ -64,8 +117,8 @@ pub enum CtfCommand { /// Condition ID (0x-prefixed 32-byte hex) #[arg(long)] condition: B256, - /// Collateral token address (defaults to USDC) - #[arg(long, default_value = USDC_ADDRESS_STR)] + /// Collateral token address (defaults to pUSD) + #[arg(long, default_value = COLLATERAL_ADDRESS_STR)] collateral: Address, /// Custom index sets as comma-separated values (e.g. "1,2" for binary, "1" for YES only) #[arg(long)] @@ -79,7 +132,7 @@ pub enum CtfCommand { /// Condition ID (0x-prefixed 32-byte hex) #[arg(long)] condition: B256, - /// Comma-separated amounts in USDC for each outcome (e.g. "10,5") + /// Comma-separated amounts in pUSD for each outcome (e.g. "10,5") #[arg(long)] amounts: String, }, @@ -109,8 +162,8 @@ pub enum CtfCommand { }, /// Calculate a position ID (ERC1155 token ID) from collateral and collection PositionId { - /// Collateral token address (defaults to USDC) - #[arg(long, default_value = USDC_ADDRESS_STR)] + /// Collateral token address (defaults to pUSD) + #[arg(long, default_value = COLLATERAL_ADDRESS_STR)] collateral: Address, /// Collection ID (0x-prefixed 32-byte hex) #[arg(long)] @@ -118,12 +171,12 @@ pub enum CtfCommand { }, } -fn usdc_to_raw(val: Decimal) -> Result { - let multiplier = Decimal::from(10u64.pow(USDC_DECIMALS)); +fn collateral_to_raw(val: Decimal) -> Result { + let multiplier = Decimal::from(10u64.pow(COLLATERAL_DECIMALS)); let raw = val * multiplier; anyhow::ensure!( raw.fract().is_zero(), - "Amount {val} exceeds USDC precision (max 6 decimal places)" + "Amount {val} exceeds {COLLATERAL_SYMBOL} precision (max {COLLATERAL_DECIMALS} decimal places)" ); let raw_u64: u64 = raw .try_into() @@ -131,13 +184,13 @@ fn usdc_to_raw(val: Decimal) -> Result { Ok(U256::from(raw_u64)) } -fn parse_usdc_amount(s: &str) -> Result { +fn parse_collateral_amount(s: &str) -> Result { let val: Decimal = s.trim().parse().context(format!("Invalid amount: {s}"))?; anyhow::ensure!(val > Decimal::ZERO, "Amount must be positive"); - usdc_to_raw(val) + collateral_to_raw(val) } -fn parse_usdc_amounts(s: &str) -> Result> { +fn parse_collateral_amounts(s: &str) -> Result> { s.split(',') .map(|part| { let trimmed = part.trim(); @@ -148,7 +201,7 @@ fn parse_usdc_amounts(s: &str) -> Result> { val >= Decimal::ZERO, "Amount must be non-negative: {trimmed}" ); - usdc_to_raw(val) + collateral_to_raw(val) }) .collect() } @@ -171,7 +224,12 @@ fn binary_u256_vec() -> Vec { DEFAULT_BINARY_SETS.iter().map(|&n| U256::from(n)).collect() } -pub async fn execute(args: CtfArgs, output: OutputFormat, private_key: Option<&str>) -> Result<()> { +pub async fn execute( + args: CtfArgs, + output: OutputFormat, + private_key: Option<&str>, + signature_type: Option<&str>, +) -> Result<()> { match args.command { CtfCommand::Split { condition, @@ -180,30 +238,29 @@ pub async fn execute(args: CtfArgs, output: OutputFormat, private_key: Option<&s partition, parent_collection, } => { - let usdc_amount = parse_usdc_amount(&amount)?; + let collateral_amount = parse_collateral_amount(&amount)?; let parent = parent_collection.unwrap_or_default(); let partition = match partition { Some(p) => parse_u256_csv(&p)?, None => binary_u256_vec(), }; - let provider = auth::create_provider(private_key).await?; - let client = ctf::Client::new(provider, POLYGON)?; - - let req = SplitPositionRequest::builder() - .collateral_token(collateral) - .parent_collection_id(parent) - .condition_id(condition) - .partition(partition) - .amount(usdc_amount) - .build(); - - let resp = client - .split_position(&req) - .await - .context("Split position failed")?; - - ctf_output::print_tx_result("split", resp.transaction_hash, resp.block_number, &output) + let use_proxy = proxy::is_proxy_mode(signature_type)?; + let calldata = IConditionalTokens::splitPositionCall { + collateralToken: collateral, + parentCollectionId: parent, + conditionId: condition, + partition, + amount: collateral_amount, + } + .abi_encode(); + + let (tx_hash, block_number) = + proxy::send_call(private_key, use_proxy, CONDITIONAL_TOKENS, calldata) + .await + .context("Split position failed")?; + + ctf_output::print_tx_result("split", tx_hash, block_number, &output) } CtfCommand::Merge { condition, @@ -212,30 +269,29 @@ pub async fn execute(args: CtfArgs, output: OutputFormat, private_key: Option<&s partition, parent_collection, } => { - let usdc_amount = parse_usdc_amount(&amount)?; + let collateral_amount = parse_collateral_amount(&amount)?; let parent = parent_collection.unwrap_or_default(); let partition = match partition { Some(p) => parse_u256_csv(&p)?, None => binary_u256_vec(), }; - let provider = auth::create_provider(private_key).await?; - let client = ctf::Client::new(provider, POLYGON)?; - - let req = MergePositionsRequest::builder() - .collateral_token(collateral) - .parent_collection_id(parent) - .condition_id(condition) - .partition(partition) - .amount(usdc_amount) - .build(); - - let resp = client - .merge_positions(&req) - .await - .context("Merge positions failed")?; - - ctf_output::print_tx_result("merge", resp.transaction_hash, resp.block_number, &output) + let use_proxy = proxy::is_proxy_mode(signature_type)?; + let calldata = IConditionalTokens::mergePositionsCall { + collateralToken: collateral, + parentCollectionId: parent, + conditionId: condition, + partition, + amount: collateral_amount, + } + .abi_encode(); + + let (tx_hash, block_number) = + proxy::send_call(private_key, use_proxy, CONDITIONAL_TOKENS, calldata) + .await + .context("Merge positions failed")?; + + ctf_output::print_tx_result("merge", tx_hash, block_number, &output) } CtfCommand::Redeem { condition, @@ -249,45 +305,38 @@ pub async fn execute(args: CtfArgs, output: OutputFormat, private_key: Option<&s None => binary_u256_vec(), }; - let provider = auth::create_provider(private_key).await?; - let client = ctf::Client::new(provider, POLYGON)?; + let use_proxy = proxy::is_proxy_mode(signature_type)?; + let calldata = IConditionalTokens::redeemPositionsCall { + collateralToken: collateral, + parentCollectionId: parent, + conditionId: condition, + indexSets: index_sets, + } + .abi_encode(); + + let (tx_hash, block_number) = + proxy::send_call(private_key, use_proxy, CONDITIONAL_TOKENS, calldata) + .await + .context("Redeem positions failed")?; + + ctf_output::print_tx_result("redeem", tx_hash, block_number, &output) + } + CtfCommand::RedeemNegRisk { condition, amounts } => { + let amounts = parse_collateral_amounts(&amounts)?; - let req = RedeemPositionsRequest::builder() - .collateral_token(collateral) - .parent_collection_id(parent) - .condition_id(condition) - .index_sets(index_sets) - .build(); + let use_proxy = proxy::is_proxy_mode(signature_type)?; + let calldata = INegRiskAdapter::redeemPositionsCall { + conditionId: condition, + amounts, + } + .abi_encode(); - let resp = client - .redeem_positions(&req) - .await - .context("Redeem positions failed")?; + let (tx_hash, block_number) = + proxy::send_call(private_key, use_proxy, NEG_RISK_ADAPTER, calldata) + .await + .context("Redeem neg-risk positions failed")?; - ctf_output::print_tx_result("redeem", resp.transaction_hash, resp.block_number, &output) - } - CtfCommand::RedeemNegRisk { condition, amounts } => { - let amounts = parse_usdc_amounts(&amounts)?; - - let provider = auth::create_provider(private_key).await?; - let client = ctf::Client::with_neg_risk(provider, POLYGON)?; - - let req = RedeemNegRiskRequest::builder() - .condition_id(condition) - .amounts(amounts) - .build(); - - let resp = client - .redeem_neg_risk(&req) - .await - .context("Redeem neg-risk positions failed")?; - - ctf_output::print_tx_result( - "redeem-neg-risk", - resp.transaction_hash, - resp.block_number, - &output, - ) + ctf_output::print_tx_result("redeem-neg-risk", tx_hash, block_number, &output) } CtfCommand::ConditionId { oracle, @@ -295,16 +344,12 @@ pub async fn execute(args: CtfArgs, output: OutputFormat, private_key: Option<&s outcomes, } => { let provider = auth::create_readonly_provider().await?; - let client = ctf::Client::new(provider, POLYGON)?; - - let req = ConditionIdRequest::builder() - .oracle(oracle) - .question_id(question) - .outcome_slot_count(U256::from(outcomes)) - .build(); - - let resp = client.condition_id(&req).await?; - ctf_output::print_condition_id(resp.condition_id, &output) + let contract = IConditionalTokens::new(CONDITIONAL_TOKENS, provider); + let condition_id = contract + .getConditionId(oracle, question, U256::from(outcomes)) + .call() + .await?; + ctf_output::print_condition_id(condition_id, &output) } CtfCommand::CollectionId { condition, @@ -314,31 +359,24 @@ pub async fn execute(args: CtfArgs, output: OutputFormat, private_key: Option<&s let parent = parent_collection.unwrap_or_default(); let provider = auth::create_readonly_provider().await?; - let client = ctf::Client::new(provider, POLYGON)?; - - let req = CollectionIdRequest::builder() - .parent_collection_id(parent) - .condition_id(condition) - .index_set(U256::from(index_set)) - .build(); - - let resp = client.collection_id(&req).await?; - ctf_output::print_collection_id(resp.collection_id, &output) + let contract = IConditionalTokens::new(CONDITIONAL_TOKENS, provider); + let collection_id = contract + .getCollectionId(parent, condition, U256::from(index_set)) + .call() + .await?; + ctf_output::print_collection_id(collection_id, &output) } CtfCommand::PositionId { collateral, collection, } => { let provider = auth::create_readonly_provider().await?; - let client = ctf::Client::new(provider, POLYGON)?; - - let req = PositionIdRequest::builder() - .collateral_token(collateral) - .collection_id(collection) - .build(); - - let resp = client.position_id(&req).await?; - ctf_output::print_position_id(resp.position_id, &output) + let contract = IConditionalTokens::new(CONDITIONAL_TOKENS, provider); + let position_id = contract + .getPositionId(collateral, collection) + .call() + .await?; + ctf_output::print_position_id(position_id, &output) } } } @@ -348,59 +386,61 @@ mod tests { use super::*; #[test] - fn parse_usdc_amount_whole_dollars() { - let result = parse_usdc_amount("10").unwrap(); + fn parse_collateral_amount_whole_dollars() { + let result = parse_collateral_amount("10").unwrap(); assert_eq!(result, U256::from(10_000_000u64)); } #[test] - fn parse_usdc_amount_fractional() { - let result = parse_usdc_amount("1.5").unwrap(); + fn parse_collateral_amount_fractional() { + let result = parse_collateral_amount("1.5").unwrap(); assert_eq!(result, U256::from(1_500_000u64)); } #[test] - fn parse_usdc_amount_small() { - let result = parse_usdc_amount("0.01").unwrap(); + fn parse_collateral_amount_small() { + let result = parse_collateral_amount("0.01").unwrap(); assert_eq!(result, U256::from(10_000u64)); } #[test] - fn parse_usdc_amount_smallest_unit() { - let result = parse_usdc_amount("0.000001").unwrap(); + fn parse_collateral_amount_smallest_unit() { + let result = parse_collateral_amount("0.000001").unwrap(); assert_eq!(result, U256::from(1u64)); } #[test] - fn parse_usdc_amount_rejects_excess_precision() { - let err = parse_usdc_amount("1.0000001").unwrap_err().to_string(); + fn parse_collateral_amount_rejects_excess_precision() { + let err = parse_collateral_amount("1.0000001") + .unwrap_err() + .to_string(); assert!(err.contains("precision"), "got: {err}"); } #[test] - fn parse_usdc_amount_rejects_zero() { - assert!(parse_usdc_amount("0").is_err()); + fn parse_collateral_amount_rejects_zero() { + assert!(parse_collateral_amount("0").is_err()); } #[test] - fn parse_usdc_amount_rejects_negative() { - assert!(parse_usdc_amount("-5").is_err()); + fn parse_collateral_amount_rejects_negative() { + assert!(parse_collateral_amount("-5").is_err()); } #[test] - fn parse_usdc_amount_rejects_non_numeric() { - assert!(parse_usdc_amount("abc").is_err()); + fn parse_collateral_amount_rejects_non_numeric() { + assert!(parse_collateral_amount("abc").is_err()); } #[test] - fn parse_usdc_amounts_single() { - let result = parse_usdc_amounts("10").unwrap(); + fn parse_collateral_amounts_single() { + let result = parse_collateral_amounts("10").unwrap(); assert_eq!(result, vec![U256::from(10_000_000u64)]); } #[test] - fn parse_usdc_amounts_multiple() { - let result = parse_usdc_amounts("10,5").unwrap(); + fn parse_collateral_amounts_multiple() { + let result = parse_collateral_amounts("10,5").unwrap(); assert_eq!( result, vec![U256::from(10_000_000u64), U256::from(5_000_000u64)] @@ -408,8 +448,8 @@ mod tests { } #[test] - fn parse_usdc_amounts_with_spaces() { - let result = parse_usdc_amounts("10, 5, 2.5").unwrap(); + fn parse_collateral_amounts_with_spaces() { + let result = parse_collateral_amounts("10, 5, 2.5").unwrap(); assert_eq!( result, vec![ @@ -421,19 +461,19 @@ mod tests { } #[test] - fn parse_usdc_amounts_zero_is_allowed() { - let result = parse_usdc_amounts("0,10").unwrap(); + fn parse_collateral_amounts_zero_is_allowed() { + let result = parse_collateral_amounts("0,10").unwrap(); assert_eq!(result, vec![U256::from(0u64), U256::from(10_000_000u64)]); } #[test] - fn parse_usdc_amounts_rejects_negative() { - assert!(parse_usdc_amounts("10,-5").is_err()); + fn parse_collateral_amounts_rejects_negative() { + assert!(parse_collateral_amounts("10,-5").is_err()); } #[test] - fn parse_usdc_amounts_rejects_non_numeric() { - assert!(parse_usdc_amounts("abc").is_err()); + fn parse_collateral_amounts_rejects_non_numeric() { + assert!(parse_collateral_amounts("abc").is_err()); } #[test] diff --git a/src/commands/data.rs b/src/commands/data.rs index ac92d44..3156157 100644 --- a/src/commands/data.rs +++ b/src/commands/data.rs @@ -1,6 +1,6 @@ use anyhow::Result; use clap::{Args, Subcommand}; -use polymarket_client_sdk::data::{ +use polymarket_client_sdk_v2::data::{ self, types::request::{ ActivityRequest, BuilderLeaderboardRequest, BuilderVolumeRequest, ClosedPositionsRequest, @@ -8,7 +8,7 @@ use polymarket_client_sdk::data::{ TraderLeaderboardRequest, TradesRequest, ValueRequest, }, }; -use polymarket_client_sdk::types::{Address, B256}; +use polymarket_client_sdk_v2::types::{Address, B256}; use crate::output::OutputFormat; use crate::output::data::{ @@ -165,7 +165,7 @@ pub enum TimePeriod { All, } -impl From for polymarket_client_sdk::data::types::TimePeriod { +impl From for polymarket_client_sdk_v2::data::types::TimePeriod { fn from(v: TimePeriod) -> Self { match v { TimePeriod::Day => Self::Day, @@ -182,7 +182,7 @@ pub enum OrderBy { Vol, } -impl From for polymarket_client_sdk::data::types::LeaderboardOrderBy { +impl From for polymarket_client_sdk_v2::data::types::LeaderboardOrderBy { fn from(v: OrderBy) -> Self { match v { OrderBy::Pnl => Self::Pnl, diff --git a/src/commands/events.rs b/src/commands/events.rs index d001210..bd7f045 100644 --- a/src/commands/events.rs +++ b/src/commands/events.rs @@ -1,6 +1,6 @@ use anyhow::Result; use clap::{Args, Subcommand}; -use polymarket_client_sdk::gamma::{ +use polymarket_client_sdk_v2::gamma::{ self, types::request::{EventByIdRequest, EventBySlugRequest, EventTagsRequest, EventsRequest}, }; diff --git a/src/commands/markets.rs b/src/commands/markets.rs index 2544d18..f3ab0a2 100644 --- a/src/commands/markets.rs +++ b/src/commands/markets.rs @@ -1,6 +1,6 @@ use anyhow::Result; use clap::{Args, Subcommand}; -use polymarket_client_sdk::gamma::{ +use polymarket_client_sdk_v2::gamma::{ self, types::{ request::{ diff --git a/src/commands/mod.rs b/src/commands/mod.rs index d4c985b..9885365 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,5 +1,96 @@ -pub(crate) const USDC_ADDRESS_STR: &str = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"; -pub(crate) const USDC_DECIMALS: u32 = 6; +use polymarket_client_sdk_v2::types::{Address, address}; + +pub(crate) const COLLATERAL_SYMBOL: &str = "pUSD"; +pub(crate) const COLLATERAL_ADDRESS_STR: &str = "0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB"; +pub(crate) const COLLATERAL_ADDRESS: Address = + address!("0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB"); +pub(crate) const COLLATERAL_DECIMALS: u32 = 6; +pub(crate) const CONDITIONAL_TOKENS: Address = + address!("0x4D97DCd97eC945f40cF65F87097ACe5EA0476045"); +pub(crate) const CTF_EXCHANGE: Address = address!("0xE111180000d2663C0091e4f400237545B87B996B"); +pub(crate) const NEG_RISK_CTF_EXCHANGE: Address = + address!("0xe2222d279d744050d28e00520010520000310F59"); +pub(crate) const NEG_RISK_ADAPTER: Address = address!("0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296"); +pub(crate) const CTF_COLLATERAL_ADAPTER: Address = + address!("0xADa100874d00e3331D00F2007a9c336a65009718"); +pub(crate) const NEG_RISK_CTF_COLLATERAL_ADAPTER: Address = + address!("0xAdA200001000ef00D07553cEE7006808F895c6F1"); + +pub(crate) mod proxy { + use alloy::primitives::U256; + use alloy::sol; + use anyhow::{Context, Result}; + use polymarket_client_sdk_v2::POLYGON; + use polymarket_client_sdk_v2::types::{Address, B256}; + + use crate::auth; + + // Polymarket Proxy Wallet Factory interface (CallType: INVALID=0, CALL=1, DELEGATECALL=2). + sol! { + #[sol(rpc)] + interface IProxyWallet { + struct ProxyCall { + uint8 typeCode; + address to; + uint256 value; + bytes data; + } + + function proxy(ProxyCall[] memory calls) + external payable returns (bytes[] memory returnValues); + } + } + + const PROXY_FACTORY: Address = + polymarket_client_sdk_v2::types::address!("0xaB45c5A4B0c941a2F231C04C3f49182e1A254052"); + + pub(crate) fn is_proxy_mode(signature_type: Option<&str>) -> Result { + Ok(crate::config::resolve_signature_type(signature_type)? == "proxy") + } + + pub(crate) fn derive_proxy_address(private_key: Option<&str>) -> Result
{ + let signer = auth::resolve_signer(private_key)?; + let eoa = polymarket_client_sdk_v2::auth::Signer::address(&signer); + polymarket_client_sdk_v2::derive_proxy_wallet(eoa, POLYGON) + .ok_or_else(|| anyhow::anyhow!("Proxy wallet derivation not supported on this chain")) + } + + pub(crate) async fn send_call( + private_key: Option<&str>, + use_proxy: bool, + target: Address, + calldata: Vec, + ) -> Result<(B256, u64)> { + use alloy::providers::Provider as _; + + let provider = auth::create_provider(private_key).await?; + + let (tx_hash, block_number) = if use_proxy { + let factory = IProxyWallet::new(PROXY_FACTORY, &provider); + let call = IProxyWallet::ProxyCall { + typeCode: 1, + to: target, + value: U256::ZERO, + data: calldata.into(), + }; + let pending = factory.proxy(vec![call]).send().await?; + let hash = *pending.tx_hash(); + let receipt = pending.get_receipt().await?; + (hash, receipt.block_number) + } else { + let tx = alloy::rpc::types::TransactionRequest::default() + .to(target) + .input(alloy::primitives::Bytes::from(calldata).into()); + let pending = provider.send_transaction(tx).await?; + let hash = *pending.tx_hash(); + let receipt = pending.get_receipt().await?; + (hash, receipt.block_number) + }; + + let block_number = block_number.context("Block number not available in receipt")?; + Ok((tx_hash, block_number)) + } +} pub(crate) mod approve; pub(crate) mod bridge; diff --git a/src/commands/profiles.rs b/src/commands/profiles.rs index 7c95dfa..b94937f 100644 --- a/src/commands/profiles.rs +++ b/src/commands/profiles.rs @@ -1,7 +1,7 @@ use anyhow::Result; use clap::{Args, Subcommand}; -use polymarket_client_sdk::gamma::{self, types::request::PublicProfileRequest}; -use polymarket_client_sdk::types::Address; +use polymarket_client_sdk_v2::gamma::{self, types::request::PublicProfileRequest}; +use polymarket_client_sdk_v2::types::Address; use crate::output::OutputFormat; use crate::output::profiles::print_profile; diff --git a/src/commands/series.rs b/src/commands/series.rs index fc5e884..41a5aa5 100644 --- a/src/commands/series.rs +++ b/src/commands/series.rs @@ -1,6 +1,6 @@ use anyhow::Result; use clap::{Args, Subcommand}; -use polymarket_client_sdk::gamma::{ +use polymarket_client_sdk_v2::gamma::{ self, types::request::{SeriesByIdRequest, SeriesListRequest}, }; diff --git a/src/commands/setup.rs b/src/commands/setup.rs index 2e4a66d..0f43ae0 100644 --- a/src/commands/setup.rs +++ b/src/commands/setup.rs @@ -2,9 +2,9 @@ use std::io::{self, BufRead, Write}; use std::str::FromStr; use anyhow::{Context, Result}; -use polymarket_client_sdk::auth::{LocalSigner, Signer as _}; -use polymarket_client_sdk::types::Address; -use polymarket_client_sdk::{POLYGON, derive_proxy_wallet}; +use polymarket_client_sdk_v2::auth::{LocalSigner, Signer as _}; +use polymarket_client_sdk_v2::types::Address; +use polymarket_client_sdk_v2::{POLYGON, derive_proxy_wallet}; use crate::config; @@ -154,7 +154,7 @@ fn finish_setup(address: Address) -> Result<()> { Some(proxy) => { println!(" ✓ Proxy wallet derived"); println!(" Proxy: {proxy}"); - println!(" Deposit USDC to this address to start trading."); + println!(" Deposit pUSD to this address to start trading."); } None => { println!(" ✗ Could not derive proxy wallet"); @@ -167,9 +167,9 @@ fn finish_setup(address: Address) -> Result<()> { step_header(3, total, "Fund Wallet"); let deposit_addr = proxy.unwrap_or(address); - println!(" ○ Deposit USDC to your wallet to start trading"); + println!(" ○ Deposit pUSD to your wallet to start trading"); println!(" Run: polymarket bridge deposit {deposit_addr}"); - println!(" Or transfer USDC directly on Polygon"); + println!(" Or transfer pUSD directly on Polygon"); println!(); diff --git a/src/commands/sports.rs b/src/commands/sports.rs index 0782fd1..ae92607 100644 --- a/src/commands/sports.rs +++ b/src/commands/sports.rs @@ -1,6 +1,6 @@ use anyhow::Result; use clap::{Args, Subcommand}; -use polymarket_client_sdk::gamma::{self, types::request::TeamsRequest}; +use polymarket_client_sdk_v2::gamma::{self, types::request::TeamsRequest}; use crate::output::OutputFormat; use crate::output::sports::{print_sport_types, print_sports, print_teams}; diff --git a/src/commands/tags.rs b/src/commands/tags.rs index 64aae6a..0434846 100644 --- a/src/commands/tags.rs +++ b/src/commands/tags.rs @@ -1,6 +1,6 @@ use anyhow::Result; use clap::{Args, Subcommand}; -use polymarket_client_sdk::gamma::{ +use polymarket_client_sdk_v2::gamma::{ self, types::request::{ RelatedTagsByIdRequest, RelatedTagsBySlugRequest, TagByIdRequest, TagBySlugRequest, diff --git a/src/commands/wallet.rs b/src/commands/wallet.rs index d54a63b..9be088e 100644 --- a/src/commands/wallet.rs +++ b/src/commands/wallet.rs @@ -2,9 +2,9 @@ use std::str::FromStr; use anyhow::{Context, Result, bail}; use clap::{Args, Subcommand}; -use polymarket_client_sdk::auth::LocalSigner; -use polymarket_client_sdk::auth::Signer as _; -use polymarket_client_sdk::{POLYGON, derive_proxy_wallet}; +use polymarket_client_sdk_v2::auth::LocalSigner; +use polymarket_client_sdk_v2::auth::Signer as _; +use polymarket_client_sdk_v2::{POLYGON, derive_proxy_wallet}; use crate::config; use crate::output::OutputFormat; diff --git a/src/main.rs b/src/main.rs index 2abb55f..aaf5840 100644 --- a/src/main.rs +++ b/src/main.rs @@ -82,9 +82,9 @@ async fn main() -> ExitCode { #[allow(clippy::too_many_lines)] pub(crate) async fn run(cli: Cli) -> anyhow::Result<()> { // Lazy-init so we only pay for the client we actually use. - let gamma = std::cell::LazyCell::new(polymarket_client_sdk::gamma::Client::default); - let data = std::cell::LazyCell::new(polymarket_client_sdk::data::Client::default); - let bridge = std::cell::LazyCell::new(polymarket_client_sdk::bridge::Client::default); + let gamma = std::cell::LazyCell::new(polymarket_client_sdk_v2::gamma::Client::default); + let data = std::cell::LazyCell::new(polymarket_client_sdk_v2::data::Client::default); + let bridge = std::cell::LazyCell::new(polymarket_client_sdk_v2::bridge::Client::default); match cli.command { Commands::Setup => commands::setup::execute(), @@ -97,7 +97,13 @@ pub(crate) async fn run(cli: Cli) -> anyhow::Result<()> { Commands::Profiles(args) => commands::profiles::execute(&gamma, args, cli.output).await, Commands::Sports(args) => commands::sports::execute(&gamma, args, cli.output).await, Commands::Approve(args) => { - commands::approve::execute(args, cli.output, cli.private_key.as_deref()).await + commands::approve::execute( + args, + cli.output, + cli.private_key.as_deref(), + cli.signature_type.as_deref(), + ) + .await } Commands::Clob(args) => { commands::clob::execute( @@ -109,7 +115,13 @@ pub(crate) async fn run(cli: Cli) -> anyhow::Result<()> { .await } Commands::Ctf(args) => { - commands::ctf::execute(args, cli.output, cli.private_key.as_deref()).await + commands::ctf::execute( + args, + cli.output, + cli.private_key.as_deref(), + cli.signature_type.as_deref(), + ) + .await } Commands::Data(args) => commands::data::execute(&data, args, cli.output).await, Commands::Bridge(args) => commands::bridge::execute(&bridge, args, cli.output).await, diff --git a/src/output/approve.rs b/src/output/approve.rs index ec133e6..81abd7c 100644 --- a/src/output/approve.rs +++ b/src/output/approve.rs @@ -8,9 +8,9 @@ use super::OutputFormat; pub struct ApprovalStatus { pub contract_name: String, pub contract_address: String, - pub usdc_allowance: U256, - pub ctf_approved: bool, - pub usdc_error: Option, + pub collateral_allowance: U256, + pub ctf_approved: Option, + pub collateral_error: Option, pub ctf_error: Option, } @@ -18,8 +18,8 @@ pub struct ApprovalStatus { struct ApprovalRow { #[tabled(rename = "Contract")] contract: String, - #[tabled(rename = "USDC")] - usdc: String, + #[tabled(rename = "pUSD")] + collateral: String, #[tabled(rename = "CTF Tokens")] ctf: String, } @@ -30,17 +30,17 @@ fn format_allowance(allowance: U256) -> String { } else if allowance == U256::ZERO { "\u{2717} None".to_string() } else { - let usdc_decimals = U256::from(1_000_000); - let whole = allowance / usdc_decimals; - format!("\u{2713} {whole} USDC") + let collateral_decimals = U256::from(10u64.pow(crate::commands::COLLATERAL_DECIMALS)); + let whole = allowance / collateral_decimals; + format!("\u{2713} {whole} {}", crate::commands::COLLATERAL_SYMBOL) } } -fn format_ctf(approved: bool) -> String { - if approved { - "\u{2713} Approved".to_string() - } else { - "\u{2717} Not set".to_string() +fn format_ctf(approved: Option) -> String { + match approved { + Some(true) => "\u{2713} Approved".to_string(), + Some(false) => "\u{2717} Not set".to_string(), + None => "N/A".to_string(), } } @@ -53,12 +53,14 @@ pub fn print_approval_status(statuses: &[ApprovalStatus], output: &OutputFormat) let mut obj = serde_json::json!({ "contract": s.contract_name, "address": s.contract_address, - "usdc_allowance": s.usdc_allowance.to_string(), - "usdc_approved": s.usdc_allowance > U256::ZERO, + "collateral": crate::commands::COLLATERAL_SYMBOL, + "collateral_allowance": s.collateral_allowance.to_string(), + "collateral_approved": s.collateral_allowance > U256::ZERO, + "ctf_required": s.ctf_approved.is_some(), "ctf_approved": s.ctf_approved, }); - if let Some(ref err) = s.usdc_error { - obj["usdc_error"] = serde_json::Value::String(err.clone()); + if let Some(ref err) = s.collateral_error { + obj["collateral_error"] = serde_json::Value::String(err.clone()); } if let Some(ref err) = s.ctf_error { obj["ctf_error"] = serde_json::Value::String(err.clone()); @@ -74,10 +76,10 @@ pub fn print_approval_status(statuses: &[ApprovalStatus], output: &OutputFormat) .iter() .map(|s| ApprovalRow { contract: s.contract_name.clone(), - usdc: if let Some(ref err) = s.usdc_error { + collateral: if let Some(ref err) = s.collateral_error { format!("\u{2717} RPC error: {err}") } else { - format_allowance(s.usdc_allowance) + format_allowance(s.collateral_allowance) }, ctf: if let Some(ref err) = s.ctf_error { format!("\u{2717} RPC error: {err}") diff --git a/src/output/bridge.rs b/src/output/bridge.rs index 229ce52..2407977 100644 --- a/src/output/bridge.rs +++ b/src/output/bridge.rs @@ -1,4 +1,4 @@ -use polymarket_client_sdk::bridge::types::{ +use polymarket_client_sdk_v2::bridge::types::{ DepositResponse, DepositTransactionStatus, StatusResponse, SupportedAssetsResponse, }; use serde_json::json; diff --git a/src/output/clob/account.rs b/src/output/clob/account.rs index 4ca2466..6c07b1f 100644 --- a/src/output/clob/account.rs +++ b/src/output/clob/account.rs @@ -1,10 +1,10 @@ -use polymarket_client_sdk::auth::Credentials; -use polymarket_client_sdk::clob::types::response::{ +use polymarket_client_sdk_v2::auth::Credentials; +use polymarket_client_sdk_v2::clob::types::response::{ ApiKeysResponse, BalanceAllowanceResponse, BanStatusResponse, CurrentRewardResponse, GeoblockResponse, MarketRewardResponse, NotificationResponse, Page, RewardsPercentagesResponse, TotalUserEarningResponse, UserEarningResponse, UserRewardsEarningResponse, }; -use polymarket_client_sdk::types::Decimal; +use polymarket_client_sdk_v2::types::Decimal; use serde_json::json; use tabled::settings::Style; use tabled::{Table, Tabled}; @@ -58,7 +58,7 @@ pub fn print_balance( is_collateral: bool, output: &OutputFormat, ) -> anyhow::Result<()> { - let divisor = Decimal::from(10u64.pow(crate::commands::USDC_DECIMALS)); + let divisor = Decimal::from(10u64.pow(crate::commands::COLLATERAL_DECIMALS)); let human_balance = result.balance / divisor; match output { OutputFormat::Table => { @@ -262,12 +262,12 @@ pub fn print_earnings( } pub fn print_user_earnings_markets( - result: &[UserRewardsEarningResponse], + result: &Page, output: &OutputFormat, ) -> anyhow::Result<()> { match output { OutputFormat::Table => { - if result.is_empty() { + if result.data.is_empty() { println!("No earnings data found."); return Ok(()); } @@ -285,6 +285,7 @@ pub fn print_user_earnings_markets( min_size: String, } let rows: Vec = result + .data .iter() .map(|e| Row { question: truncate(&e.question, 40), @@ -296,9 +297,13 @@ pub fn print_user_earnings_markets( .collect(); let table = Table::new(rows).with(Style::rounded()).to_string(); println!("{table}"); + if result.next_cursor != END_CURSOR { + println!("Next cursor: {}", result.next_cursor); + } } OutputFormat::Json => { let data: Vec<_> = result + .data .iter() .map(|e| { json!({ @@ -333,7 +338,8 @@ pub fn print_user_earnings_markets( }) }) .collect(); - crate::output::print_json(&data)?; + let wrapper = json!({"data": data, "next_cursor": result.next_cursor}); + crate::output::print_json(&wrapper)?; } } Ok(()) diff --git a/src/output/clob/books.rs b/src/output/clob/books.rs index 71a322b..aaecf33 100644 --- a/src/output/clob/books.rs +++ b/src/output/clob/books.rs @@ -1,4 +1,4 @@ -use polymarket_client_sdk::clob::types::response::{ +use polymarket_client_sdk_v2::clob::types::response::{ LastTradePriceResponse, LastTradesPricesResponse, OrderBookSummaryResponse, }; use serde_json::json; diff --git a/src/output/clob/markets.rs b/src/output/clob/markets.rs index 2251149..7260b2d 100644 --- a/src/output/clob/markets.rs +++ b/src/output/clob/markets.rs @@ -1,4 +1,4 @@ -use polymarket_client_sdk::clob::types::response::{ +use polymarket_client_sdk_v2::clob::types::response::{ FeeRateResponse, MarketResponse, NegRiskResponse, Page, PriceHistoryResponse, SimplifiedMarketResponse, TickSizeResponse, }; diff --git a/src/output/clob/orders.rs b/src/output/clob/orders.rs index f6a0da7..fa0ff44 100644 --- a/src/output/clob/orders.rs +++ b/src/output/clob/orders.rs @@ -1,4 +1,4 @@ -use polymarket_client_sdk::clob::types::response::{ +use polymarket_client_sdk_v2::clob::types::response::{ CancelOrdersResponse, OpenOrderResponse, OrderScoringResponse, OrdersScoringResponse, Page, PostOrderResponse, TradeResponse, }; diff --git a/src/output/clob/prices.rs b/src/output/clob/prices.rs index 00f7588..5398620 100644 --- a/src/output/clob/prices.rs +++ b/src/output/clob/prices.rs @@ -1,4 +1,4 @@ -use polymarket_client_sdk::clob::types::response::{ +use polymarket_client_sdk_v2::clob::types::response::{ MidpointResponse, MidpointsResponse, PriceResponse, PricesResponse, SpreadResponse, SpreadsResponse, }; diff --git a/src/output/comments.rs b/src/output/comments.rs index 4204afb..dac81bd 100644 --- a/src/output/comments.rs +++ b/src/output/comments.rs @@ -1,4 +1,4 @@ -use polymarket_client_sdk::gamma::types::response::Comment; +use polymarket_client_sdk_v2::gamma::types::response::Comment; use tabled::settings::Style; use tabled::{Table, Tabled}; diff --git a/src/output/data.rs b/src/output/data.rs index 673cad2..36c1477 100644 --- a/src/output/data.rs +++ b/src/output/data.rs @@ -1,4 +1,4 @@ -use polymarket_client_sdk::data::types::response::{ +use polymarket_client_sdk_v2::data::types::response::{ Activity, BuilderLeaderboardEntry, BuilderVolumeEntry, ClosedPosition, LiveVolume, Market, MetaHolder, OpenInterest, Position, Trade, Traded, TraderLeaderboardEntry, Value, }; @@ -81,7 +81,7 @@ pub fn print_positions(positions: &[Position], output: &OutputFormat) -> anyhow: "proxy_wallet": p.proxy_wallet.to_string(), "redeemable": p.redeemable, "mergeable": p.mergeable, - "end_date": p.end_date.to_string(), + "end_date": p.end_date.map(|d| d.to_string()), "negative_risk": p.negative_risk, }) }) @@ -291,7 +291,7 @@ pub fn print_activity(activity: &[Activity], output: &OutputFormat) -> anyhow::R price: String, #[tabled(rename = "Size")] size: String, - #[tabled(rename = "USDC")] + #[tabled(rename = "Collateral")] usdc_size: String, #[tabled(rename = "Tx")] tx: String, diff --git a/src/output/events.rs b/src/output/events.rs index 5b4def2..2c72a2b 100644 --- a/src/output/events.rs +++ b/src/output/events.rs @@ -1,4 +1,4 @@ -use polymarket_client_sdk::gamma::types::response::Event; +use polymarket_client_sdk_v2::gamma::types::response::Event; use tabled::settings::Style; use tabled::{Table, Tabled}; diff --git a/src/output/markets.rs b/src/output/markets.rs index b9a3588..9ac53c4 100644 --- a/src/output/markets.rs +++ b/src/output/markets.rs @@ -1,5 +1,5 @@ -use polymarket_client_sdk::gamma::types::response::Market; -use polymarket_client_sdk::types::Decimal; +use polymarket_client_sdk_v2::gamma::types::response::Market; +use polymarket_client_sdk_v2::types::Decimal; use tabled::settings::Style; use tabled::{Table, Tabled}; diff --git a/src/output/mod.rs b/src/output/mod.rs index 05de836..97fbae5 100644 --- a/src/output/mod.rs +++ b/src/output/mod.rs @@ -12,7 +12,7 @@ pub(crate) mod sports; pub(crate) mod tags; use chrono::{DateTime, Utc}; -use polymarket_client_sdk::types::Decimal; +use polymarket_client_sdk_v2::types::Decimal; use rust_decimal::prelude::ToPrimitive; use tabled::Table; use tabled::settings::object::Columns; @@ -70,10 +70,20 @@ pub(crate) fn print_json(data: &(impl serde::Serialize + ?Sized)) -> anyhow::Res pub(crate) fn print_error(error: &anyhow::Error, format: OutputFormat) { match format { OutputFormat::Json => { - println!("{}", serde_json::json!({"error": error.to_string()})); + let chain: Vec = error.chain().map(ToString::to_string).collect(); + println!( + "{}", + serde_json::json!({ + "error": error.to_string(), + "causes": chain, + }) + ); } OutputFormat::Table => { eprintln!("Error: {error}"); + for cause in error.chain().skip(1) { + eprintln!(" caused by: {cause}"); + } } } } diff --git a/src/output/profiles.rs b/src/output/profiles.rs index 6cf8c70..56920b1 100644 --- a/src/output/profiles.rs +++ b/src/output/profiles.rs @@ -1,4 +1,4 @@ -use polymarket_client_sdk::gamma::types::response::PublicProfile; +use polymarket_client_sdk_v2::gamma::types::response::PublicProfile; use super::{OutputFormat, detail_field, print_detail_table, print_json}; diff --git a/src/output/series.rs b/src/output/series.rs index 99f1cfb..402c62c 100644 --- a/src/output/series.rs +++ b/src/output/series.rs @@ -1,4 +1,4 @@ -use polymarket_client_sdk::gamma::types::response::Series; +use polymarket_client_sdk_v2::gamma::types::response::Series; use tabled::settings::Style; use tabled::{Table, Tabled}; diff --git a/src/output/sports.rs b/src/output/sports.rs index 0eae756..31f2c30 100644 --- a/src/output/sports.rs +++ b/src/output/sports.rs @@ -1,4 +1,4 @@ -use polymarket_client_sdk::gamma::types::response::{ +use polymarket_client_sdk_v2::gamma::types::response::{ SportsMarketTypesResponse, SportsMetadata, Team, }; use tabled::settings::Style; diff --git a/src/output/tags.rs b/src/output/tags.rs index 3c57f55..1b44e02 100644 --- a/src/output/tags.rs +++ b/src/output/tags.rs @@ -1,4 +1,4 @@ -use polymarket_client_sdk::gamma::types::response::{RelatedTag, Tag}; +use polymarket_client_sdk_v2::gamma::types::response::{RelatedTag, Tag}; use tabled::settings::Style; use tabled::{Table, Tabled};