Skip to content

Commit e182e95

Browse files
authored
✨ Add support for space, lamports and owner attributes to account macro (#320)
* ✨ Add support for space, lamports and owner attributes to account, so various empty data accounts can be initialized * ✨ Changelog * 🚑️ Fix error message
1 parent 4045955 commit e182e95

File tree

6 files changed

+134
-3
lines changed

6 files changed

+134
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ incremented upon a breaking change and the patch version will be incremented for
1212

1313
**Added**
1414

15+
- Add support for space, lamports and owner to TridentAccounts macro ([320](https://github.com/Ackee-Blockchain/trident/pull/320))
1516
- Added support for simple fuzzing metrics for AFL ([309](https://github.com/Ackee-Blockchain/trident/pull/309))
1617
- Code coverage tracking support for both AFL and Honggfuzz fuzzing via the `-g, --generate-coverage` flag. Coverage data can be visualized using the VS Code extension (ADD_PR_NUMBER)
1718

crates/syn/src/codegen/trident_accounts.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,38 @@ impl ToTokens for TridentAccountsStruct {
7777
quote!(program_id)
7878
};
7979

80+
// Create AccountMetadata if space, owner, or lamports are specified
81+
let account_metadata = if f.constraints.space.is_some() || f.constraints.owner.is_some() || f.constraints.lamports.is_some() {
82+
let space = f.constraints.space.as_ref().map(|s| quote!(#s as usize)).unwrap_or_else(|| quote!(0));
83+
let owner = f.constraints.owner.as_ref().map(|o| quote!(#o)).unwrap_or_else(|| quote!(solana_sdk::system_program::ID));
84+
let lamports = f.constraints.lamports.as_ref().map(|l| quote!(#l)).unwrap_or_else(|| quote!(500 * solana_sdk::native_token::LAMPORTS_PER_SOL));
85+
86+
quote!(Some(AccountMetadata::new(#lamports, #space, #owner)))
87+
} else {
88+
quote!(None)
89+
};
90+
8091
quote! {
8192
storage_accounts
8293
.#storage_ident
83-
.get_or_create(self.#field_name.account_id, client, Some(PdaSeeds::new(&[#(#seeds),*], #program_id_to_use)), None)
94+
.get_or_create(self.#field_name.account_id, client, Some(PdaSeeds::new(&[#(#seeds),*], #program_id_to_use)), #account_metadata)
8495
}
8596
} else {
97+
// Create AccountMetadata if space, owner, or lamports are specified
98+
let account_metadata = if f.constraints.space.is_some() || f.constraints.owner.is_some() || f.constraints.lamports.is_some() {
99+
let space = f.constraints.space.as_ref().map(|s| quote!(#s as usize)).unwrap_or_else(|| quote!(0));
100+
let owner = f.constraints.owner.as_ref().map(|o| quote!(#o)).unwrap_or_else(|| quote!(solana_sdk::system_program::ID));
101+
let lamports = f.constraints.lamports.as_ref().map(|l| quote!(#l)).unwrap_or_else(|| quote!(500 * solana_sdk::native_token::LAMPORTS_PER_SOL));
102+
103+
quote!(Some(AccountMetadata::new(#lamports, #space, #owner)))
104+
} else {
105+
quote!(None)
106+
};
107+
86108
quote! {
87109
storage_accounts
88110
.#storage_ident
89-
.get_or_create(self.#field_name.account_id, client, None, None)
111+
.get_or_create(self.#field_name.account_id, client, None, #account_metadata)
90112
}
91113
};
92114

crates/syn/src/parser/trident_accounts.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,30 @@ fn parse_constraints(attrs: &[Attribute]) -> ParseResult<TridentConstraints> {
196196
}
197197
Ok(())
198198
}
199+
"space" => {
200+
if meta.input.peek(syn::Token![=]) {
201+
meta.input.parse::<syn::Token![=]>()?;
202+
let expr: syn::Expr = meta.input.parse()?;
203+
constraints.space = Some(expr);
204+
}
205+
Ok(())
206+
}
207+
"owner" => {
208+
if meta.input.peek(syn::Token![=]) {
209+
meta.input.parse::<syn::Token![=]>()?;
210+
let expr: syn::Expr = meta.input.parse()?;
211+
constraints.owner = Some(expr);
212+
}
213+
Ok(())
214+
}
215+
"lamports" => {
216+
if meta.input.peek(syn::Token![=]) {
217+
meta.input.parse::<syn::Token![=]>()?;
218+
let expr: syn::Expr = meta.input.parse()?;
219+
constraints.lamports = Some(expr);
220+
}
221+
Ok(())
222+
}
199223
_ => Err(meta.error("unsupported constraint")),
200224
}
201225
})?;
@@ -211,5 +235,25 @@ fn parse_constraints(attrs: &[Attribute]) -> ParseResult<TridentConstraints> {
211235
));
212236
}
213237

238+
// Validate that owner is specified when space is used
239+
if constraints.space.is_some() && constraints.owner.is_none() {
240+
return Err(ParseError::new(
241+
proc_macro2::Span::call_site(),
242+
"space requires non-optional storage and owner attributes",
243+
));
244+
}
245+
246+
// Validate that space, owner, and lamports can only be used with storage
247+
if (constraints.space.is_some()
248+
|| constraints.owner.is_some()
249+
|| constraints.lamports.is_some())
250+
&& constraints.storage.is_none()
251+
{
252+
return Err(ParseError::new(
253+
proc_macro2::Span::call_site(),
254+
"space, owner, and lamports attributes require non-optional storage attribute",
255+
));
256+
}
257+
214258
Ok(constraints)
215259
}

crates/syn/src/types/trident_accounts.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ pub struct TridentConstraints {
5353
pub storage: Option<Ident>,
5454
pub seeds: Option<Vec<syn::Expr>>, // Store the raw expressions from the array
5555
pub program_id: Option<syn::Expr>,
56+
pub space: Option<syn::Expr>,
57+
pub owner: Option<syn::Expr>,
58+
pub lamports: Option<syn::Expr>,
5659
}
5760

5861
pub struct SeedDependency {

documentation/docs/trident-api-macro/trident-macros/trident-accounts.md

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,67 @@ pub struct ExampleAccounts {
211211
#[account(
212212
storage = custom_pda,
213213
seeds = [b"seed"],
214-
program_id = pubkey!("11111111111111111111111111111111"))]
214+
program_id = pubkey!("11111111111111111111111111111111")
215+
)]
216+
pub custom_pda: TridentAccount,
217+
}
218+
```
219+
220+
221+
---
222+
223+
### `account(lamports)`
224+
225+
Specifies the lamports for the account. If not provided, the default is 500 * LAMPORTS_PER_SOL.
226+
227+
`This attribute is optional but requires the storage attribute`
228+
229+
```rust
230+
#[derive(TridentAccounts)]
231+
pub struct ExampleAccounts {
232+
#[account(
233+
storage = custom_pda,
234+
lamports = 5 * LAMPORTS_PER_SOL
235+
)]
236+
pub wallet: TridentAccount,
237+
}
238+
```
239+
240+
---
241+
242+
### `account(space)`
243+
244+
Specifies the space for the account. If not provided, the default is 0.
245+
246+
`This attribute is optional but requires the storage attribute and owner attribute`
247+
248+
```rust
249+
#[derive(TridentAccounts)]
250+
pub struct ExampleAccounts {
251+
#[account(
252+
storage = custom_pda,
253+
space = 8 + 100,
254+
owner = pubkey!("program id goes here")
255+
)]
256+
pub custom_pda: TridentAccount,
257+
}
258+
```
259+
260+
---
261+
262+
### `account(owner)`
263+
264+
Specifies the owner for the account. If not provided, the system program is default.
265+
266+
`This attribute is optional but requires the storage attribute`
267+
268+
```rust
269+
#[derive(TridentAccounts)]
270+
pub struct ExampleAccounts {
271+
#[account(
272+
storage = custom_pda,
273+
owner = pubkey!("program id goes here")
274+
)]
215275
pub custom_pda: TridentAccount,
216276
}
217277
```

examples/hello_world/trident-tests/fuzz_0/instructions/initialize_fn.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub struct InitializeFnInstructionAccounts {
1818
#[account(
1919
mut,
2020
storage = hello_world_account,
21+
lamports = 5 * LAMPORTS_PER_SOL,
2122
seeds = [b"hello_world_seed"],
2223
)]
2324
pub hello_world_account: TridentAccount,

0 commit comments

Comments
 (0)