1use super::msg::Msg;
9use crate::{Signature, VerifyingKey};
10use cosmos_sdk_proto::cosmos::tx::v1beta1::{
11 mode_info, AuthInfo, Fee, ModeInfo, SignDoc, SignerInfo, TxBody, TxRaw,
12};
13use ecdsa::signature::Signer;
14use eyre::Result;
15use tendermint::{block, chain};
16
17pub struct Builder {
19 chain_id: chain::Id,
21
22 account_number: u64,
24}
25
26impl Builder {
27 pub fn new(chain_id: impl Into<chain::Id>, account_number: u64) -> Self {
29 Self {
30 chain_id: chain_id.into(),
31 account_number,
32 }
33 }
34
35 pub fn chain_id(&self) -> &chain::Id {
37 &self.chain_id
38 }
39
40 pub fn account_number(&self) -> u64 {
42 self.account_number
43 }
44
45 pub fn sign_tx<S>(
47 &self,
48 signer: &S,
49 sequence: u64,
50 messages: &[Msg],
51 fee: Fee,
52 memo: impl Into<String>,
53 timeout_height: block::Height,
54 ) -> Result<Vec<u8>>
55 where
56 S: Signer<Signature>,
57 VerifyingKey: for<'a> From<&'a S>,
58 {
59 let body = TxBody {
61 messages: messages.iter().map(|msg| msg.0.clone()).collect(),
62 memo: memo.into(),
63 timeout_height: timeout_height.into(),
64 extension_options: Default::default(),
65 non_critical_extension_options: Default::default(),
66 };
67
68 let mut body_buf = Vec::new();
70 prost::Message::encode(&body, &mut body_buf).unwrap();
71
72 let pk = VerifyingKey::from(signer);
73 let mut pk_buf = Vec::new();
74 prost::Message::encode(&pk.to_bytes().to_vec(), &mut pk_buf).unwrap();
75
76 let pk_any = prost_types::Any {
78 type_url: "/cosmos.crypto.secp256k1.PubKey".to_string(),
79 value: Vec::from(&pk.to_bytes()[..]),
80 };
81
82 let single = mode_info::Single { mode: 1 };
83
84 let mode = Some(ModeInfo {
85 sum: Some(mode_info::Sum::Single(single)),
86 });
87
88 let signer_info = SignerInfo {
89 public_key: Some(pk_any),
90 mode_info: mode,
91 sequence,
92 };
93
94 let auth_info = AuthInfo {
95 signer_infos: vec![signer_info],
96 fee: Some(fee),
97 };
98
99 let mut auth_buf = Vec::new();
101 prost::Message::encode(&auth_info, &mut auth_buf)?;
102
103 let sign_doc = SignDoc {
104 body_bytes: body_buf.clone(),
105 auth_info_bytes: auth_buf.clone(),
106 chain_id: self.chain_id.to_string(),
107 account_number: self.account_number,
108 };
109
110 let mut signdoc_buf = Vec::new();
112 prost::Message::encode(&sign_doc, &mut signdoc_buf)?;
113
114 let signed = signer.sign(&signdoc_buf);
116
117 let tx_raw = TxRaw {
118 body_bytes: body_buf,
119 auth_info_bytes: auth_buf,
120 signatures: vec![signed.as_ref().to_vec()],
121 };
122
123 let mut txraw_buf = Vec::new();
124 prost::Message::encode(&tx_raw, &mut txraw_buf)?;
125
126 Ok(txraw_buf)
127 }
128}