1
// Copyright 2019-2025 PureStake Inc.
2
// This file is part of Moonbeam.
3

            
4
// Moonbeam is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Moonbeam is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
//! Test utilities
18

            
19
use ethereum::{AuthorizationList, TransactionAction};
20
use frame_support::{
21
	parameter_types,
22
	traits::{ConstU32, FindAuthor, InstanceFilter},
23
	weights::Weight,
24
	ConsensusEngineId, PalletId,
25
};
26
use frame_system::{pallet_prelude::BlockNumberFor, EnsureRoot};
27
use pallet_evm::{
28
	AddressMapping, EnsureAddressTruncated, FeeCalculator, FrameSystemAccountProvider,
29
};
30
use rlp::RlpStream;
31
use sp_core::{hashing::keccak_256, H160, H256, U256};
32
use sp_runtime::{
33
	traits::{BlakeTwo256, IdentityLookup},
34
	AccountId32, BuildStorage,
35
};
36

            
37
use super::*;
38
use pallet_ethereum::{IntermediateStateRoot, PostLogContent};
39
use sp_runtime::{
40
	traits::DispatchInfoOf,
41
	transaction_validity::{TransactionValidity, TransactionValidityError},
42
};
43

            
44
pub type BlockNumber = BlockNumberFor<Test>;
45

            
46
type Block = frame_system::mocking::MockBlock<Test>;
47

            
48
550
frame_support::construct_runtime! {
49
550
	pub enum Test
50
550
	{
51
550
		System: frame_system,
52
550
		Balances: pallet_balances,
53
550
		Timestamp: pallet_timestamp,
54
550
		EVM: pallet_evm,
55
550
		Ethereum: pallet_ethereum,
56
550
		EthereumXcm: crate,
57
550
		Proxy: pallet_proxy,
58
550
	}
59
3269
}
60

            
61
parameter_types! {
62
	pub const BlockHashCount: u32 = 250;
63
	pub BlockWeights: frame_system::limits::BlockWeights =
64
		frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1024, 1));
65
}
66

            
67
impl frame_system::Config for Test {
68
	type BaseCallFilter = frame_support::traits::Everything;
69
	type BlockWeights = ();
70
	type BlockLength = ();
71
	type DbWeight = ();
72
	type RuntimeOrigin = RuntimeOrigin;
73
	type RuntimeTask = RuntimeTask;
74
	type Nonce = u64;
75
	type Block = Block;
76
	type Hash = H256;
77
	type RuntimeCall = RuntimeCall;
78
	type Hashing = BlakeTwo256;
79
	type AccountId = AccountId32;
80
	type Lookup = IdentityLookup<Self::AccountId>;
81
	type RuntimeEvent = RuntimeEvent;
82
	type BlockHashCount = BlockHashCount;
83
	type Version = ();
84
	type PalletInfo = PalletInfo;
85
	type AccountData = pallet_balances::AccountData<u64>;
86
	type OnNewAccount = ();
87
	type OnKilledAccount = ();
88
	type SystemWeightInfo = ();
89
	type SS58Prefix = ();
90
	type OnSetCode = ();
91
	type MaxConsumers = ConstU32<16>;
92
	type SingleBlockMigrations = ();
93
	type MultiBlockMigrator = ();
94
	type PreInherents = ();
95
	type PostInherents = ();
96
	type PostTransactions = ();
97
	type ExtensionsWeightInfo = ();
98
}
99

            
100
parameter_types! {
101
	// For weight estimation, we assume that the most locks on an individual account will be 50.
102
	// This number may need to be adjusted in the future if this assumption no longer holds true.
103
	pub const MaxLocks: u32 = 50;
104
	pub const ExistentialDeposit: u64 = 500;
105
}
106

            
107
impl pallet_balances::Config for Test {
108
	type MaxLocks = MaxLocks;
109
	type Balance = u64;
110
	type RuntimeEvent = RuntimeEvent;
111
	type DustRemoval = ();
112
	type ExistentialDeposit = ExistentialDeposit;
113
	type AccountStore = System;
114
	type WeightInfo = ();
115
	type MaxReserves = ();
116
	type ReserveIdentifier = ();
117
	type RuntimeHoldReason = ();
118
	type FreezeIdentifier = ();
119
	type MaxFreezes = ();
120
	type RuntimeFreezeReason = ();
121
	type DoneSlashHandler = ();
122
}
123

            
124
parameter_types! {
125
	pub const MinimumPeriod: u64 = 6000 / 2;
126
}
127

            
128
impl pallet_timestamp::Config for Test {
129
	type Moment = u64;
130
	type OnTimestampSet = ();
131
	type MinimumPeriod = MinimumPeriod;
132
	type WeightInfo = ();
133
}
134

            
135
pub struct FixedGasPrice;
136
impl FeeCalculator for FixedGasPrice {
137
54
	fn min_gas_price() -> (U256, Weight) {
138
54
		(1.into(), Weight::zero())
139
54
	}
140
}
141

            
142
pub struct FindAuthorTruncated;
143
impl FindAuthor<H160> for FindAuthorTruncated {
144
54
	fn find_author<'a, I>(_digests: I) -> Option<H160>
145
54
	where
146
54
		I: 'a + IntoIterator<Item = (ConsensusEngineId, &'a [u8])>,
