Skip to main content

o2_tools/
signature_ext.rs

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