1mod error;
2mod transfer_v1;
3mod transfer_v2;
4
5use alloc::vec::Vec;
6
7#[cfg(any(feature = "testing", test))]
8use crate::testing::TestRng;
9#[cfg(feature = "datasize")]
10use datasize::DataSize;
11#[cfg(feature = "json-schema")]
12use once_cell::sync::Lazy;
13#[cfg(any(feature = "testing", test))]
14use rand::Rng;
15#[cfg(feature = "json-schema")]
16use schemars::JsonSchema;
17use serde::{Deserialize, Serialize};
18
19#[cfg(feature = "json-schema")]
20use crate::{account::AccountHash, transaction::TransactionV1Hash, URef};
21use crate::{
22 bytesrepr::{self, FromBytes, ToBytes, U8_SERIALIZED_LENGTH},
23 U512,
24};
25#[cfg(any(feature = "testing", feature = "json-schema", test))]
26use crate::{transaction::TransactionHash, Gas, InitiatorAddr};
27pub use error::TransferFromStrError;
28pub use transfer_v1::{TransferAddr, TransferV1, TRANSFER_ADDR_LENGTH};
29pub use transfer_v2::TransferV2;
30
31const V1_TAG: u8 = 0;
32const V2_TAG: u8 = 1;
33
34#[cfg(feature = "json-schema")]
35pub(super) static TRANSFER: Lazy<Transfer> = Lazy::new(|| {
36 let transaction_hash = TransactionHash::V1(TransactionV1Hash::from_raw([1; 32]));
37 let from = InitiatorAddr::AccountHash(AccountHash::new([2; 32]));
38 let to = Some(AccountHash::new([3; 32]));
39 let source = URef::from_formatted_str(
40 "uref-0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a-007",
41 )
42 .unwrap();
43 let target = URef::from_formatted_str(
44 "uref-1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b-000",
45 )
46 .unwrap();
47 let amount = U512::from(1_000_000_000_000_u64);
48 let gas = Gas::new(2_500_000_000_u64);
49 let id = Some(999);
50 Transfer::V2(TransferV2::new(
51 transaction_hash,
52 from,
53 to,
54 source,
55 target,
56 amount,
57 gas,
58 id,
59 ))
60});
61
62#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Debug)]
64#[cfg_attr(feature = "datasize", derive(DataSize))]
65#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
66pub enum Transfer {
67 #[serde(rename = "Version1")]
69 V1(TransferV1),
70 #[serde(rename = "Version2")]
72 V2(TransferV2),
73}
74
75impl Transfer {
76 pub fn amount(&self) -> U512 {
78 match self {
79 Transfer::V1(transfer_v1) => transfer_v1.amount,
80 Transfer::V2(transfer_v2) => transfer_v2.amount,
81 }
82 }
83
84 #[doc(hidden)]
86 #[cfg(feature = "json-schema")]
87 pub fn example() -> &'static Self {
88 &TRANSFER
89 }
90
91 #[cfg(any(feature = "testing", test))]
93 pub fn random(rng: &mut TestRng) -> Self {
94 use crate::DeployHash;
95
96 if rng.gen() {
97 Transfer::V1(TransferV1::new(
98 DeployHash::random(rng),
99 rng.gen(),
100 rng.gen(),
101 rng.gen(),
102 rng.gen(),
103 rng.gen(),
104 rng.gen(),
105 rng.gen(),
106 ))
107 } else {
108 Transfer::V2(TransferV2::new(
109 TransactionHash::random(rng),
110 InitiatorAddr::random(rng),
111 rng.gen(),
112 rng.gen(),
113 rng.gen(),
114 rng.gen(),
115 Gas::new(rng.gen::<u64>()),
116 rng.gen(),
117 ))
118 }
119 }
120}
121
122impl From<TransferV1> for Transfer {
123 fn from(v1_transfer: TransferV1) -> Self {
124 Transfer::V1(v1_transfer)
125 }
126}
127
128impl From<TransferV2> for Transfer {
129 fn from(v2_transfer: TransferV2) -> Self {
130 Transfer::V2(v2_transfer)
131 }
132}
133
134impl ToBytes for Transfer {
135 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
136 let mut buffer = bytesrepr::allocate_buffer(self)?;
137 self.write_bytes(&mut buffer)?;
138 Ok(buffer)
139 }
140
141 fn serialized_length(&self) -> usize {
142 U8_SERIALIZED_LENGTH
143 + match self {
144 Transfer::V1(transfer) => transfer.serialized_length(),
145 Transfer::V2(transfer) => transfer.serialized_length(),
146 }
147 }
148
149 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
150 match self {
151 Transfer::V1(transfer) => {
152 V1_TAG.write_bytes(writer)?;
153 transfer.write_bytes(writer)
154 }
155 Transfer::V2(transfer) => {
156 V2_TAG.write_bytes(writer)?;
157 transfer.write_bytes(writer)
158 }
159 }
160 }
161}
162
163impl FromBytes for Transfer {
164 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
165 let (tag, remainder) = u8::from_bytes(bytes)?;
166 match tag {
167 V1_TAG => {
168 let (transfer, remainder) = TransferV1::from_bytes(remainder)?;
169 Ok((Transfer::V1(transfer), remainder))
170 }
171 V2_TAG => {
172 let (transfer, remainder) = TransferV2::from_bytes(remainder)?;
173 Ok((Transfer::V2(transfer), remainder))
174 }
175 _ => Err(bytesrepr::Error::Formatting),
176 }
177 }
178}
179
180#[cfg(any(feature = "testing", feature = "gens", test))]
182pub mod gens {
183 use proptest::{
184 array,
185 prelude::{prop::option, Arbitrary, Strategy},
186 };
187
188 use super::*;
189 use crate::{
190 gens::{account_hash_arb, u512_arb, uref_arb},
191 transaction::gens::deploy_hash_arb,
192 };
193
194 pub fn transfer_v1_addr_arb() -> impl Strategy<Value = TransferAddr> {
195 array::uniform32(<u8>::arbitrary()).prop_map(TransferAddr::new)
196 }
197
198 pub fn transfer_v1_arb() -> impl Strategy<Value = TransferV1> {
199 (
200 deploy_hash_arb(),
201 account_hash_arb(),
202 option::of(account_hash_arb()),
203 uref_arb(),
204 uref_arb(),
205 u512_arb(),
206 u512_arb(),
207 option::of(<u64>::arbitrary()),
208 )
209 .prop_map(|(deploy_hash, from, to, source, target, amount, gas, id)| {
210 TransferV1 {
211 deploy_hash,
212 from,
213 to,
214 source,
215 target,
216 amount,
217 gas,
218 id,
219 }
220 })
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use crate::bytesrepr;
227
228 use super::*;
229
230 #[test]
231 fn bytesrepr_roundtrip() {
232 let rng = &mut TestRng::new();
233
234 let transfer = Transfer::random(rng);
235 bytesrepr::test_serialization_roundtrip(&transfer);
236 }
237}