Skip to main content

polymarket_relayer/
types.rs

1use serde::{Deserialize, Serialize};
2
3/// A transaction to be relayed.
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct Transaction {
6    /// Target contract address (checksummed hex).
7    pub to: String,
8    /// Encoded function calldata (hex with 0x prefix).
9    pub data: String,
10    /// Native token value to send (usually "0").
11    pub value: String,
12}
13
14/// Wallet type for relayed transactions.
15///
16/// Maps to Polymarket's `signature_type` values:
17///   0 = EOA (direct wallet, no abstraction)
18///   1 = POLY_PROXY (magic.link proxy wallet)
19///   2 = POLY_GNOSIS_SAFE (Gnosis Safe wallet)
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21#[repr(u8)]
22pub enum RelayerTxType {
23    /// EOA wallet — direct signing, no wallet abstraction (signature_type=0).
24    Eoa = 0,
25    /// Proxy wallet — auto-deploys on first tx (signature_type=1, e.g. magic.link).
26    Proxy = 1,
27    /// Gnosis Safe wallet — must call deploy() before first tx (signature_type=2).
28    Safe = 2,
29}
30
31impl RelayerTxType {
32    pub fn as_str(&self) -> &'static str {
33        match self {
34            RelayerTxType::Eoa => "EOA",
35            RelayerTxType::Proxy => "PROXY",
36            RelayerTxType::Safe => "SAFE",
37        }
38    }
39
40    /// The numeric signature_type used by the Polymarket API.
41    pub fn signature_type(&self) -> u8 {
42        *self as u8
43    }
44
45    /// Parse from Polymarket's numeric signature_type.
46    pub fn from_signature_type(sig_type: u8) -> Option<Self> {
47        match sig_type {
48            0 => Some(RelayerTxType::Eoa),
49            1 => Some(RelayerTxType::Proxy),
50            2 => Some(RelayerTxType::Safe),
51            _ => None,
52        }
53    }
54}
55
56/// Transaction state in the relayer pipeline.
57#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
58#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
59pub enum TxState {
60    New,
61    Executed,
62    Mined,
63    Confirmed,
64    Failed,
65    Invalid,
66}
67
68impl TxState {
69    pub fn is_terminal(&self) -> bool {
70        matches!(self, TxState::Confirmed | TxState::Failed | TxState::Invalid)
71    }
72
73    pub fn is_success(&self) -> bool {
74        matches!(self, TxState::Mined | TxState::Confirmed)
75    }
76}
77
78/// Result of a relayed transaction.
79#[derive(Debug, Clone, Serialize, Deserialize)]
80#[serde(rename_all = "camelCase")]
81pub struct RelayerTransactionResponse {
82    #[serde(rename = "transactionID", alias = "transactionId")]
83    pub transaction_id: String,
84    pub state: String,
85    #[serde(default)]
86    pub hash: Option<String>,
87    #[serde(default, rename = "transactionHash")]
88    pub transaction_hash: Option<String>,
89}
90
91/// Parsed transaction result.
92#[derive(Debug, Clone)]
93pub struct TxResult {
94    pub state: TxState,
95    pub tx_hash: Option<String>,
96    pub proxy_address: Option<String>,
97    pub error: Option<String>,
98}
99
100/// Signature parameters for Safe transactions.
101#[derive(Debug, Clone, Serialize)]
102#[serde(rename_all = "camelCase")]
103pub struct SafeSignatureParams {
104    pub gas_price: String,
105    pub operation: String,
106    pub safe_txn_gas: String,
107    pub base_gas: String,
108    pub gas_token: String,
109    pub refund_receiver: String,
110}
111
112impl Default for SafeSignatureParams {
113    fn default() -> Self {
114        Self {
115            gas_price: "0".to_string(),
116            operation: "0".to_string(),
117            safe_txn_gas: "0".to_string(),
118            base_gas: "0".to_string(),
119            gas_token: "0x0000000000000000000000000000000000000000".to_string(),
120            refund_receiver: "0x0000000000000000000000000000000000000000".to_string(),
121        }
122    }
123}
124
125/// Signature parameters for Proxy transactions.
126#[derive(Debug, Clone, Serialize)]
127#[serde(rename_all = "camelCase")]
128pub struct ProxySignatureParams {
129    pub gas_price: String,
130    pub gas_limit: String,
131    pub relayer_fee: String,
132    pub relay_hub: String,
133    pub relay: String,
134}
135
136/// Signature parameters for Safe-Create transactions.
137#[derive(Debug, Clone, Serialize)]
138#[serde(rename_all = "camelCase")]
139pub struct CreateSignatureParams {
140    pub payment_token: String,
141    pub payment: String,
142    pub payment_receiver: String,
143}
144
145impl Default for CreateSignatureParams {
146    fn default() -> Self {
147        Self {
148            payment_token: "0x0000000000000000000000000000000000000000".to_string(),
149            payment: "0".to_string(),
150            payment_receiver: "0x0000000000000000000000000000000000000000".to_string(),
151        }
152    }
153}
154
155/// The full request body for POST /submit.
156///
157/// Shape varies per `tx_type`:
158///   * `SAFE` / `PROXY` — populate `data`, `signature`, `signature_params`
159///   * `SAFE-CREATE` — same but with `data="0x"` and a `CreateSignatureParams`
160///   * `WALLET` — populate `signature` + `deposit_wallet_params`; `data` and
161///     `signature_params` are omitted (calls live inside `depositWalletParams`)
162///   * `WALLET-CREATE` — only `tx_type`, `from`, `to` are needed
163#[derive(Debug, Clone, Serialize)]
164#[serde(rename_all = "camelCase")]
165pub struct TransactionRequest {
166    #[serde(rename = "type")]
167    pub tx_type: String,
168    pub from: String,
169    pub to: String,
170    #[serde(skip_serializing_if = "Option::is_none")]
171    pub proxy_wallet: Option<String>,
172    #[serde(skip_serializing_if = "Option::is_none")]
173    pub data: Option<String>,
174    #[serde(skip_serializing_if = "Option::is_none")]
175    pub signature: Option<String>,
176    #[serde(skip_serializing_if = "Option::is_none")]
177    pub nonce: Option<String>,
178    #[serde(skip_serializing_if = "Option::is_none")]
179    pub signature_params: Option<serde_json::Value>,
180    #[serde(skip_serializing_if = "Option::is_none")]
181    pub metadata: Option<String>,
182    #[serde(skip_serializing_if = "Option::is_none")]
183    pub value: Option<String>,
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub deposit_wallet_params: Option<DepositWalletParams>,
186}
187
188/// A single call dispatched by a Deposit Wallet inside a `Batch`.
189///
190/// Mirrors the TypeScript SDK's `DepositWalletCall`. Note the field name is
191/// `target` (matching the EIP-712 typed-data field), not `to`.
192#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct DepositWalletCall {
194    pub target: String,
195    pub value: String,
196    pub data: String,
197}
198
199impl DepositWalletCall {
200    /// Construct a `DepositWalletCall` with a zero `value`.
201    pub fn new(target: impl Into<String>, data: impl Into<String>) -> Self {
202        Self {
203            target: target.into(),
204            value: "0".to_string(),
205            data: data.into(),
206        }
207    }
208}
209
210impl From<Transaction> for DepositWalletCall {
211    /// Adapt an internal `Transaction` (`{to, data, value}`) into a Deposit
212    /// Wallet call (`{target, value, data}`).
213    fn from(tx: Transaction) -> Self {
214        Self {
215            target: tx.to,
216            value: tx.value,
217            data: tx.data,
218        }
219    }
220}
221
222/// Body of the `depositWalletParams` field for a `type: "WALLET"` submit.
223#[derive(Debug, Clone, Serialize, Deserialize)]
224#[serde(rename_all = "camelCase")]
225pub struct DepositWalletParams {
226    pub deposit_wallet: String,
227    pub deadline: String,
228    pub calls: Vec<DepositWalletCall>,
229}
230
231/// Relay payload returned by GET /relay-payload.
232#[derive(Debug, Clone, Deserialize)]
233pub struct RelayPayload {
234    pub address: String,
235    pub nonce: String,
236}