Skip to content
Closed
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions frame/ethereum/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ fp-evm = { version = "3.0.0-dev", path = "../../primitives/evm", default-feature
fp-rpc = { version = "3.0.0-dev", path = "../../primitives/rpc", default-features = false }
fp-self-contained = { version = "1.0.0-dev", path = "../../primitives/self-contained", default-features = false }
fp-storage = { version = "2.0.0", path = "../../primitives/storage", default-features = false }
fp-xcm = { version = "1.0.0-dev", path = "../../primitives/xcm", default-features = false }
pallet-evm = { version = "6.0.0-dev", path = "../evm", default-features = false }

[dev-dependencies]
Expand Down Expand Up @@ -72,6 +73,7 @@ std = [
"fp-rpc/std",
"fp-self-contained/std",
"fp-storage/std",
"fp-xcm/std",
"pallet-evm/std",
]
runtime-benchmarks = [
Expand Down
94 changes: 92 additions & 2 deletions frame/ethereum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,12 @@ pub use ethereum::{
TransactionAction, TransactionV2 as Transaction,
};
pub use fp_rpc::TransactionStatus;
pub use fp_xcm::{EthereumXcmTransaction, XcmToEthereum};

#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
pub enum RawOrigin {
EthereumTransaction(H160),
XcmEthereumTransaction(H160),
}

pub fn ensure_ethereum_transaction<OuterOrigin>(o: OuterOrigin) -> Result<H160, &'static str>
Expand All @@ -79,6 +81,16 @@ where
}
}

pub fn ensure_xcm_ethereum_transaction<OuterOrigin>(o: OuterOrigin) -> Result<H160, &'static str>
where
OuterOrigin: Into<Result<RawOrigin, OuterOrigin>>,
{
match o.into() {
Ok(RawOrigin::XcmEthereumTransaction(n)) => Ok(n),
_ => Err("bad origin: expected to be a xcm Ethereum transaction"),
}
}

#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq)]
struct TransactionData {
action: TransactionAction,
Expand Down Expand Up @@ -120,8 +132,9 @@ impl<O: Into<Result<RawOrigin, O>> + From<RawOrigin>> EnsureOrigin<O>
{
type Success = H160;
fn try_origin(o: O) -> Result<Self::Success, O> {
o.into().map(|o| match o {
RawOrigin::EthereumTransaction(id) => id,
o.into().and_then(|o| match o {
RawOrigin::EthereumTransaction(id) => Ok(id),
_ => Err(o.into()),
})
}

Expand All @@ -131,6 +144,24 @@ impl<O: Into<Result<RawOrigin, O>> + From<RawOrigin>> EnsureOrigin<O>
}
}

pub struct EnsureXcmEthereumTransaction;
impl<O: Into<Result<RawOrigin, O>> + From<RawOrigin>> EnsureOrigin<O>
for EnsureXcmEthereumTransaction
{
type Success = H160;
fn try_origin(o: O) -> Result<Self::Success, O> {
o.into().and_then(|o| match o {
RawOrigin::XcmEthereumTransaction(id) => Ok(id),
_ => Err(o.into()),
})
}

#[cfg(feature = "runtime-benchmarks")]
fn successful_origin() -> O {
O::from(RawOrigin::XcmEthereumTransaction(Default::default()))
}
}

impl<T> Call<T>
where
OriginFor<T>: Into<Result<RawOrigin, OriginFor<T>>>,
Expand Down Expand Up @@ -206,6 +237,8 @@ pub mod pallet {
type Event: From<Event> + IsType<<Self as frame_system::Config>::Event>;
/// How Ethereum state root is calculated.
type StateRoot: Get<H256>;
/// Origin for xcm transact
type XcmEthereumOrigin: EnsureOrigin<Self::Origin, Success = H160>;
}

#[pallet::pallet]
Expand Down Expand Up @@ -301,6 +334,63 @@ pub mod pallet {

Self::apply_validated_transaction(source, transaction)
}

/// Xcm Transact an Ethereum transaction.
#[pallet::weight(<T as pallet_evm::Config>::GasWeightMapping::gas_to_weight({
match xcm_transaction {
EthereumXcmTransaction::V1(v1_tx) => v1_tx.gas_limit.unique_saturated_into()
}
}))]
pub fn transact_xcm(
origin: OriginFor<T>,
xcm_transaction: EthereumXcmTransaction,
) -> DispatchResultWithPostInfo {
let source = T::XcmEthereumOrigin::ensure_origin(origin)?;

let (base_fee, base_fee_weight) = T::FeeCalculator::min_gas_price();
let (who, account_weight) = pallet_evm::Pallet::<T>::account_basic(&source);

let transaction: Option<Transaction> =
xcm_transaction.into_transaction_v2(base_fee, who.nonce);
if let Some(transaction) = transaction {
let transaction_data = Pallet::<T>::transaction_data(&transaction);

let _ = CheckEvmTransaction::<InvalidTransactionWrapper>::new(
CheckEvmTransactionConfig {
evm_config: T::config(),
block_gas_limit: T::BlockGasLimit::get(),
base_fee,
chain_id: 0u64,
is_transactional: true,
},
transaction_data.into(),
)
.validate_in_block_for(&who)
.and_then(|v| v.with_base_fee())
.and_then(|v| v.with_balance_for(&who))
.map_err(|_| sp_runtime::DispatchErrorWithPostInfo {
post_info: PostDispatchInfo {
actual_weight: Some(base_fee_weight.saturating_add(account_weight)),
pays_fee: Pays::Yes,
},
error: sp_runtime::DispatchError::Other(
"Failed to validate ethereum transaction",
),
})?;

Self::apply_validated_transaction(source, transaction)
} else {
Err(sp_runtime::DispatchErrorWithPostInfo {
post_info: PostDispatchInfo {
actual_weight: Some(base_fee_weight.saturating_add(account_weight)),
pays_fee: Pays::Yes,
},
error: sp_runtime::DispatchError::Other(
"Cannot convert xcm payload to known type",
),
})
}
}
}

#[pallet::event]
Expand Down
1 change: 1 addition & 0 deletions frame/ethereum/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ impl pallet_evm::Config for Test {
impl crate::Config for Test {
type Event = Event;
type StateRoot = IntermediateStateRoot<Self>;
type XcmEthereumOrigin = crate::EnsureXcmEthereumTransaction;
}

impl fp_self_contained::SelfContainedCall for Call {
Expand Down
Loading