|
@@ -196,6 +196,47 @@ pub fn parse_token(stream: ParseStream) -> ParseResult<ConstraintToken> {
|
|
|
))
|
|
|
}
|
|
|
}
|
|
|
+ "realloc" => {
|
|
|
+ if stream.peek(Token![=]) {
|
|
|
+ stream.parse::<Token![=]>()?;
|
|
|
+ let span = ident
|
|
|
+ .span()
|
|
|
+ .join(stream.span())
|
|
|
+ .unwrap_or_else(|| ident.span());
|
|
|
+ ConstraintToken::Realloc(Context::new(
|
|
|
+ span,
|
|
|
+ ConstraintRealloc {
|
|
|
+ space: stream.parse()?,
|
|
|
+ },
|
|
|
+ ))
|
|
|
+ } else {
|
|
|
+ stream.parse::<Token![:]>()?;
|
|
|
+ stream.parse::<Token![:]>()?;
|
|
|
+ let kw = stream.call(Ident::parse_any)?.to_string();
|
|
|
+ stream.parse::<Token![=]>()?;
|
|
|
+
|
|
|
+ let span = ident
|
|
|
+ .span()
|
|
|
+ .join(stream.span())
|
|
|
+ .unwrap_or_else(|| ident.span());
|
|
|
+
|
|
|
+ match kw.as_str() {
|
|
|
+ "payer" => ConstraintToken::ReallocPayer(Context::new(
|
|
|
+ span,
|
|
|
+ ConstraintReallocPayer {
|
|
|
+ target: stream.parse()?,
|
|
|
+ },
|
|
|
+ )),
|
|
|
+ "zero" => ConstraintToken::ReallocZero(Context::new(
|
|
|
+ span,
|
|
|
+ ConstraintReallocZero {
|
|
|
+ zero: stream.parse()?,
|
|
|
+ },
|
|
|
+ )),
|
|
|
+ _ => return Err(ParseError::new(ident.span(), "Invalid attribute. realloc::payer and realloc::zero are the only valid attributes")),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
_ => {
|
|
|
stream.parse::<Token![=]>()?;
|
|
|
let span = ident
|
|
@@ -313,6 +354,9 @@ pub struct ConstraintGroupBuilder<'ty> {
|
|
|
pub mint_decimals: Option<Context<ConstraintMintDecimals>>,
|
|
|
pub bump: Option<Context<ConstraintTokenBump>>,
|
|
|
pub program_seed: Option<Context<ConstraintProgramSeed>>,
|
|
|
+ pub realloc: Option<Context<ConstraintRealloc>>,
|
|
|
+ pub realloc_payer: Option<Context<ConstraintReallocPayer>>,
|
|
|
+ pub realloc_zero: Option<Context<ConstraintReallocZero>>,
|
|
|
}
|
|
|
|
|
|
impl<'ty> ConstraintGroupBuilder<'ty> {
|
|
@@ -344,6 +388,9 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
|
|
|
mint_decimals: None,
|
|
|
bump: None,
|
|
|
program_seed: None,
|
|
|
+ realloc: None,
|
|
|
+ realloc_payer: None,
|
|
|
+ realloc_zero: None,
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -439,6 +486,22 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // Realloc.
|
|
|
+ if let Some(r) = &self.realloc {
|
|
|
+ if self.realloc_payer.is_none() {
|
|
|
+ return Err(ParseError::new(
|
|
|
+ r.span(),
|
|
|
+ "realloc::payer must be provided when using realloc",
|
|
|
+ ));
|
|
|
+ }
|
|
|
+ if self.realloc_zero.is_none() {
|
|
|
+ return Err(ParseError::new(
|
|
|
+ r.span(),
|
|
|
+ "realloc::zero must be provided when using realloc",
|
|
|
+ ));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// Zero.
|
|
|
if let Some(z) = &self.zeroed {
|
|
|
match self.mutable {
|
|
@@ -526,6 +589,9 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
|
|
|
mint_decimals,
|
|
|
bump,
|
|
|
program_seed,
|
|
|
+ realloc,
|
|
|
+ realloc_payer,
|
|
|
+ realloc_zero,
|
|
|
} = self;
|
|
|
|
|
|
// Converts Option<Context<T>> -> Option<T>.
|
|
@@ -644,6 +710,11 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
|
|
|
}
|
|
|
},
|
|
|
})).transpose()?,
|
|
|
+ realloc: realloc.as_ref().map(|r| ConstraintReallocGroup {
|
|
|
+ payer: into_inner!(realloc_payer).unwrap().target,
|
|
|
+ space: r.space.clone(),
|
|
|
+ zero: into_inner!(realloc_zero).unwrap().zero,
|
|
|
+ }),
|
|
|
zeroed: into_inner!(zeroed),
|
|
|
mutable: into_inner!(mutable),
|
|
|
signer: into_inner!(signer),
|
|
@@ -690,6 +761,9 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
|
|
|
ConstraintToken::MintDecimals(c) => self.add_mint_decimals(c),
|
|
|
ConstraintToken::Bump(c) => self.add_bump(c),
|
|
|
ConstraintToken::ProgramSeed(c) => self.add_program_seed(c),
|
|
|
+ ConstraintToken::Realloc(c) => self.add_realloc(c),
|
|
|
+ ConstraintToken::ReallocPayer(c) => self.add_realloc_payer(c),
|
|
|
+ ConstraintToken::ReallocZero(c) => self.add_realloc_zero(c),
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -757,6 +831,56 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
+ fn add_realloc(&mut self, c: Context<ConstraintRealloc>) -> ParseResult<()> {
|
|
|
+ if !matches!(self.f_ty, Some(Ty::Account(_)))
|
|
|
+ && !matches!(self.f_ty, Some(Ty::AccountLoader(_)))
|
|
|
+ {
|
|
|
+ return Err(ParseError::new(
|
|
|
+ c.span(),
|
|
|
+ "realloc must be on an Account or AccountLoader",
|
|
|
+ ));
|
|
|
+ }
|
|
|
+ if self.mutable.is_none() {
|
|
|
+ return Err(ParseError::new(
|
|
|
+ c.span(),
|
|
|
+ "mut must be provided before realloc",
|
|
|
+ ));
|
|
|
+ }
|
|
|
+ if self.realloc.is_some() {
|
|
|
+ return Err(ParseError::new(c.span(), "realloc already provided"));
|
|
|
+ }
|
|
|
+ self.realloc.replace(c);
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
+ fn add_realloc_payer(&mut self, c: Context<ConstraintReallocPayer>) -> ParseResult<()> {
|
|
|
+ if self.realloc.is_none() {
|
|
|
+ return Err(ParseError::new(
|
|
|
+ c.span(),
|
|
|
+ "realloc must be provided before realloc::payer",
|
|
|
+ ));
|
|
|
+ }
|
|
|
+ if self.realloc_payer.is_some() {
|
|
|
+ return Err(ParseError::new(c.span(), "realloc::payer already provided"));
|
|
|
+ }
|
|
|
+ self.realloc_payer.replace(c);
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
+ fn add_realloc_zero(&mut self, c: Context<ConstraintReallocZero>) -> ParseResult<()> {
|
|
|
+ if self.realloc.is_none() {
|
|
|
+ return Err(ParseError::new(
|
|
|
+ c.span(),
|
|
|
+ "realloc must be provided before realloc::zero",
|
|
|
+ ));
|
|
|
+ }
|
|
|
+ if self.realloc_zero.is_some() {
|
|
|
+ return Err(ParseError::new(c.span(), "realloc::zero already provided"));
|
|
|
+ }
|
|
|
+ self.realloc_zero.replace(c);
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
fn add_close(&mut self, c: Context<ConstraintClose>) -> ParseResult<()> {
|
|
|
if !matches!(self.f_ty, Some(Ty::ProgramAccount(_)))
|
|
|
&& !matches!(self.f_ty, Some(Ty::Account(_)))
|