147
54
	{
148
54
		Some(address_build(0).address)
149
54
	}
150
}
151

            
152
const MAX_POV_SIZE: u64 = 5 * 1024 * 1024;
153
/// Block storage limit in bytes. Set to 40 KB.
154
const BLOCK_STORAGE_LIMIT: u64 = 40 * 1024;
155

            
156
parameter_types! {
157
	pub const TransactionByteFee: u64 = 1;
158
	pub const ChainId: u64 = 42;
159
	pub const EVMModuleId: PalletId = PalletId(*b"py/evmpa");
160
	pub const BlockGasLimit: U256 = U256::MAX;
161
	pub WeightPerGas: Weight = Weight::from_parts(1, 0);
162
	pub GasLimitPovSizeRatio: u64 = {
163
		let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64();
164
		block_gas_limit.saturating_div(MAX_POV_SIZE)
165
	};
166
	pub GasLimitStorageGrowthRatio: u64 = {
167
		let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64();
168
		block_gas_limit.saturating_div(BLOCK_STORAGE_LIMIT)
169
	};
170
}
171

            
172
pub struct HashedAddressMapping;
173

            
174
impl AddressMapping<AccountId32> for HashedAddressMapping {
175
238
	fn into_account_id(address: H160) -> AccountId32 {
176
238
		let mut data = [0u8; 32];
177
238
		data[0..20].copy_from_slice(&address[..]);
178
238
		AccountId32::from(Into::<[u8; 32]>::into(data))
179
238
	}
180
}
181

            
182
impl pallet_evm::Config for Test {
183
	type FeeCalculator = FixedGasPrice;
184
	type GasWeightMapping = pallet_evm::FixedGasWeightMapping<Self>;
185
	type WeightPerGas = WeightPerGas;
186
	type CallOrigin = EnsureAddressTruncated;
187
	type WithdrawOrigin = EnsureAddressTruncated;
188
	type AddressMapping = HashedAddressMapping;
189
	type Currency = Balances;
190
	type RuntimeEvent = RuntimeEvent;
191
	type PrecompilesType = ();
192
	type PrecompilesValue = ();
193
	type Runner = pallet_evm::runner::stack::Runner<Self>;
194
	type ChainId = ChainId;
195
	type BlockGasLimit = BlockGasLimit;
196
	type OnChargeTransaction = ();
197
	type FindAuthor = FindAuthorTruncated;
198
	type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping<Self>;
199
	type OnCreate = ();
200
	type GasLimitPovSizeRatio = GasLimitPovSizeRatio;
201
	type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio;
202
	type Timestamp = Timestamp;
203
	type WeightInfo = pallet_evm::weights::SubstrateWeight<Test>;
204
	type AccountProvider = FrameSystemAccountProvider<Test>;
205
	type CreateOriginFilter = ();
206
	type CreateInnerOriginFilter = ();
207
}
208

            
209
parameter_types! {
210
	pub const PostBlockAndTxnHashes: PostLogContent = PostLogContent::BlockAndTxnHashes;
211
}
212

            
213
impl pallet_ethereum::Config for Test {
214
	type RuntimeEvent = RuntimeEvent;
215
	type StateRoot = IntermediateStateRoot<<Test as frame_system::Config>::Version>;
216
	type PostLogContent = PostBlockAndTxnHashes;
217
	type ExtraDataLength = ConstU32<30>;
218
}
219

            
220
parameter_types! {
221
	pub ReservedXcmpWeight: Weight = Weight::from_parts(u64::max_value(), 1);
222
}
223

            
224
#[derive(
225
	Copy,
