gmsol_solana_utils/
signer.rs

1use std::{collections::HashMap, fmt, ops::Deref, sync::Arc};
2
3use dyn_clone::{clone_trait_object, DynClone};
4use solana_sdk::{
5    hash::Hash,
6    pubkey::Pubkey,
7    signature::Signature,
8    signer::{Signer, SignerError},
9    transaction::VersionedTransaction,
10};
11
12use crate::{
13    address_lookup_table::AddressLookupTables, instruction_group::GetInstructionsOptions,
14    AtomicGroup,
15};
16
17/// Boxed Signer.
18pub type LocalBoxSigner = Box<dyn Signer>;
19
20/// Boxed Clonable Signer.
21#[derive(Clone)]
22pub struct BoxClonableSigner<'a>(pub Box<dyn CloneableSigner + 'a>);
23
24impl fmt::Debug for BoxClonableSigner<'_> {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        f.debug_struct("BoxClonableSigner")
27            .field("pubkey", &self.0.pubkey())
28            .finish_non_exhaustive()
29    }
30}
31
32impl<'a> BoxClonableSigner<'a> {
33    /// Create from `impl Signer`.
34    pub fn new(signer: impl Signer + Clone + 'a) -> Self {
35        Self(Box::new(signer))
36    }
37}
38
39/// Clonable Signer.
40pub trait CloneableSigner: Signer + DynClone {}
41
42impl<T: Signer + Clone> CloneableSigner for T {}
43
44clone_trait_object!(CloneableSigner);
45
46impl Signer for BoxClonableSigner<'_> {
47    fn pubkey(&self) -> Pubkey {
48        self.0.pubkey()
49    }
50
51    fn sign_message(&self, message: &[u8]) -> Signature {
52        self.0.sign_message(message)
53    }
54
55    fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
56        self.0.try_pubkey()
57    }
58
59    fn try_sign_message(&self, message: &[u8]) -> Result<Signature, SignerError> {
60        self.0.try_sign_message(message)
61    }
62
63    fn is_interactive(&self) -> bool {
64        self.0.is_interactive()
65    }
66}
67
68/// Boxed signer.
69pub struct BoxSigner(Box<dyn Signer + Send + Sync>);
70
71impl Signer for BoxSigner {
72    fn pubkey(&self) -> Pubkey {
73        self.0.pubkey()
74    }
75
76    fn try_pubkey(&self) -> Result<Pubkey, solana_sdk::signer::SignerError> {
77        self.0.try_pubkey()
78    }
79
80    fn sign_message(&self, message: &[u8]) -> solana_sdk::signature::Signature {
81        self.0.sign_message(message)
82    }
83
84    fn try_sign_message(
85        &self,
86        message: &[u8],
87    ) -> Result<solana_sdk::signature::Signature, solana_sdk::signer::SignerError> {
88        self.0.try_sign_message(message)
89    }
90
91    fn is_interactive(&self) -> bool {
92        self.0.is_interactive()
93    }
94}
95
96/// Shared Signer.
97pub type SignerRef = Arc<BoxSigner>;
98
99/// Create a new shared signer.
100pub fn shared_signer(signer: impl Signer + Send + Sync + 'static) -> SignerRef {
101    SignerRef::new(BoxSigner(Box::new(signer)))
102}
103
104/// Transaction Signers.
105#[derive(Debug, Clone)]
106pub struct TransactionSigners<C> {
107    signers: HashMap<Pubkey, C>,
108}
109
110impl<C> Default for TransactionSigners<C> {
111    fn default() -> Self {
112        Self {
113            signers: Default::default(),
114        }
115    }
116}
117
118impl<C> TransactionSigners<C> {
119    fn project(&self, ag: &AtomicGroup) -> HashMap<Pubkey, &C> {
120        ag.external_signers()
121            .filter_map(|pubkey| self.signers.get(pubkey).map(|s| (*pubkey, s)))
122            .collect()
123    }
124}
125
126impl<C: Deref<Target = impl Signer>> TransactionSigners<C> {
127    /// Insert a signer.
128    pub fn insert(&mut self, signer: C) -> Option<C> {
129        self.signers.insert(signer.pubkey(), signer)
130    }
131
132    /// Sign the given [`AtomicGroup`].
133    pub fn sign_atomic_instruction_group(
134        &self,
135        ag: &AtomicGroup,
136        recent_blockhash: Hash,
137        options: GetInstructionsOptions,
138        luts: Option<&AddressLookupTables>,
139        allow_partial_sign: bool,
140    ) -> crate::Result<VersionedTransaction> {
141        let signers = self.project(ag);
142        let mut tx = ag.partially_signed_transaction_with_blockhash_and_options(
143            recent_blockhash,
144            options,
145            luts,
146        )?;
147        let message = tx.message.serialize();
148        let expected_signers = &tx.message.static_account_keys()
149            [0..(tx.message.header().num_required_signatures as usize)];
150        let default_signature = Signature::default();
151        for (idx, signature) in tx.signatures.iter_mut().enumerate() {
152            if *signature == default_signature {
153                let pubkey = expected_signers[idx];
154                let Some(signer) = signers.get(&pubkey) else {
155                    if allow_partial_sign {
156                        continue;
157                    } else {
158                        return Err(crate::Error::Signer(SignerError::Custom(format!(
159                            "missing signer for {pubkey}"
160                        ))));
161                    }
162                };
163                *signature = signer.sign_message(&message);
164            }
165        }
166        Ok(tx)
167    }
168}
169
170impl<C: Deref<Target = impl Signer>> FromIterator<C> for TransactionSigners<C> {
171    fn from_iter<T: IntoIterator<Item = C>>(iter: T) -> Self {
172        let mut this = Self::default();
173        for signer in iter {
174            this.insert(signer);
175        }
176        this
177    }
178}
179
180impl<C: Deref<Target = impl Signer>> Extend<C> for TransactionSigners<C> {
181    fn extend<T: IntoIterator<Item = C>>(&mut self, iter: T) {
182        for signer in iter {
183            self.insert(signer);
184        }
185    }
186}