use std::str::FromStr;
use base58::ToBase58;
use blake2::{Blake2b512, Digest};
use parity_scale_codec::{Compact, Decode, Encode, Error, Input};
use serde::{Deserialize, Serialize};
pub use sp_core::crypto::{AccountId32, Ss58AddressFormat, Ss58AddressFormatRegistry};
pub use sp_core::ecdsa::{Public as EcdsaPublic, Signature as EcdsaSignature};
pub use sp_core::ed25519::{Public as Ed25519Public, Signature as Ed25519Signature};
pub use sp_core::{blake2_256, H256};
use crate::pallets::timestamp::decode_timestamp;
use crate::ss58::Ss58Codec;
use crate::utils::deser_number_or_hex;
pub mod client;
pub mod network;
pub mod pallets;
pub mod rpc;
pub mod ss58;
mod utils;
pub type GenericAddress = MultiAddress<AccountId32, ()>;
pub type Balance = u128;
#[derive(Clone, Debug, Decode, Encode, PartialEq, Eq)]
pub enum MultiAddress<AccountId, AccountIndex> {
Id(AccountId),
Index(#[codec(compact)] AccountIndex),
Raw(Vec<u8>),
Address32([u8; 32]),
Address20([u8; 20]),
}
impl From<AccountId32> for GenericAddress {
fn from(a: AccountId32) -> Self {
MultiAddress::Id(a)
}
}
impl From<EcdsaPublic> for GenericAddress {
fn from(p: EcdsaPublic) -> Self {
let acct = public_into_account(p);
MultiAddress::Id(acct)
}
}
impl FromStr for GenericAddress {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(AccountId32::from_ss58check(s)
.map_err(|_| "Expected ss58 address")?
.into())
}
}
pub fn public_into_account(p: EcdsaPublic) -> AccountId32 {
let hash = blake2_256(&p.0);
hash.into()
}
pub fn pub_to_account_string(
p: [u8; 32],
prefix: u16,
) -> Result<String, Box<dyn std::error::Error>> {
let raw_key = p;
let mut hasher = Blake2b512::new();
hasher.update(b"SS58PRE");
let simple_prefix: u8 = (prefix & 0x3F) as _;
let full_prefix = 0x4000 | ((prefix >> 8) & 0x3F) | ((prefix & 0xFF) << 6);
let prefix_hi: u8 = (full_prefix >> 8) as _;
let prefix_low: u8 = (full_prefix & 0xFF) as _;
if prefix == simple_prefix as u16 {
hasher.update([simple_prefix]);
} else {
hasher.update([prefix_hi]);
hasher.update([prefix_low]);
}
hasher.update(raw_key);
let checksum = hasher.finalize();
let mut raw_address: Vec<u8> = Vec::with_capacity(64);
if prefix == simple_prefix as u16 {
raw_address.push(simple_prefix);
} else {
raw_address.push(prefix_hi);
raw_address.push(prefix_low);
}
raw_address.append(&mut raw_key.to_vec());
raw_address.extend_from_slice(&checksum[0..2]);
Ok(raw_address[..].to_base58())
}
#[derive(Clone, Debug, Decode, Encode, PartialEq, Eq)]
pub enum MultiSignature {
#[codec(index = 0)]
Ed25519(Ed25519Signature),
#[codec(index = 2)]
Ecdsa(EcdsaSignature),
}
impl From<EcdsaSignature> for MultiSignature {
fn from(value: EcdsaSignature) -> Self {
MultiSignature::Ecdsa(value)
}
}
impl From<Ed25519Signature> for MultiSignature {
fn from(value: Ed25519Signature) -> Self {
MultiSignature::Ed25519(value)
}
}
pub type SignedExtra = (u32, u32, H256, H256, (), (), ());
#[derive(Clone, Copy, Debug, Decode, Encode, PartialEq, Eq)]
pub struct GenericExtra(Era, Compact<u32>, Compact<Balance>);
impl GenericExtra {
pub fn new(era: Era, nonce: u32) -> GenericExtra {
GenericExtra(era, Compact(nonce), Compact(0u128))
}
}
#[derive(Clone, Copy, Debug, Decode, Encode, PartialEq, Eq)]
pub enum Era {
Immortal,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UncheckedExtrinsic<Call> {
pub signature: Option<(GenericAddress, MultiSignature, GenericExtra)>,
pub function: Call,
}
impl<Call: Encode> UncheckedExtrinsic<Call> {
pub fn as_hex(&self) -> String {
format!("0x{}", hex::encode(self.encode()))
}
pub fn call_as_hex(&self) -> String {
format!("0x{}", hex::encode(self.function.encode()))
}
}
impl<Call> Encode for UncheckedExtrinsic<Call>
where
Call: Encode,
{
fn encode(&self) -> Vec<u8> {
encode_with_vec_prefix::<Self, _>(|v| {
match self.signature.as_ref() {
Some(s) => {
v.push(4 | 0b1000_0000);
s.encode_to(v);
}
None => {
v.push(4 & 0b0111_1111);
}
}
self.function.encode_to(v);
})
}
}
impl<Call> Decode for UncheckedExtrinsic<Call>
where
Call: Decode + Encode,
{
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
let _length_do_not_remove_me_see_above: Vec<()> = Decode::decode(input)?;
let version = input.read_byte()?;
let is_signed = version & 0b1000_0000 != 0;
let version = version & 0b0111_1111;
if version != 4 {
return Err("Invalid transaction version".into());
}
Ok(UncheckedExtrinsic {
signature: if is_signed {
Some(Decode::decode(input)?)
} else {
None
},
function: Decode::decode(input)?,
})
}
}
fn encode_with_vec_prefix<T: Encode, F: Fn(&mut Vec<u8>)>(encoder: F) -> Vec<u8> {
let size = core::mem::size_of::<T>();
let reserve = match size {
0..=0b0011_1111 => 1,
0b0100_0000..=0b0011_1111_1111_1111 => 2,
_ => 4,
};
let mut v = Vec::with_capacity(reserve + size);
v.resize(reserve, 0);
encoder(&mut v);
let mut length: Vec<()> = Vec::new();
length.resize(v.len() - reserve, ());
length.using_encoded(|s| {
v.splice(0..reserve, s.iter().cloned());
});
v
}
#[derive(Clone, Copy, Debug, Encode)]
pub struct SignedPayload<Call>((Call, GenericExtra, SignedExtra));
impl<Call: Encode> SignedPayload<Call> {
pub fn new(call: Call, extra: GenericExtra, s_extra: SignedExtra) -> SignedPayload<Call> {
SignedPayload((call, extra, s_extra))
}
pub fn encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
self.0.using_encoded(|payload| {
if payload.len() > 256 {
f(&blake2_256(payload))
} else {
f(payload)
}
})
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Encode, Decode)]
#[serde(rename_all = "camelCase")]
pub struct RuntimeVersion {
pub spec_version: u32,
pub transaction_version: u32,
}
#[derive(Clone, Eq, PartialEq, Default, Debug, Encode, Decode, Deserialize)]
pub struct AccountDataGen<Balance> {
pub free: Balance,
pub reserved: Balance,
pub frozen: Balance,
pub flags: ExtraFlags,
}
const IS_NEW_LOGIC: u128 = 0x80000000_00000000_00000000_00000000u128;
#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, Deserialize)]
pub struct ExtraFlags(u128);
impl Default for ExtraFlags {
fn default() -> Self {
Self(IS_NEW_LOGIC)
}
}
impl ExtraFlags {
pub fn old_logic() -> Self {
Self(0)
}
pub fn set_new_logic(&mut self) {
self.0 |= IS_NEW_LOGIC
}
pub fn is_new_logic(&self) -> bool {
(self.0 & IS_NEW_LOGIC) == IS_NEW_LOGIC
}
}
pub type RefCount = u32;
pub type Index = u32;
#[derive(Clone, Eq, PartialEq, Default, Debug, Encode, Decode, Deserialize)]
pub struct AccountInfoGen<Index, AccountData> {
pub nonce: Index,
pub consumers: RefCount,
pub providers: RefCount,
pub sufficients: RefCount,
pub data: AccountData,
}
pub type AccountData = AccountDataGen<Balance>;
pub type AccountInfo = AccountInfoGen<Index, AccountData>;
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FeeDetails {
pub inclusion_fee: Option<InclusionFee>,
#[serde(skip)]
#[serde(deserialize_with = "deser_number_or_hex")]
pub tip: Balance,
}
impl FeeDetails {
pub fn final_fee(&self) -> Balance {
self.inclusion_fee
.as_ref()
.map(|i| i.inclusion_fee())
.unwrap_or_default()
.saturating_add(self.tip)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InclusionFee {
#[serde(deserialize_with = "deser_number_or_hex")]
pub base_fee: Balance,
#[serde(deserialize_with = "deser_number_or_hex")]
pub len_fee: Balance,
#[serde(deserialize_with = "deser_number_or_hex")]
pub adjusted_weight_fee: Balance,
}
impl InclusionFee {
pub fn inclusion_fee(&self) -> Balance {
self.base_fee
.saturating_add(self.len_fee)
.saturating_add(self.adjusted_weight_fee)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SignedBlock {
pub block: Block,
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Block {
pub header: Header,
pub extrinsics: Vec<String>,
}
impl Block {
pub fn timestamp(&self) -> Option<u64> {
self.extrinsics.iter().find_map(|e| decode_timestamp(e))
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Header {
pub parent_hash: H256,
#[serde(deserialize_with = "deser_number_or_hex")]
pub number: u128,
}