snowbridge_core/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
3//! # Core
4//!
5//! Common traits and types
6#![cfg_attr(not(feature = "std"), no_std)]
7
8#[cfg(test)]
9mod tests;
10
11pub mod location;
12pub mod operating_mode;
13pub mod pricing;
14pub mod reward;
15pub mod ringbuffer;
16pub mod sparse_bitmap;
17
18pub use location::{AgentId, AgentIdOf, TokenId, TokenIdOf};
19pub use polkadot_parachain_primitives::primitives::{
20	Id as ParaId, IsSystem, Sibling as SiblingParaId,
21};
22pub use ringbuffer::{RingBufferMap, RingBufferMapImpl};
23pub use sp_core::U256;
24
25use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
26use frame_support::{traits::Contains, BoundedVec};
27use hex_literal::hex;
28use scale_info::TypeInfo;
29use sp_core::{ConstU32, H256};
30use sp_io::hashing::keccak_256;
31use sp_runtime::{traits::AccountIdConversion, RuntimeDebug};
32use sp_std::prelude::*;
33use xcm::latest::{Asset, Junction::Parachain, Location, Result as XcmResult, XcmContext};
34use xcm_executor::traits::TransactAsset;
35
36/// The ID of an agent contract
37pub use operating_mode::BasicOperatingMode;
38
39pub use pricing::{PricingParameters, Rewards};
40
41pub fn sibling_sovereign_account<T>(para_id: ParaId) -> T::AccountId
42where
43	T: frame_system::Config,
44{
45	SiblingParaId::from(para_id).into_account_truncating()
46}
47
48pub struct AllowSiblingsOnly;
49impl Contains<Location> for AllowSiblingsOnly {
50	fn contains(location: &Location) -> bool {
51		matches!(location.unpack(), (1, [Parachain(_)]))
52	}
53}
54
55pub fn gwei(x: u128) -> U256 {
56	U256::from(1_000_000_000u128).saturating_mul(x.into())
57}
58
59pub fn meth(x: u128) -> U256 {
60	U256::from(1_000_000_000_000_000u128).saturating_mul(x.into())
61}
62
63pub fn eth(x: u128) -> U256 {
64	U256::from(1_000_000_000_000_000_000u128).saturating_mul(x.into())
65}
66
67pub const ROC: u128 = 1_000_000_000_000;
68
69/// Identifier for a message channel
70#[derive(
71	Clone,
72	Copy,
73	Encode,
74	Decode,
75	DecodeWithMemTracking,
76	PartialEq,
77	Eq,
78	Default,
79	RuntimeDebug,
80	MaxEncodedLen,
81	TypeInfo,
82)]
83pub struct ChannelId([u8; 32]);
84
85/// Deterministically derive a ChannelId for a sibling parachain
86/// Generator: keccak256("para" + big_endian_bytes(para_id))
87///
88/// The equivalent generator on the Solidity side is in
89/// contracts/src/Types.sol:into().
90fn derive_channel_id_for_sibling(para_id: ParaId) -> ChannelId {
91	let para_id: u32 = para_id.into();
92	let para_id_bytes: [u8; 4] = para_id.to_be_bytes();
93	let prefix: [u8; 4] = *b"para";
94	let preimage: Vec<u8> = prefix.into_iter().chain(para_id_bytes).collect();
95	keccak_256(&preimage).into()
96}
97
98impl ChannelId {
99	pub const fn new(id: [u8; 32]) -> Self {
100		ChannelId(id)
101	}
102}
103
104impl From<ParaId> for ChannelId {
105	fn from(value: ParaId) -> Self {
106		derive_channel_id_for_sibling(value)
107	}
108}
109
110impl From<[u8; 32]> for ChannelId {
111	fn from(value: [u8; 32]) -> Self {
112		ChannelId(value)
113	}
114}
115
116impl From<ChannelId> for [u8; 32] {
117	fn from(value: ChannelId) -> Self {
118		value.0
119	}
120}
121
122impl<'a> From<&'a [u8; 32]> for ChannelId {
123	fn from(value: &'a [u8; 32]) -> Self {
124		ChannelId(*value)
125	}
126}
127
128impl From<H256> for ChannelId {
129	fn from(value: H256) -> Self {
130		ChannelId(value.into())
131	}
132}
133
134impl AsRef<[u8]> for ChannelId {
135	fn as_ref(&self) -> &[u8] {
136		&self.0
137	}
138}
139
140#[derive(Clone, Encode, Decode, RuntimeDebug, MaxEncodedLen, TypeInfo)]
141pub struct Channel {
142	/// ID of the agent contract deployed on Ethereum
143	pub agent_id: AgentId,
144	/// ID of the parachain who will receive or send messages using this channel
145	pub para_id: ParaId,
146}
147
148pub trait StaticLookup {
149	/// Type to lookup from.
150	type Source;
151	/// Type to lookup into.
152	type Target;
153	/// Attempt a lookup.
154	fn lookup(s: Self::Source) -> Option<Self::Target>;
155}
156
157/// Channel for high-priority governance commands
158pub const PRIMARY_GOVERNANCE_CHANNEL: ChannelId =
159	ChannelId::new(hex!("0000000000000000000000000000000000000000000000000000000000000001"));
160
161/// Channel for lower-priority governance commands
162pub const SECONDARY_GOVERNANCE_CHANNEL: ChannelId =
163	ChannelId::new(hex!("0000000000000000000000000000000000000000000000000000000000000002"));
164
165/// Metadata to include in the instantiated ERC20 token contract
166#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, RuntimeDebug, TypeInfo)]
167pub struct AssetMetadata {
168	pub name: BoundedVec<u8, ConstU32<METADATA_FIELD_MAX_LEN>>,
169	pub symbol: BoundedVec<u8, ConstU32<METADATA_FIELD_MAX_LEN>>,
170	pub decimals: u8,
171}
172
173#[cfg(any(test, feature = "std", feature = "runtime-benchmarks"))]
174impl Default for AssetMetadata {
175	fn default() -> Self {
176		AssetMetadata {
177			name: BoundedVec::truncate_from(vec![]),
178			symbol: BoundedVec::truncate_from(vec![]),
179			decimals: 0,
180		}
181	}
182}
183
184/// Maximum length of a string field in ERC20 token metada
185const METADATA_FIELD_MAX_LEN: u32 = 32;
186
187/// Helper function that validates `fee` can be burned, then withdraws it from `origin` and burns
188/// it.
189/// Note: Make sure this is called from a transactional storage context so that side-effects
190/// are rolled back on errors.
191pub fn burn_for_teleport<AssetTransactor>(origin: &Location, fee: &Asset) -> XcmResult
192where
193	AssetTransactor: TransactAsset,
194{
195	let dummy_context = XcmContext { origin: None, message_id: Default::default(), topic: None };
196	AssetTransactor::can_check_out(origin, fee, &dummy_context)?;
197	AssetTransactor::check_out(origin, fee, &dummy_context);
198	AssetTransactor::withdraw_asset(fee, origin, None)?;
199	Ok(())
200}