eth_twc_sdk/send_transaction/
self_transfer.rs1use alloy::primitives::{Address, TxHash};
7use alloy::providers::Provider;
8
9use crate::config::{self, assert_transfer_contract_configured};
10use crate::contract::{CommittedTransferDetail as SolCommitted, TransferDetail as SolDetail, TransferWithCommitment};
11use crate::error::SdkError;
12use crate::types::args::{
13 SelfBatchTransferWithCommitArgs, SelfSingleTransferArgs, SelfUniCommitTransfersArgs,
14};
15use crate::utils::assert_provider_supported_chain;
16
17pub async fn transfer<P: Provider>(
33 provider: &P,
34 from: Address,
35 args: SelfSingleTransferArgs,
36) -> Result<TxHash, SdkError> {
37 assert_transfer_contract_configured()?;
38 assert_provider_supported_chain(provider).await?;
39 let c = TransferWithCommitment::new(config::transfer_with_commitment_address(), provider);
40 c.transfer_0(args.token, args.to, args.value, args.commitment)
41 .from(from)
42 .call()
43 .await?;
44 let pending = c
45 .transfer_0(args.token, args.to, args.value, args.commitment)
46 .from(from)
47 .send()
48 .await?;
49 Ok(*pending.tx_hash())
50}
51
52pub async fn unified_transfer<P: Provider>(
59 provider: &P,
60 from: Address,
61 args: SelfUniCommitTransfersArgs,
62) -> Result<TxHash, SdkError> {
63 if args.details.is_empty() {
64 return Err(SdkError::EmptyTransferDetails);
65 }
66 assert_transfer_contract_configured()?;
67 assert_provider_supported_chain(provider).await?;
68 let details: Vec<SolDetail> = args
69 .details
70 .iter()
71 .map(|d| SolDetail {
72 to: d.to,
73 token: d.token,
74 value: d.value,
75 })
76 .collect();
77 let c = TransferWithCommitment::new(config::transfer_with_commitment_address(), provider);
78 c.transfer_1(details.clone(), args.commitment)
79 .from(from)
80 .call()
81 .await?;
82 let pending = c
83 .transfer_1(details, args.commitment)
84 .from(from)
85 .send()
86 .await?;
87 Ok(*pending.tx_hash())
88}
89
90pub async fn batch_transfer<P: Provider>(
97 provider: &P,
98 from: Address,
99 args: SelfBatchTransferWithCommitArgs,
100) -> Result<TxHash, SdkError> {
101 if args.details.is_empty() {
102 return Err(SdkError::EmptyTransferDetails);
103 }
104 assert_transfer_contract_configured()?;
105 assert_provider_supported_chain(provider).await?;
106 let details: Vec<SolCommitted> = args
107 .details
108 .iter()
109 .map(|d| SolCommitted {
110 to: d.to,
111 token: d.token,
112 value: d.value,
113 commitment: d.commitment,
114 })
115 .collect();
116 let c = TransferWithCommitment::new(config::transfer_with_commitment_address(), provider);
117 c.transfer_2(details.clone())
118 .from(from)
119 .call()
120 .await?;
121 let pending = c.transfer_2(details).from(from).send().await?;
122 Ok(*pending.tx_hash())
123}
124
125#[cfg(all(test, not(feature = "test-config")))]
126mod tests {
127 use alloy::primitives::{address, B256, U256};
128
129 use alloy::providers::ProviderBuilder;
130
131 use crate::types::{
132 CommittedTransferDetail, SelfBatchTransferWithCommitArgs, SelfSingleTransferArgs,
133 SelfUniCommitTransfersArgs, TransferDetail,
134 };
135
136 use super::{batch_transfer, transfer, unified_transfer};
137
138 #[tokio::test]
139 async fn transfer_fails_when_rpc_unreachable() {
140 let provider = ProviderBuilder::new().connect_http("http://127.0.0.1:65531".parse().unwrap());
141 let from = address!("0x1111111111111111111111111111111111111111");
142 let args = SelfSingleTransferArgs {
143 token: from,
144 to: address!("0x3333333333333333333333333333333333333333"),
145 value: U256::from(50u64),
146 commitment: B256::repeat_byte(0xaa),
147 };
148 let r = transfer(&provider, from, args).await;
149 assert!(matches!(r, Err(crate::SdkError::Alloy(_))));
150 }
151
152 #[tokio::test]
153 async fn unified_transfer_errors_on_empty_details_before_contract_configured() {
154 let provider = ProviderBuilder::new().connect_http("http://127.0.0.1:65529".parse().unwrap());
155 let from = address!("0x1111111111111111111111111111111111111111");
156 let args = SelfUniCommitTransfersArgs {
157 details: vec![],
158 commitment: B256::ZERO,
159 };
160 let r = unified_transfer(&provider, from, args).await;
161 assert!(matches!(
162 r,
163 Err(crate::SdkError::EmptyTransferDetails)
164 ));
165 }
166
167 #[tokio::test]
168 async fn batch_transfer_errors_on_empty_details_before_contract_configured() {
169 let provider = ProviderBuilder::new().connect_http("http://127.0.0.1:65528".parse().unwrap());
170 let from = address!("0x1111111111111111111111111111111111111111");
171 let args = SelfBatchTransferWithCommitArgs { details: vec![] };
172 let r = batch_transfer(&provider, from, args).await;
173 assert!(matches!(
174 r,
175 Err(crate::SdkError::EmptyTransferDetails)
176 ));
177 }
178
179 #[tokio::test]
180 async fn unified_transfer_fails_with_alloy_when_rpc_unreachable_after_nonempty_details() {
181 let provider = ProviderBuilder::new().connect_http("http://127.0.0.1:65527".parse().unwrap());
182 let from = address!("0x1111111111111111111111111111111111111111");
183 let detail = TransferDetail {
184 to: address!("0x3333333333333333333333333333333333333333"),
185 token: from,
186 value: U256::from(1u64),
187 };
188 let args = SelfUniCommitTransfersArgs {
189 details: vec![detail],
190 commitment: B256::repeat_byte(0xbb),
191 };
192 let r = unified_transfer(&provider, from, args).await;
193 assert!(matches!(r, Err(crate::SdkError::Alloy(_))));
194 }
195
196 #[tokio::test]
197 async fn batch_transfer_fails_with_alloy_when_rpc_unreachable_after_nonempty_details() {
198 let provider = ProviderBuilder::new().connect_http("http://127.0.0.1:65526".parse().unwrap());
199 let from = address!("0x1111111111111111111111111111111111111111");
200 let row = CommittedTransferDetail {
201 to: address!("0x3333333333333333333333333333333333333333"),
202 token: from,
203 value: U256::from(1u64),
204 commitment: B256::repeat_byte(0xcc),
205 };
206 let args = SelfBatchTransferWithCommitArgs {
207 details: vec![row],
208 };
209 let r = batch_transfer(&provider, from, args).await;
210 assert!(matches!(r, Err(crate::SdkError::Alloy(_))));
211 }
212}