bankr_agent_api/types.rs
1//! Request and response types for the Bankr Agent API.
2//!
3//! All types are derived from the official API documentation at
4//! <https://docs.bankr.bot/agent-api/overview>.
5
6use serde::{Deserialize, Serialize};
7
8// ---------------------------------------------------------------------------
9// User Info — GET /agent/me
10// ---------------------------------------------------------------------------
11
12/// Wallet entry returned by the `/agent/me` endpoint.
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct Wallet {
15 /// Chain identifier (`"evm"` or `"solana"`).
16 pub chain: String,
17 /// Wallet address.
18 pub address: String,
19}
20
21/// Social account linked to the user.
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct SocialAccount {
24 /// Platform name (e.g. `farcaster`, `twitter`, `telegram`).
25 pub platform: String,
26 /// Username on that platform.
27 pub username: Option<String>,
28}
29
30/// Bankr Club subscription info.
31#[derive(Debug, Clone, Serialize, Deserialize)]
32#[serde(rename_all = "camelCase")]
33pub struct BankrClub {
34 /// Whether the subscription is active.
35 pub active: bool,
36 /// `"monthly"` or `"yearly"`.
37 pub subscription_type: Option<String>,
38 /// Unix timestamp (ms) of next renewal or cancellation.
39 pub renew_or_cancel_on: Option<u64>,
40}
41
42/// Leaderboard entry.
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct Leaderboard {
45 /// User score.
46 pub score: u64,
47 /// Leaderboard rank.
48 pub rank: Option<u64>,
49}
50
51/// Successful response from `GET /agent/me`.
52#[derive(Debug, Clone, Serialize, Deserialize)]
53#[serde(rename_all = "camelCase")]
54pub struct UserInfoResponse {
55 /// Always `true` on success.
56 pub success: bool,
57 /// Wallet addresses.
58 pub wallets: Vec<Wallet>,
59 /// Connected social accounts.
60 pub social_accounts: Vec<SocialAccount>,
61 /// Referral code.
62 pub ref_code: Option<String>,
63 /// Bankr Club subscription info.
64 pub bankr_club: Option<BankrClub>,
65 /// Leaderboard info.
66 pub leaderboard: Option<Leaderboard>,
67}
68
69// ---------------------------------------------------------------------------
70// Prompt — POST /agent/prompt
71// ---------------------------------------------------------------------------
72
73/// Request body for `POST /agent/prompt`.
74#[derive(Debug, Clone, Serialize, Deserialize)]
75#[serde(rename_all = "camelCase")]
76pub struct PromptRequest {
77 /// Natural language command (max 10 000 characters).
78 pub prompt: String,
79 /// Optional thread ID to continue a conversation.
80 #[serde(skip_serializing_if = "Option::is_none")]
81 pub thread_id: Option<String>,
82}
83
84/// Success response (202 Accepted) from `POST /agent/prompt`.
85#[derive(Debug, Clone, Serialize, Deserialize)]
86#[serde(rename_all = "camelCase")]
87pub struct PromptResponse {
88 /// Always `true` on success.
89 pub success: bool,
90 /// Unique job identifier.
91 pub job_id: String,
92 /// Conversation thread ID.
93 pub thread_id: String,
94 /// Current status (always `"pending"` on creation).
95 pub status: String,
96 /// Human-readable message.
97 pub message: String,
98}
99
100// ---------------------------------------------------------------------------
101// Job Management — GET /agent/job/{jobId}, POST /agent/job/{jobId}/cancel
102// ---------------------------------------------------------------------------
103
104/// Job status values.
105#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
106#[serde(rename_all = "camelCase")]
107pub enum JobStatus {
108 /// Job is queued for processing.
109 Pending,
110 /// Job is currently being processed.
111 Processing,
112 /// Job finished successfully.
113 Completed,
114 /// Job encountered an error.
115 Failed,
116 /// Job was cancelled by the user.
117 Cancelled,
118}
119
120impl std::fmt::Display for JobStatus {
121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 match self {
123 Self::Pending => write!(f, "pending"),
124 Self::Processing => write!(f, "processing"),
125 Self::Completed => write!(f, "completed"),
126 Self::Failed => write!(f, "failed"),
127 Self::Cancelled => write!(f, "cancelled"),
128 }
129 }
130}
131
132/// A single status-update entry within a job.
133#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct StatusUpdate {
135 /// Status update message.
136 pub message: Option<String>,
137 /// Timestamp of the update.
138 pub timestamp: Option<String>,
139}
140
141/// Rich data item returned with completed jobs.
142#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct RichDataItem {
144 /// Type discriminator (e.g. `"token_info"`, `"chart"`).
145 #[serde(rename = "type")]
146 pub kind: String,
147 /// Remaining fields vary by type.
148 #[serde(flatten)]
149 pub extra: serde_json::Value,
150}
151
152/// Response from `GET /agent/job/{jobId}`.
153#[derive(Debug, Clone, Serialize, Deserialize)]
154#[serde(rename_all = "camelCase")]
155pub struct JobResponse {
156 /// Whether the request succeeded.
157 pub success: bool,
158 /// Job identifier.
159 pub job_id: String,
160 /// Conversation thread ID.
161 pub thread_id: Option<String>,
162 /// Current job status.
163 pub status: JobStatus,
164 /// Original prompt submitted.
165 pub prompt: String,
166 /// ISO 8601 creation timestamp.
167 pub created_at: String,
168 /// Whether the job can still be cancelled.
169 pub cancellable: Option<bool>,
170 /// Progress messages during processing.
171 pub status_updates: Option<Vec<StatusUpdate>>,
172 /// When processing started.
173 pub started_at: Option<String>,
174 /// Agent response text (when completed).
175 pub response: Option<String>,
176 /// Additional structured data (when completed).
177 pub rich_data: Option<Vec<RichDataItem>>,
178 /// When the job finished (completed or failed).
179 pub completed_at: Option<String>,
180 /// Processing duration in milliseconds (when completed).
181 pub processing_time: Option<u64>,
182 /// Error message (when failed).
183 pub error: Option<String>,
184 /// When the job was cancelled.
185 pub cancelled_at: Option<String>,
186}
187
188/// Response from `POST /agent/job/{jobId}/cancel`.
189#[derive(Debug, Clone, Serialize, Deserialize)]
190#[serde(rename_all = "camelCase")]
191pub struct CancelJobResponse {
192 /// Whether the request succeeded.
193 pub success: bool,
194 /// Job identifier.
195 pub job_id: String,
196 /// Status after cancellation.
197 pub status: String,
198 /// Original prompt.
199 pub prompt: Option<String>,
200 /// Creation timestamp.
201 pub created_at: Option<String>,
202 /// Cancellation timestamp.
203 pub cancelled_at: Option<String>,
204}
205
206// ---------------------------------------------------------------------------
207// Sign — POST /agent/sign
208// ---------------------------------------------------------------------------
209
210/// Signature type discriminator.
211#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
212pub enum SignatureType {
213 /// Standard Ethereum personal-sign.
214 #[serde(rename = "personal_sign")]
215 PersonalSign,
216 /// EIP-712 structured data signing.
217 #[serde(rename = "eth_signTypedData_v4")]
218 EthSignTypedDataV4,
219 /// Sign a transaction without broadcasting.
220 #[serde(rename = "eth_signTransaction")]
221 EthSignTransaction,
222}
223
224impl std::fmt::Display for SignatureType {
225 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226 match self {
227 Self::PersonalSign => write!(f, "personal_sign"),
228 Self::EthSignTypedDataV4 => write!(f, "eth_signTypedData_v4"),
229 Self::EthSignTransaction => write!(f, "eth_signTransaction"),
230 }
231 }
232}
233
234/// EVM transaction parameters used by both sign and submit endpoints.
235#[derive(Debug, Clone, Serialize, Deserialize)]
236#[serde(rename_all = "camelCase")]
237pub struct EvmTransaction {
238 /// Destination address.
239 pub to: String,
240 /// Chain ID.
241 pub chain_id: u64,
242 /// Value in wei (as string).
243 #[serde(skip_serializing_if = "Option::is_none")]
244 pub value: Option<String>,
245 /// Calldata (hex string starting with `0x`).
246 #[serde(skip_serializing_if = "Option::is_none")]
247 pub data: Option<String>,
248 /// Gas limit.
249 #[serde(skip_serializing_if = "Option::is_none")]
250 pub gas: Option<String>,
251 /// Legacy gas price in wei.
252 #[serde(skip_serializing_if = "Option::is_none")]
253 pub gas_price: Option<String>,
254 /// EIP-1559 max fee per gas.
255 #[serde(skip_serializing_if = "Option::is_none")]
256 pub max_fee_per_gas: Option<String>,
257 /// EIP-1559 priority fee.
258 #[serde(skip_serializing_if = "Option::is_none")]
259 pub max_priority_fee_per_gas: Option<String>,
260 /// Transaction nonce.
261 #[serde(skip_serializing_if = "Option::is_none")]
262 pub nonce: Option<u64>,
263}
264
265/// Request body for `POST /agent/sign`.
266///
267/// The body shape depends on `signature_type`:
268/// - `personal_sign` → `message` is required
269/// - `eth_signTypedData_v4` → `typed_data` is required
270/// - `eth_signTransaction` → `transaction` is required
271#[derive(Debug, Clone, Serialize, Deserialize)]
272#[serde(rename_all = "camelCase")]
273pub struct SignRequest {
274 /// The type of signature to produce.
275 pub signature_type: SignatureType,
276 /// Plain text message (for `personal_sign`).
277 #[serde(skip_serializing_if = "Option::is_none")]
278 pub message: Option<String>,
279 /// EIP-712 typed data (for `eth_signTypedData_v4`).
280 #[serde(skip_serializing_if = "Option::is_none")]
281 pub typed_data: Option<serde_json::Value>,
282 /// Transaction to sign (for `eth_signTransaction`).
283 #[serde(skip_serializing_if = "Option::is_none")]
284 pub transaction: Option<EvmTransaction>,
285}
286
287/// Success response from `POST /agent/sign`.
288#[derive(Debug, Clone, Serialize, Deserialize)]
289#[serde(rename_all = "camelCase")]
290pub struct SignResponse {
291 /// `true` if signing succeeded.
292 pub success: bool,
293 /// The hex-encoded signature.
294 pub signature: Option<String>,
295 /// Address that produced the signature.
296 pub signer: Option<String>,
297 /// The signature type used.
298 pub signature_type: Option<SignatureType>,
299 /// Error message if signing failed.
300 pub error: Option<String>,
301}
302
303// ---------------------------------------------------------------------------
304// Submit — POST /agent/submit
305// ---------------------------------------------------------------------------
306
307/// Request body for `POST /agent/submit`.
308#[derive(Debug, Clone, Serialize, Deserialize)]
309#[serde(rename_all = "camelCase")]
310pub struct SubmitRequest {
311 /// The transaction to submit.
312 pub transaction: EvmTransaction,
313 /// Human-readable description for logging.
314 #[serde(skip_serializing_if = "Option::is_none")]
315 pub description: Option<String>,
316 /// Wait for on-chain confirmation (default: `true`).
317 #[serde(skip_serializing_if = "Option::is_none")]
318 pub wait_for_confirmation: Option<bool>,
319}
320
321/// Response from `POST /agent/submit`.
322#[derive(Debug, Clone, Serialize, Deserialize)]
323#[serde(rename_all = "camelCase")]
324pub struct SubmitResponse {
325 /// `true` if submission succeeded.
326 pub success: bool,
327 /// The transaction hash.
328 pub transaction_hash: Option<String>,
329 /// `"success"`, `"reverted"`, `"pending"`, or `"failed"`.
330 pub status: Option<String>,
331 /// Block number (if confirmed).
332 pub block_number: Option<String>,
333 /// Gas used (if confirmed).
334 pub gas_used: Option<String>,
335 /// Address that signed the transaction.
336 pub signer: Option<String>,
337 /// Chain ID.
338 pub chain_id: Option<u64>,
339 /// Error message if submission failed.
340 pub error: Option<String>,
341}
342
343// ---------------------------------------------------------------------------
344// Common API Error Response
345// ---------------------------------------------------------------------------
346
347/// Standard error envelope returned by the Bankr API.
348#[derive(Debug, Clone, Serialize, Deserialize)]
349#[serde(rename_all = "camelCase")]
350pub struct ApiErrorBody {
351 /// Error type/title.
352 pub error: Option<String>,
353 /// Human-readable error message.
354 pub message: Option<String>,
355 /// When the rate-limit counter resets (Unix ms) — for 429 errors.
356 pub reset_at: Option<u64>,
357 /// Rate-limit quota.
358 pub limit: Option<u64>,
359 /// Number of messages used in the current window.
360 pub used: Option<u64>,
361}