polymarket_client/
account_client.rs1#[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}