use codec::{Decode, Encode};
use frame_support::PalletError;
use scale_info::TypeInfo;
use sp_arithmetic::traits::{BaseArithmetic, Unsigned};
use sp_core::{RuntimeDebug, H256};
pub use v1::{AgentExecuteCommand, Command, Initializer, Message, OperatingMode, QueuedMessage};
#[derive(Encode, Decode, TypeInfo, Clone, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(PartialEq))]
pub enum VersionedQueuedMessage {
V1(QueuedMessage),
}
impl TryFrom<VersionedQueuedMessage> for QueuedMessage {
type Error = ();
fn try_from(x: VersionedQueuedMessage) -> Result<Self, Self::Error> {
use VersionedQueuedMessage::*;
match x {
V1(x) => Ok(x),
}
}
}
impl<T: Into<QueuedMessage>> From<T> for VersionedQueuedMessage {
fn from(x: T) -> Self {
VersionedQueuedMessage::V1(x.into())
}
}
mod v1 {
use crate::{pricing::UD60x18, ChannelId};
use codec::{Decode, Encode};
use ethabi::Token;
use scale_info::TypeInfo;
use sp_core::{RuntimeDebug, H160, H256, U256};
use sp_std::{borrow::ToOwned, vec, vec::Vec};
#[derive(Encode, Decode, TypeInfo, Clone, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(PartialEq))]
pub struct Message {
pub id: Option<H256>,
pub channel_id: ChannelId,
pub command: Command,
}
#[derive(Copy, Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub enum OperatingMode {
Normal,
RejectingOutboundMessages,
}
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
#[cfg_attr(feature = "std", derive(PartialEq))]
pub enum Command {
AgentExecute {
agent_id: H256,
command: AgentExecuteCommand,
},
Upgrade {
impl_address: H160,
impl_code_hash: H256,
initializer: Option<Initializer>,
},
CreateAgent {
agent_id: H256,
},
CreateChannel {
channel_id: ChannelId,
agent_id: H256,
mode: OperatingMode,
},
UpdateChannel {
channel_id: ChannelId,
mode: OperatingMode,
},
SetOperatingMode {
mode: OperatingMode,
},
TransferNativeFromAgent {
agent_id: H256,
recipient: H160,
amount: u128,
},
SetTokenTransferFees {
create_asset_xcm: u128,
transfer_asset_xcm: u128,
register_token: U256,
},
SetPricingParameters {
exchange_rate: UD60x18,
delivery_cost: u128,
multiplier: UD60x18,
},
TransferNativeToken {
agent_id: H256,
token: H160,
recipient: H160,
amount: u128,
},
RegisterForeignToken {
token_id: H256,
name: Vec<u8>,
symbol: Vec<u8>,
decimals: u8,
},
MintForeignToken {
token_id: H256,
recipient: H160,
amount: u128,
},
}
impl Command {
pub fn index(&self) -> u8 {
match self {
Command::AgentExecute { .. } => 0,
Command::Upgrade { .. } => 1,
Command::CreateAgent { .. } => 2,
Command::CreateChannel { .. } => 3,
Command::UpdateChannel { .. } => 4,
Command::SetOperatingMode { .. } => 5,
Command::TransferNativeFromAgent { .. } => 6,
Command::SetTokenTransferFees { .. } => 7,
Command::SetPricingParameters { .. } => 8,
Command::TransferNativeToken { .. } => 9,
Command::RegisterForeignToken { .. } => 10,
Command::MintForeignToken { .. } => 11,
}
}
pub fn abi_encode(&self) -> Vec<u8> {
match self {
Command::AgentExecute { agent_id, command } =>
ethabi::encode(&[Token::Tuple(vec![
Token::FixedBytes(agent_id.as_bytes().to_owned()),
Token::Bytes(command.abi_encode()),
])]),
Command::Upgrade { impl_address, impl_code_hash, initializer, .. } =>
ethabi::encode(&[Token::Tuple(vec![
Token::Address(*impl_address),
Token::FixedBytes(impl_code_hash.as_bytes().to_owned()),
initializer
.clone()
.map_or(Token::Bytes(vec![]), |i| Token::Bytes(i.params)),
])]),
Command::CreateAgent { agent_id } =>
ethabi::encode(&[Token::Tuple(vec![Token::FixedBytes(
agent_id.as_bytes().to_owned(),
)])]),
Command::CreateChannel { channel_id, agent_id, mode } =>
ethabi::encode(&[Token::Tuple(vec![
Token::FixedBytes(channel_id.as_ref().to_owned()),
Token::FixedBytes(agent_id.as_bytes().to_owned()),
Token::Uint(U256::from((*mode) as u64)),
])]),
Command::UpdateChannel { channel_id, mode } =>
ethabi::encode(&[Token::Tuple(vec![
Token::FixedBytes(channel_id.as_ref().to_owned()),
Token::Uint(U256::from((*mode) as u64)),
])]),
Command::SetOperatingMode { mode } =>
ethabi::encode(&[Token::Tuple(vec![Token::Uint(U256::from((*mode) as u64))])]),
Command::TransferNativeFromAgent { agent_id, recipient, amount } =>
ethabi::encode(&[Token::Tuple(vec![
Token::FixedBytes(agent_id.as_bytes().to_owned()),
Token::Address(*recipient),
Token::Uint(U256::from(*amount)),
])]),
Command::SetTokenTransferFees {
create_asset_xcm,
transfer_asset_xcm,
register_token,
} => ethabi::encode(&[Token::Tuple(vec![
Token::Uint(U256::from(*create_asset_xcm)),
Token::Uint(U256::from(*transfer_asset_xcm)),
Token::Uint(*register_token),
])]),
Command::SetPricingParameters { exchange_rate, delivery_cost, multiplier } =>
ethabi::encode(&[Token::Tuple(vec![
Token::Uint(exchange_rate.clone().into_inner()),
Token::Uint(U256::from(*delivery_cost)),
Token::Uint(multiplier.clone().into_inner()),
])]),
Command::TransferNativeToken { agent_id, token, recipient, amount } =>
ethabi::encode(&[Token::Tuple(vec![
Token::FixedBytes(agent_id.as_bytes().to_owned()),
Token::Address(*token),
Token::Address(*recipient),
Token::Uint(U256::from(*amount)),
])]),
Command::RegisterForeignToken { token_id, name, symbol, decimals } =>
ethabi::encode(&[Token::Tuple(vec![
Token::FixedBytes(token_id.as_bytes().to_owned()),
Token::String(name.to_owned()),
Token::String(symbol.to_owned()),
Token::Uint(U256::from(*decimals)),
])]),
Command::MintForeignToken { token_id, recipient, amount } =>
ethabi::encode(&[Token::Tuple(vec![
Token::FixedBytes(token_id.as_bytes().to_owned()),
Token::Address(*recipient),
Token::Uint(U256::from(*amount)),
])]),
}
}
}
#[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)]
pub struct Initializer {
pub params: Vec<u8>,
pub maximum_required_gas: u64,
}
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
#[cfg_attr(feature = "std", derive(PartialEq))]
pub enum AgentExecuteCommand {
TransferToken {
token: H160,
recipient: H160,
amount: u128,
},
}
impl AgentExecuteCommand {
fn index(&self) -> u8 {
match self {
AgentExecuteCommand::TransferToken { .. } => 0,
}
}
pub fn abi_encode(&self) -> Vec<u8> {
match self {
AgentExecuteCommand::TransferToken { token, recipient, amount } =>
ethabi::encode(&[
Token::Uint(self.index().into()),
Token::Bytes(ethabi::encode(&[
Token::Address(*token),
Token::Address(*recipient),
Token::Uint(U256::from(*amount)),
])),
]),
}
}
}
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
#[cfg_attr(feature = "std", derive(PartialEq))]
pub struct QueuedMessage {
pub id: H256,
pub channel_id: ChannelId,
pub command: Command,
}
}
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
#[cfg_attr(feature = "std", derive(PartialEq))]
pub struct Fee<Balance>
where
Balance: BaseArithmetic + Unsigned + Copy,
{
pub local: Balance,
pub remote: Balance,
}
impl<Balance> Fee<Balance>
where
Balance: BaseArithmetic + Unsigned + Copy,
{
pub fn total(&self) -> Balance {
self.local.saturating_add(self.remote)
}
}
impl<Balance> From<(Balance, Balance)> for Fee<Balance>
where
Balance: BaseArithmetic + Unsigned + Copy,
{
fn from((local, remote): (Balance, Balance)) -> Self {
Self { local, remote }
}
}
pub trait SendMessage: SendMessageFeeProvider {
type Ticket: Clone + Encode + Decode;
fn validate(
message: &Message,
) -> Result<(Self::Ticket, Fee<<Self as SendMessageFeeProvider>::Balance>), SendError>;
fn deliver(ticket: Self::Ticket) -> Result<H256, SendError>;
}
pub trait Ticket: Encode + Decode + Clone {
fn message_id(&self) -> H256;
}
pub trait SendMessageFeeProvider {
type Balance: BaseArithmetic + Unsigned + Copy;
fn local_fee() -> Self::Balance;
}
#[derive(Copy, Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, PalletError, TypeInfo)]
pub enum SendError {
MessageTooLarge,
Halted,
InvalidChannel,
}
pub trait GasMeter {
const MAXIMUM_BASE_GAS: u64;
fn maximum_gas_used_at_most(command: &Command) -> u64 {
Self::MAXIMUM_BASE_GAS + Self::maximum_dispatch_gas_used_at_most(command)
}
fn maximum_dispatch_gas_used_at_most(command: &Command) -> u64;
}
pub struct ConstantGasMeter;
impl GasMeter for ConstantGasMeter {
const MAXIMUM_BASE_GAS: u64 = 185_000;
fn maximum_dispatch_gas_used_at_most(command: &Command) -> u64 {
match command {
Command::CreateAgent { .. } => 275_000,
Command::CreateChannel { .. } => 100_000,
Command::UpdateChannel { .. } => 50_000,
Command::TransferNativeFromAgent { .. } => 60_000,
Command::SetOperatingMode { .. } => 40_000,
Command::AgentExecute { command, .. } => match command {
AgentExecuteCommand::TransferToken { .. } => 100_000,
},
Command::Upgrade { initializer, .. } => {
let initializer_max_gas = match *initializer {
Some(Initializer { maximum_required_gas, .. }) => maximum_required_gas,
None => 0,
};
50_000 + initializer_max_gas
},
Command::SetTokenTransferFees { .. } => 60_000,
Command::SetPricingParameters { .. } => 60_000,
Command::TransferNativeToken { .. } => 100_000,
Command::RegisterForeignToken { .. } => 1_200_000,
Command::MintForeignToken { .. } => 100_000,
}
}
}
impl GasMeter for () {
const MAXIMUM_BASE_GAS: u64 = 1;
fn maximum_dispatch_gas_used_at_most(_: &Command) -> u64 {
1
}
}
pub const ETHER_DECIMALS: u8 = 18;