mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
add specific network/coin/balance types (#619)
* add specific network/coin/balance types * misc fixes * fix clippy * misc fixes * fix pr comments * Make halting for external networks * fix encode/decode
This commit is contained in:
@@ -28,6 +28,7 @@ sp-application-crypto = { git = "https://github.com/serai-dex/substrate", defaul
|
||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
sp-io = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
|
||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
|
||||
@@ -35,7 +36,7 @@ frame-support = { git = "https://github.com/serai-dex/substrate", default-featur
|
||||
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
|
||||
|
||||
[features]
|
||||
std = ["zeroize", "scale/std", "borsh?/std", "serde?/std", "scale-info/std", "sp-core/std", "sp-runtime/std", "frame-support/std"]
|
||||
std = ["zeroize", "scale/std", "borsh?/std", "serde?/std", "scale-info/std", "sp-core/std", "sp-runtime/std", "sp-std/std", "frame-support/std"]
|
||||
borsh = ["dep:borsh"]
|
||||
serde = ["dep:serde"]
|
||||
default = ["std"]
|
||||
|
||||
@@ -29,6 +29,8 @@ pub type SubstrateAmount = u64;
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Amount(pub SubstrateAmount);
|
||||
|
||||
// TODO: these impl shouldn't panic and return error to be dealt with.
|
||||
// Otherwise we might have a panic that stops the network.
|
||||
impl Add for Amount {
|
||||
type Output = Amount;
|
||||
fn add(self, other: Amount) -> Amount {
|
||||
|
||||
@@ -11,7 +11,7 @@ use serde::{Serialize, Deserialize};
|
||||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
use crate::{Coin, Amount};
|
||||
use crate::{Amount, Coin, ExternalCoin};
|
||||
|
||||
/// The type used for balances (a Coin and Balance).
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
@@ -23,6 +23,34 @@ pub struct Balance {
|
||||
pub amount: Amount,
|
||||
}
|
||||
|
||||
/// The type used for balances (a Coin and Balance).
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct ExternalBalance {
|
||||
pub coin: ExternalCoin,
|
||||
pub amount: Amount,
|
||||
}
|
||||
|
||||
impl From<ExternalBalance> for Balance {
|
||||
fn from(balance: ExternalBalance) -> Self {
|
||||
Balance { coin: balance.coin.into(), amount: balance.amount }
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Balance> for ExternalBalance {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(balance: Balance) -> Result<Self, Self::Error> {
|
||||
match balance.coin {
|
||||
Coin::Serai => Err(())?,
|
||||
Coin::External(coin) => Ok(ExternalBalance { coin, amount: balance.amount }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: these impl either should be removed or return errors in case of overflows
|
||||
impl Add<Amount> for Balance {
|
||||
type Output = Balance;
|
||||
fn add(self, other: Amount) -> Balance {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#[cfg(feature = "std")]
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale::{Decode, Encode, EncodeLike, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
@@ -10,54 +10,342 @@ use borsh::{BorshSerialize, BorshDeserialize};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use sp_core::{ConstU32, bounded::BoundedVec};
|
||||
use sp_std::{vec, vec::Vec};
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
use crate::{borsh_serialize_bounded_vec, borsh_deserialize_bounded_vec};
|
||||
|
||||
/// The type used to identify networks.
|
||||
#[derive(
|
||||
Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode, PartialOrd, Ord, MaxEncodedLen, TypeInfo,
|
||||
)]
|
||||
/// The type used to identify external networks.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum NetworkId {
|
||||
Serai,
|
||||
pub enum ExternalNetworkId {
|
||||
Bitcoin,
|
||||
Ethereum,
|
||||
Monero,
|
||||
}
|
||||
impl NetworkId {
|
||||
pub fn coins(&self) -> &'static [Coin] {
|
||||
|
||||
impl Encode for ExternalNetworkId {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
match self {
|
||||
Self::Serai => &[Coin::Serai],
|
||||
Self::Bitcoin => &[Coin::Bitcoin],
|
||||
Self::Ethereum => &[Coin::Ether, Coin::Dai],
|
||||
Self::Monero => &[Coin::Monero],
|
||||
ExternalNetworkId::Bitcoin => vec![1],
|
||||
ExternalNetworkId::Ethereum => vec![2],
|
||||
ExternalNetworkId::Monero => vec![3],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const NETWORKS: [NetworkId; 4] =
|
||||
[NetworkId::Serai, NetworkId::Bitcoin, NetworkId::Ethereum, NetworkId::Monero];
|
||||
impl Decode for ExternalNetworkId {
|
||||
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
|
||||
let kind = input.read_byte()?;
|
||||
match kind {
|
||||
1 => Ok(Self::Bitcoin),
|
||||
2 => Ok(Self::Ethereum),
|
||||
3 => Ok(Self::Monero),
|
||||
_ => Err(scale::Error::from("invalid format")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const COINS: [Coin; 5] = [Coin::Serai, Coin::Bitcoin, Coin::Ether, Coin::Dai, Coin::Monero];
|
||||
impl MaxEncodedLen for ExternalNetworkId {
|
||||
fn max_encoded_len() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
/// The type used to identify coins.
|
||||
#[derive(
|
||||
Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encode, Decode, MaxEncodedLen, TypeInfo,
|
||||
)]
|
||||
impl EncodeLike for ExternalNetworkId {}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshSerialize for ExternalNetworkId {
|
||||
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
writer.write_all(&self.encode())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshDeserialize for ExternalNetworkId {
|
||||
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
|
||||
let mut kind = [0; 1];
|
||||
reader.read_exact(&mut kind)?;
|
||||
ExternalNetworkId::decode(&mut kind.as_slice())
|
||||
.map_err(|_| std::io::Error::other("invalid format"))
|
||||
}
|
||||
}
|
||||
|
||||
/// The type used to identify networks.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum Coin {
|
||||
pub enum NetworkId {
|
||||
Serai,
|
||||
External(ExternalNetworkId),
|
||||
}
|
||||
|
||||
impl Encode for NetworkId {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
match self {
|
||||
NetworkId::Serai => vec![0],
|
||||
NetworkId::External(network) => network.encode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for NetworkId {
|
||||
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
|
||||
let kind = input.read_byte()?;
|
||||
match kind {
|
||||
0 => Ok(Self::Serai),
|
||||
_ => Ok(ExternalNetworkId::decode(&mut [kind].as_slice())?.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MaxEncodedLen for NetworkId {
|
||||
fn max_encoded_len() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeLike for NetworkId {}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshSerialize for NetworkId {
|
||||
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
writer.write_all(&self.encode())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshDeserialize for NetworkId {
|
||||
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
|
||||
let mut kind = [0; 1];
|
||||
reader.read_exact(&mut kind)?;
|
||||
NetworkId::decode(&mut kind.as_slice()).map_err(|_| std::io::Error::other("invalid format"))
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternalNetworkId {
|
||||
pub fn coins(&self) -> Vec<ExternalCoin> {
|
||||
match self {
|
||||
Self::Bitcoin => vec![ExternalCoin::Bitcoin],
|
||||
Self::Ethereum => vec![ExternalCoin::Ether, ExternalCoin::Dai],
|
||||
Self::Monero => vec![ExternalCoin::Monero],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkId {
|
||||
pub fn coins(&self) -> Vec<Coin> {
|
||||
match self {
|
||||
Self::Serai => vec![Coin::Serai],
|
||||
Self::External(network) => {
|
||||
network.coins().into_iter().map(core::convert::Into::into).collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExternalNetworkId> for NetworkId {
|
||||
fn from(network: ExternalNetworkId) -> Self {
|
||||
NetworkId::External(network)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<NetworkId> for ExternalNetworkId {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(network: NetworkId) -> Result<Self, Self::Error> {
|
||||
match network {
|
||||
NetworkId::Serai => Err(())?,
|
||||
NetworkId::External(n) => Ok(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const EXTERNAL_NETWORKS: [ExternalNetworkId; 3] =
|
||||
[ExternalNetworkId::Bitcoin, ExternalNetworkId::Ethereum, ExternalNetworkId::Monero];
|
||||
|
||||
pub const NETWORKS: [NetworkId; 4] = [
|
||||
NetworkId::Serai,
|
||||
NetworkId::External(ExternalNetworkId::Bitcoin),
|
||||
NetworkId::External(ExternalNetworkId::Ethereum),
|
||||
NetworkId::External(ExternalNetworkId::Monero),
|
||||
];
|
||||
|
||||
pub const EXTERNAL_COINS: [ExternalCoin; 4] =
|
||||
[ExternalCoin::Bitcoin, ExternalCoin::Ether, ExternalCoin::Dai, ExternalCoin::Monero];
|
||||
|
||||
pub const COINS: [Coin; 5] = [
|
||||
Coin::Serai,
|
||||
Coin::External(ExternalCoin::Bitcoin),
|
||||
Coin::External(ExternalCoin::Ether),
|
||||
Coin::External(ExternalCoin::Dai),
|
||||
Coin::External(ExternalCoin::Monero),
|
||||
];
|
||||
|
||||
/// The type used to identify external coins.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum ExternalCoin {
|
||||
Bitcoin,
|
||||
Ether,
|
||||
Dai,
|
||||
Monero,
|
||||
}
|
||||
|
||||
impl Encode for ExternalCoin {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
match self {
|
||||
ExternalCoin::Bitcoin => vec![4],
|
||||
ExternalCoin::Ether => vec![5],
|
||||
ExternalCoin::Dai => vec![6],
|
||||
ExternalCoin::Monero => vec![7],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for ExternalCoin {
|
||||
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
|
||||
let kind = input.read_byte()?;
|
||||
match kind {
|
||||
4 => Ok(Self::Bitcoin),
|
||||
5 => Ok(Self::Ether),
|
||||
6 => Ok(Self::Dai),
|
||||
7 => Ok(Self::Monero),
|
||||
_ => Err(scale::Error::from("invalid format")),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl MaxEncodedLen for ExternalCoin {
|
||||
fn max_encoded_len() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeLike for ExternalCoin {}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshSerialize for ExternalCoin {
|
||||
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
writer.write_all(&self.encode())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshDeserialize for ExternalCoin {
|
||||
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
|
||||
let mut kind = [0; 1];
|
||||
reader.read_exact(&mut kind)?;
|
||||
ExternalCoin::decode(&mut kind.as_slice()).map_err(|_| std::io::Error::other("invalid format"))
|
||||
}
|
||||
}
|
||||
|
||||
/// The type used to identify coins.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum Coin {
|
||||
Serai,
|
||||
External(ExternalCoin),
|
||||
}
|
||||
|
||||
impl Encode for Coin {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
match self {
|
||||
Coin::Serai => vec![0],
|
||||
Coin::External(ec) => ec.encode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for Coin {
|
||||
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
|
||||
let kind = input.read_byte()?;
|
||||
match kind {
|
||||
0 => Ok(Self::Serai),
|
||||
_ => Ok(ExternalCoin::decode(&mut [kind].as_slice())?.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MaxEncodedLen for Coin {
|
||||
fn max_encoded_len() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeLike for Coin {}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshSerialize for Coin {
|
||||
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
writer.write_all(&self.encode())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshDeserialize for Coin {
|
||||
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
|
||||
let mut kind = [0; 1];
|
||||
reader.read_exact(&mut kind)?;
|
||||
Coin::decode(&mut kind.as_slice()).map_err(|_| std::io::Error::other("invalid format"))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExternalCoin> for Coin {
|
||||
fn from(coin: ExternalCoin) -> Self {
|
||||
Coin::External(coin)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Coin> for ExternalCoin {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(coin: Coin) -> Result<Self, Self::Error> {
|
||||
match coin {
|
||||
Coin::Serai => Err(())?,
|
||||
Coin::External(c) => Ok(c),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternalCoin {
|
||||
pub fn network(&self) -> ExternalNetworkId {
|
||||
match self {
|
||||
ExternalCoin::Bitcoin => ExternalNetworkId::Bitcoin,
|
||||
ExternalCoin::Ether | ExternalCoin::Dai => ExternalNetworkId::Ethereum,
|
||||
ExternalCoin::Monero => ExternalNetworkId::Monero,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
ExternalCoin::Bitcoin => "Bitcoin",
|
||||
ExternalCoin::Ether => "Ether",
|
||||
ExternalCoin::Dai => "Dai Stablecoin",
|
||||
ExternalCoin::Monero => "Monero",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symbol(&self) -> &'static str {
|
||||
match self {
|
||||
ExternalCoin::Bitcoin => "BTC",
|
||||
ExternalCoin::Ether => "ETH",
|
||||
ExternalCoin::Dai => "DAI",
|
||||
ExternalCoin::Monero => "XMR",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decimals(&self) -> u32 {
|
||||
match self {
|
||||
// Ether and DAI have 18 decimals, yet we only track 8 in order to fit them within u64s
|
||||
ExternalCoin::Bitcoin | ExternalCoin::Ether | ExternalCoin::Dai => 8,
|
||||
ExternalCoin::Monero => 12,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Coin {
|
||||
pub fn native() -> Coin {
|
||||
Coin::Serai
|
||||
@@ -66,37 +354,28 @@ impl Coin {
|
||||
pub fn network(&self) -> NetworkId {
|
||||
match self {
|
||||
Coin::Serai => NetworkId::Serai,
|
||||
Coin::Bitcoin => NetworkId::Bitcoin,
|
||||
Coin::Ether | Coin::Dai => NetworkId::Ethereum,
|
||||
Coin::Monero => NetworkId::Monero,
|
||||
Coin::External(c) => c.network().into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Coin::Serai => "Serai",
|
||||
Coin::Bitcoin => "Bitcoin",
|
||||
Coin::Ether => "Ether",
|
||||
Coin::Dai => "Dai Stablecoin",
|
||||
Coin::Monero => "Monero",
|
||||
Coin::External(c) => c.name(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symbol(&self) -> &'static str {
|
||||
match self {
|
||||
Coin::Serai => "SRI",
|
||||
Coin::Bitcoin => "BTC",
|
||||
Coin::Ether => "ETH",
|
||||
Coin::Dai => "DAI",
|
||||
Coin::Monero => "XMR",
|
||||
Coin::External(c) => c.symbol(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decimals(&self) -> u32 {
|
||||
match self {
|
||||
// Ether and DAI have 18 decimals, yet we only track 8 in order to fit them within u64s
|
||||
Coin::Serai | Coin::Bitcoin | Coin::Ether | Coin::Dai => 8,
|
||||
Coin::Monero => 12,
|
||||
Coin::Serai => 8,
|
||||
Coin::External(c) => c.decimals(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user