1#![cfg_attr(all(doc, not(doctest)), feature(doc_auto_cfg))]
2
3use af_sui_types::{
15 Address as SuiAddress,
16 GasData,
17 TransactionData,
18 TransactionDataV1,
19 UserSignature,
20};
21use crypto::Signature;
22use eyre::{Context as _, Result};
23pub use intent::Intent;
24pub use keystore::{FileBasedKeystore, ReadOnlyAccountKeystore};
25use multisig::{MultiSig, MultiSigSigner, ThresholdUnit};
26
27pub mod crypto;
28pub mod intent;
29pub mod keystore;
30pub mod multisig;
31
32pub fn signatures<K: ReadOnlyAccountKeystore>(
51 tx_data: &TransactionData,
52 multisig_sender: Option<MultiSigSigner>,
53 multisig_sponsor: Option<MultiSigSigner>,
54 keystore: &K,
55) -> Result<Vec<UserSignature>> {
56 let TransactionData::V1(TransactionDataV1 {
57 sender,
58 gas_data: GasData { owner: sponsor, .. },
59 ..
60 }) = tx_data;
61
62 let sender_signature = sign_for_address(tx_data, sender, multisig_sender, keystore)
63 .context("Signing for sender")?;
64 let mut signatures = vec![sender_signature];
65
66 if sender != sponsor {
67 signatures.push(
68 sign_for_address(tx_data, sponsor, multisig_sponsor, keystore)
69 .context("Signing for sponsor")?,
70 );
71 };
72 Ok(signatures)
73}
74
75pub fn sign_for_address<K: ReadOnlyAccountKeystore>(
76 tx_data: &TransactionData,
77 address: &SuiAddress,
78 multisig_signer: Option<MultiSigSigner>,
79 keystore: &K,
80) -> Result<UserSignature> {
81 let signature = if let Some(multisig) = multisig_signer {
82 let msig_address = SuiAddress::from(&multisig.multisig_pk);
83 eyre::ensure!(
84 msig_address == *address,
85 "Multisig address {msig_address} doesn't match target address {address}"
86 );
87 sign_transaction_multisig(tx_data, multisig, keystore)?.into()
88 } else {
89 sign_transaction(tx_data, address, keystore)?.into()
90 };
91 Ok(signature)
92}
93
94pub fn sign_transaction<K: ReadOnlyAccountKeystore>(
95 tx_data: &TransactionData,
96 signer: &SuiAddress,
97 keystore: &K,
98) -> Result<Signature> {
99 let signature = keystore.sign_hashed(signer, &tx_data.signing_digest())?;
100 Ok(signature)
101}
102
103pub fn sign_transaction_multisig<K: ReadOnlyAccountKeystore>(
104 tx_data: &TransactionData,
105 MultiSigSigner {
106 multisig_pk,
107 signers,
108 }: MultiSigSigner,
109 keystore: &K,
110) -> Result<MultiSig> {
111 let mut total_weight = 0;
112 let mut signatures = vec![];
113
114 let hashed = tx_data.signing_digest();
115 for idx in signers {
116 let (pk, weight) = multisig_pk.pubkeys().get(idx).ok_or_else(|| {
117 eyre::eyre!("Signer idx {idx} out of bounds for multisig {multisig_pk:?}")
118 })?;
119 total_weight += *weight as ThresholdUnit;
120 signatures.push(keystore.sign_hashed(&pk.to_sui_address(), &hashed)?);
121 }
122
123 if total_weight < *multisig_pk.threshold() {
124 eyre::bail!("Signers do not have enought weight to sign for multisig");
125 }
126
127 Ok(MultiSig::combine(signatures, multisig_pk)?)
128}