use super::{
SignedExtra,
SignedPayload,
UncheckedExtrinsic,
};
use crate::runtimes::Runtime;
use codec::Encode;
use sp_core::Pair;
use sp_runtime::traits::{
IdentifyAccount,
SignedExtension,
Verify,
};
use std::{
future::Future,
pin::Pin,
};
pub trait Signer<T: Runtime> {
fn account_id(&self) -> &T::AccountId;
fn nonce(&self) -> Option<T::Index>;
fn sign(
&self,
extrinsic: SignedPayload<T>,
) -> Pin<Box<dyn Future<Output = Result<UncheckedExtrinsic<T>, String>> + Send + Sync>>;
}
pub struct PairSigner<T: Runtime, P: Pair> {
account_id: T::AccountId,
nonce: Option<T::Index>,
signer: P,
}
impl<T, P> PairSigner<T, P>
where
T: Runtime,
T::Signature: From<P::Signature>,
<T::Signature as Verify>::Signer:
From<P::Public> + IdentifyAccount<AccountId = T::AccountId>,
P: Pair,
{
pub fn new(signer: P) -> Self {
let account_id =
<T::Signature as Verify>::Signer::from(signer.public()).into_account();
Self {
account_id,
nonce: None,
signer,
}
}
pub fn set_nonce(&mut self, nonce: T::Index) {
self.nonce = Some(nonce);
}
pub fn increment_nonce(&mut self) {
self.nonce = self.nonce.map(|nonce| nonce + 1.into());
}
pub fn signer(&self) -> &P {
&self.signer
}
}
impl<T, P> Signer<T> for PairSigner<T, P>
where
T: Runtime,
T::AccountId: Into<T::Address> + 'static,
<<T::Extra as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
Send + Sync,
P: Pair + 'static,
P::Signature: Into<T::Signature> + 'static,
{
fn account_id(&self) -> &T::AccountId {
&self.account_id
}
fn nonce(&self) -> Option<T::Index> {
self.nonce
}
fn sign(
&self,
extrinsic: SignedPayload<T>,
) -> Pin<Box<dyn Future<Output = Result<UncheckedExtrinsic<T>, String>> + Send + Sync>>
{
let signature = extrinsic.using_encoded(|payload| self.signer.sign(payload));
let (call, extra, _) = extrinsic.deconstruct();
let extrinsic = UncheckedExtrinsic::<T>::new_signed(
call,
self.account_id.clone().into(),
signature.into(),
extra,
);
Box::pin(async move { Ok(extrinsic) })
}
}