use ink_env::{
DefaultEnvironment,
Environment,
};
use sp_core::Pair;
use std::{
fmt::Debug,
str::FromStr,
};
use subxt::{
Config,
PolkadotConfig,
SubstrateConfig,
config::{
PolkadotExtrinsicParams,
SubstrateExtrinsicParams,
},
tx::Signer as SignerT,
utils::{
AccountId32,
MultiSignature,
},
};
pub trait SignerConfig<C: Config + Environment> {
type Signer: SignerT<C> + FromStr + Clone;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Ecdsachain {}
impl Config for Ecdsachain {
type AccountId = <SubstrateConfig as Config>::AccountId;
type Address = <SubstrateConfig as Config>::Address;
type Signature = <SubstrateConfig as Config>::Signature;
type Hasher = <SubstrateConfig as Config>::Hasher;
type Header = <SubstrateConfig as Config>::Header;
type ExtrinsicParams = SubstrateExtrinsicParams<Self>;
type AssetId = <SubstrateConfig as Config>::AssetId;
}
impl Environment for Ecdsachain {
const NATIVE_TO_ETH_RATIO: u32 =
<DefaultEnvironment as Environment>::NATIVE_TO_ETH_RATIO;
const TRUST_BACKED_ASSETS_PRECOMPILE_INDEX: u16 =
<DefaultEnvironment as Environment>::TRUST_BACKED_ASSETS_PRECOMPILE_INDEX;
const POOL_ASSETS_PRECOMPILE_INDEX: u16 =
<DefaultEnvironment as Environment>::POOL_ASSETS_PRECOMPILE_INDEX;
type AccountId = <DefaultEnvironment as Environment>::AccountId;
type Balance = <DefaultEnvironment as Environment>::Balance;
type Hash = <DefaultEnvironment as Environment>::Hash;
type Timestamp = <DefaultEnvironment as Environment>::Timestamp;
type BlockNumber = <DefaultEnvironment as Environment>::BlockNumber;
type EventRecord = ();
}
impl SignerConfig<Self> for Ecdsachain
where
<Self as Config>::Signature: From<MultiSignature>,
{
type Signer = SignerEcdsa<Self>;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Substrate {}
impl Config for Substrate {
type AccountId = <SubstrateConfig as Config>::AccountId;
type Address = <SubstrateConfig as Config>::Address;
type Signature = <SubstrateConfig as Config>::Signature;
type Hasher = <SubstrateConfig as Config>::Hasher;
type Header = <SubstrateConfig as Config>::Header;
type ExtrinsicParams = SubstrateExtrinsicParams<Self>;
type AssetId = <SubstrateConfig as Config>::AssetId;
}
impl Environment for Substrate {
const NATIVE_TO_ETH_RATIO: u32 =
<DefaultEnvironment as Environment>::NATIVE_TO_ETH_RATIO;
const TRUST_BACKED_ASSETS_PRECOMPILE_INDEX: u16 =
<DefaultEnvironment as Environment>::TRUST_BACKED_ASSETS_PRECOMPILE_INDEX;
const POOL_ASSETS_PRECOMPILE_INDEX: u16 =
<DefaultEnvironment as Environment>::POOL_ASSETS_PRECOMPILE_INDEX;
type AccountId = <DefaultEnvironment as Environment>::AccountId;
type Balance = <DefaultEnvironment as Environment>::Balance;
type Hash = <DefaultEnvironment as Environment>::Hash;
type Timestamp = <DefaultEnvironment as Environment>::Timestamp;
type BlockNumber = <DefaultEnvironment as Environment>::BlockNumber;
type EventRecord = ();
}
impl SignerConfig<Self> for Substrate {
type Signer = SignerSR25519<Self>;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Polkadot {}
impl Config for Polkadot {
type AccountId = <PolkadotConfig as Config>::AccountId;
type Address = <PolkadotConfig as Config>::Address;
type Signature = <PolkadotConfig as Config>::Signature;
type Hasher = <PolkadotConfig as Config>::Hasher;
type Header = <PolkadotConfig as Config>::Header;
type ExtrinsicParams = PolkadotExtrinsicParams<Self>;
type AssetId = <PolkadotConfig as Config>::AssetId;
}
impl Environment for Polkadot {
const NATIVE_TO_ETH_RATIO: u32 =
<DefaultEnvironment as Environment>::NATIVE_TO_ETH_RATIO;
const TRUST_BACKED_ASSETS_PRECOMPILE_INDEX: u16 =
<DefaultEnvironment as Environment>::TRUST_BACKED_ASSETS_PRECOMPILE_INDEX;
const POOL_ASSETS_PRECOMPILE_INDEX: u16 =
<DefaultEnvironment as Environment>::POOL_ASSETS_PRECOMPILE_INDEX;
type AccountId = <DefaultEnvironment as Environment>::AccountId;
type Balance = <DefaultEnvironment as Environment>::Balance;
type Hash = <DefaultEnvironment as Environment>::Hash;
type Timestamp = <DefaultEnvironment as Environment>::Timestamp;
type BlockNumber = <DefaultEnvironment as Environment>::BlockNumber;
type EventRecord = ();
}
impl SignerConfig<Self> for Polkadot {
type Signer = SignerSR25519<Self>;
}
#[derive(Clone)]
pub struct SignerSR25519<C: Config> {
account_id: <C as Config>::AccountId,
signer: sp_core::sr25519::Pair,
}
impl<C: Config<AccountId: From<AccountId32>>> SignerSR25519<C> {
pub fn new(signer: sp_core::sr25519::Pair) -> Self {
Self {
account_id: AccountId32(signer.public().0).into(),
signer,
}
}
}
impl<C: Config<AccountId: From<AccountId32>>> FromStr for SignerSR25519<C> {
type Err = anyhow::Error;
fn from_str(input: &str) -> Result<SignerSR25519<C>, Self::Err> {
let keypair = sp_core::sr25519::Pair::from_string(input, None)?;
Ok(Self::new(keypair))
}
}
impl<C: Config<Signature: From<MultiSignature>>> SignerT<C> for SignerSR25519<C> {
fn account_id(&self) -> <C as Config>::AccountId {
self.account_id.clone()
}
fn sign(&self, signer_payload: &[u8]) -> C::Signature {
MultiSignature::Sr25519(self.signer.sign(signer_payload).0).into()
}
}
#[derive(Clone)]
pub struct SignerEcdsa<C: Config> {
account_id: <C as Config>::AccountId,
signer: sp_core::ecdsa::Pair,
}
impl<C: Config<AccountId: From<AccountId32>>> SignerEcdsa<C> {
pub fn new(signer: sp_core::ecdsa::Pair) -> Self {
Self {
account_id: AccountId32(sp_core::blake2_256(signer.public().as_ref())).into(),
signer,
}
}
}
impl<C: Config<AccountId: From<AccountId32>>> FromStr for SignerEcdsa<C> {
type Err = anyhow::Error;
fn from_str(input: &str) -> Result<SignerEcdsa<C>, Self::Err> {
let keypair = sp_core::ecdsa::Pair::from_string(input, None)?;
Ok(Self::new(keypair))
}
}
impl<C: Config<Signature: From<MultiSignature>>> SignerT<C> for SignerEcdsa<C> {
fn account_id(&self) -> <C as Config>::AccountId {
self.account_id.clone()
}
fn sign(&self, signer_payload: &[u8]) -> C::Signature {
MultiSignature::Ecdsa(self.signer.sign(signer_payload).0).into()
}
}
#[macro_export]
macro_rules! call_with_config_internal {
($obj:tt ,$function:tt, $config_name:expr, $( ($config_str:literal, $config_obj:ty) ),*) => {
match $config_name {
$(
$config_str => $obj.$function::<$config_obj>().await,
)*
_ => {
let configs = vec![$($config_str),*].iter()
.map(|s| s.to_string())
.collect::<Vec<_>>()
.join(", ");
Err(ErrorVariant::Generic(
contract_extrinsics::GenericError::from_message(
format!("Chain configuration {} not found, allowed configurations: {configs}", $config_name)
)))
},
}
};
}
#[macro_export]
macro_rules! call_with_config {
($obj:tt, $function:ident, $config_name:expr) => {{
assert!(
!format!("{}", $config_name).contains("::"),
"The supplied config name `{}` is not allowed to contain `::`.",
$config_name
);
$crate::call_with_config_internal!(
$obj,
$function,
$config_name,
("Polkadot", $crate::cmd::config::Polkadot),
("Substrate", $crate::cmd::config::Substrate),
("Ecdsachain", $crate::cmd::config::Ecdsachain)
)
}};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sr25519_signer_works() {
let suri = "//Alice";
let pair = sp_core::sr25519::Pair::from_string(suri, None).unwrap();
let signer = SignerSR25519::<SubstrateConfig>::from_str(suri).unwrap();
assert_eq!(signer.account_id, AccountId32(pair.public().0));
assert_eq!(
signer.account_id().to_string(),
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
);
assert_eq!(signer.signer.public(), pair.public());
assert!(matches!(signer.sign(b"test"), MultiSignature::Sr25519(_)))
}
#[test]
fn ecdsa_signer_works() {
let suri = "//Alice";
let pair = sp_core::ecdsa::Pair::from_string(suri, None).unwrap();
let signer = SignerEcdsa::<SubstrateConfig>::from_str(suri).unwrap();
assert_eq!(
signer.account_id,
AccountId32(sp_core::blake2_256(&pair.public().0))
);
assert_eq!(
signer.account_id().to_string(),
"5C7C2Z5sWbytvHpuLTvzKunnnRwQxft1jiqrLD5rhucQ5S9X"
);
assert_eq!(signer.signer.public(), pair.public());
assert!(matches!(signer.sign(b"test"), MultiSignature::Ecdsa(_)))
}
}