Skip to main content

polymarket_client/
account_client.rs

1#[cfg(feature = "account")]
2use std::str::FromStr as _;
3
4#[cfg(feature = "account")]
5use polymarket_client_sdk_v2::data::types::request::{
6    ActivityRequest, PositionsRequest, ValueRequest,
7};
8#[cfg(feature = "account")]
9use polymarket_client_sdk_v2::types::{Address, B256};
10
11#[cfg(feature = "account")]
12use crate::account::{
13    decode_offset_cursor, mappers, next_offset_cursor, validate_page_size, validate_user, Activity,
14    FetchPortfolioValueError, FetchPortfolioValueRequest, ListActivityError, ListActivityRequest,
15    ListPositionsError, ListPositionsRequest, PortfolioValue, Position,
16};
17#[cfg(feature = "account")]
18use crate::error::user_input;
19#[cfg(feature = "account")]
20use crate::pagination::{Page, Paginator};
21#[cfg(feature = "account")]
22use crate::public_client::PublicClient;
23
24#[cfg(feature = "account")]
25pub type ListPositionsPaginator = Paginator<Vec<Position>, ListPositionsError>;
26
27#[cfg(feature = "account")]
28pub type ListActivityPaginator = Paginator<Vec<Activity>, ListActivityError>;
29
30#[cfg(feature = "account")]
31impl PublicClient {
32    pub fn list_positions(
33        &self,
34        request: ListPositionsRequest,
35    ) -> Result<ListPositionsPaginator, ListPositionsError> {
36        validate_user(&request.user)?;
37
38        let page_size = validate_page_size(request.page_size).map_err(ListPositionsError::from)?;
39        let data = self.data.clone();
40        let user = parse_address(&request.user).map_err(ListPositionsError::from)?;
41        let initial_cursor = request.cursor;
42
43        Ok(Paginator::new(
44            move |cursor| {
45                let data = data.clone();
46                Box::pin(async move {
47                    let state = decode_offset_cursor(cursor.as_ref(), page_size)
48                        .map_err(ListPositionsError::from)?;
49                    let limit = i32::try_from(state.page_size.saturating_add(1))
50                        .map_err(|_| ListPositionsError::from(user_input("page_size too large")))?;
51                    let offset = i32::try_from(state.offset)
52                        .map_err(|_| ListPositionsError::from(user_input("offset too large")))?;
53
54                    let req = PositionsRequest::builder()
55                        .user(user)
56                        .limit(limit)
57                        .map_err(|e| ListPositionsError::Data(e.to_string()))?
58                        .offset(offset)
59                        .map_err(|e| ListPositionsError::Data(e.to_string()))?
60                        .build();
61
62                    let items = data
63                        .positions(&req)
64                        .await
65                        .map_err(|e| ListPositionsError::Data(e.to_string()))?;
66
67                    let page_size_usize = state.page_size as usize;
68                    let has_more = items.len() > page_size_usize;
69                    let page_items = items
70                        .into_iter()
71                        .take(page_size_usize)
72                        .map(mappers::map_position)
73                        .collect();
74
75                    Ok(Page {
76                        items: page_items,
77                        has_more,
78                        next_cursor: has_more.then(|| next_offset_cursor(state)),
79                    })
80                })
81            },
82            initial_cursor,
83        ))
84    }
85
86    pub async fn fetch_portfolio_value(
87        &self,
88        request: FetchPortfolioValueRequest,
89    ) -> Result<Vec<PortfolioValue>, FetchPortfolioValueError> {
90        validate_user(&request.user)?;
91        let user = parse_address(&request.user).map_err(FetchPortfolioValueError::from)?;
92
93        let req = if request.markets.is_empty() {
94            ValueRequest::builder().user(user).build()
95        } else {
96            let parsed: Result<Vec<B256>, _> =
97                request.markets.iter().map(|m| parse_b256(m)).collect();
98            ValueRequest::builder()
99                .user(user)
100                .markets(parsed.map_err(FetchPortfolioValueError::from)?)
101                .build()
102        };
103
104        let values = self
105            .data
106            .value(&req)
107            .await
108            .map_err(|e| FetchPortfolioValueError::Data(e.to_string()))?;
109
110        Ok(values
111            .into_iter()
112            .map(mappers::map_portfolio_value)
113            .collect())
114    }
115
116    pub fn list_activity(
117        &self,
118        request: ListActivityRequest,
119    ) -> Result<ListActivityPaginator, ListActivityError> {
120        validate_user(&request.user)?;
121
122        let page_size = validate_page_size(request.page_size).map_err(ListActivityError::from)?;
123        let data = self.data.clone();
124        let user = parse_address(&request.user).map_err(ListActivityError::from)?;
125        let initial_cursor = request.cursor;
126
127        Ok(Paginator::new(
128            move |cursor| {
129                let data = data.clone();
130                Box::pin(async move {
131                    let state = decode_offset_cursor(cursor.as_ref(), page_size)
132                        .map_err(ListActivityError::from)?;
133                    let limit = i32::try_from(state.page_size.saturating_add(1))
134                        .map_err(|_| ListActivityError::from(user_input("page_size too large")))?;
135                    let offset = i32::try_from(state.offset)
136                        .map_err(|_| ListActivityError::from(user_input("offset too large")))?;
137
138                    let req = ActivityRequest::builder()
139                        .user(user)
140                        .limit(limit)
141                        .map_err(|e| ListActivityError::Data(e.to_string()))?
142                        .offset(offset)
143                        .map_err(|e| ListActivityError::Data(e.to_string()))?
144                        .build();
145
146                    let items = data
147                        .activity(&req)
148                        .await
149                        .map_err(|e| ListActivityError::Data(e.to_string()))?;
150
151                    let page_size_usize = state.page_size as usize;
152                    let has_more = items.len() > page_size_usize;
153                    let page_items = items
154                        .into_iter()
155                        .take(page_size_usize)
156                        .map(mappers::map_activity)
157                        .collect();
158
159                    Ok(Page {
160                        items: page_items,
161                        has_more,
162                        next_cursor: has_more.then(|| next_offset_cursor(state)),
163                    })
164                })
165            },
166            initial_cursor,
167        ))
168    }
169}
170
171#[cfg(feature = "account")]
172fn parse_address(value: &str) -> Result<Address, crate::error::UserInputError> {
173    Address::from_str(value).map_err(|e| user_input(format!("invalid address: {e}")))
174}
175
176#[cfg(feature = "account")]
177fn parse_b256(value: &str) -> Result<B256, crate::error::UserInputError> {
178    B256::from_str(value).map_err(|e| user_input(format!("invalid market id: {e}")))
179}