Skip to content

Commit 9a9c15e

Browse files
committed
feat: ✨ add support for EIP-7702
1 parent 7f73613 commit 9a9c15e

File tree

12 files changed

+839
-6
lines changed

12 files changed

+839
-6
lines changed

client/rpc-core/src/types/pubsub.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ impl PubSubResult {
126126
let receipt_logs = match receipt {
127127
EthereumReceipt::Legacy(d)
128128
| EthereumReceipt::EIP2930(d)
129-
| EthereumReceipt::EIP1559(d) => d.logs,
129+
| EthereumReceipt::EIP1559(d)
130+
| EthereumReceipt::EIP7702(d) => d.logs,
130131
};
131132

132133
let transaction_hash: Option<H256> = if !receipt_logs.is_empty() {

frame/ethereum/src/lib.rs

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use alloc::{vec, vec::Vec};
3636
use core::marker::PhantomData;
3737
pub use ethereum::{
3838
AccessListItem, BlockV2 as Block, LegacyTransactionMessage, Log, ReceiptV3 as Receipt,
39-
TransactionAction, TransactionV2 as Transaction,
39+
TransactionAction, TransactionV3 as Transaction,
4040
};
4141
use ethereum_types::{Bloom, BloomInput, H160, H256, H64, U256};
4242
use evm::ExitReason;
@@ -432,6 +432,14 @@ impl<T: Config> Pallet<T> {
432432
&ethereum::EIP1559TransactionMessage::from(t.clone()).hash()[..],
433433
);
434434
}
435+
Transaction::EIP7702(t) => {
436+
sig[0..32].copy_from_slice(&t.r[..]);
437+
sig[32..64].copy_from_slice(&t.s[..]);
438+
sig[64] = t.odd_y_parity as u8;
439+
msg.copy_from_slice(
440+
&ethereum::EIP7702TransactionMessage::from(t.clone()).hash()[..],
441+
);
442+
}
435443
}
436444
let pubkey = sp_io::crypto::secp256k1_ecdsa_recover(&sig, &msg).ok()?;
437445
Some(H160::from(H256::from(sp_io::hashing::keccak_256(&pubkey))))
@@ -710,6 +718,12 @@ impl<T: Config> Pallet<T> {
710718
logs_bloom,
711719
logs,
712720
}),
721+
Transaction::EIP7702(_) => Receipt::EIP7702(ethereum::EIP7702ReceiptData {
722+
status_code,
723+
used_gas: cumulative_gas_used,
724+
logs_bloom,
725+
logs,
726+
}),
713727
}
714728
};
715729

@@ -819,9 +833,63 @@ impl<T: Config> Pallet<T> {
819833
access_list,
820834
)
821835
}
836+
Transaction::EIP7702(t) => {
837+
let access_list: Vec<(H160, Vec<H256>)> = t
838+
.access_list
839+
.iter()
840+
.map(|item| (item.address, item.storage_keys.clone()))
841+
.collect();
842+
(
843+
t.data.clone(),
844+
t.value,
845+
t.gas_limit,
846+
Some(t.max_fee_per_gas),
847+
Some(t.max_priority_fee_per_gas),
848+
Some(t.nonce),
849+
t.destination,
850+
access_list,
851+
)
852+
}
822853
}
823854
};
824855

