use alloy_primitives::{Address as EvmAddress, B256, Bytes};
#[cfg(any(feature = "facilitator", feature = "client"))]
use alloy_sol_types::sol;
pub use r402_core::scheme::ExactScheme;
use r402_core::wire::UnixTimestamp;
use serde::{Deserialize, Serialize};
use crate::chain::TronTokenAmount;
#[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 sender(&self) -> EvmAddress {
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: EvmAddress,
pub amount: TronTokenAmount,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Permit2Witness {
pub to: EvmAddress,
pub valid_after: TronTokenAmount,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Permit2Authorization {
pub from: EvmAddress,
pub permitted: Permit2TokenPermissions,
pub spender: EvmAddress,
pub nonce: TronTokenAmount,
pub deadline: TronTokenAmount,
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: EvmAddress,
pub to: EvmAddress,
pub value: TronTokenAmount,
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 const fn new(
name: String,
version: String,
asset_transfer_method: Option<AssetTransferMethod>,
) -> Self {
Self {
name,
version,
asset_transfer_method,
}
}
}
#[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 TronTokenPermissions {
address token;
uint256 amount;
}
#[derive(Serialize, Deserialize)]
struct TronWitness {
address to;
uint256 validAfter;
}
#[derive(Serialize, Deserialize)]
struct TronPermitWitnessTransferFrom {
TronTokenPermissions permitted;
address spender;
uint256 nonce;
uint256 deadline;
TronWitness witness;
}
);
pub mod v2 {
use r402_core::wire as proto_v2;
use super::{ExactPayload, ExactScheme, PaymentRequirementsExtra};
use crate::chain::{Address, TronTokenAmount};
pub type VerifyRequest = proto_v2::TypedVerifyRequest<2, PaymentPayload, PaymentRequirements>;
pub type SettleRequest = VerifyRequest;
pub type PaymentPayload = proto_v2::PaymentPayload<PaymentRequirements, ExactPayload>;
pub type PaymentRequirements = proto_v2::PaymentRequirements<
ExactScheme,
TronTokenAmount,
Address,
PaymentRequirementsExtra,
>;
}
#[cfg(test)]
#[cfg(any(feature = "facilitator", feature = "client"))]
mod tests {
use alloy_primitives::{U256, keccak256};
use alloy_sol_types::SolStruct;
use super::*;
#[test]
fn witness_typehash_equals_spec_byte_string() {
let witness = TronWitness {
to: EvmAddress::ZERO,
validAfter: U256::ZERO,
};
let expected = keccak256(b"TronWitness(address to,uint256 validAfter)");
assert_eq!(witness.eip712_type_hash(), expected);
}
}