r402_evm/batch_settlement/
types.rs1use alloy_primitives::{Address, B256, Bytes, address};
7use r402_core::scheme::BatchSettlementScheme;
8use serde::{Deserialize, Serialize};
9
10use crate::asset::AssetTransferMethod;
11use crate::chain::{ChecksummedAddress, TokenAmount};
12
13pub const BATCH_SETTLEMENT_ADDRESS: Address =
15 address!("0x4020074e9dF2ce1deE5A9C1b5c3f541D02a10003");
16
17pub const ERC3009_DEPOSIT_COLLECTOR_ADDRESS: Address =
19 address!("0x4020806089470a89826cB9fB1f4059150b550004");
20
21pub const PERMIT2_DEPOSIT_COLLECTOR_ADDRESS: Address =
23 address!("0x4020425FAf3B746C082C2f942b4E5159887B0005");
24
25pub const MIN_WITHDRAW_DELAY: u64 = 900;
27pub const MAX_WITHDRAW_DELAY: u64 = 2_592_000;
29
30pub const BATCH_SETTLEMENT_DOMAIN_NAME: &str = "x402 Batch Settlement";
32pub const BATCH_SETTLEMENT_DOMAIN_VERSION: &str = "1";
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
37#[serde(rename_all = "camelCase")]
38pub struct ChannelConfig {
39 pub payer: Address,
41 pub payer_authorizer: Address,
43 pub receiver: Address,
45 pub receiver_authorizer: Address,
47 pub token: Address,
49 pub withdraw_delay: u64,
51 pub salt: B256,
53}
54
55#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
57#[serde(rename_all = "camelCase")]
58pub struct VoucherFields {
59 pub channel_id: B256,
61 pub max_claimable_amount: TokenAmount,
63 pub signature: Bytes,
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
69#[serde(rename_all = "camelCase")]
70pub struct BatchSettlementExtra {
71 pub receiver_authorizer: ChecksummedAddress,
73 pub withdraw_delay: u64,
75 pub name: String,
77 pub version: String,
79 #[serde(default, skip_serializing_if = "Option::is_none")]
81 pub asset_transfer_method: Option<AssetTransferMethod>,
82}
83
84impl BatchSettlementExtra {
85 #[must_use]
87 pub const fn transfer_method(&self) -> AssetTransferMethod {
88 match self.asset_transfer_method {
89 Some(m) => m,
90 None => AssetTransferMethod::Eip3009,
91 }
92 }
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub struct ChannelState {
98 pub balance: TokenAmount,
100 pub total_claimed: TokenAmount,
102 pub charged_cumulative: TokenAmount,
104 pub refund_nonce: TokenAmount,
106}
107
108impl Default for ChannelState {
109 fn default() -> Self {
110 use alloy_primitives::U256;
111 Self {
112 balance: TokenAmount::from(U256::ZERO),
113 total_claimed: TokenAmount::from(U256::ZERO),
114 charged_cumulative: TokenAmount::from(U256::ZERO),
115 refund_nonce: TokenAmount::from(U256::ZERO),
116 }
117 }
118}
119
120#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
122#[serde(rename_all = "camelCase")]
123pub struct DepositErc3009Authorization {
124 pub valid_after: TokenAmount,
126 pub valid_before: TokenAmount,
128 pub salt: B256,
130 pub signature: Bytes,
132}
133
134#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
136#[serde(rename_all = "camelCase")]
137pub struct DepositPermit2Authorization {
138 pub from: Address,
140 pub permitted: DepositTokenPermissions,
142 pub spender: Address,
144 pub nonce: TokenAmount,
146 pub deadline: TokenAmount,
148 pub witness: DepositWitness,
150 pub signature: Bytes,
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
156pub struct DepositTokenPermissions {
157 pub token: Address,
159 pub amount: TokenAmount,
161}
162
163#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
165#[serde(rename_all = "camelCase")]
166pub struct DepositWitness {
167 pub channel_id: B256,
169}
170
171#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
173#[serde(untagged)]
174pub enum DepositAuthorization {
175 Permit2 {
177 #[serde(rename = "permit2Authorization")]
179 permit2_authorization: DepositPermit2Authorization,
180 },
181 Erc3009 {
183 #[serde(rename = "erc3009Authorization")]
185 erc3009_authorization: DepositErc3009Authorization,
186 },
187}
188
189#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
191#[serde(tag = "type", rename_all = "camelCase")]
192pub enum BatchSettlementPayload {
193 Deposit {
195 channel_config: ChannelConfig,
197 voucher: VoucherFields,
199 deposit: Box<DepositBody>,
201 },
202 Voucher {
204 channel_config: ChannelConfig,
206 voucher: VoucherFields,
208 },
209 Refund {
211 channel_config: ChannelConfig,
213 voucher: VoucherFields,
215 #[serde(default, skip_serializing_if = "Option::is_none")]
217 amount: Option<TokenAmount>,
218 },
219}
220
221#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
223#[serde(rename_all = "camelCase")]
224pub struct DepositBody {
225 pub amount: TokenAmount,
227 pub authorization: DepositAuthorization,
229}
230
231impl BatchSettlementPayload {
232 #[must_use]
234 pub const fn channel_config(&self) -> &ChannelConfig {
235 match self {
236 Self::Deposit { channel_config, .. }
237 | Self::Voucher { channel_config, .. }
238 | Self::Refund { channel_config, .. } => channel_config,
239 }
240 }
241
242 #[must_use]
244 pub const fn voucher(&self) -> &VoucherFields {
245 match self {
246 Self::Deposit { voucher, .. }
247 | Self::Voucher { voucher, .. }
248 | Self::Refund { voucher, .. } => voucher,
249 }
250 }
251}
252
253pub mod v2 {
255 use r402_core::wire;
256
257 use super::{BatchSettlementExtra, BatchSettlementPayload, BatchSettlementScheme};
258 use crate::chain::{ChecksummedAddress, TokenAmount};
259
260 pub type VerifyRequest = wire::TypedVerifyRequest<2, PaymentPayload, PaymentRequirements>;
262 pub type SettleRequest = VerifyRequest;
264 pub type PaymentPayload = wire::PaymentPayload<PaymentRequirements, BatchSettlementPayload>;
266 pub type PaymentRequirements = wire::PaymentRequirements<
268 BatchSettlementScheme,
269 TokenAmount,
270 ChecksummedAddress,
271 BatchSettlementExtra,
272 >;
273}
274
275#[cfg(test)]
276mod tests {
277 use super::*;
278
279 #[test]
280 fn addresses() {
281 assert_eq!(
282 BATCH_SETTLEMENT_ADDRESS.to_checksum(None),
283 "0x4020074e9dF2ce1deE5A9C1b5c3f541D02a10003"
284 );
285 }
286}