mirror of
https://gitlab.com/pezkuwichain/pezkuwi-sdk.git
synced 2026-11-10 20:57:07 +00:00
- Fix pezpallet-welati EnsureOrigin implementations (3 fixes)
- Remove incorrect #[cfg(not(feature = "runtime-benchmarks"))] blocks
- Affects EnsureSerok, EnsureParlementer, EnsureDiwan
- Fix asset-hub-zagros governance origins macros (2 fixes)
- Remove non-benchmark try_successful_origin from decl_unit_ensures!
- Remove non-benchmark try_successful_origin from decl_ensure!
- Rename snowbridge -> pezsnowbridge for consistency
- Update WORKFLOW_PLAN.md with build status and package names
- Correct package names: pezkuwi-teyrchain-bin, pezstaging-node-cli
- Mark completed builds: pezkuwi, pezkuwi-teyrchain-bin,
pezstaging-node-cli, teyrchain-template-node
268 lines
8.2 KiB
Rust
268 lines
8.2 KiB
Rust
// SPDX-License-Identifier: Apache-2.0
|
|
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
|
//! Converts messages from Ethereum to XCM messages
|
|
|
|
use crate::{v2::IGatewayV2::Payload, Log};
|
|
use alloy_core::{
|
|
primitives::B256,
|
|
sol,
|
|
sol_types::{SolEvent, SolType},
|
|
};
|
|
use codec::{Decode, Encode};
|
|
use pezsp_core::{RuntimeDebug, H160, H256};
|
|
use pezsp_std::prelude::*;
|
|
use scale_info::TypeInfo;
|
|
|
|
sol! {
|
|
interface IGatewayV2 {
|
|
struct AsNativeTokenERC20 {
|
|
address token_id;
|
|
uint128 value;
|
|
}
|
|
struct AsForeignTokenERC20 {
|
|
bytes32 token_id;
|
|
uint128 value;
|
|
}
|
|
struct EthereumAsset {
|
|
uint8 kind;
|
|
bytes data;
|
|
}
|
|
struct Xcm {
|
|
uint8 kind;
|
|
bytes data;
|
|
}
|
|
struct XcmCreateAsset {
|
|
address token;
|
|
uint8 network;
|
|
}
|
|
struct Payload {
|
|
address origin;
|
|
EthereumAsset[] assets;
|
|
Xcm xcm;
|
|
bytes claimer;
|
|
uint128 value;
|
|
uint128 executionFee;
|
|
uint128 relayerFee;
|
|
}
|
|
event OutboundMessageAccepted(uint64 nonce, Payload payload);
|
|
}
|
|
}
|
|
|
|
impl core::fmt::Debug for IGatewayV2::Payload {
|
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
f.debug_struct("Payload")
|
|
.field("origin", &self.origin)
|
|
.field("assets", &self.assets)
|
|
.field("xcm", &self.xcm)
|
|
.field("claimer", &self.claimer)
|
|
.field("value", &self.value)
|
|
.field("executionFee", &self.executionFee)
|
|
.field("relayerFee", &self.relayerFee)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl core::fmt::Debug for IGatewayV2::EthereumAsset {
|
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
f.debug_struct("EthereumAsset")
|
|
.field("kind", &self.kind)
|
|
.field("data", &self.data)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl core::fmt::Debug for IGatewayV2::Xcm {
|
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
f.debug_struct("Xcm")
|
|
.field("kind", &self.kind)
|
|
.field("data", &self.data)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
|
|
pub enum XcmPayload {
|
|
/// Represents raw XCM bytes
|
|
Raw(Vec<u8>),
|
|
/// A token registration template
|
|
CreateAsset { token: H160, network: Network },
|
|
}
|
|
|
|
/// Network enum for cross-chain message destination
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Encode, Decode, TypeInfo)]
|
|
pub enum Network {
|
|
/// Pezkuwi network
|
|
Pezkuwi,
|
|
}
|
|
|
|
/// The ethereum side sends messages which are transcoded into XCM on BH. These messages are
|
|
/// self-contained, in that they can be transcoded using only information in the message.
|
|
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
|
|
pub struct Message {
|
|
/// The address of the outbound queue on Ethereum that emitted this message as an event log
|
|
pub gateway: H160,
|
|
/// A nonce for enforcing replay protection and ordering.
|
|
pub nonce: u64,
|
|
/// The address on Ethereum that initiated the message.
|
|
pub origin: H160,
|
|
/// The assets sent from Ethereum (ERC-20s).
|
|
pub assets: Vec<EthereumAsset>,
|
|
/// The command originating from the Gateway contract.
|
|
pub xcm: XcmPayload,
|
|
/// The claimer in the case that funds get trapped. Expected to be an XCM::v5::Location.
|
|
pub claimer: Option<Vec<u8>>,
|
|
/// Native ether bridged over from Ethereum
|
|
pub value: u128,
|
|
/// Fee in eth to cover the xcm execution on AH.
|
|
pub execution_fee: u128,
|
|
/// Relayer reward in eth. Needs to cover all costs of sending a message.
|
|
pub relayer_fee: u128,
|
|
}
|
|
|
|
/// An asset that will be transacted on AH. The asset will be reserved/withdrawn and placed into
|
|
/// the holding register. The user needs to provide additional xcm to deposit the asset
|
|
/// in a beneficiary account.
|
|
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
|
|
pub enum EthereumAsset {
|
|
NativeTokenERC20 {
|
|
/// The native token ID
|
|
token_id: H160,
|
|
/// The monetary value of the asset
|
|
value: u128,
|
|
},
|
|
ForeignTokenERC20 {
|
|
/// The foreign token ID
|
|
token_id: H256,
|
|
/// The monetary value of the asset
|
|
value: u128,
|
|
},
|
|
}
|
|
|
|
#[derive(Copy, Clone, RuntimeDebug)]
|
|
pub struct MessageDecodeError;
|
|
|
|
impl TryFrom<&Log> for Message {
|
|
type Error = MessageDecodeError;
|
|
|
|
fn try_from(log: &Log) -> Result<Self, Self::Error> {
|
|
// Convert to B256 for Alloy decoding
|
|
let topics: Vec<B256> = log.topics.iter().map(|x| B256::from_slice(x.as_ref())).collect();
|
|
|
|
// Decode the Solidity event from raw logs
|
|
let event = IGatewayV2::OutboundMessageAccepted::decode_raw_log_validate(topics, &log.data)
|
|
.map_err(|_| MessageDecodeError)?;
|
|
|
|
let payload = event.payload;
|
|
|
|
let bizinikiwi_assets = Self::extract_assets(&payload)?;
|
|
|
|
let xcm = XcmPayload::try_from(&payload)?;
|
|
|
|
let mut claimer = None;
|
|
if payload.claimer.len() > 0 {
|
|
claimer = Some(payload.claimer.to_vec());
|
|
}
|
|
|
|
let message = Message {
|
|
gateway: log.address,
|
|
nonce: event.nonce,
|
|
origin: H160::from(payload.origin.as_ref()),
|
|
assets: bizinikiwi_assets,
|
|
xcm,
|
|
claimer,
|
|
value: payload.value,
|
|
execution_fee: payload.executionFee,
|
|
relayer_fee: payload.relayerFee,
|
|
};
|
|
|
|
Ok(message)
|
|
}
|
|
}
|
|
|
|
impl Message {
|
|
fn extract_assets(
|
|
payload: &IGatewayV2::Payload,
|
|
) -> Result<Vec<EthereumAsset>, MessageDecodeError> {
|
|
let mut bizinikiwi_assets = vec![];
|
|
for asset in &payload.assets {
|
|
bizinikiwi_assets.push(EthereumAsset::try_from(asset)?);
|
|
}
|
|
Ok(bizinikiwi_assets)
|
|
}
|
|
}
|
|
|
|
impl TryFrom<&IGatewayV2::Payload> for XcmPayload {
|
|
type Error = MessageDecodeError;
|
|
|
|
fn try_from(payload: &Payload) -> Result<Self, Self::Error> {
|
|
let xcm = match payload.xcm.kind {
|
|
0 => XcmPayload::Raw(payload.xcm.data.to_vec()),
|
|
1 => {
|
|
let create_asset =
|
|
IGatewayV2::XcmCreateAsset::abi_decode_validate(&payload.xcm.data)
|
|
.map_err(|_| MessageDecodeError)?;
|
|
// Convert u8 network to Network enum
|
|
let network = match create_asset.network {
|
|
0 => Network::Pezkuwi,
|
|
_ => return Err(MessageDecodeError),
|
|
};
|
|
XcmPayload::CreateAsset { token: H160::from(create_asset.token.as_ref()), network }
|
|
},
|
|
_ => return Err(MessageDecodeError),
|
|
};
|
|
Ok(xcm)
|
|
}
|
|
}
|
|
|
|
impl TryFrom<&IGatewayV2::EthereumAsset> for EthereumAsset {
|
|
type Error = MessageDecodeError;
|
|
|
|
fn try_from(asset: &IGatewayV2::EthereumAsset) -> Result<EthereumAsset, Self::Error> {
|
|
let asset = match asset.kind {
|
|
0 => {
|
|
let native_data = IGatewayV2::AsNativeTokenERC20::abi_decode_validate(&asset.data)
|
|
.map_err(|_| MessageDecodeError)?;
|
|
EthereumAsset::NativeTokenERC20 {
|
|
token_id: H160::from(native_data.token_id.as_ref()),
|
|
value: native_data.value,
|
|
}
|
|
},
|
|
1 => {
|
|
let foreign_data =
|
|
IGatewayV2::AsForeignTokenERC20::abi_decode_validate(&asset.data)
|
|
.map_err(|_| MessageDecodeError)?;
|
|
EthereumAsset::ForeignTokenERC20 {
|
|
token_id: H256::from(foreign_data.token_id.as_ref()),
|
|
value: foreign_data.value,
|
|
}
|
|
},
|
|
_ => return Err(MessageDecodeError),
|
|
};
|
|
Ok(asset)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use hex_literal::hex;
|
|
use pezframe_support::assert_ok;
|
|
use pezsp_core::H160;
|
|
|
|
#[test]
|
|
fn test_decode() {
|
|
let log = Log{
|
|
address: hex!("b1185ede04202fe62d38f5db72f71e38ff3e8305").into(),
|
|
topics: vec![hex!("550e2067494b1736ea5573f2d19cdc0ac95b410fff161bf16f11c6229655ec9c").into()],
|
|
data: hex!("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000b1185ede04202fe62d38f5db72f71e38ff3e830500000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000009184e72a0000000000000000000000000000000000000000000000000000000015d3ef798000000000000000000000000000000000000000000000000000000015d3ef798000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000b8ea8cb425d85536b158d661da1ef0895bb92f1d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").to_vec(),
|
|
};
|
|
|
|
let result = Message::try_from(&log);
|
|
assert_ok!(result.clone());
|
|
let message = result.unwrap();
|
|
|
|
assert_eq!(H160::from(hex!("b1185ede04202fe62d38f5db72f71e38ff3e8305")), message.gateway);
|
|
assert_eq!(H160::from(hex!("b1185ede04202fe62d38f5db72f71e38ff3e8305")), message.origin);
|
|
}
|
|
}
|