226
	Clone,
227
	Eq,
228
	PartialEq,
229
	Ord,
230
	PartialOrd,
231
	Encode,
232
	Decode,
233
	Debug,
234
	MaxEncodedLen,
235
	TypeInfo,
236
	DecodeWithMemTracking,
237
)]
238
pub enum ProxyType {
239
5
	NotAllowed = 0,
240
12
	Any = 1,
241
}
242

            
243
impl pallet_evm_precompile_proxy::EvmProxyCallFilter for ProxyType {}
244

            
245
impl InstanceFilter<RuntimeCall> for ProxyType {
246
	fn filter(&self, _c: &RuntimeCall) -> bool {
247
		match self {
248
			ProxyType::NotAllowed => false,
249
			ProxyType::Any => true,
250
		}
251
	}
252
	fn is_superset(&self, _o: &Self) -> bool {
253
		false
254
	}
255
}
256

            
257
impl Default for ProxyType {
258
	fn default() -> Self {
259
		Self::NotAllowed
260
	}
261
}
262

            
263
parameter_types! {
264
	pub const ProxyCost: u64 = 1;
265
}
266

            
267
impl pallet_proxy::Config for Test {
268
	type RuntimeEvent = RuntimeEvent;
269
	type RuntimeCall = RuntimeCall;
270
	type Currency = Balances;
271
	type ProxyType = ProxyType;
272
	type ProxyDepositBase = ProxyCost;
273
	type ProxyDepositFactor = ProxyCost;
274
	type MaxProxies = ConstU32<32>;
275
	type WeightInfo = pallet_proxy::weights::SubstrateWeight<Test>;
276
	type MaxPending = ConstU32<32>;
277
	type CallHasher = BlakeTwo256;
278
	type AnnouncementDepositBase = ProxyCost;
279
	type AnnouncementDepositFactor = ProxyCost;
280
	type BlockNumberProvider = System;
281
}
282

            
283
pub struct EthereumXcmEnsureProxy;
284
impl xcm_primitives::EnsureProxy<AccountId32> for EthereumXcmEnsureProxy {
285
17
	fn ensure_ok(delegator: AccountId32, delegatee: AccountId32) -> Result<(), &'static str> {
286
17
		let f = |x: &pallet_proxy::ProxyDefinition<AccountId32, ProxyType, BlockNumber>| -> bool {
287
12
			x.delegate == delegatee && (x.proxy_type == ProxyType::Any)
288
12
		};
289
17
		Proxy::proxies(delegator)
290
17
			.0
291
17
			.into_iter()
292
17
			.find(f)
293
17
			.map(|_| ())
294
17
			.ok_or("proxy error: expected `ProxyType::Any`")
295
17
	}
