aztec_account/
meta_payment.rs1use async_trait::async_trait;
6use std::sync::Arc;
7
8use aztec_core::constants::protocol_contract_address;
9use aztec_core::tx::ExecutionPayload;
10use aztec_core::types::AztecAddress;
11use aztec_core::Error;
12use aztec_fee::FeePaymentMethod;
13
14use crate::account::Account;
15use crate::entrypoint::{AccountFeePaymentMethodOptions, DefaultAccountEntrypointOptions};
16
17pub struct AccountEntrypointMetaPaymentMethod {
25 account: Arc<dyn Account>,
26 inner: Option<Arc<dyn FeePaymentMethod>>,
27 fee_entrypoint_options: Option<DefaultAccountEntrypointOptions>,
28}
29
30impl AccountEntrypointMetaPaymentMethod {
31 pub fn new(
39 account: Arc<dyn Account>,
40 inner: Option<Arc<dyn FeePaymentMethod>>,
41 fee_entrypoint_options: Option<DefaultAccountEntrypointOptions>,
42 ) -> Self {
43 Self {
44 account,
45 inner,
46 fee_entrypoint_options,
47 }
48 }
49}
50
51#[async_trait]
52impl FeePaymentMethod for AccountEntrypointMetaPaymentMethod {
53 async fn get_asset(&self) -> Result<AztecAddress, Error> {
54 match &self.inner {
55 Some(method) => method.get_asset().await,
56 None => Ok(protocol_contract_address::fee_juice()),
57 }
58 }
59
60 async fn get_fee_payer(&self) -> Result<AztecAddress, Error> {
61 match &self.inner {
62 Some(method) => method.get_fee_payer().await,
63 None => Ok(self.account.address()),
64 }
65 }
66
67 async fn get_fee_execution_payload(&self) -> Result<ExecutionPayload, Error> {
68 let inner_payload = match &self.inner {
70 Some(method) => method.get_fee_execution_payload().await?,
71 None => ExecutionPayload::default(),
72 };
73
74 let options = match self.fee_entrypoint_options.clone() {
76 Some(opts) => opts,
77 None => {
78 let fee_payer = self.get_fee_payer().await?;
79 let is_payer = fee_payer == self.account.address();
80 let fee_payment_method_options = if is_payer && !inner_payload.calls.is_empty() {
81 AccountFeePaymentMethodOptions::FeeJuiceWithClaim
82 } else if is_payer {
83 AccountFeePaymentMethodOptions::PreexistingFeeJuice
84 } else {
85 AccountFeePaymentMethodOptions::External
86 };
87
88 DefaultAccountEntrypointOptions {
89 cancellable: false,
90 tx_nonce: None,
91 fee_payment_method_options,
92 }
93 }
94 };
95
96 self.account
98 .wrap_execution_payload(inner_payload, options.into())
99 .await
100 }
101}
102
103#[cfg(test)]
104#[allow(clippy::unwrap_used, clippy::expect_used)]
105mod tests {
106 use super::*;
107 use async_trait::async_trait;
108 use aztec_core::tx::AuthWitness;
109 use aztec_core::types::{CompleteAddress, Fr, PublicKeys};
110
111 use crate::account::{AuthorizationProvider, EntrypointOptions};
112 use crate::wallet::{ChainInfo, MessageHashOrIntent};
113
114 struct MockPayerAccount {
115 addr: CompleteAddress,
116 }
117
118 #[async_trait]
119 impl AuthorizationProvider for MockPayerAccount {
120 async fn create_auth_wit(
121 &self,
122 _intent: MessageHashOrIntent,
123 _chain_info: &ChainInfo,
124 ) -> Result<AuthWitness, Error> {
125 Ok(AuthWitness::default())
126 }
127 }
128
129 #[async_trait]
130 impl Account for MockPayerAccount {
131 fn complete_address(&self) -> &CompleteAddress {
132 &self.addr
133 }
134
135 fn address(&self) -> AztecAddress {
136 self.addr.address
137 }
138
139 async fn create_tx_execution_request(
140 &self,
141 _exec: ExecutionPayload,
142 _gas_settings: aztec_core::fee::GasSettings,
143 _chain_info: &ChainInfo,
144 _options: EntrypointOptions,
145 ) -> Result<crate::account::TxExecutionRequest, Error> {
146 unimplemented!()
147 }
148
149 async fn wrap_execution_payload(
150 &self,
151 exec: ExecutionPayload,
152 _options: EntrypointOptions,
153 ) -> Result<ExecutionPayload, Error> {
154 Ok(ExecutionPayload {
156 fee_payer: Some(self.addr.address),
157 ..exec
158 })
159 }
160 }
161
162 fn mock_account(addr: u64) -> Arc<dyn Account> {
163 Arc::new(MockPayerAccount {
164 addr: CompleteAddress {
165 address: AztecAddress::from(addr),
166 public_keys: PublicKeys::default(),
167 partial_address: Fr::zero(),
168 },
169 })
170 }
171
172 struct MockInnerFeeMethod {
173 payer: AztecAddress,
174 }
175
176 #[async_trait]
177 impl FeePaymentMethod for MockInnerFeeMethod {
178 async fn get_asset(&self) -> Result<AztecAddress, Error> {
179 Ok(protocol_contract_address::fee_juice())
180 }
181
182 async fn get_fee_payer(&self) -> Result<AztecAddress, Error> {
183 Ok(self.payer)
184 }
185
186 async fn get_fee_execution_payload(&self) -> Result<ExecutionPayload, Error> {
187 Ok(ExecutionPayload::default())
188 }
189 }
190
191 #[tokio::test]
192 async fn no_inner_returns_wrapped_empty_payload() {
193 let account = mock_account(42);
194 let meta = AccountEntrypointMetaPaymentMethod::new(account, None, None);
195
196 let payload = meta.get_fee_execution_payload().await.expect("payload");
197 assert_eq!(payload.fee_payer, Some(AztecAddress::from(42u64)));
199 }
200
201 #[tokio::test]
202 async fn get_asset_delegates_to_inner() {
203 let account = mock_account(42);
204 let inner: Arc<dyn FeePaymentMethod> = Arc::new(MockInnerFeeMethod {
205 payer: AztecAddress::from(42u64),
206 });
207 let meta = AccountEntrypointMetaPaymentMethod::new(account, Some(inner), None);
208
209 let asset = meta.get_asset().await.expect("asset");
210 assert_eq!(asset, protocol_contract_address::fee_juice());
211 }
212
213 #[tokio::test]
214 async fn get_fee_payer_with_no_inner() {
215 let account = mock_account(42);
216 let meta = AccountEntrypointMetaPaymentMethod::new(account, None, None);
217
218 let payer = meta.get_fee_payer().await.expect("payer");
219 assert_eq!(payer, AztecAddress::from(42u64));
220 }
221
222 #[tokio::test]
223 async fn get_fee_payer_delegates_to_inner() {
224 let account = mock_account(42);
225 let inner: Arc<dyn FeePaymentMethod> = Arc::new(MockInnerFeeMethod {
226 payer: AztecAddress::from(99u64),
227 });
228 let meta = AccountEntrypointMetaPaymentMethod::new(account, Some(inner), None);
229
230 let payer = meta.get_fee_payer().await.expect("payer");
231 assert_eq!(payer, AztecAddress::from(99u64));
232 }
233
234 #[tokio::test]
235 async fn explicit_options_override_auto_detection() {
236 let account = mock_account(42);
237 let explicit_opts = DefaultAccountEntrypointOptions {
238 cancellable: true,
239 tx_nonce: Some(Fr::from(7u64)),
240 fee_payment_method_options: AccountFeePaymentMethodOptions::External,
241 };
242 let meta = AccountEntrypointMetaPaymentMethod::new(account, None, Some(explicit_opts));
243
244 let _payload = meta.get_fee_execution_payload().await.expect("payload");
246 }
247}