use alloy_primitives::{Address, B256, Bytes, address};
use r402_core::scheme::AuthCaptureScheme;
use r402_core::wire::UnixTimestamp;
use serde::{Deserialize, Serialize};
use crate::asset::AssetTransferMethod;
use crate::chain::{ChecksummedAddress, TokenAmount};
pub const AUTH_CAPTURE_ESCROW_ADDRESS: Address =
address!("0xBdEA0D1bcC5966192B070Fdf62aB4EF5b4420cff");
pub const EIP3009_TOKEN_COLLECTOR_ADDRESS: Address =
address!("0x0E3dF9510de65469C4518D7843919c0b8C7A7757");
pub const PERMIT2_TOKEN_COLLECTOR_ADDRESS: Address =
address!("0x992476B9Ee81d52a5BdA0622C333938D0Af0aB26");
pub const AUTH_CAPTURE_CLOCK_SKEW_SECS: u64 = 6;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthCaptureExtra {
pub name: String,
pub version: String,
pub capture_authorizer: ChecksummedAddress,
pub capture_deadline: u64,
pub refund_deadline: u64,
pub fee_recipient: ChecksummedAddress,
pub min_fee_bps: u16,
pub max_fee_bps: u16,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub auto_capture: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub asset_transfer_method: Option<AssetTransferMethod>,
}
impl AuthCaptureExtra {
#[must_use]
pub const fn transfer_method(&self) -> AssetTransferMethod {
match self.asset_transfer_method {
Some(m) => m,
None => AssetTransferMethod::Eip3009,
}
}
#[must_use]
pub const fn auto_capture(&self) -> bool {
matches!(self.auto_capture, Some(true))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthCaptureEip3009Authorization {
pub from: Address,
pub to: Address,
pub value: TokenAmount,
pub valid_after: UnixTimestamp,
pub valid_before: UnixTimestamp,
pub nonce: B256,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthCaptureEip3009Payload {
pub authorization: AuthCaptureEip3009Authorization,
pub signature: Bytes,
pub salt: B256,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuthCaptureTokenPermissions {
pub token: Address,
pub amount: TokenAmount,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthCapturePermit2Authorization {
pub from: Address,
pub permitted: AuthCaptureTokenPermissions,
pub spender: Address,
pub nonce: TokenAmount,
pub deadline: TokenAmount,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthCapturePermit2Payload {
pub permit2_authorization: AuthCapturePermit2Authorization,
pub signature: Bytes,
pub salt: B256,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum AuthCapturePayload {
Permit2(AuthCapturePermit2Payload),
Eip3009(AuthCaptureEip3009Payload),
}
impl AuthCapturePayload {
#[must_use]
pub const fn payer(&self) -> Address {
match self {
Self::Eip3009(p) => p.authorization.from,
Self::Permit2(p) => p.permit2_authorization.from,
}
}
#[must_use]
pub const fn salt(&self) -> B256 {
match self {
Self::Eip3009(p) => p.salt,
Self::Permit2(p) => p.salt,
}
}
#[must_use]
pub const fn signature(&self) -> &Bytes {
match self {
Self::Eip3009(p) => &p.signature,
Self::Permit2(p) => &p.signature,
}
}
#[must_use]
pub const fn transfer_method(&self) -> AssetTransferMethod {
match self {
Self::Eip3009(_) => AssetTransferMethod::Eip3009,
Self::Permit2(_) => AssetTransferMethod::Permit2,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PaymentInfo {
pub operator: Address,
pub payer: Address,
pub receiver: Address,
pub token: Address,
pub max_amount: alloy_primitives::U256,
pub pre_approval_expiry: u64,
pub authorization_expiry: u64,
pub refund_expiry: u64,
pub min_fee_bps: u16,
pub max_fee_bps: u16,
pub fee_receiver: Address,
pub salt: alloy_primitives::U256,
}
pub mod v2 {
use r402_core::wire as proto_v2;
use super::{AuthCaptureExtra, AuthCapturePayload, AuthCaptureScheme};
use crate::chain::{ChecksummedAddress, TokenAmount};
pub type VerifyRequest = proto_v2::TypedVerifyRequest<2, PaymentPayload, PaymentRequirements>;
pub type SettleRequest = VerifyRequest;
pub type PaymentPayload = proto_v2::PaymentPayload<PaymentRequirements, AuthCapturePayload>;
pub type PaymentRequirements = proto_v2::PaymentRequirements<
AuthCaptureScheme,
TokenAmount,
ChecksummedAddress,
AuthCaptureExtra,
>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn canonical_addresses_checksum() {
assert_eq!(
AUTH_CAPTURE_ESCROW_ADDRESS.to_checksum(None),
"0xBdEA0D1bcC5966192B070Fdf62aB4EF5b4420cff"
);
assert_eq!(
EIP3009_TOKEN_COLLECTOR_ADDRESS.to_checksum(None),
"0x0E3dF9510de65469C4518D7843919c0b8C7A7757"
);
assert_eq!(
PERMIT2_TOKEN_COLLECTOR_ADDRESS.to_checksum(None),
"0x992476B9Ee81d52a5BdA0622C333938D0Af0aB26"
);
}
}