use alloy_primitives::{Address, B256, Bytes, address};
#[cfg(any(feature = "facilitator", feature = "client"))]
use alloy_sol_types::sol;
use r402::proto::UnixTimestamp;
pub use r402::scheme::ExactScheme;
use serde::{Deserialize, Serialize};
use crate::chain::TokenAmount;
pub const PERMIT2_ADDRESS: Address = address!("0x000000000022D473030F116dDEE9F6B43aC78BA3");
pub const X402_EXACT_PERMIT2_PROXY: Address =
address!("0x4020615294c913F045dc10f0a5cdEbd86c280001");
pub const X402_UPTO_PERMIT2_PROXY: Address = address!("0x4020633461b2895a48930Ff97eE8fCdE8E520002");
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum AssetTransferMethod {
Eip3009,
Permit2,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ExactPayload {
Permit2(Permit2Payload),
Eip3009(Eip3009Payload),
}
impl ExactPayload {
#[must_use]
pub const fn transfer_method(&self) -> AssetTransferMethod {
match self {
Self::Eip3009(_) => AssetTransferMethod::Eip3009,
Self::Permit2(_) => AssetTransferMethod::Permit2,
}
}
#[must_use]
pub const fn from_address(&self) -> Address {
match self {
Self::Eip3009(p) => p.authorization.from,
Self::Permit2(p) => p.permit2_authorization.from,
}
}
pub const fn signature(&self) -> &Bytes {
match self {
Self::Eip3009(p) => &p.signature,
Self::Permit2(p) => &p.signature,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Eip3009Payload {
pub signature: Bytes,
pub authorization: Eip3009Authorization,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Permit2TokenPermissions {
pub token: Address,
pub amount: TokenAmount,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Permit2Witness {
pub to: Address,
pub valid_after: TokenAmount,
pub extra: Bytes,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Permit2Authorization {
pub from: Address,
pub permitted: Permit2TokenPermissions,
pub spender: Address,
pub nonce: TokenAmount,
pub deadline: TokenAmount,
pub witness: Permit2Witness,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Permit2Payload {
pub signature: Bytes,
pub permit2_authorization: Permit2Authorization,
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Eip3009Authorization {
pub from: Address,
pub to: Address,
pub value: TokenAmount,
pub valid_after: UnixTimestamp,
pub valid_before: UnixTimestamp,
pub nonce: B256,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentRequirementsExtra {
pub name: String,
pub version: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub asset_transfer_method: Option<AssetTransferMethod>,
}
impl PaymentRequirementsExtra {
#[must_use]
pub fn from_deployment(
eip712: Option<crate::chain::TokenDeploymentEip712>,
method: Option<AssetTransferMethod>,
) -> Option<serde_json::Value> {
let extra = match (eip712, method) {
(Some(eip712), method) => Self::from(eip712).with_transfer_method(method),
(None, Some(m)) => Self {
name: String::new(),
version: String::new(),
asset_transfer_method: Some(m),
},
(None, None) => return None,
};
serde_json::to_value(extra).ok()
}
#[must_use]
pub const fn with_transfer_method(mut self, method: Option<AssetTransferMethod>) -> Self {
self.asset_transfer_method = method;
self
}
}
impl From<crate::chain::TokenDeploymentEip712> for PaymentRequirementsExtra {
fn from(eip712: crate::chain::TokenDeploymentEip712) -> Self {
Self {
name: eip712.name,
version: eip712.version,
asset_transfer_method: None,
}
}
}
#[cfg(any(feature = "facilitator", feature = "client"))]
sol!(
#[derive(Serialize, Deserialize)]
struct TransferWithAuthorization {
address from;
address to;
uint256 value;
uint256 validAfter;
uint256 validBefore;
bytes32 nonce;
}
);
#[cfg(any(feature = "facilitator", feature = "client"))]
sol!(
#[derive(Serialize, Deserialize)]
struct TokenPermissions {
address token;
uint256 amount;
}
#[derive(Serialize, Deserialize)]
struct Witness {
address to;
uint256 validAfter;
bytes extra;
}
#[derive(Serialize, Deserialize)]
struct PermitWitnessTransferFrom {
TokenPermissions permitted;
address spender;
uint256 nonce;
uint256 deadline;
Witness witness;
}
);
pub mod v2 {
use r402::proto::v2 as proto_v2;
use super::{ExactPayload, ExactScheme, PaymentRequirementsExtra};
use crate::chain::{ChecksummedAddress, TokenAmount};
pub type VerifyRequest = proto_v2::VerifyRequest<PaymentPayload, PaymentRequirements>;
pub type SettleRequest = VerifyRequest;
pub type PaymentPayload = proto_v2::PaymentPayload<PaymentRequirements, ExactPayload>;
pub type PaymentRequirements = proto_v2::PaymentRequirements<
ExactScheme,
TokenAmount,
ChecksummedAddress,
PaymentRequirementsExtra,
>;
}