1use fuels::{
2 core::{
3 codec::calldata,
4 traits::Tokenizable,
5 },
6 crypto::Message,
7 types::{
8 Bits256,
9 ChainId,
10 Identity,
11 U256 as FuelU256,
12 },
13};
14
15use crate::{
16 trade_account::{
17 generate_session_signing_payload,
18 generate_signing_payload,
19 },
20 trade_account_deploy::{
21 CallContractArg,
22 CallContractArgs as TradeAccountCallContractArgs,
23 CallParams,
24 Domain,
25 EIP712Domain,
26 Encoding,
27 MultiCallContractArgs,
28 ParallelCallContractArgs,
29 ParallelMultiCallContractArgs,
30 ParallelRevokeArgs,
31 ParallelSessionArgs,
32 ParallelWithdrawArgs,
33 RevokeArgs,
34 SRC16Domain,
35 Secp256k1,
36 SessionArgs,
37 SetProxyTargetArgs,
38 Signature,
39 WithdrawArgs,
40 },
41};
42use ethers_core::{
43 types::{
44 H160,
45 RecoveryMessage,
46 Signature as EthSig,
47 U256,
48 U256 as Ethers256,
49 transaction::eip712::EIP712Domain as EVMDomain,
50 },
51 utils::keccak256,
52};
53
54const EVM_ADDRESS_PADDING_LENGTH: usize = 12;
55
56fn fuel_contract_id_to_evm_address(id: [u8; 32]) -> [u8; 20] {
59 let mut addr = [0u8; 20];
60 addr.copy_from_slice(&id[12..32]);
62 addr
63}
64
65pub trait DomainExt {
66 fn separator(&self) -> [u8; 32];
67 fn encode<T>(&self, struct_type: T) -> [u8; 32]
68 where
69 T: SRC16Encode;
70}
71
72impl DomainExt for Domain {
73 fn separator(&self) -> [u8; 32] {
74 match self {
75 Domain::EIP712Domain(d) => EVMDomain::from(d.clone()).separator(),
76 Domain::SRC16Domain(d) => d.separator(),
77 }
78 }
79
80 fn encode<T>(&self, struct_type: T) -> [u8; 32]
81 where
82 T: SRC16Encode,
83 {
84 let ds = self.separator();
85 let encoding = match self {
86 Domain::SRC16Domain(_) => Encoding::SRC16,
87 Domain::EIP712Domain(_) => Encoding::EIP712,
88 };
89 let sh = struct_type.struct_hash(encoding);
90
91 let mut preimage = Vec::with_capacity(2 + 32 + 32);
92 preimage.extend_from_slice(&[0x19, 0x01]);
93 preimage.extend_from_slice(ds.as_slice());
94 preimage.extend_from_slice(sh.as_slice());
95
96 keccak256(preimage)
97 }
98}
99
100impl SRC16Domain {
101 pub fn separator(&self) -> [u8; 32] {
103 let mut tokens: Vec<u8> = Vec::new();
104
105 tokens.extend_from_slice(src16_domain_type_hash(self).as_ref());
106
107 if self.name.is_some() {
108 tokens.extend_from_slice(keccak256(self.name.clone().unwrap()).as_slice());
109 }
110 if self.version.is_some() {
111 tokens.extend_from_slice(keccak256(self.version.clone().unwrap()).as_slice());
112 }
113
114 if let Some(chain_id) = self.chain_id {
115 tokens.extend(chain_id.0.iter().flat_map(|x| x.to_be_bytes()));
116 }
117
118 if let Some(verifying_contract) = self.verifying_contract {
119 tokens.extend_from_slice(verifying_contract.as_slice());
120 }
121
122 if let Some(salt) = self.salt {
123 tokens.extend_from_slice(salt.0.as_slice());
124 }
125
126 keccak256(tokens)
127 }
128}
129
130fn src16_domain_type_hash(domain: &SRC16Domain) -> [u8; 32] {
131 let mut fields = Vec::new();
132
133 if domain.name.is_some() {
135 fields.push("string name");
136 }
137 if domain.version.is_some() {
138 fields.push("string version");
139 }
140 if domain.chain_id.is_some() {
141 fields.push("u256 chain_id");
142 }
143 if domain.verifying_contract.is_some() {
144 fields.push("contractId verifying_contract");
145 }
146 if domain.salt.is_some() {
147 fields.push("b256 salt");
148 }
149
150 let type_string = format!("SRC16Domain({})", fields.join(","));
151
152 keccak256(type_string.as_bytes())
153}
154
155fn fuel_u256_to_bytes(value: &FuelU256) -> [u8; 32] {
156 let mut bytes = [0u8; 32];
157 value.to_big_endian(&mut bytes);
158 bytes
159}
160
161impl From<EIP712Domain> for EVMDomain {
162 fn from(domain: EIP712Domain) -> Self {
163 let chain_id = domain.chain_id.map(|id| Ethers256(id.0));
164 let verifying_contract = domain
165 .verifying_contract
166 .map(|contract| H160(fuel_contract_id_to_evm_address(contract.0)));
167 let salt = domain.salt.map(|s| s.0);
168
169 EVMDomain {
170 name: domain.name,
171 version: domain.version,
172 chain_id,
173 verifying_contract,
174 salt,
175 }
176 }
177}
178
179pub trait SRC16Encode {
180 #[allow(dead_code)]
181 fn type_hash(encoding: Encoding) -> [u8; 32];
182 fn struct_hash(self, encoding: Encoding) -> [u8; 32];
183}
184
185fn from_compact_or_standard(bytes: &[u8]) -> anyhow::Result<EthSig> {
186 match bytes.len() {
187 65 => Ok(EthSig::try_from(bytes)?),
188 64 => {
189 let r = U256::from_big_endian(&bytes[..32]);
190 let mut s_bytes = [0u8; 32];
191 s_bytes.copy_from_slice(&bytes[32..64]);
192
193 let y_parity = (s_bytes[0] >> 7) & 1;
194 s_bytes[0] &= 0x7F;
197
198 let s = U256::from_big_endian(&s_bytes);
199 let v = 27 + y_parity as u64;
201
202 Ok(EthSig { r, s, v })
203 }
204 n => anyhow::bail!("invalid length {n}, expected 64 or 65"),
205 }
206}
207
208pub trait SignatureCurve {
209 type Address;
210
211 fn verify_owner<T>(
213 &self,
214 nonce: u64,
215 chain_id: ChainId,
216 f_name: String,
217 args: Option<T>,
218 address: &Self::Address,
219 ) -> anyhow::Result<()>
220 where
221 T: Tokenizable;
222
223 fn verify_session<T>(
225 &self,
226 nonce: u64,
227 args: T,
228 address: &Self::Address,
229 ) -> anyhow::Result<()>
230 where
231 T: Tokenizable;
232
233 fn verify_typed<T>(
234 &self,
235 args: T,
236 domain: &Domain,
237 address: &Self::Address,
238 ) -> anyhow::Result<()>
239 where
240 T: SRC16Encode;
241}
242
243pub struct Secp256k1Address(pub fuels::types::Address);
244
245impl SignatureCurve for Secp256k1 {
246 type Address = Secp256k1Address;
247
248 fn verify_owner<T>(
249 &self,
250 nonce: u64,
251 chain_id: ChainId,
252 f_name: String,
253 args: Option<T>,
254 address: &Self::Address,
255 ) -> anyhow::Result<()>
256 where
257 T: Tokenizable,
258 {
259 let address = &address.0;
260
261 if address.starts_with([0u8; EVM_ADDRESS_PADDING_LENGTH].as_ref()) {
264 let evm_address = &address[EVM_ADDRESS_PADDING_LENGTH..];
265 let evm_message = if let Some(args_some) = args {
266 calldata!((nonce, *chain_id, f_name, args_some))?
267 } else {
268 calldata!((nonce, *chain_id, f_name))?
269 };
270 let evm_sig = from_compact_or_standard(&self.bits)?;
271 let recovered_address = evm_sig.recover(&evm_message[..])?;
272
273 if recovered_address.as_bytes() != evm_address {
274 anyhow::bail!("invalid evm address provided");
275 }
276
277 evm_sig.verify(evm_message, recovered_address)?;
278 } else {
279 let fuel_message = generate_signing_payload(nonce, *chain_id, f_name, args);
280 let fuels_sig = fuels::crypto::Signature::from_bytes(self.bits);
281 let recovered_pub_key = fuels_sig.recover(&fuel_message)?;
282
283 if *recovered_pub_key.hash() != address.as_ref() {
284 anyhow::bail!("invalid fuel address provided");
285 }
286
287 fuels_sig.verify(&recovered_pub_key, &fuel_message)?;
288 }
289
290 Ok(())
291 }
292
293 fn verify_session<T>(
294 &self,
295 nonce: u64,
296 args: T,
297 address: &Self::Address,
298 ) -> anyhow::Result<()>
299 where
300 T: Tokenizable,
301 {
302 let address = &address.0;
303 let fuel_message = generate_session_signing_payload(nonce, args);
304
305 let fuels_sig = fuels::crypto::Signature::from_bytes(self.bits);
306 let recovered_pub_key = fuels_sig.recover(&fuel_message)?;
307
308 if *recovered_pub_key.hash() != address.as_ref() {
309 anyhow::bail!("invalid fuel address provided");
310 }
311
312 fuels_sig.verify(&recovered_pub_key, &fuel_message)?;
313
314 Ok(())
315 }
316
317 fn verify_typed<T>(
318 &self,
319 args: T,
320 domain: &Domain,
321 address: &Self::Address,
322 ) -> anyhow::Result<()>
323 where
324 T: SRC16Encode,
325 {
326 let address = &address.0;
327
328 let message: Message = Message::from_bytes(domain.encode(args));
330
331 if address.starts_with([0u8; EVM_ADDRESS_PADDING_LENGTH].as_ref()) {
333 let evm_address = &address[EVM_ADDRESS_PADDING_LENGTH..];
335 let evm_sig = from_compact_or_standard(&self.bits)?;
336 let recovered_address =
337 evm_sig.recover(RecoveryMessage::Hash((*message).into()))?;
338
339 if recovered_address.as_bytes() != evm_address {
340 anyhow::bail!("invalid evm address provided");
341 }
342
343 evm_sig
344 .verify(RecoveryMessage::Hash((*message).into()), recovered_address)?;
345 } else {
346 let fuels_sig = fuels::crypto::Signature::from_bytes(self.bits);
348 let recovered_pub_key = fuels_sig.recover(&message)?;
349
350 if *recovered_pub_key.hash() != address.as_ref() {
351 anyhow::bail!("invalid fuel address provided");
352 }
353
354 fuels_sig.verify(&recovered_pub_key, &message)?;
355 }
356
357 Ok(())
358 }
359}
360
361pub enum Address {
362 Secp256k1(Secp256k1Address),
363 Secp256r1,
364 Ed25519,
365}
366
367impl From<fuels::types::Address> for Address {
368 fn from(addr: fuels::types::Address) -> Self {
369 Address::Secp256k1(Secp256k1Address(addr))
370 }
371}
372
373impl Signature {
374 pub fn verify_owner<T>(
375 &self,
376 nonce: u64,
377 chain_id: ChainId,
378 f_name: String,
379 args: Option<T>,
380 address: &Address,
381 ) -> anyhow::Result<()>
382 where
383 T: Tokenizable,
384 {
385 match (self, address) {
386 (Signature::Secp256k1(sig), Address::Secp256k1(addr)) => {
387 sig.verify_owner(nonce, chain_id, f_name, args, addr)
388 }
389 (Signature::Secp256r1(_), Address::Secp256r1) => {
390 anyhow::bail!(
391 "owner signature verification not supported for p256 signatures"
392 )
393 }
394 (Signature::Ed25519(_), Address::Ed25519) => {
395 anyhow::bail!(
396 "owner signature verification not supported for ed25519 signatures"
397 )
398 }
399 _ => anyhow::bail!("signature and address type mismatch"),
400 }
401 }
402
403 pub fn verify_session<T>(
404 &self,
405 nonce: u64,
406 args: T,
407 address: &Address,
408 ) -> anyhow::Result<()>
409 where
410 T: Tokenizable,
411 {
412 match (self, address) {
413 (Signature::Secp256k1(sig), Address::Secp256k1(addr)) => {
414 sig.verify_session(nonce, args, addr)
415 }
416 (Signature::Secp256r1(_), Address::Secp256r1) => {
417 anyhow::bail!(
418 "session signature verification not supported for p256 signatures"
419 )
420 }
421 (Signature::Ed25519(_), Address::Ed25519) => {
422 anyhow::bail!(
423 "session signature verification not supported for ed25519 signatures"
424 )
425 }
426 _ => anyhow::bail!("signature and address type mismatch"),
427 }
428 }
429
430 pub fn verify_typed<T>(
431 &self,
432 args: T,
433 domain: &Domain,
434 address: &Address,
435 ) -> anyhow::Result<()>
436 where
437 T: SRC16Encode,
438 {
439 match (self, address) {
440 (Signature::Secp256k1(sig), Address::Secp256k1(addr)) => {
441 sig.verify_typed(args, domain, addr)
442 }
443 (Signature::Secp256r1(_), Address::Secp256r1) => {
444 anyhow::bail!(
445 "typed signature verification not supported for p256 signatures"
446 )
447 }
448 (Signature::Ed25519(_), Address::Ed25519) => {
449 anyhow::bail!(
450 "typed signature verification not supported for ed25519 signatures"
451 )
452 }
453 _ => anyhow::bail!("signature and address type mismatch"),
454 }
455 }
456}
457
458pub const SRC_16_SIGNED_SESSION_TYPE_HASH: &str =
462 "0x2eb25d47a049c7084035785d680582656395f354f9484e7e3bbb2993368e46b7";
463
464pub const EIP_712_SIGNED_SESSION_TYPE_HASH: &str =
468 "0xf91528e233badfd402ad99c4d1dd9d109ec218093c9dcf7432699f5bb94721e2";
469
470impl SRC16Encode for SessionArgs {
471 fn type_hash(encoding: Encoding) -> [u8; 32] {
472 match encoding {
473 Encoding::SRC16 => {
474 Bits256::from_hex_str(SRC_16_SIGNED_SESSION_TYPE_HASH)
475 .unwrap()
476 .0
477 }
478 Encoding::EIP712 => {
479 Bits256::from_hex_str(EIP_712_SIGNED_SESSION_TYPE_HASH)
480 .unwrap()
481 .0
482 }
483 }
484 }
485 fn struct_hash(self, encoding: Encoding) -> [u8; 32] {
486 let mut tokens: Vec<u8> = Vec::new();
487 match encoding {
488 Encoding::SRC16 => tokens.extend_from_slice(
489 Bits256::from_hex_str(SRC_16_SIGNED_SESSION_TYPE_HASH)
490 .unwrap()
491 .0
492 .as_slice(),
493 ),
494 Encoding::EIP712 => tokens.extend_from_slice(
495 Bits256::from_hex_str(EIP_712_SIGNED_SESSION_TYPE_HASH)
496 .unwrap()
497 .0
498 .as_slice(),
499 ),
500 }
501
502 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
504 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
505 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
506 tokens.extend_from_slice(self.nonce.to_be_bytes().as_slice());
507 match self.session_id {
509 Identity::Address(bits) => tokens.extend_from_slice(bits.as_slice()),
510 Identity::ContractId(bits) => tokens.extend_from_slice(bits.as_slice()),
511 }
512 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
514 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
515 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
516 tokens.extend_from_slice(self.expiry.unix.to_be_bytes().as_slice());
517
518 let contract_id_bytes: Vec<u8> = self
520 .contract_ids
521 .iter()
522 .flat_map(|id| id.as_ref())
523 .copied()
524 .collect();
525
526 tokens.extend_from_slice(keccak256(contract_id_bytes).as_slice());
527
528 keccak256(tokens)
529 }
530}
531
532pub const SRC_16_SIGNED_WITHDRAW_TYPE_HASH: &str =
536 "0x9732cd3b83bcf964465644c9c8546dff5d9f2e5c16c077dba9a2a2bc2b9f7188";
537
538pub const EIP_712_SIGNED_WITHDRAW_TYPE_HASH: &str =
542 "0x138ab031e51a15ab8c1d21d9a203f6cc5c134d7dbb44a9fd3f6acc3231af1c32";
543
544impl SRC16Encode for &WithdrawArgs {
545 fn type_hash(encoding: Encoding) -> [u8; 32] {
546 match encoding {
547 Encoding::SRC16 => {
548 Bits256::from_hex_str(SRC_16_SIGNED_WITHDRAW_TYPE_HASH)
549 .unwrap()
550 .0
551 }
552 Encoding::EIP712 => {
553 Bits256::from_hex_str(EIP_712_SIGNED_WITHDRAW_TYPE_HASH)
554 .unwrap()
555 .0
556 }
557 }
558 }
559 fn struct_hash(self, encoding: Encoding) -> [u8; 32] {
560 let mut tokens: Vec<u8> = Vec::new();
561 match encoding {
562 Encoding::SRC16 => tokens.extend_from_slice(
563 Bits256::from_hex_str(SRC_16_SIGNED_WITHDRAW_TYPE_HASH)
564 .unwrap()
565 .0
566 .as_slice(),
567 ),
568 Encoding::EIP712 => tokens.extend_from_slice(
569 Bits256::from_hex_str(EIP_712_SIGNED_WITHDRAW_TYPE_HASH)
570 .unwrap()
571 .0
572 .as_slice(),
573 ),
574 }
575
576 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
577 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
578 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
579 tokens.extend_from_slice(self.nonce.to_be_bytes().as_slice());
580 match self.to {
581 Identity::Address(bits) => tokens.extend_from_slice(bits.as_slice()),
582 Identity::ContractId(bits) => tokens.extend_from_slice(bits.as_slice()),
583 }
584 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
585 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
586 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
587 tokens.extend_from_slice(self.amount.to_be_bytes().as_slice());
588 tokens.extend_from_slice(self.asset_id.as_slice());
589
590 keccak256(tokens)
591 }
592}
593
594pub const SRC_16_SIGNED_CALL_PARAMS_TYPE_HASH: &str =
598 "0x05816efb1220d5dd49f0abf9882712729285e7080da3dc2ba051a6ab701cf3b8";
599
600pub const EIP_712_SIGNED_CALL_PARAMS_TYPE_HASH: &str =
604 "0x322b030cfd61eddd3d0acc5c37358539477197bfc2599dc09a9c75ad47f6e5b8";
605
606impl SRC16Encode for CallParams {
607 fn type_hash(encoding: Encoding) -> [u8; 32] {
608 match encoding {
609 Encoding::SRC16 => {
610 Bits256::from_hex_str(SRC_16_SIGNED_CALL_PARAMS_TYPE_HASH)
611 .unwrap()
612 .0
613 }
614 Encoding::EIP712 => {
615 Bits256::from_hex_str(EIP_712_SIGNED_CALL_PARAMS_TYPE_HASH)
616 .unwrap()
617 .0
618 }
619 }
620 }
621 fn struct_hash(self, encoding: Encoding) -> [u8; 32] {
622 let mut tokens: Vec<u8> = Vec::new();
623 match encoding {
624 Encoding::SRC16 => tokens.extend_from_slice(
625 Bits256::from_hex_str(SRC_16_SIGNED_CALL_PARAMS_TYPE_HASH)
626 .unwrap()
627 .0
628 .as_slice(),
629 ),
630 Encoding::EIP712 => tokens.extend_from_slice(
631 Bits256::from_hex_str(EIP_712_SIGNED_CALL_PARAMS_TYPE_HASH)
632 .unwrap()
633 .0
634 .as_slice(),
635 ),
636 }
637
638 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
640 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
641 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
642 tokens.extend_from_slice(self.coins.to_be_bytes().as_slice());
643 tokens.extend_from_slice(self.asset_id.as_slice());
644 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
645 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
646 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
647 tokens.extend_from_slice(self.gas.to_be_bytes().as_slice());
648
649 keccak256(tokens)
650 }
651}
652
653pub const SRC_16_SIGNED_CALL_CONTRACT_ARG_TYPE_HASH: &str =
657 "0xc8ac163d1f87886f48788901b8e5eb4eebb3edad4ae111859b2a051059920048";
658
659pub const EIP_712_SIGNED_CALL_CONTRACT_ARG_TYPE_HASH: &str =
663 "0x325b44fd679189ccd8f708a440a27448cbfd6f47f90205e8ace3f1efc27c92d6";
664
665impl SRC16Encode for CallContractArg {
666 fn type_hash(encoding: Encoding) -> [u8; 32] {
667 match encoding {
668 Encoding::SRC16 => {
669 Bits256::from_hex_str(SRC_16_SIGNED_CALL_CONTRACT_ARG_TYPE_HASH)
670 .unwrap()
671 .0
672 }
673 Encoding::EIP712 => {
674 Bits256::from_hex_str(EIP_712_SIGNED_CALL_CONTRACT_ARG_TYPE_HASH)
675 .unwrap()
676 .0
677 }
678 }
679 }
680 fn struct_hash(self, encoding: Encoding) -> [u8; 32] {
681 let mut tokens: Vec<u8> = Vec::new();
682 match encoding {
683 Encoding::SRC16 => tokens.extend_from_slice(
684 Bits256::from_hex_str(SRC_16_SIGNED_CALL_CONTRACT_ARG_TYPE_HASH)
685 .unwrap()
686 .0
687 .as_slice(),
688 ),
689 Encoding::EIP712 => tokens.extend_from_slice(
690 Bits256::from_hex_str(EIP_712_SIGNED_CALL_CONTRACT_ARG_TYPE_HASH)
691 .unwrap()
692 .0
693 .as_slice(),
694 ),
695 }
696
697 tokens.extend_from_slice(self.contract_id.as_slice());
699
700 tokens.extend_from_slice(keccak256(self.function_selector.0).as_slice());
702
703 tokens.extend_from_slice(self.call_params.struct_hash(encoding).as_slice());
705
706 match self.call_data {
708 Some(data) => tokens.extend_from_slice(keccak256(data.0).as_slice()),
709 None => tokens.extend_from_slice(keccak256(Vec::new()).as_slice()),
710 }
711
712 keccak256(tokens)
713 }
714}
715
716pub const SRC_16_SIGNED_CALL_CONTRACT_TYPE_HASH: &str =
720 "0x3f9265dacb0b84ea31e56285f5757a2200e743f27f0b27c3d58db09c9f62cb46";
721
722pub const EIP_712_SIGNED_CALL_CONTRACT_TYPE_HASH: &str =
726 "0x26ade4c76b3df03eed7d7a2d1387896e8717f8a862414199904b8a4455b25699";
727
728impl SRC16Encode for TradeAccountCallContractArgs {
729 fn type_hash(encoding: Encoding) -> [u8; 32] {
730 match encoding {
731 Encoding::SRC16 => {
732 Bits256::from_hex_str(SRC_16_SIGNED_CALL_CONTRACT_TYPE_HASH)
733 .unwrap()
734 .0
735 }
736 Encoding::EIP712 => {
737 Bits256::from_hex_str(EIP_712_SIGNED_CALL_CONTRACT_TYPE_HASH)
738 .unwrap()
739 .0
740 }
741 }
742 }
743 fn struct_hash(self, encoding: Encoding) -> [u8; 32] {
744 let mut tokens: Vec<u8> = Vec::new();
745 match encoding {
746 Encoding::SRC16 => tokens.extend_from_slice(
747 Bits256::from_hex_str(SRC_16_SIGNED_CALL_CONTRACT_TYPE_HASH)
748 .unwrap()
749 .0
750 .as_slice(),
751 ),
752 Encoding::EIP712 => tokens.extend_from_slice(
753 Bits256::from_hex_str(EIP_712_SIGNED_CALL_CONTRACT_TYPE_HASH)
754 .unwrap()
755 .0
756 .as_slice(),
757 ),
758 }
759
760 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
762 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
763 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
764 tokens.extend_from_slice(self.nonce.to_be_bytes().as_slice());
765
766 tokens
768 .extend_from_slice(self.call_contract_args.struct_hash(encoding).as_slice());
769
770 keccak256(tokens)
771 }
772}
773
774pub const SRC_16_SIGNED_MULTI_CALL_CONTRACT_TYPE_HASH: &str =
778 "0x44c30cb1e12dbc44e06a72b087359f9af5caae554d43dada454d5f6619eabf5f";
779
780pub const EIP_712_SIGNED_MULTI_CALL_CONTRACT_TYPE_HASH: &str =
784 "0x292b2913cef817f041ea01e3b02c4dd2b7184e6490f21a84ece8443e406640a1";
785
786impl SRC16Encode for MultiCallContractArgs {
787 fn type_hash(encoding: Encoding) -> [u8; 32] {
788 match encoding {
789 Encoding::SRC16 => {
790 Bits256::from_hex_str(SRC_16_SIGNED_MULTI_CALL_CONTRACT_TYPE_HASH)
791 .unwrap()
792 .0
793 }
794 Encoding::EIP712 => {
795 Bits256::from_hex_str(EIP_712_SIGNED_MULTI_CALL_CONTRACT_TYPE_HASH)
796 .unwrap()
797 .0
798 }
799 }
800 }
801 fn struct_hash(self, encoding: Encoding) -> [u8; 32] {
802 let mut tokens: Vec<u8> = Vec::new();
803 match encoding {
804 Encoding::SRC16 => tokens.extend_from_slice(
805 Bits256::from_hex_str(SRC_16_SIGNED_MULTI_CALL_CONTRACT_TYPE_HASH)
806 .unwrap()
807 .0
808 .as_slice(),
809 ),
810 Encoding::EIP712 => tokens.extend_from_slice(
811 Bits256::from_hex_str(EIP_712_SIGNED_MULTI_CALL_CONTRACT_TYPE_HASH)
812 .unwrap()
813 .0
814 .as_slice(),
815 ),
816 }
817
818 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
820 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
821 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
822 tokens.extend_from_slice(self.nonce.to_be_bytes().as_slice());
823
824 let mut call_buffer: Vec<u8> = Vec::new();
826 for call in self.call_contract_args {
827 let h = call.struct_hash(encoding.clone());
828 call_buffer.extend_from_slice(h.as_slice());
829 }
830 tokens.extend_from_slice(keccak256(call_buffer.as_slice()).as_slice());
831
832 keccak256(tokens)
833 }
834}
835
836pub const SRC_16_REVOKE_TYPE_HASH: &str =
840 "0x97c9c74c84314e9fcc4ba97ad28e9cde1eda9b79b0229b54219de0e79560ac86";
841
842pub const EIP_712_REVOKE_TYPE_HASH: &str =
846 "0xf863f4b733d291f6bd744df3ba070a394dd83fced6f038a22195c1b34009dabb";
847
848impl SRC16Encode for RevokeArgs {
849 fn type_hash(encoding: Encoding) -> [u8; 32] {
850 match encoding {
851 Encoding::SRC16 => Bits256::from_hex_str(SRC_16_REVOKE_TYPE_HASH).unwrap().0,
852 Encoding::EIP712 => {
853 Bits256::from_hex_str(EIP_712_REVOKE_TYPE_HASH).unwrap().0
854 }
855 }
856 }
857 fn struct_hash(self, encoding: Encoding) -> [u8; 32] {
858 let mut tokens: Vec<u8> = Vec::new();
859 match encoding {
860 Encoding::SRC16 => tokens.extend_from_slice(
861 Bits256::from_hex_str(SRC_16_REVOKE_TYPE_HASH)
862 .unwrap()
863 .0
864 .as_slice(),
865 ),
866 Encoding::EIP712 => tokens.extend_from_slice(
867 Bits256::from_hex_str(EIP_712_REVOKE_TYPE_HASH)
868 .unwrap()
869 .0
870 .as_slice(),
871 ),
872 }
873
874 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
875 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
876 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
877 tokens.extend_from_slice(self.nonce.to_be_bytes().as_slice());
878
879 keccak256(tokens)
880 }
881}
882
883pub const SRC_16_PROXY_TARGET_TYPE_HASH: &str =
887 "0x523db1f70cbdfc0982627ed4e52dc429384c32e94e16ddc5fa5ab921035d8d6d";
888
889pub const EIP_712_PROXY_TARGET_TYPE_HASH: &str =
893 "0xf12c7a18e931f36d11f09bd52f37183c4993a3a509ac0c2c6cec459cee7a004d";
894
895impl SRC16Encode for SetProxyTargetArgs {
896 fn type_hash(encoding: Encoding) -> [u8; 32] {
897 match encoding {
898 Encoding::SRC16 => {
899 Bits256::from_hex_str(SRC_16_PROXY_TARGET_TYPE_HASH)
900 .unwrap()
901 .0
902 }
903 Encoding::EIP712 => {
904 Bits256::from_hex_str(EIP_712_PROXY_TARGET_TYPE_HASH)
905 .unwrap()
906 .0
907 }
908 }
909 }
910 fn struct_hash(self, encoding: Encoding) -> [u8; 32] {
911 let mut tokens: Vec<u8> = Vec::new();
912 match encoding {
913 Encoding::SRC16 => tokens.extend_from_slice(
914 Bits256::from_hex_str(SRC_16_PROXY_TARGET_TYPE_HASH)
915 .unwrap()
916 .0
917 .as_slice(),
918 ),
919 Encoding::EIP712 => tokens.extend_from_slice(
920 Bits256::from_hex_str(EIP_712_PROXY_TARGET_TYPE_HASH)
921 .unwrap()
922 .0
923 .as_slice(),
924 ),
925 }
926
927 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
928 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
929 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
930 tokens.extend_from_slice(self.nonce.to_be_bytes().as_slice());
931
932 keccak256(tokens)
933 }
934}
935
936pub const SRC_16_PARALLEL_SIGNED_SESSION_TYPE_HASH: &str =
937 "0x7bcb65ebf5b9c6a7eb6a5d92462eeb2b3eba824e9c8f22c2b01533c2e3364019";
938
939pub const EIP_712_PARALLEL_SIGNED_SESSION_TYPE_HASH: &str =
940 "0x67a8adc215ea9fc6e3a50e038c7c123758629a96e6f33c3a5b6bd8534842d9a7";
941
942impl SRC16Encode for &ParallelSessionArgs {
943 fn type_hash(encoding: Encoding) -> [u8; 32] {
944 match encoding {
945 Encoding::SRC16 => {
946 Bits256::from_hex_str(SRC_16_PARALLEL_SIGNED_SESSION_TYPE_HASH)
947 .unwrap()
948 .0
949 }
950 Encoding::EIP712 => {
951 Bits256::from_hex_str(EIP_712_PARALLEL_SIGNED_SESSION_TYPE_HASH)
952 .unwrap()
953 .0
954 }
955 }
956 }
957
958 fn struct_hash(self, encoding: Encoding) -> [u8; 32] {
959 let mut tokens: Vec<u8> = Vec::new();
960 match encoding {
961 Encoding::SRC16 => tokens.extend_from_slice(
962 Bits256::from_hex_str(SRC_16_PARALLEL_SIGNED_SESSION_TYPE_HASH)
963 .unwrap()
964 .0
965 .as_slice(),
966 ),
967 Encoding::EIP712 => tokens.extend_from_slice(
968 Bits256::from_hex_str(EIP_712_PARALLEL_SIGNED_SESSION_TYPE_HASH)
969 .unwrap()
970 .0
971 .as_slice(),
972 ),
973 }
974
975 tokens.extend_from_slice(fuel_u256_to_bytes(&self.nonce).as_slice());
976 match self.session_id {
977 Identity::Address(bits) => tokens.extend_from_slice(bits.as_slice()),
978 Identity::ContractId(bits) => tokens.extend_from_slice(bits.as_slice()),
979 }
980 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
981 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
982 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
983 tokens.extend_from_slice(self.expiry.unix.to_be_bytes().as_slice());
984
985 let contract_id_bytes: Vec<u8> = self
986 .contract_ids
987 .iter()
988 .flat_map(|id| id.as_ref())
989 .copied()
990 .collect();
991 tokens.extend_from_slice(keccak256(contract_id_bytes).as_slice());
992
993 keccak256(tokens)
994 }
995}
996
997pub const SRC_16_PARALLEL_SIGNED_WITHDRAW_TYPE_HASH: &str =
998 "0x95a9c4c23dd3607b14ecdc67fcc42946f61a4f1d153dc7dfbd77ca6c2af1c6f6";
999
1000pub const EIP_712_PARALLEL_SIGNED_WITHDRAW_TYPE_HASH: &str =
1001 "0x7d2e70c357f073fd00c187b1871bf74c440ec8303b435f001d884d6ffd77ca1c";
1002
1003impl SRC16Encode for &ParallelWithdrawArgs {
1004 fn type_hash(encoding: Encoding) -> [u8; 32] {
1005 match encoding {
1006 Encoding::SRC16 => {
1007 Bits256::from_hex_str(SRC_16_PARALLEL_SIGNED_WITHDRAW_TYPE_HASH)
1008 .unwrap()
1009 .0
1010 }
1011 Encoding::EIP712 => {
1012 Bits256::from_hex_str(EIP_712_PARALLEL_SIGNED_WITHDRAW_TYPE_HASH)
1013 .unwrap()
1014 .0
1015 }
1016 }
1017 }
1018
1019 fn struct_hash(self, encoding: Encoding) -> [u8; 32] {
1020 let mut tokens: Vec<u8> = Vec::new();
1021 match encoding {
1022 Encoding::SRC16 => tokens.extend_from_slice(
1023 Bits256::from_hex_str(SRC_16_PARALLEL_SIGNED_WITHDRAW_TYPE_HASH)
1024 .unwrap()
1025 .0
1026 .as_slice(),
1027 ),
1028 Encoding::EIP712 => tokens.extend_from_slice(
1029 Bits256::from_hex_str(EIP_712_PARALLEL_SIGNED_WITHDRAW_TYPE_HASH)
1030 .unwrap()
1031 .0
1032 .as_slice(),
1033 ),
1034 }
1035
1036 tokens.extend_from_slice(fuel_u256_to_bytes(&self.nonce).as_slice());
1037 match self.to {
1038 Identity::Address(bits) => tokens.extend_from_slice(bits.as_slice()),
1039 Identity::ContractId(bits) => tokens.extend_from_slice(bits.as_slice()),
1040 }
1041 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
1042 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
1043 tokens.extend_from_slice(0u64.to_be_bytes().as_slice());
1044 tokens.extend_from_slice(self.amount.to_be_bytes().as_slice());
1045 tokens.extend_from_slice(self.asset_id.as_slice());
1046
1047 keccak256(tokens)
1048 }
1049}
1050
1051pub const SRC_16_PARALLEL_SIGNED_CALL_CONTRACT_TYPE_HASH: &str =
1052 "0xfed97554285faeaf02128aa961f1b12bc144175e0bc3095deee2999f9c0563c1";
1053
1054pub const EIP_712_PARALLEL_SIGNED_CALL_CONTRACT_TYPE_HASH: &str =
1055 "0x936febfd2859fb63ad8d03cb75f762221befa5f66b3ed7a2716039b602fdc944";
1056
1057impl SRC16Encode for ParallelCallContractArgs {
1058 fn type_hash(encoding: Encoding) -> [u8; 32] {
1059 match encoding {
1060 Encoding::SRC16 => {
1061 Bits256::from_hex_str(SRC_16_PARALLEL_SIGNED_CALL_CONTRACT_TYPE_HASH)
1062 .unwrap()
1063 .0
1064 }
1065 Encoding::EIP712 => {
1066 Bits256::from_hex_str(EIP_712_PARALLEL_SIGNED_CALL_CONTRACT_TYPE_HASH)
1067 .unwrap()
1068 .0
1069 }
1070 }
1071 }
1072
1073 fn struct_hash(self, encoding: Encoding) -> [u8; 32] {
1074 let mut tokens: Vec<u8> = Vec::new();
1075 match encoding {
1076 Encoding::SRC16 => tokens.extend_from_slice(
1077 Bits256::from_hex_str(SRC_16_PARALLEL_SIGNED_CALL_CONTRACT_TYPE_HASH)
1078 .unwrap()
1079 .0
1080 .as_slice(),
1081 ),
1082 Encoding::EIP712 => tokens.extend_from_slice(
1083 Bits256::from_hex_str(EIP_712_PARALLEL_SIGNED_CALL_CONTRACT_TYPE_HASH)
1084 .unwrap()
1085 .0
1086 .as_slice(),
1087 ),
1088 }
1089
1090 tokens.extend_from_slice(fuel_u256_to_bytes(&self.nonce).as_slice());
1091 tokens
1092 .extend_from_slice(self.call_contract_args.struct_hash(encoding).as_slice());
1093
1094 keccak256(tokens)
1095 }
1096}
1097
1098pub const SRC_16_PARALLEL_SIGNED_MULTI_CALL_CONTRACT_TYPE_HASH: &str =
1099 "0xb913eb232c21ef9250e65abc3c1cb9f75af53ed044455ea484d4f3fa3b4b0ee5";
1100
1101pub const EIP_712_PARALLEL_SIGNED_MULTI_CALL_CONTRACT_TYPE_HASH: &str =
1102 "0x3c5f4ecf0e01c024666fd99c1a6b05d93d7ca2ca31879f4d66d008356c8dcbf2";
1103
1104impl SRC16Encode for ParallelMultiCallContractArgs {
1105 fn type_hash(encoding: Encoding) -> [u8; 32] {
1106 match encoding {
1107 Encoding::SRC16 => {
1108 Bits256::from_hex_str(
1109 SRC_16_PARALLEL_SIGNED_MULTI_CALL_CONTRACT_TYPE_HASH,
1110 )
1111 .unwrap()
1112 .0
1113 }
1114 Encoding::EIP712 => {
1115 Bits256::from_hex_str(
1116 EIP_712_PARALLEL_SIGNED_MULTI_CALL_CONTRACT_TYPE_HASH,
1117 )
1118 .unwrap()
1119 .0
1120 }
1121 }
1122 }
1123
1124 fn struct_hash(self, encoding: Encoding) -> [u8; 32] {
1125 let mut tokens: Vec<u8> = Vec::new();
1126 match encoding {
1127 Encoding::SRC16 => tokens.extend_from_slice(
1128 Bits256::from_hex_str(
1129 SRC_16_PARALLEL_SIGNED_MULTI_CALL_CONTRACT_TYPE_HASH,
1130 )
1131 .unwrap()
1132 .0
1133 .as_slice(),
1134 ),
1135 Encoding::EIP712 => tokens.extend_from_slice(
1136 Bits256::from_hex_str(
1137 EIP_712_PARALLEL_SIGNED_MULTI_CALL_CONTRACT_TYPE_HASH,
1138 )
1139 .unwrap()
1140 .0
1141 .as_slice(),
1142 ),
1143 }
1144
1145 tokens.extend_from_slice(fuel_u256_to_bytes(&self.nonce).as_slice());
1146
1147 let mut call_buffer: Vec<u8> = Vec::new();
1148 for call in self.call_contract_args {
1149 call_buffer.extend_from_slice(call.struct_hash(encoding.clone()).as_slice());
1150 }
1151 tokens.extend_from_slice(keccak256(call_buffer).as_slice());
1152
1153 keccak256(tokens)
1154 }
1155}
1156
1157pub const SRC_16_PARALLEL_REVOKE_TYPE_HASH: &str =
1158 "0xac7c930989abf28d71694ae7f327a130cdc1ef80040d94c6873cf0686024382d";
1159
1160pub const EIP_712_PARALLEL_REVOKE_TYPE_HASH: &str =
1161 "0x51c1376821126b4be9647fdbbf51877dd5a72c6a76e58fb957f37ac985962ea8";
1162
1163impl SRC16Encode for ParallelRevokeArgs {
1164 fn type_hash(encoding: Encoding) -> [u8; 32] {
1165 match encoding {
1166 Encoding::SRC16 => {
1167 Bits256::from_hex_str(SRC_16_PARALLEL_REVOKE_TYPE_HASH)
1168 .unwrap()
1169 .0
1170 }
1171 Encoding::EIP712 => {
1172 Bits256::from_hex_str(EIP_712_PARALLEL_REVOKE_TYPE_HASH)
1173 .unwrap()
1174 .0
1175 }
1176 }
1177 }
1178
1179 fn struct_hash(self, encoding: Encoding) -> [u8; 32] {
1180 let mut tokens: Vec<u8> = Vec::new();
1181 match encoding {
1182 Encoding::SRC16 => tokens.extend_from_slice(
1183 Bits256::from_hex_str(SRC_16_PARALLEL_REVOKE_TYPE_HASH)
1184 .unwrap()
1185 .0
1186 .as_slice(),
1187 ),
1188 Encoding::EIP712 => tokens.extend_from_slice(
1189 Bits256::from_hex_str(EIP_712_PARALLEL_REVOKE_TYPE_HASH)
1190 .unwrap()
1191 .0
1192 .as_slice(),
1193 ),
1194 }
1195
1196 tokens.extend_from_slice(fuel_u256_to_bytes(&self.nonce).as_slice());
1197
1198 keccak256(tokens)
1199 }
1200}