1use alloy_contract::SolCallBuilder;
46use alloy_primitives::{Address, B256, Bytes, Signature, TxHash, U256, address, hex};
47use alloy_provider::bindings::IMulticall3;
48use alloy_provider::{
49 MULTICALL3_ADDRESS, MulticallError, MulticallItem, PendingTransactionError, Provider,
50};
51use alloy_sol_types::{Eip712Domain, SolCall, SolStruct, SolType, eip712_domain, sol};
52use alloy_transport::TransportError;
53use serde::{Deserialize, Serialize};
54use std::collections::HashMap;
55use std::sync::Arc;
56use tracing::Instrument;
57use tracing::instrument;
58use tracing_core::Level;
59
60pub mod client;
61pub mod types;
62
63use crate::chain::ChainProvider;
64use crate::chain::eip155::{
65 ChecksummedAddress, Eip155ChainReference, Eip155MetaTransactionProvider, Eip155TokenDeployment,
66 MetaTransaction, MetaTransactionSendError,
67};
68use crate::chain::{ChainId, ChainProviderOps, DeployedTokenAmount};
69use crate::proto;
70use crate::proto::{PaymentVerificationError, v1};
71use crate::scheme::{
72 X402SchemeFacilitator, X402SchemeFacilitatorBuilder, X402SchemeFacilitatorError, X402SchemeId,
73};
74use crate::timestamp::UnixTimestamp;
75
76pub use types::*;
77
78pub const VALIDATOR_ADDRESS: Address = address!("0xdAcD51A54883eb67D95FAEb2BBfdC4a9a6BD2a3B");
81
82pub struct V1Eip155Exact;
83
84impl V1Eip155Exact {
85 #[allow(dead_code)] pub fn price_tag<A: Into<ChecksummedAddress>>(
87 pay_to: A,
88 asset: DeployedTokenAmount<U256, Eip155TokenDeployment>,
89 ) -> v1::PriceTag {
90 let chain_id: ChainId = asset.token.chain_reference.into();
91 let network = chain_id
92 .as_network_name()
93 .unwrap_or_else(|| panic!("Can not get network name for chain id {}", chain_id));
94 let extra = asset
95 .token
96 .eip712
97 .and_then(|eip712| serde_json::to_value(&eip712).ok());
98 v1::PriceTag {
99 scheme: ExactScheme.to_string(),
100 pay_to: pay_to.into().to_string(),
101 asset: asset.token.address.to_string(),
102 network: network.to_string(),
103 amount: asset.amount.to_string(),
104 max_timeout_seconds: 300,
105 extra,
106 enricher: None,
107 }
108 }
109}
110
111impl X402SchemeId for V1Eip155Exact {
112 fn x402_version(&self) -> u8 {
113 1
114 }
115 fn namespace(&self) -> &str {
116 "eip155"
117 }
118 fn scheme(&self) -> &str {
119 ExactScheme.as_ref()
120 }
121}
122
123impl X402SchemeFacilitatorBuilder<&ChainProvider> for V1Eip155Exact {
124 fn build(
125 &self,
126 provider: &ChainProvider,
127 config: Option<serde_json::Value>,
128 ) -> Result<Box<dyn X402SchemeFacilitator>, Box<dyn std::error::Error>> {
129 let eip155_provider = if let ChainProvider::Eip155(provider) = provider {
130 Arc::clone(provider)
131 } else {
132 return Err("V1Eip155Exact::build: provider must be an Eip155ChainProvider".into());
133 };
134 self.build(eip155_provider, config)
135 }
136}
137
138impl<P> X402SchemeFacilitatorBuilder<P> for V1Eip155Exact
139where
140 P: Eip155MetaTransactionProvider + ChainProviderOps + Send + Sync + 'static,
141 Eip155ExactError: From<P::Error>,
142{
143 fn build(
144 &self,
145 provider: P,
146 _config: Option<serde_json::Value>,
147 ) -> Result<Box<dyn X402SchemeFacilitator>, Box<dyn std::error::Error>> {
148 Ok(Box::new(V1Eip155ExactFacilitator::new(provider)))
149 }
150}
151
152pub struct V1Eip155ExactFacilitator<P> {
153 provider: P,
154}
155
156impl<P> V1Eip155ExactFacilitator<P> {
157 pub fn new(provider: P) -> Self {
159 Self { provider }
160 }
161}
162
163#[async_trait::async_trait]
164impl<P> X402SchemeFacilitator for V1Eip155ExactFacilitator<P>
165where
166 P: Eip155MetaTransactionProvider + ChainProviderOps + Send + Sync,
167 P::Inner: Provider,
168 Eip155ExactError: From<P::Error>,
169{
170 async fn verify(
171 &self,
172 request: &proto::VerifyRequest,
173 ) -> Result<proto::VerifyResponse, X402SchemeFacilitatorError> {
174 let request = types::VerifyRequest::from_proto(request.clone())?;
175 let payload = &request.payment_payload;
176 let requirements = &request.payment_requirements;
177 let (contract, payment, eip712_domain) = assert_valid_payment(
178 self.provider.inner(),
179 self.provider.chain(),
180 payload,
181 requirements,
182 )
183 .await?;
184
185 let payer =
186 verify_payment(self.provider.inner(), &contract, &payment, &eip712_domain).await?;
187
188 Ok(v1::VerifyResponse::valid(payer.to_string()).into())
189 }
190
191 async fn settle(
192 &self,
193 request: &proto::SettleRequest,
194 ) -> Result<proto::SettleResponse, X402SchemeFacilitatorError> {
195 let request = types::SettleRequest::from_proto(request.clone())?;
196 let payload = &request.payment_payload;
197 let requirements = &request.payment_requirements;
198 let (contract, payment, eip712_domain) = assert_valid_payment(
199 self.provider.inner(),
200 self.provider.chain(),
201 payload,
202 requirements,
203 )
204 .await?;
205
206 let tx_hash = settle_payment(&self.provider, &contract, &payment, &eip712_domain).await?;
207 Ok(v1::SettleResponse::Success {
208 payer: payment.from.to_string(),
209 transaction: tx_hash.to_string(),
210 network: payload.network.clone(),
211 }
212 .into())
213 }
214
215 async fn supported(&self) -> Result<proto::SupportedResponse, X402SchemeFacilitatorError> {
216 let chain_id = self.provider.chain_id();
217 let kinds = {
218 let mut kinds = Vec::with_capacity(1);
219 let network = chain_id.as_network_name();
220 if let Some(network) = network {
221 kinds.push(proto::SupportedPaymentKind {
222 x402_version: v1::X402Version1.into(),
223 scheme: ExactScheme.to_string(),
224 network: network.to_string(),
225 extra: None,
226 });
227 }
228 kinds
229 };
230 let signers = {
231 let mut signers = HashMap::with_capacity(1);
232 signers.insert(chain_id, self.provider.signer_addresses());
233 signers
234 };
235 Ok(proto::SupportedResponse {
236 kinds,
237 extensions: Vec::new(),
238 signers,
239 })
240 }
241}
242
243#[derive(Debug)]
245pub struct ExactEvmPayment {
246 pub from: Address,
248 pub to: Address,
250 pub value: U256,
252 pub valid_after: UnixTimestamp,
254 pub valid_before: UnixTimestamp,
256 pub nonce: B256,
258 pub signature: Bytes,
260}
261
262sol!(
263 #[allow(missing_docs)]
264 #[allow(clippy::too_many_arguments)]
265 #[derive(Debug)]
266 #[sol(rpc)]
267 IEIP3009,
268 "abi/IEIP3009.json"
269);
270
271sol! {
272 #[allow(missing_docs)]
273 #[allow(clippy::too_many_arguments)]
274 #[derive(Debug)]
275 #[sol(rpc)]
276 Validator6492,
277 "abi/Validator6492.json"
278}
279
280#[instrument(skip_all, err)]
287async fn assert_valid_payment<'a, P: Provider>(
288 provider: &'a P,
289 chain: &Eip155ChainReference,
290 payload: &types::PaymentPayload,
291 requirements: &types::PaymentRequirements,
292) -> Result<
293 (
294 IEIP3009::IEIP3009Instance<&'a P>,
295 ExactEvmPayment,
296 Eip712Domain,
297 ),
298 Eip155ExactError,
299> {
300 let chain_id: ChainId = chain.into();
301 let payload_chain_id = ChainId::from_network_name(&payload.network)
302 .ok_or(PaymentVerificationError::UnsupportedChain)?;
303 if payload_chain_id != chain_id {
304 return Err(PaymentVerificationError::ChainIdMismatch.into());
305 }
306 let requirements_chain_id = ChainId::from_network_name(&requirements.network)
307 .ok_or(PaymentVerificationError::UnsupportedChain)?;
308 if requirements_chain_id != chain_id {
309 return Err(PaymentVerificationError::ChainIdMismatch.into());
310 }
311 let authorization = &payload.payload.authorization;
312 if authorization.to != requirements.pay_to {
313 return Err(PaymentVerificationError::RecipientMismatch.into());
314 }
315 let valid_after = authorization.valid_after;
316 let valid_before = authorization.valid_before;
317 assert_time(valid_after, valid_before)?;
318 let asset_address = requirements.asset;
319 let contract = IEIP3009::new(asset_address, provider);
320
321 let domain = assert_domain(chain, &contract, &asset_address, &requirements.extra).await?;
322
323 let amount_required = requirements.max_amount_required;
324 assert_enough_balance(&contract, &authorization.from, amount_required).await?;
325 assert_enough_value(&authorization.value, &amount_required)?;
326
327 let payment = ExactEvmPayment {
328 from: authorization.from,
329 to: authorization.to,
330 value: authorization.value,
331 valid_after: authorization.valid_after,
332 valid_before: authorization.valid_before,
333 nonce: authorization.nonce,
334 signature: payload.payload.signature.clone(),
335 };
336
337 Ok((contract, payment, domain))
338}
339
340#[instrument(skip_all, err)]
344pub fn assert_time(
345 valid_after: UnixTimestamp,
346 valid_before: UnixTimestamp,
347) -> Result<(), PaymentVerificationError> {
348 let now = UnixTimestamp::now();
349 if valid_before < now + 6 {
350 return Err(PaymentVerificationError::Expired);
351 }
352 if valid_after > now {
353 return Err(PaymentVerificationError::Early);
354 }
355 Ok(())
356}
357
358#[instrument(skip_all, err, fields(
360 network = %chain.as_chain_id(),
361 asset = %asset_address
362))]
363pub async fn assert_domain<P: Provider>(
364 chain: &Eip155ChainReference,
365 token_contract: &IEIP3009::IEIP3009Instance<P>,
366 asset_address: &Address,
367 extra: &Option<PaymentRequirementsExtra>,
368) -> Result<Eip712Domain, Eip155ExactError> {
369 let name = extra.as_ref().map(|extra| extra.name.clone());
370 let name = if let Some(name) = name {
371 name
372 } else {
373 token_contract
374 .name()
375 .call()
376 .into_future()
377 .instrument(tracing::info_span!(
378 "fetch_eip712_name",
379 otel.kind = "client",
380 ))
381 .await?
382 };
383 let version = extra.as_ref().map(|extra| extra.version.clone());
384 let version = if let Some(version) = version {
385 version
386 } else {
387 token_contract
388 .version()
389 .call()
390 .into_future()
391 .instrument(tracing::info_span!(
392 "fetch_eip712_version",
393 otel.kind = "client",
394 ))
395 .await?
396 };
397 let domain = eip712_domain! {
398 name: name,
399 version: version,
400 chain_id: chain.inner(),
401 verifying_contract: *asset_address,
402 };
403 Ok(domain)
404}
405
406#[instrument(skip_all, err, fields(
410 sender = %sender,
411 max_required = %max_amount_required,
412 token_contract = %ieip3009_token_contract.address()
413))]
414pub async fn assert_enough_balance<P: Provider>(
415 ieip3009_token_contract: &IEIP3009::IEIP3009Instance<P>,
416 sender: &Address,
417 max_amount_required: U256,
418) -> Result<(), Eip155ExactError> {
419 let balance = ieip3009_token_contract
420 .balanceOf(*sender)
421 .call()
422 .into_future()
423 .instrument(tracing::info_span!(
424 "fetch_token_balance",
425 token_contract = %ieip3009_token_contract.address(),
426 sender = %sender,
427 otel.kind = "client"
428 ))
429 .await?;
430
431 if balance < max_amount_required {
432 Err(PaymentVerificationError::InsufficientFunds.into())
433 } else {
434 Ok(())
435 }
436}
437
438#[instrument(skip_all, err, fields(
442 sent = %sent,
443 max_amount_required = %max_amount_required
444))]
445pub fn assert_enough_value(
446 sent: &U256,
447 max_amount_required: &U256,
448) -> Result<(), PaymentVerificationError> {
449 if sent < max_amount_required {
450 Err(PaymentVerificationError::InvalidPaymentAmount)
451 } else {
452 Ok(())
453 }
454}
455
456#[derive(Debug, Clone)]
458struct SignedMessage {
459 address: Address,
461 hash: B256,
463 signature: StructuredSignature,
465}
466
467sol!(
468 #[derive(Serialize, Deserialize)]
478 struct TransferWithAuthorization {
479 address from;
480 address to;
481 uint256 value;
482 uint256 validAfter;
483 uint256 validBefore;
484 bytes32 nonce;
485 }
486);
487
488impl SignedMessage {
489 pub fn extract(
508 payment: &ExactEvmPayment,
509 domain: &Eip712Domain,
510 ) -> Result<Self, StructuredSignatureFormatError> {
511 let transfer_with_authorization = TransferWithAuthorization {
512 from: payment.from,
513 to: payment.to,
514 value: payment.value,
515 validAfter: U256::from(payment.valid_after.as_secs()),
516 validBefore: U256::from(payment.valid_before.as_secs()),
517 nonce: payment.nonce,
518 };
519 let eip712_hash = transfer_with_authorization.eip712_signing_hash(domain);
520 let structured_signature: StructuredSignature = StructuredSignature::try_from_bytes(
521 payment.signature.clone(),
522 payment.from,
523 &eip712_hash,
524 )?;
525 let signed_message = Self {
526 address: payment.from,
527 hash: eip712_hash,
528 signature: structured_signature,
529 };
530 Ok(signed_message)
531 }
532}
533
534#[derive(Debug, Clone)]
543enum StructuredSignature {
544 EIP6492 {
546 factory: Address,
548 factory_calldata: Bytes,
550 inner: Bytes,
552 original: Bytes,
554 },
555 #[allow(clippy::upper_case_acronyms)]
557 EOA(Signature),
558 EIP1271(Bytes),
560}
561
562const EIP6492_MAGIC_SUFFIX: [u8; 32] =
567 hex!("6492649264926492649264926492649264926492649264926492649264926492");
568
569sol! {
570 #[derive(Debug)]
574 struct Sig6492 {
575 address factory;
576 bytes factoryCalldata;
577 bytes innerSig;
578 }
579}
580
581#[derive(Debug, thiserror::Error)]
582pub enum StructuredSignatureFormatError {
583 #[error(transparent)]
584 InvalidEIP6492Format(alloy_sol_types::Error),
585}
586
587impl StructuredSignature {
588 pub fn try_from_bytes(
589 bytes: Bytes,
590 expected_signer: Address,
591 prehash: &B256,
592 ) -> Result<Self, StructuredSignatureFormatError> {
593 let is_eip6492 = bytes.len() >= 32 && bytes[bytes.len() - 32..] == EIP6492_MAGIC_SUFFIX;
594 let signature = if is_eip6492 {
595 let body = &bytes[..bytes.len() - 32];
596 let sig6492 = Sig6492::abi_decode_params(body)
597 .map_err(StructuredSignatureFormatError::InvalidEIP6492Format)?;
598 StructuredSignature::EIP6492 {
599 factory: sig6492.factory,
600 factory_calldata: sig6492.factoryCalldata,
601 inner: sig6492.innerSig,
602 original: bytes,
603 }
604 } else {
605 let eoa_signature = if bytes.len() == 65 {
607 Signature::from_raw(&bytes).ok().map(|s| s.normalized_s())
608 } else if bytes.len() == 64 {
609 Some(Signature::from_erc2098(&bytes).normalized_s())
610 } else {
611 None
612 };
613 match eoa_signature {
614 None => StructuredSignature::EIP1271(bytes),
615 Some(s) => {
616 let is_expected_signer = s
617 .recover_address_from_prehash(prehash)
618 .ok()
619 .map(|r| r == expected_signer)
620 .unwrap_or(false);
621 if is_expected_signer {
622 StructuredSignature::EOA(s)
623 } else {
624 StructuredSignature::EIP1271(bytes)
625 }
626 }
627 }
628 };
629 Ok(signature)
630 }
631}
632
633impl TryFrom<Bytes> for StructuredSignature {
634 type Error = StructuredSignatureFormatError;
635
636 fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
644 let is_eip6492 = bytes.len() >= 32 && bytes[bytes.len() - 32..] == EIP6492_MAGIC_SUFFIX;
645 let signature = if is_eip6492 {
646 let body = &bytes[..bytes.len() - 32];
647 let sig6492 = Sig6492::abi_decode_params(body)
648 .map_err(StructuredSignatureFormatError::InvalidEIP6492Format)?;
649 StructuredSignature::EIP6492 {
650 factory: sig6492.factory,
651 factory_calldata: sig6492.factoryCalldata,
652 inner: sig6492.innerSig,
653 original: bytes,
654 }
655 } else {
656 StructuredSignature::EIP1271(bytes)
657 };
658 Ok(signature)
659 }
660}
661
662pub struct TransferWithAuthorization0Call<P>(
663 pub TransferWithAuthorizationCall<P, IEIP3009::transferWithAuthorization_0Call, Bytes>,
664);
665
666impl<'a, P: Provider> TransferWithAuthorization0Call<&'a P> {
667 pub fn new(
675 contract: &'a IEIP3009::IEIP3009Instance<P>,
676 payment: &ExactEvmPayment,
677 signature: Bytes,
678 ) -> Self {
679 let from = payment.from;
680 let to = payment.to;
681 let value = payment.value;
682 let valid_after = U256::from(payment.valid_after.as_secs());
683 let valid_before = U256::from(payment.valid_before.as_secs());
684 let nonce = payment.nonce;
685 let tx = contract.transferWithAuthorization_0(
686 from,
687 to,
688 value,
689 valid_after,
690 valid_before,
691 nonce,
692 signature.clone(),
693 );
694 TransferWithAuthorization0Call(TransferWithAuthorizationCall {
695 tx,
696 from,
697 to,
698 value,
699 valid_after,
700 valid_before,
701 nonce,
702 signature,
703 contract_address: *contract.address(),
704 })
705 }
706}
707
708pub struct TransferWithAuthorization1Call<P>(
709 pub TransferWithAuthorizationCall<P, IEIP3009::transferWithAuthorization_1Call, Signature>,
710);
711
712impl<'a, P: Provider> TransferWithAuthorization1Call<&'a P> {
713 pub fn new(
722 contract: &'a IEIP3009::IEIP3009Instance<P>,
723 payment: &ExactEvmPayment,
724 signature: Signature,
725 ) -> Self {
726 let from = payment.from;
727 let to = payment.to;
728 let value = payment.value;
729 let valid_after = U256::from(payment.valid_after.as_secs());
730 let valid_before = U256::from(payment.valid_before.as_secs());
731 let nonce = payment.nonce;
732 let v = 27 + (signature.v() as u8);
733 let r = B256::from(signature.r());
734 let s = B256::from(signature.s());
735 let tx = contract.transferWithAuthorization_1(
736 from,
737 to,
738 value,
739 valid_after,
740 valid_before,
741 nonce,
742 v,
743 r,
744 s,
745 );
746 TransferWithAuthorization1Call(TransferWithAuthorizationCall {
747 tx,
748 from,
749 to,
750 value,
751 valid_after,
752 valid_before,
753 nonce,
754 signature,
755 contract_address: *contract.address(),
756 })
757 }
758}
759
760pub struct TransferWithAuthorizationCall<P, TCall, TSignature> {
765 pub tx: SolCallBuilder<P, TCall>,
767 pub from: Address,
769 pub to: Address,
771 pub value: U256,
773 pub valid_after: U256,
775 pub valid_before: U256,
777 pub nonce: B256,
779 pub signature: TSignature,
781 pub contract_address: Address,
783}
784
785async fn is_contract_deployed<P: Provider>(
791 provider: &P,
792 address: &Address,
793) -> Result<bool, TransportError> {
794 let bytes = provider
795 .get_code_at(*address)
796 .into_future()
797 .instrument(tracing::info_span!("get_code_at",
798 address = %address,
799 otel.kind = "client",
800 ))
801 .await?;
802 Ok(!bytes.is_empty())
803}
804
805pub async fn verify_payment<P: Provider>(
806 provider: &P,
807 contract: &IEIP3009::IEIP3009Instance<&P>,
808 payment: &ExactEvmPayment,
809 eip712_domain: &Eip712Domain,
810) -> Result<Address, Eip155ExactError> {
811 let signed_message = SignedMessage::extract(payment, eip712_domain)?;
812
813 let payer = signed_message.address;
814 let hash = signed_message.hash;
815 match signed_message.signature {
816 StructuredSignature::EIP6492 {
817 factory: _,
818 factory_calldata: _,
819 inner,
820 original,
821 } => {
822 let validator6492 = Validator6492::new(VALIDATOR_ADDRESS, &provider);
824 let is_valid_signature_call =
825 validator6492.isValidSigWithSideEffects(payer, hash, original);
826 let transfer_call = TransferWithAuthorization0Call::new(contract, payment, inner);
828 let transfer_call = transfer_call.0;
829 let (is_valid_signature_result, transfer_result) = provider
831 .multicall()
832 .add(is_valid_signature_call)
833 .add(transfer_call.tx)
834 .aggregate3()
835 .instrument(tracing::info_span!("call_transferWithAuthorization_0",
836 from = %transfer_call.from,
837 to = %transfer_call.to,
838 value = %transfer_call.value,
839 valid_after = %transfer_call.valid_after,
840 valid_before = %transfer_call.valid_before,
841 nonce = %transfer_call.nonce,
842 signature = %transfer_call.signature,
843 token_contract = %transfer_call.contract_address,
844 otel.kind = "client",
845 ))
846 .await?;
847 let is_valid_signature_result = is_valid_signature_result
848 .map_err(|e| PaymentVerificationError::InvalidSignature(e.to_string()))?;
849 if !is_valid_signature_result {
850 return Err(PaymentVerificationError::InvalidSignature(
851 "Chain reported signature to be invalid".to_string(),
852 )
853 .into());
854 }
855 transfer_result
856 .map_err(|e| PaymentVerificationError::TransactionSimulation(e.to_string()))?;
857 }
858 StructuredSignature::EIP1271(signature) => {
859 let transfer_call = TransferWithAuthorization0Call::new(contract, payment, signature);
861 let transfer_call = transfer_call.0;
862 transfer_call
863 .tx
864 .call()
865 .into_future()
866 .instrument(tracing::info_span!("call_transferWithAuthorization_0",
867 from = %transfer_call.from,
868 to = %transfer_call.to,
869 value = %transfer_call.value,
870 valid_after = %transfer_call.valid_after,
871 valid_before = %transfer_call.valid_before,
872 nonce = %transfer_call.nonce,
873 signature = %transfer_call.signature,
874 token_contract = %transfer_call.contract_address,
875 otel.kind = "client",
876 ))
877 .await?;
878 }
879 StructuredSignature::EOA(signature) => {
880 let transfer_call = TransferWithAuthorization1Call::new(contract, payment, signature);
882 let transfer_call = transfer_call.0;
883 transfer_call
884 .tx
885 .call()
886 .into_future()
887 .instrument(tracing::info_span!("call_transferWithAuthorization_1",
888 from = %transfer_call.from,
889 to = %transfer_call.to,
890 value = %transfer_call.value,
891 valid_after = %transfer_call.valid_after,
892 valid_before = %transfer_call.valid_before,
893 nonce = %transfer_call.nonce,
894 signature = %transfer_call.signature,
895 token_contract = %transfer_call.contract_address,
896 otel.kind = "client",
897 ))
898 .await?;
899 }
900 }
901
902 Ok(payer)
903}
904
905pub async fn settle_payment<P, E>(
906 provider: &P,
907 contract: &IEIP3009::IEIP3009Instance<&P::Inner>,
908 payment: &ExactEvmPayment,
909 eip712_domain: &Eip712Domain,
910) -> Result<TxHash, Eip155ExactError>
911where
912 P: Eip155MetaTransactionProvider<Error = E>,
913 Eip155ExactError: From<E>,
914{
915 let signed_message = SignedMessage::extract(payment, eip712_domain)?;
916 let payer = payment.from;
917 let transaction_receipt_fut = match signed_message.signature {
918 StructuredSignature::EIP6492 {
919 factory,
920 factory_calldata,
921 inner,
922 original: _,
923 } => {
924 let is_contract_deployed = is_contract_deployed(provider.inner(), &payer).await?;
925 let transfer_call = TransferWithAuthorization0Call::new(contract, payment, inner);
926 let transfer_call = transfer_call.0;
927 if is_contract_deployed {
928 Eip155MetaTransactionProvider::send_transaction(
930 provider,
931 MetaTransaction {
932 to: transfer_call.tx.target(),
933 calldata: transfer_call.tx.calldata().clone(),
934 confirmations: 1,
935 },
936 )
937 .instrument(
938 tracing::info_span!("call_transferWithAuthorization_0",
939 from = %transfer_call.from,
940 to = %transfer_call.to,
941 value = %transfer_call.value,
942 valid_after = %transfer_call.valid_after,
943 valid_before = %transfer_call.valid_before,
944 nonce = %transfer_call.nonce,
945 signature = %transfer_call.signature,
946 token_contract = %transfer_call.contract_address,
947 sig_kind="EIP6492.deployed",
948 otel.kind = "client",
949 ),
950 )
951 } else {
952 let deployment_call = IMulticall3::Call3 {
954 allowFailure: true,
955 target: factory,
956 callData: factory_calldata,
957 };
958 let transfer_with_authorization_call = IMulticall3::Call3 {
959 allowFailure: false,
960 target: transfer_call.tx.target(),
961 callData: transfer_call.tx.calldata().clone(),
962 };
963 let aggregate_call = IMulticall3::aggregate3Call {
964 calls: vec![deployment_call, transfer_with_authorization_call],
965 };
966 Eip155MetaTransactionProvider::send_transaction(
967 provider,
968 MetaTransaction {
969 to: MULTICALL3_ADDRESS,
970 calldata: aggregate_call.abi_encode().into(),
971 confirmations: 1,
972 },
973 )
974 .instrument(
975 tracing::info_span!("call_transferWithAuthorization_0",
976 from = %transfer_call.from,
977 to = %transfer_call.to,
978 value = %transfer_call.value,
979 valid_after = %transfer_call.valid_after,
980 valid_before = %transfer_call.valid_before,
981 nonce = %transfer_call.nonce,
982 signature = %transfer_call.signature,
983 token_contract = %transfer_call.contract_address,
984 sig_kind="EIP6492.counterfactual",
985 otel.kind = "client",
986 ),
987 )
988 }
989 }
990 StructuredSignature::EIP1271(eip1271_signature) => {
991 let transfer_call =
992 TransferWithAuthorization0Call::new(contract, payment, eip1271_signature);
993 let transfer_call = transfer_call.0;
994 Eip155MetaTransactionProvider::send_transaction(
996 provider,
997 MetaTransaction {
998 to: transfer_call.tx.target(),
999 calldata: transfer_call.tx.calldata().clone(),
1000 confirmations: 1,
1001 },
1002 )
1003 .instrument(tracing::info_span!("call_transferWithAuthorization_0",
1004 from = %transfer_call.from,
1005 to = %transfer_call.to,
1006 value = %transfer_call.value,
1007 valid_after = %transfer_call.valid_after,
1008 valid_before = %transfer_call.valid_before,
1009 nonce = %transfer_call.nonce,
1010 signature = %transfer_call.signature,
1011 token_contract = %transfer_call.contract_address,
1012 sig_kind="EIP1271",
1013 otel.kind = "client",
1014 ))
1015 }
1016 StructuredSignature::EOA(signature) => {
1017 let transfer_call = TransferWithAuthorization1Call::new(contract, payment, signature);
1018 let transfer_call = transfer_call.0;
1019 Eip155MetaTransactionProvider::send_transaction(
1021 provider,
1022 MetaTransaction {
1023 to: transfer_call.tx.target(),
1024 calldata: transfer_call.tx.calldata().clone(),
1025 confirmations: 1,
1026 },
1027 )
1028 .instrument(tracing::info_span!("call_transferWithAuthorization_1",
1029 from = %transfer_call.from,
1030 to = %transfer_call.to,
1031 value = %transfer_call.value,
1032 valid_after = %transfer_call.valid_after,
1033 valid_before = %transfer_call.valid_before,
1034 nonce = %transfer_call.nonce,
1035 signature = %transfer_call.signature,
1036 token_contract = %transfer_call.contract_address,
1037 sig_kind="EOA",
1038 otel.kind = "client",
1039 ))
1040 }
1041 };
1042 let receipt = transaction_receipt_fut.await?;
1043 let success = receipt.status();
1044 if success {
1045 tracing::event!(Level::INFO,
1046 status = "ok",
1047 tx = %receipt.transaction_hash,
1048 "transferWithAuthorization_0 succeeded"
1049 );
1050 Ok(receipt.transaction_hash)
1051 } else {
1052 tracing::event!(
1053 Level::WARN,
1054 status = "failed",
1055 tx = %receipt.transaction_hash,
1056 "transferWithAuthorization_0 failed"
1057 );
1058 Err(Eip155ExactError::TransactionReverted(
1059 receipt.transaction_hash,
1060 ))
1061 }
1062}
1063
1064#[derive(Debug, thiserror::Error)]
1065pub enum Eip155ExactError {
1066 #[error(transparent)]
1067 Transport(#[from] TransportError),
1068 #[error(transparent)]
1069 PendingTransaction(#[from] PendingTransactionError),
1070 #[error("Transaction {0} reverted")]
1071 TransactionReverted(TxHash),
1072 #[error("Contract call failed: {0}")]
1073 ContractCall(String),
1074 #[error(transparent)]
1075 PaymentVerification(#[from] PaymentVerificationError),
1076}
1077
1078impl From<Eip155ExactError> for X402SchemeFacilitatorError {
1079 fn from(value: Eip155ExactError) -> Self {
1080 match value {
1081 Eip155ExactError::Transport(_) => Self::OnchainFailure(value.to_string()),
1082 Eip155ExactError::PendingTransaction(_) => Self::OnchainFailure(value.to_string()),
1083 Eip155ExactError::TransactionReverted(_) => Self::OnchainFailure(value.to_string()),
1084 Eip155ExactError::ContractCall(_) => Self::OnchainFailure(value.to_string()),
1085 Eip155ExactError::PaymentVerification(e) => Self::PaymentVerification(e),
1086 }
1087 }
1088}
1089
1090impl From<StructuredSignatureFormatError> for Eip155ExactError {
1091 fn from(e: StructuredSignatureFormatError) -> Self {
1092 Self::PaymentVerification(PaymentVerificationError::InvalidSignature(e.to_string()))
1093 }
1094}
1095
1096impl From<MetaTransactionSendError> for Eip155ExactError {
1097 fn from(e: MetaTransactionSendError) -> Self {
1098 match e {
1099 MetaTransactionSendError::Transport(e) => Self::Transport(e),
1100 MetaTransactionSendError::PendingTransaction(e) => Self::PendingTransaction(e),
1101 MetaTransactionSendError::Custom(e) => Self::ContractCall(e),
1102 }
1103 }
1104}
1105
1106impl From<MulticallError> for Eip155ExactError {
1107 fn from(e: MulticallError) -> Self {
1108 match e {
1109 MulticallError::ValueTx => Self::PaymentVerification(
1110 PaymentVerificationError::TransactionSimulation(e.to_string()),
1111 ),
1112 MulticallError::DecodeError(_) => Self::PaymentVerification(
1113 PaymentVerificationError::TransactionSimulation(e.to_string()),
1114 ),
1115 MulticallError::NoReturnData => Self::PaymentVerification(
1116 PaymentVerificationError::TransactionSimulation(e.to_string()),
1117 ),
1118 MulticallError::CallFailed(_) => Self::PaymentVerification(
1119 PaymentVerificationError::TransactionSimulation(e.to_string()),
1120 ),
1121 MulticallError::TransportError(transport_error) => Self::Transport(transport_error),
1122 }
1123 }
1124}
1125
1126impl From<alloy_contract::Error> for Eip155ExactError {
1127 fn from(e: alloy_contract::Error) -> Self {
1128 match e {
1129 alloy_contract::Error::UnknownFunction(_) => Self::ContractCall(e.to_string()),
1130 alloy_contract::Error::UnknownSelector(_) => Self::ContractCall(e.to_string()),
1131 alloy_contract::Error::NotADeploymentTransaction => Self::ContractCall(e.to_string()),
1132 alloy_contract::Error::ContractNotDeployed => Self::ContractCall(e.to_string()),
1133 alloy_contract::Error::ZeroData(_, _) => Self::ContractCall(e.to_string()),
1134 alloy_contract::Error::AbiError(_) => Self::ContractCall(e.to_string()),
1135 alloy_contract::Error::TransportError(e) => Self::Transport(e),
1136 alloy_contract::Error::PendingTransactionError(e) => Self::PendingTransaction(e),
1137 }
1138 }
1139}