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        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
56/// Takes the rightmost 20 BYTES of a 32-byte Fuel contract ID.
57/// Interprets "rightmost" as the last bytes in the array / hex string.
58fn fuel_contract_id_to_evm_address(id: [u8; 32]) -> [u8; 20] {
59    let mut addr = [0u8; 20];
60    // last 20 bytes: indices 12..32
61    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    // Compute the domain separator;
102    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    // IMPORTANT: fixed order
134    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            // In compact ECDSA signatures, the y-parity is encoded in the most significant bit
195            // of the first byte of the s value. This bit must be cleared to obtain the actual s value.
196            s_bytes[0] &= 0x7F;
197
198            let s = U256::from_big_endian(&s_bytes);
199            // Ethereum uses v = 27 or 28 for legacy signatures; here, we add 27 to the parity bit.
200            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    /// Verify that the owner signed the payload
212    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    /// Verify that the session key signed the payload
224    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 the first 12 bytes of the address are all zeros, then we assume it
262        // is an EVM (Ethereum) address and signature
263        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        // Encode the typed data
329        let message: Message = Message::from_bytes(domain.encode(args));
330
331        // Check if this is an EVM address or Fuel address based on padding
332        if address.starts_with([0u8; EVM_ADDRESS_PADDING_LENGTH].as_ref()) {
333            // EVM (Ethereum) address verification
334            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            // Fuel address verification
347            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
458/// The Keccak256 hash of the type SessionArgs as UTF8 encoded bytes.
459///
460/// "SessionArgs(u64 nonce,b256 session_id,u64 expiry,contractId[] contract_ids)"
461pub const SRC_16_SIGNED_SESSION_TYPE_HASH: &str =
462    "0x2eb25d47a049c7084035785d680582656395f354f9484e7e3bbb2993368e46b7";
463
464/// The Keccak256 hash of the type SessionArgs as UTF8 encoded bytes.
465///
466/// "SessionArgs(uint64 nonce,bytes32 session_id,uint64 expiry,bytes32[] contract_ids)"
467pub 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        // nonce u64
503        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        // session id b256
508        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        // expiry u64
513        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        // contract ids b256[]
519        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
532/// The Keccak256 hash of the type WithdrawArgs as UTF8 encoded bytes.
533///
534/// "WithdrawArgs(u64 nonce,identity to,u64 amount,assetId asset_id)"
535pub const SRC_16_SIGNED_WITHDRAW_TYPE_HASH: &str =
536    "0x9732cd3b83bcf964465644c9c8546dff5d9f2e5c16c077dba9a2a2bc2b9f7188";
537
538/// The Keccak256 hash of the type WithdrawArgs as UTF8 encoded bytes.
539///
540/// "WithdrawArgs(uint64 nonce,bytes32 to,uint64 amount,bytes32 asset_id)"
541pub 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
594/// The Keccak256 hash of the type CallParams as UTF8 encoded bytes.
595///
596/// "CallParams(u64 coins,assetId asset_id,u64 gas)"
597pub const SRC_16_SIGNED_CALL_PARAMS_TYPE_HASH: &str =
598    "0x05816efb1220d5dd49f0abf9882712729285e7080da3dc2ba051a6ab701cf3b8";
599
600/// The Keccak256 hash of the type CallParams as UTF8 encoded bytes.
601///
602/// "CallParams(uint64 coins,bytes32 asset_id,uint64 gas)"
603pub 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        // Call Params
639        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
653/// The Keccak256 hash of the type CallContractArg as UTF8 encoded bytes.
654///
655/// "CallContractArg(contractId contract_id,bytes function_selector,CallParams call_params,bytes call_data)CallParams(u64 coins,assetId asset_id,u64 gas)"
656pub const SRC_16_SIGNED_CALL_CONTRACT_ARG_TYPE_HASH: &str =
657    "0xc8ac163d1f87886f48788901b8e5eb4eebb3edad4ae111859b2a051059920048";
658
659/// The Keccak256 hash of the type CallContractArg as UTF8 encoded bytes.
660///
661/// "CallContractArg(bytes32 contract_id,bytes function_selector,CallParams call_params,bytes call_data)CallParams(uint64 coins,bytes32 asset_id,uint64 gas)"
662pub 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        // Contract Id
698        tokens.extend_from_slice(self.contract_id.as_slice());
699
700        // Function selector
701        tokens.extend_from_slice(keccak256(self.function_selector.0).as_slice());
702
703        // Call Params
704        tokens.extend_from_slice(self.call_params.struct_hash(encoding).as_slice());
705
706        // Call data
707        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
716/// The Keccak256 hash of the type CallContractArgs as UTF8 encoded bytes.
717///
718/// "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)"
719pub const SRC_16_SIGNED_CALL_CONTRACT_TYPE_HASH: &str =
720    "0x3f9265dacb0b84ea31e56285f5757a2200e743f27f0b27c3d58db09c9f62cb46";
721
722/// The Keccak256 hash of the type CallContractArgs as UTF8 encoded bytes.
723///
724/// "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)"
725pub 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        // Nonce
761        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        // Call Contract args
767        tokens
768            .extend_from_slice(self.call_contract_args.struct_hash(encoding).as_slice());
769
770        keccak256(tokens)
771    }
772}
773
774/// The Keccak256 hash of the type MultiCallContractArgs as UTF8 encoded bytes.
775///
776/// "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)"
777pub const SRC_16_SIGNED_MULTI_CALL_CONTRACT_TYPE_HASH: &str =
778    "0x44c30cb1e12dbc44e06a72b087359f9af5caae554d43dada454d5f6619eabf5f";
779
780/// The Keccak256 hash of the type MultiCallContractArgs as UTF8 encoded bytes.
781///
782/// "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)"
783pub 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        // Nonce
819        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        // Contract call params
825        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
836/// The Keccak256 hash of the type RevokeArgs as UTF8 encoded bytes.
837///
838/// "RevokeArgs(u64 nonce)"
839pub const SRC_16_REVOKE_TYPE_HASH: &str =
840    "0x97c9c74c84314e9fcc4ba97ad28e9cde1eda9b79b0229b54219de0e79560ac86";
841
842/// The Keccak256 hash of the type RevokeArgs as UTF8 encoded bytes.
843///
844/// "RevokeArgs(uint64 nonce)"
845pub 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
883/// The Keccak256 hash of the type SetProxyTargetArgs as UTF8 encoded bytes.
884///
885/// "SetProxyTargetArgs(u64 nonce)"
886pub const SRC_16_PROXY_TARGET_TYPE_HASH: &str =
887    "0x523db1f70cbdfc0982627ed4e52dc429384c32e94e16ddc5fa5ab921035d8d6d";
888
889/// The Keccak256 hash of the type RevokeArgs as UTF8 encoded bytes.
890///
891/// "SetProxyTargetArgs(uint64 nonce)"
892pub 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}