856+
// Process EIP-7702 authorizations before execution
857+
if let Transaction::EIP7702(ref eip7702_tx) = transaction {
858+
// Convert authorization list to our format
859+
let authorizations: Vec<fp_ethereum::Authorization> = eip7702_tx
860+
.authorization_list
861+
.iter()
862+
.map(|item| fp_ethereum::Authorization::from(item.clone()))
863+
.collect();
864+
865+
// Apply authorizations to EVM state
866+
let current_chain_id = T::ChainId::get();
867+
fp_ethereum::Authorization::apply_authorization_list(
868+
&authorizations,
869+
current_chain_id,
870+
|address, code| {
871+
// Set the delegation designator code directly in EVM storage
872+
pallet_evm::AccountCodes::<T>::insert(address, &code);
873+
874+
// Update code metadata for the new code (create manually since from_code is private)
875+
let code_metadata = pallet_evm::CodeMetadata {
876+
size: code.len() as u64,
877+
hash: H256::from(sp_io::hashing::keccak_256(&code)),
878+
};
879+
pallet_evm::AccountCodesMetadata::<T>::insert(address, code_metadata);
880+
881+
Ok(())
882+
},
883+
)
884+
.map_err(|e| DispatchErrorWithPostInfo {
885+
post_info: PostDispatchInfo {
886+
actual_weight: None,
887+
pays_fee: Pays::Yes,
888+
},
889+
error: sp_runtime::DispatchError::Other(e),
890+
})?;
891+
}
892+
825893
match action {
826894
ethereum::TransactionAction::Call(target) => {
827895
let res = match T::Runner::call(

frame/ethereum/src/mock.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,3 +414,59 @@ impl EIP1559UnsignedTransaction {
414414
})
415415
}
416416
}
417+
418+
pub struct EIP7702UnsignedTransaction {
419+
pub nonce: U256,
420+
pub max_priority_fee_per_gas: U256,
421+
pub max_fee_per_gas: U256,
422+
pub gas_limit: U256,
423+
pub destination: TransactionAction,
424+
pub value: U256,
425+
pub data: Vec<u8>,
426+
pub authorization_list: Vec<ethereum::AuthorizationListItem>,
427+
}
428+
429+
impl EIP7702UnsignedTransaction {
430+
pub fn sign(&self, secret: &H256, chain_id: Option<u64>) -> Transaction {
431+
let secret = {
432+
let mut sk: [u8; 32] = [0u8; 32];
433+
sk.copy_from_slice(&secret[0..]);
434+
libsecp256k1::SecretKey::parse(&sk).unwrap()
435+
};
436+
let chain_id = chain_id.unwrap_or(ChainId::get());
437+
let msg = ethereum::EIP7702TransactionMessage {
438+
chain_id,
439+
nonce: self.nonce,
440+
max_priority_fee_per_gas: self.max_priority_fee_per_gas,
441+
max_fee_per_gas: self.max_fee_per_gas,
442+
gas_limit: self.gas_limit,
443+
destination: self.destination,
444+
value: self.value,
445+
data: self.data.clone(),
446+
access_list: vec![],
447+
authorization_list: self.authorization_list.clone(),
448+
};
449+
let signing_message = libsecp256k1::Message::parse_slice(&msg.hash()[..]).unwrap();
450+
451+
let (signature, recid) = libsecp256k1::sign(&signing_message, &secret);
452+
let rs = signature.serialize();
453+
let r = H256::from_slice(&rs[0..32]);
454+
let s = H256::from_slice(&rs[32..64]);
455+
Transaction::EIP7702(ethereum::EIP7702Transaction {
456+
chain_id: msg.chain_id,
457+
nonce: msg.nonce,
458+
max_priority_fee_per_gas: msg.max_priority_fee_per_gas,
459+
max_fee_per_gas: msg.max_fee_per_gas,
460+
gas_limit: msg.gas_limit,
461+
destination: msg.destination,
462+
value: msg.value,
463+
data: msg.data.clone(),
464+
access_list: msg.access_list,
465+
authorization_list: msg.authorization_list,
466+
odd_y_parity: recid.serialize() != 0,
467+
r,
468+
s,
469+
})
470+
}
471+
}
472+

frame/ethereum/src/tests/eip1559.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,7 @@ fn proof_size_base_cost_should_keep_the_same_in_execution_and_estimate() {
614614
raw_tx.value,
615615
Some(100),
616616
vec![],
617+
vec![],
617618
);
618619
assert_eq!(
619620
estimate_tx_data.proof_size_base_cost(),

frame/ethereum/src/tests/eip2930.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,7 @@ fn proof_size_base_cost_should_keep_the_same_in_execution_and_estimate() {
539539
raw_tx.value,
540540
Some(100),
541541
vec![],
542+
vec![],
542543
);
543544
assert_eq!(
544545
estimate_tx_data.proof_size_base_cost(),

0 commit comments

Comments
 (0)