296
}
297

            
298
impl crate::Config for Test {
299
	type RuntimeEvent = RuntimeEvent;
300
	type InvalidEvmTransactionError = pallet_ethereum::InvalidTransactionWrapper;
301
	type ValidatedTransaction = pallet_ethereum::ValidatedTransaction<Self>;
302
	type XcmEthereumOrigin = crate::EnsureXcmEthereumTransaction;
303
	type ReservedXcmpWeight = ReservedXcmpWeight;
304
	type EnsureProxy = EthereumXcmEnsureProxy;
305
	type ControllerOrigin = EnsureRoot<AccountId32>;
306
	type ForceOrigin = EnsureRoot<AccountId32>;
307
}
308

            
309
impl fp_self_contained::SelfContainedCall for RuntimeCall {
310
	type SignedInfo = H160;
311

            
312
	fn is_self_contained(&self) -> bool {
313
		match self {
314
			RuntimeCall::Ethereum(call) => call.is_self_contained(),
315
			_ => false,
316
		}
317
	}
318

            
319
	fn check_self_contained(&self) -> Option<Result<Self::SignedInfo, TransactionValidityError>> {
320
		match self {
321
			RuntimeCall::Ethereum(call) => call.check_self_contained(),
322
			_ => None,
323
		}
324
	}
325

            
326
	fn validate_self_contained(
327
		&self,
328
		info: &Self::SignedInfo,
329
		dispatch_info: &DispatchInfoOf<RuntimeCall>,
330
		len: usize,
331
	) -> Option<TransactionValidity> {
332
		match self {
333
			RuntimeCall::Ethereum(call) => call.validate_self_contained(info, dispatch_info, len),
334
			_ => None,
335
		}
336
	}
337

            
338
	fn pre_dispatch_self_contained(
339
		&self,
340
		info: &Self::SignedInfo,
341
		dispatch_info: &DispatchInfoOf<RuntimeCall>,
342
		len: usize,
343
	) -> Option<Result<(), TransactionValidityError>> {
344
		match self {
345
			RuntimeCall::Ethereum(call) => {
346
				call.pre_dispatch_self_contained(info, dispatch_info, len)
347
			}
348
			_ => None,
349
		}
350
	}
351

            
352
	fn apply_self_contained(
353
		self,
354
		info: Self::SignedInfo,
355
	) -> Option<sp_runtime::DispatchResultWithInfo<sp_runtime::traits::PostDispatchInfoOf<Self>>> {
356
		use sp_runtime::traits::Dispatchable as _;
357
		match self {
358
			call @ RuntimeCall::Ethereum(pallet_ethereum::Call::transact { .. }) => {
359
				Some(call.dispatch(RuntimeOrigin::from(
360
					pallet_ethereum::RawOrigin::EthereumTransaction(info),
361
				)))
362
			}
363
			_ => None,
364
		}
365
	}
366
}
367

            
368
pub struct AccountInfo {
369
	pub address: H160,
370
	pub account_id: AccountId32,
371
	pub private_key: H256,
372
}
373

            
374
178
fn address_build(seed: u8) -> AccountInfo {
375
178
	let private_key = H256::from_slice(&[(seed + 1) as u8; 32]);
376
178
	let secret_key = libsecp256k1::SecretKey::parse_slice(&private_key[..]).unwrap();
377
178
	let public_key = &libsecp256k1::PublicKey::from_secret_key(&secret_key).serialize()[1..65];
378
178
	let address = H160::from(H256::from(keccak_256(public_key)));
379
178

            
380
178
	let mut data = [0u8; 32];
381
178
	data[0..20].copy_from_slice(&address[..]);
382
178

            
383
178
	AccountInfo {
384
178
		private_key,
385
178
		account_id: AccountId32::from(Into::<[u8; 32]>::into(data)),
386
178
		address,
387
178
	}
388
178
}
389

            
390
// This function basically just builds a genesis storage key/value store according to
391
// our desired mockup.
392
56
pub fn new_test_ext(accounts_len: usize) -> (Vec<AccountInfo>, sp_io::TestExternalities) {
393
56
	// sc_cli::init_logger("");
394
56
	let mut ext = frame_system::GenesisConfig::<Test>::default()
395
56
		.build_storage()
396
56
		.unwrap();
397
56

            
398
56
	let pairs = (0..accounts_len)
399
124
		.map(|i| address_build(i as u8))
400
56
		.collect::<Vec<_>>();
401
56

            
402
56
	let balances: Vec<_> = (0..accounts_len)
403
124
		.map(|i| (pairs[i].account_id.clone(), 10_000_000))
404
56
		.collect();
405
56

            
406
56
	pallet_balances::GenesisConfig::<Test> {
407
56
		balances,
408
56
		dev_accounts: Default::default(),
409
56
	}
410
56
	.assimilate_storage(&mut ext)
411
56
	.unwrap();
412
56

            
413
56
	(pairs, ext.into())
414
56
}
415

            
416
pub struct LegacyUnsignedTransaction {
417
	pub nonce: U256,
418
	pub gas_price: U256,
419
	pub gas_limit: U256,
420
	pub action: TransactionAction,
421
	pub value: U256,
422
	pub input: Vec<u8>,
423
}
424

            
425
impl LegacyUnsignedTransaction {
426
1
	fn signing_rlp_append(&self, s: &mut RlpStream) {
427
1
		s.begin_list(9);
428
1
		s.append(&self.nonce);
429
1
		s.append(&self.gas_price);
430
1
		s.append(&self.gas_limit);
431
1
		s.append(&self.action);
432
1
		s.append(&self.value);
433
1
		s.append(&self.input);
434
1
		s.append(&ChainId::get());
435
1
		s.append(&0u8);
436
1
		s.append(&0u8);
437
1
	}
438

            
439
1
	fn signing_hash(&self) -> H256 {
440
1
		let mut stream = RlpStream::new();
441
1
		self.signing_rlp_append(&mut stream);
442
1
		H256::from(keccak_256(&stream.out()))
443
1
	}
444

            
445
1
	pub fn sign(&self, key: &H256) -> Transaction {
446
1
		self.sign_with_chain_id(key, ChainId::get())
447
1
	}
448

            
449
1
	pub fn sign_with_chain_id(&self, key: &H256, chain_id: u64) -> Transaction {
450
1
		let hash = self.signing_hash();
451
1
		let msg = libsecp256k1::Message::parse(hash.as_fixed_bytes());
452
1
		let s = libsecp256k1::sign(
453
1
			&msg,
454
1
			&libsecp256k1::SecretKey::parse_slice(&key[..]).unwrap(),
455
1
		);
456
1
		let sig = s.0.serialize();
457
1

            
458
1
		let sig = ethereum::legacy::TransactionSignature::new(
459
1
			s.1.serialize() as u64 % 2 + chain_id * 2 + 35,
460
1
			H256::from_slice(&sig[0..32]),
461
1
			H256::from_slice(&sig[32..64]),
462
1
		)
463
1
		.unwrap();
464
1

            
465
1
		Transaction::Legacy(ethereum::LegacyTransaction {
466
1
			nonce: self.nonce,
467
1
			gas_price: self.gas_price,
468
1
			gas_limit: self.gas_limit,
469
1
			action: self.action,
470
1
			value: self.value,
471
1
			input: self.input.clone(),
472
1
			signature: sig,
473
1
		})
474
1
	}
475
}
476

            
477
pub struct EIP2930UnsignedTransaction {
478
	pub nonce: U256,
479
	pub gas_price: U256,
480
	pub gas_limit: U256,
481
	pub action: TransactionAction,
482
	pub value: U256,
483
	pub input: Vec<u8>,
484
}
485

            
486
impl EIP2930UnsignedTransaction {
487
1
	pub fn sign(&self, secret: &H256, chain_id: Option<u64>) -> Transaction {
488
1
		let secret = {
489
1
			let mut sk: [u8; 32] = [0u8; 32];
490
1
			sk.copy_from_slice(&secret[0..]);
491
1
			libsecp256k1::SecretKey::parse(&sk).unwrap()
492
1
		};
493
1
		let chain_id = chain_id.unwrap_or(ChainId::get());
494
1
		let msg = ethereum::EIP2930TransactionMessage {
495
1
			chain_id,
496
1
			nonce: self.nonce,
497
1
			gas_price: self.gas_price,
498
1
			gas_limit: self.gas_limit,
499
1
			action: self.action,
500
1
			value: self.value,
501
1
			input: self.input.clone(),
502
1
			access_list: vec![],
503
1
		};
504
1
		let signing_message = libsecp256k1::Message::parse_slice(&msg.hash()[..]).unwrap();
505
1

            
506
1
		let (signature, recid) = libsecp256k1::sign(&signing_message, &secret);
507
1
		let rs = signature.serialize();
508
1
		let r = H256::from_slice(&rs[0..32]);
509
1
		let s = H256::from_slice(&rs[32..64]);
510
1
		Transaction::EIP2930(ethereum::EIP2930Transaction {
511
1
			chain_id: msg.chain_id,
512
1
			nonce: msg.nonce,
513
1
			gas_price: msg.gas_price,
514
1
			gas_limit: msg.gas_limit,
515
1
			action: msg.action,
516
1
			value: msg.value,
517
1
			input: msg.input.clone(),
518
1
			access_list: msg.access_list,
519
1
			signature: ethereum::eip1559::TransactionSignature::new(recid.serialize() != 0, r, s)
520
1
				.unwrap(),
521
1
		})
522
1
	}
523
}
524

            
525
pub struct EIP1559UnsignedTransaction {
526
	pub nonce: U256,
527
	pub max_priority_fee_per_gas: U256,
528
	pub max_fee_per_gas: U256,
529
	pub gas_limit: U256,
530
	pub action: TransactionAction,
531
	pub value: U256,
532
	pub input: Vec<u8>,
533
}
534

            
535
impl EIP1559UnsignedTransaction {
536
2
	pub fn sign(&self, secret: &H256, chain_id: Option<u64>) -> Transaction {
537
2
		let secret = {
538
2
			let mut sk: [u8; 32] = [0u8; 32];
539
2
			sk.copy_from_slice(&secret[0..]);
540
2
			libsecp256k1::SecretKey::parse(&sk).unwrap()
541
2
		};
542
2
		let chain_id = chain_id.unwrap_or(ChainId::get());
543
2
		let msg = ethereum::EIP1559TransactionMessage {
544
2
			chain_id,
545
2
			nonce: self.nonce,
546
2
			max_priority_fee_per_gas: self.max_priority_fee_per_gas,
547
2
			max_fee_per_gas: self.max_fee_per_gas,
548
2
			gas_limit: self.gas_limit,
549
2
			action: self.action,
550
2
			value: self.value,
551
2
			input: self.input.clone(),
552
2
			access_list: vec![],
553
2
		};
554
2
		let signing_message = libsecp256k1::Message::parse_slice(&msg.hash()[..]).unwrap();
555
2

            
556
2
		let (signature, recid) = libsecp256k1::sign(&signing_message, &secret);
557
2
		let rs = signature.serialize();
558
2
		let r = H256::from_slice(&rs[0..32]);
559
2
		let s = H256::from_slice(&rs[32..64]);
560
2
		Transaction::EIP1559(ethereum::EIP1559Transaction {
561
2
			chain_id: msg.chain_id,
562
2
			nonce: msg.nonce,
563
2
			max_priority_fee_per_gas: msg.max_priority_fee_per_gas,
564
2
			max_fee_per_gas: msg.max_fee_per_gas,
565
2
			gas_limit: msg.gas_limit,
566
2
			action: msg.action,
567
2
			value: msg.value,
568
2
			input: msg.input.clone(),
569
2
			access_list: msg.access_list,
570
2
			signature: ethereum::eip1559::TransactionSignature::new(recid.serialize() != 0, r, s)
571
2
				.unwrap(),
572
2
		})
573
2
	}
574
}
575

            
576
pub struct EIP7702UnsignedTransaction {
577
	pub nonce: U256,
578
	pub max_priority_fee_per_gas: U256,
579
	pub max_fee_per_gas: U256,
580
	pub gas_limit: U256,
581
	pub destination: TransactionAction,
582
	pub value: U256,
583
	pub data: Vec<u8>,
584
}
585

            
586
impl EIP7702UnsignedTransaction {
587
1
	pub fn sign(
588
1
		&self,
589
1
		secret: &H256,
590
1
		chain_id: Option<u64>,
591
1
		authorization_list: AuthorizationList,
592
1
	) -> Transaction {
593
1
		let secret = {
594
1
			let mut sk: [u8; 32] = [0u8; 32];
595
1
			sk.copy_from_slice(&secret[0..]);
596
1
			libsecp256k1::SecretKey::parse(&sk).unwrap()
597
1
		};
598
1
		let chain_id = chain_id.unwrap_or(ChainId::get());
599
1
		let msg = ethereum::EIP7702TransactionMessage {
600
1
			chain_id,
601
1
			nonce: self.nonce,
602
1
			max_priority_fee_per_gas: self.max_priority_fee_per_gas,
603
1
			max_fee_per_gas: self.max_fee_per_gas,
604
1
			gas_limit: self.gas_limit,
605
1
			destination: self.destination,
606
1
			value: self.value,
607
1
			data: self.data.clone(),
608
1
			access_list: vec![],
609
1
			authorization_list,
610
1
		};
611
1
		let signing_message = libsecp256k1::Message::parse_slice(&msg.hash()[..]).unwrap();
612
1

            
613
1
		let (signature, recid) = libsecp256k1::sign(&signing_message, &secret);
614
1
		let rs = signature.serialize();
615
1
		let r = H256::from_slice(&rs[0..32]);
616
1
		let s = H256::from_slice(&rs[32..64]);
617
1
		Transaction::EIP7702(ethereum::EIP7702Transaction {
618
1
			chain_id: msg.chain_id,
619
1
			nonce: msg.nonce,
620
1
			max_priority_fee_per_gas: msg.max_priority_fee_per_gas,
621
1
			max_fee_per_gas: msg.max_fee_per_gas,
622
1
			gas_limit: msg.gas_limit,
623
1
			destination: msg.destination,
624
1
			value: msg.value,
625
1
			data: msg.data.clone(),
626
1
			access_list: msg.access_list,
627
1
			authorization_list: msg.authorization_list,
628
1
			signature: ethereum::eip1559::TransactionSignature::new(recid.serialize() != 0, r, s)
629
1
				.unwrap(),
630
1
		})
631
1
	}
632
}