aztec_account/
signerless.rs1use async_trait::async_trait;
4
5use crate::account::{Account, AuthorizationProvider, EntrypointOptions};
6use crate::entrypoint::DefaultMultiCallEntrypoint;
7use crate::tx::{AuthWitness, ExecutionPayload};
8use crate::types::{AztecAddress, CompleteAddress};
9use crate::wallet::{ChainInfo, MessageHashOrIntent};
10
11use aztec_core::fee::GasSettings;
12use aztec_core::Error;
13
14use super::account::TxExecutionRequest;
15
16pub struct SignerlessAccount {
24 entrypoint: DefaultMultiCallEntrypoint,
25}
26
27impl SignerlessAccount {
28 pub fn new() -> Self {
30 Self {
31 entrypoint: DefaultMultiCallEntrypoint::new(),
32 }
33 }
34}
35
36impl Default for SignerlessAccount {
37 fn default() -> Self {
38 Self::new()
39 }
40}
41
42#[async_trait]
43impl AuthorizationProvider for SignerlessAccount {
44 async fn create_auth_wit(
45 &self,
46 _intent: MessageHashOrIntent,
47 _chain_info: &ChainInfo,
48 ) -> Result<AuthWitness, Error> {
49 Err(Error::InvalidData(
50 "SignerlessAccount does not support authorization witnesses".into(),
51 ))
52 }
53}
54
55#[async_trait]
56impl Account for SignerlessAccount {
57 fn complete_address(&self) -> &CompleteAddress {
58 panic!("SignerlessAccount does not have a complete address")
59 }
60
61 fn address(&self) -> AztecAddress {
62 panic!("SignerlessAccount does not have an address")
63 }
64
65 async fn create_tx_execution_request(
66 &self,
67 exec: ExecutionPayload,
68 gas_settings: GasSettings,
69 chain_info: &ChainInfo,
70 _options: EntrypointOptions,
71 ) -> Result<TxExecutionRequest, Error> {
72 self.entrypoint
73 .create_tx_execution_request(exec, gas_settings, chain_info)
74 }
75
76 async fn wrap_execution_payload(
77 &self,
78 exec: ExecutionPayload,
79 _options: EntrypointOptions,
80 ) -> Result<ExecutionPayload, Error> {
81 self.entrypoint.wrap_execution_payload(exec)
82 }
83}
84
85#[cfg(test)]
86#[allow(clippy::unwrap_used, clippy::expect_used)]
87mod tests {
88 use super::*;
89 use aztec_core::abi::{AbiValue, FunctionSelector, FunctionType};
90 use aztec_core::constants::protocol_contract_address;
91 use aztec_core::fee::Gas;
92 use aztec_core::tx::FunctionCall;
93 use aztec_core::types::Fr;
94
95 fn sample_chain_info() -> ChainInfo {
96 ChainInfo {
97 chain_id: Fr::from(31337u64),
98 version: Fr::from(1u64),
99 }
100 }
101
102 #[tokio::test]
103 async fn create_auth_wit_returns_error() {
104 let account = SignerlessAccount::new();
105 let chain_info = sample_chain_info();
106 let result = account
107 .create_auth_wit(
108 MessageHashOrIntent::Hash {
109 hash: Fr::from(1u64),
110 },
111 &chain_info,
112 )
113 .await;
114 assert!(result.is_err());
115 assert!(result.unwrap_err().to_string().contains("does not support"));
116 }
117
118 #[tokio::test]
119 async fn create_tx_execution_request_delegates_to_multi_call() {
120 let account = SignerlessAccount::new();
121 let chain_info = sample_chain_info();
122
123 let exec = ExecutionPayload {
124 calls: vec![FunctionCall {
125 to: AztecAddress::from(1u64),
126 selector: FunctionSelector::from_hex("0x11223344").expect("valid"),
127 args: vec![AbiValue::Field(Fr::from(42u64))],
128 function_type: FunctionType::Private,
129 is_static: false,
130 hide_msg_sender: false,
131 }],
132 ..Default::default()
133 };
134
135 let gas_settings = GasSettings {
136 gas_limits: Some(Gas {
137 da_gas: 100,
138 l2_gas: 200,
139 }),
140 ..GasSettings::default()
141 };
142
143 let req = account
144 .create_tx_execution_request(
145 exec,
146 gas_settings.clone(),
147 &chain_info,
148 EntrypointOptions::default(),
149 )
150 .await
151 .expect("create tx");
152
153 assert_eq!(
154 req.origin,
155 protocol_contract_address::multi_call_entrypoint()
156 );
157 assert_eq!(req.tx_context.gas_settings, gas_settings);
158 assert_eq!(req.args_of_calls.len(), 6);
159 }
160
161 #[tokio::test]
162 async fn wrap_execution_payload_delegates() {
163 let account = SignerlessAccount::new();
164 let exec = ExecutionPayload {
165 calls: vec![FunctionCall {
166 to: AztecAddress::from(1u64),
167 selector: FunctionSelector::from_hex("0x11223344").expect("valid"),
168 args: vec![],
169 function_type: FunctionType::Private,
170 is_static: false,
171 hide_msg_sender: false,
172 }],
173 ..Default::default()
174 };
175
176 let wrapped = account
177 .wrap_execution_payload(exec, EntrypointOptions::default())
178 .await
179 .expect("wrap");
180
181 assert_eq!(wrapped.calls.len(), 1);
182 assert_eq!(
183 wrapped.calls[0].to,
184 protocol_contract_address::multi_call_entrypoint()
185 );
186 }
187
188 #[test]
189 fn is_send_sync() {
190 fn assert_send_sync<T: Send + Sync>() {}
191 assert_send_sync::<SignerlessAccount>();
192 }
193
194 #[test]
195 fn trait_object_safety() {
196 fn _assert(_: Box<dyn Account>) {}
197 }
198
199 #[test]
200 fn default_impl() {
201 let _ = SignerlessAccount::default();
202 }
203}