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