lang: Consistent init constraints (#641)
This commit is contained in:
parent
2604a442fd
commit
75c20856e4
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -14,13 +14,17 @@ incremented for features.
|
|||
### Features
|
||||
|
||||
* lang: Ignore `Unnamed` structs instead of panic ([#605](https://github.com/project-serum/anchor/pull/605)).
|
||||
* lang: Add constraints for initializing mint accounts as pdas, `#[account(init, seeds = [...], mint::decimals = <expr>, mint::authority = <expr>)]` ([#](https://github.com/project-serum/anchor/pull/562)).
|
||||
* lang: Add constraints for initializing mint accounts as pdas, `#[account(init, seeds = [...], mint::decimals = <expr>, mint::authority = <expr>)]` ([#562](https://github.com/project-serum/anchor/pull/562)).
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* lang: Change `#[account(init, seeds = [...], token = <expr>, authority = <expr>)]` to `#[account(init, token::mint = <expr> token::authority = <expr>)]` ([#](https://github.com/project-serum/anchor/pull/562)).
|
||||
* lang: `#[associated]` and `#[account(associated = <target>, with = <target>)]` are both removed.
|
||||
* cli: Removed `anchor launch` command
|
||||
* lang: Change `#[account(init, seeds = [...], token = <expr>, authority = <expr>)]` to `#[account(init, token::mint = <expr> token::authority = <expr>)]` ([#562](https://github.com/project-serum/anchor/pull/562)).
|
||||
* lang: `#[associated]` and `#[account(associated = <target>, with = <target>)]` are both removed ([#612](https://github.com/project-serum/anchor/pull/612)).
|
||||
* cli: Removed `anchor launch` command ([#634](https://github.com/project-serum/anchor/pull/634)).
|
||||
* lang: `#[account(init)]` now creates the account inside the same instruction to be consistent with initializing PDAs. To maintain the old behavior of `init`, replace it with `#[account(zero)]` ([#641](https://github.com/project-serum/anchor/pull/641)).
|
||||
* lang: `bump` must be provided when using the `seeds` constraint. This has been added as an extra safety constraint to ensure that whenever a PDA is initialized via a constraint the bump used is the one created by `Pubkey::find_program_address` ([#641](https://github.com/project-serum/anchor/pull/641)).
|
||||
* lang: `try_from_init` has been removed from `Loader`, `ProgramAccount`, and `CpiAccount` and replaced with `try_from_unchecked` ([#641](https://github.com/project-serum/anchor/pull/641)).
|
||||
* lang: Remove `AccountsInit` trait ([#641](https://github.com/project-serum/anchor/pull/641)).
|
||||
|
||||
## [0.13.2] - 2021-08-11
|
||||
|
||||
|
@ -32,7 +36,7 @@ incremented for features.
|
|||
|
||||
### Features
|
||||
|
||||
* cli: Programs embedded into genesis during tests will produce program logs.
|
||||
* cli: Programs embedded into genesis during tests will produce program logs ([#594](https://github.com/project-serum/anchor/pull/594)).
|
||||
|
||||
### Fixes
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ pub struct CreateCheck<'info> {
|
|||
#[account(zero)]
|
||||
check: ProgramAccount<'info, Check>,
|
||||
// Check's token vault.
|
||||
#[account(mut, "&vault.owner == check_signer.key")]
|
||||
#[account(mut, constraint = &vault.owner == check_signer.key)]
|
||||
vault: CpiAccount<'info, TokenAccount>,
|
||||
// Program derived address for the check.
|
||||
check_signer: AccountInfo<'info>,
|
||||
|
@ -94,7 +94,7 @@ pub struct CreateCheck<'info> {
|
|||
#[account(mut, has_one = owner)]
|
||||
from: CpiAccount<'info, TokenAccount>,
|
||||
// Token account the check is made to.
|
||||
#[account("from.mint == to.mint")]
|
||||
#[account(constraint = from.mint == to.mint)]
|
||||
to: CpiAccount<'info, TokenAccount>,
|
||||
// Owner of the `from` token account.
|
||||
owner: AccountInfo<'info>,
|
||||
|
@ -121,10 +121,10 @@ pub struct CashCheck<'info> {
|
|||
check: ProgramAccount<'info, Check>,
|
||||
#[account(mut)]
|
||||
vault: AccountInfo<'info>,
|
||||
#[account(seeds = [
|
||||
check.to_account_info().key.as_ref(),
|
||||
&[check.nonce],
|
||||
])]
|
||||
#[account(
|
||||
seeds = [check.to_account_info().key.as_ref()],
|
||||
bump = check.nonce,
|
||||
)]
|
||||
check_signer: AccountInfo<'info>,
|
||||
#[account(mut, has_one = owner)]
|
||||
to: CpiAccount<'info, TokenAccount>,
|
||||
|
@ -139,10 +139,10 @@ pub struct CancelCheck<'info> {
|
|||
check: ProgramAccount<'info, Check>,
|
||||
#[account(mut)]
|
||||
vault: AccountInfo<'info>,
|
||||
#[account(seeds = [
|
||||
check.to_account_info().key.as_ref(),
|
||||
&[check.nonce],
|
||||
])]
|
||||
#[account(
|
||||
seeds = [check.to_account_info().key.as_ref()],
|
||||
bump = check.nonce,
|
||||
)]
|
||||
check_signer: AccountInfo<'info>,
|
||||
#[account(mut, has_one = owner)]
|
||||
from: CpiAccount<'info, TokenAccount>,
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 3dc83f47b66a8a4189a637368c49dca1341c6b23
|
||||
Subproject commit 9a257678dfd0bda0c222e516e8c1a778b401d71e
|
|
@ -383,7 +383,10 @@ pub struct SetDistribution<'info> {
|
|||
|
||||
#[derive(Accounts)]
|
||||
pub struct SweepFees<'info> {
|
||||
#[account(seeds = [dex.dex_program.key.as_ref(), &[officer.bumps.bump]])]
|
||||
#[account(
|
||||
seeds = [dex.dex_program.key.as_ref()],
|
||||
bump = officer.bumps.bump,
|
||||
)]
|
||||
officer: ProgramAccount<'info, Officer>,
|
||||
#[account(
|
||||
mut,
|
||||
|
@ -411,7 +414,10 @@ pub struct Dex<'info> {
|
|||
|
||||
#[derive(Accounts)]
|
||||
pub struct SwapToUsdc<'info> {
|
||||
#[account(seeds = [dex_program.key().as_ref(), &[officer.bumps.bump]])]
|
||||
#[account(
|
||||
seeds = [dex_program.key().as_ref()],
|
||||
bump = officer.bumps.bump,
|
||||
)]
|
||||
officer: ProgramAccount<'info, Officer>,
|
||||
market: DexMarketAccounts<'info>,
|
||||
#[account(
|
||||
|
@ -437,7 +443,10 @@ pub struct SwapToUsdc<'info> {
|
|||
|
||||
#[derive(Accounts)]
|
||||
pub struct SwapToSrm<'info> {
|
||||
#[account(seeds = [dex_program.key().as_ref(), &[officer.bumps.bump]])]
|
||||
#[account(
|
||||
seeds = [dex_program.key().as_ref()],
|
||||
bump = officer.bumps.bump,
|
||||
)]
|
||||
officer: ProgramAccount<'info, Officer>,
|
||||
market: DexMarketAccounts<'info>,
|
||||
#[account(
|
||||
|
@ -529,7 +538,8 @@ pub struct DropStakeReward<'info> {
|
|||
)]
|
||||
officer: ProgramAccount<'info, Officer>,
|
||||
#[account(
|
||||
seeds = [b"stake", officer.key().as_ref(), &[officer.bumps.stake]]
|
||||
seeds = [b"stake", officer.key().as_ref()],
|
||||
bump = officer.bumps.stake,
|
||||
)]
|
||||
stake: CpiAccount<'info, TokenAccount>,
|
||||
#[cfg_attr(
|
||||
|
|
|
@ -60,7 +60,8 @@ pub struct CreateChatRoom<'info> {
|
|||
#[derive(Accounts)]
|
||||
pub struct SendMessage<'info> {
|
||||
#[account(
|
||||
seeds = [authority.key().as_ref(), &[user.bump]],
|
||||
seeds = [authority.key().as_ref()],
|
||||
bump = user.bump,
|
||||
has_one = authority,
|
||||
)]
|
||||
user: ProgramAccount<'info, User>,
|
||||
|
|
|
@ -232,7 +232,10 @@ impl<'info> InitializePool<'info> {
|
|||
pub struct ExchangeUsdcForRedeemable<'info> {
|
||||
#[account(has_one = redeemable_mint, has_one = pool_usdc)]
|
||||
pub pool_account: ProgramAccount<'info, PoolAccount>,
|
||||
#[account(seeds = [pool_account.watermelon_mint.as_ref(), &[pool_account.nonce]])]
|
||||
#[account(
|
||||
seeds = [pool_account.watermelon_mint.as_ref()],
|
||||
bump = pool_account.nonce,
|
||||
)]
|
||||
pool_signer: AccountInfo<'info>,
|
||||
#[account(
|
||||
mut,
|
||||
|
@ -256,7 +259,10 @@ pub struct ExchangeUsdcForRedeemable<'info> {
|
|||
pub struct ExchangeRedeemableForUsdc<'info> {
|
||||
#[account(has_one = redeemable_mint, has_one = pool_usdc)]
|
||||
pub pool_account: ProgramAccount<'info, PoolAccount>,
|
||||
#[account(seeds = [pool_account.watermelon_mint.as_ref(), &[pool_account.nonce]])]
|
||||
#[account(
|
||||
seeds = [pool_account.watermelon_mint.as_ref()],
|
||||
bump = pool_account.nonce,
|
||||
)]
|
||||
pool_signer: AccountInfo<'info>,
|
||||
#[account(
|
||||
mut,
|
||||
|
@ -280,7 +286,10 @@ pub struct ExchangeRedeemableForUsdc<'info> {
|
|||
pub struct ExchangeRedeemableForWatermelon<'info> {
|
||||
#[account(has_one = redeemable_mint, has_one = pool_watermelon)]
|
||||
pub pool_account: ProgramAccount<'info, PoolAccount>,
|
||||
#[account(seeds = [pool_account.watermelon_mint.as_ref(), &[pool_account.nonce]])]
|
||||
#[account(
|
||||
seeds = [pool_account.watermelon_mint.as_ref()],
|
||||
bump = pool_account.nonce,
|
||||
)]
|
||||
pool_signer: AccountInfo<'info>,
|
||||
#[account(
|
||||
mut,
|
||||
|
@ -304,7 +313,10 @@ pub struct ExchangeRedeemableForWatermelon<'info> {
|
|||
pub struct WithdrawPoolUsdc<'info> {
|
||||
#[account(has_one = pool_usdc, has_one = distribution_authority)]
|
||||
pub pool_account: ProgramAccount<'info, PoolAccount>,
|
||||
#[account(seeds = [pool_account.watermelon_mint.as_ref(), &[pool_account.nonce]])]
|
||||
#[account(
|
||||
seeds = [pool_account.watermelon_mint.as_ref()],
|
||||
bump = pool_account.nonce,
|
||||
)]
|
||||
pub pool_signer: AccountInfo<'info>,
|
||||
#[account(mut, constraint = pool_usdc.owner == *pool_signer.key)]
|
||||
pub pool_usdc: CpiAccount<'info, TokenAccount>,
|
||||
|
|
|
@ -217,7 +217,7 @@ pub struct CreateVesting<'info> {
|
|||
#[account(signer)]
|
||||
depositor_authority: AccountInfo<'info>,
|
||||
// Misc.
|
||||
#[account("token_program.key == &token::ID")]
|
||||
#[account(constraint = token_program.key == &token::ID)]
|
||||
token_program: AccountInfo<'info>,
|
||||
clock: Sysvar<'info, Clock>,
|
||||
}
|
||||
|
@ -251,13 +251,16 @@ pub struct Withdraw<'info> {
|
|||
beneficiary: AccountInfo<'info>,
|
||||
#[account(mut)]
|
||||
vault: CpiAccount<'info, TokenAccount>,
|
||||
#[account(seeds = [vesting.to_account_info().key.as_ref(), &[vesting.nonce]])]
|
||||
#[account(
|
||||
seeds = [vesting.to_account_info().key.as_ref()],
|
||||
bump = vesting.nonce,
|
||||
)]
|
||||
vesting_signer: AccountInfo<'info>,
|
||||
// Withdraw receiving target..
|
||||
#[account(mut)]
|
||||
token: CpiAccount<'info, TokenAccount>,
|
||||
// Misc.
|
||||
#[account("token_program.key == &token::ID")]
|
||||
#[account(constraint = token_program.key == &token::ID)]
|
||||
token_program: AccountInfo<'info>,
|
||||
clock: Sysvar<'info, Clock>,
|
||||
}
|
||||
|
@ -282,9 +285,12 @@ pub struct WhitelistTransfer<'info> {
|
|||
// Whitelist interface.
|
||||
#[account(mut, has_one = beneficiary, has_one = vault)]
|
||||
vesting: ProgramAccount<'info, Vesting>,
|
||||
#[account(mut, "&vault.owner == vesting_signer.key")]
|
||||
#[account(mut, constraint = &vault.owner == vesting_signer.key)]
|
||||
vault: CpiAccount<'info, TokenAccount>,
|
||||
#[account(seeds = [vesting.to_account_info().key.as_ref(), &[vesting.nonce]])]
|
||||
#[account(
|
||||
seeds = [vesting.to_account_info().key.as_ref()],
|
||||
bump = vesting.nonce,
|
||||
)]
|
||||
vesting_signer: AccountInfo<'info>,
|
||||
#[account("token_program.key == &token::ID")]
|
||||
token_program: AccountInfo<'info>,
|
||||
|
|
|
@ -643,15 +643,15 @@ impl<'info> CreateMember<'info> {
|
|||
pub struct BalanceSandboxAccounts<'info> {
|
||||
#[account(mut)]
|
||||
spt: CpiAccount<'info, TokenAccount>,
|
||||
#[account(mut, "vault.owner == spt.owner")]
|
||||
#[account(mut, constraint = vault.owner == spt.owner)]
|
||||
vault: CpiAccount<'info, TokenAccount>,
|
||||
#[account(
|
||||
mut,
|
||||
"vault_stake.owner == spt.owner",
|
||||
"vault_stake.mint == vault.mint"
|
||||
constraint = vault_stake.owner == spt.owner,
|
||||
constraint = vault_stake.mint == vault.mint
|
||||
)]
|
||||
vault_stake: CpiAccount<'info, TokenAccount>,
|
||||
#[account(mut, "vault_pw.owner == spt.owner", "vault_pw.mint == vault.mint")]
|
||||
#[account(mut, constraint = vault_pw.owner == spt.owner, constraint = vault_pw.mint == vault.mint)]
|
||||
vault_pw: CpiAccount<'info, TokenAccount>,
|
||||
}
|
||||
|
||||
|
@ -669,8 +669,8 @@ pub struct SetLockupProgram<'info> {
|
|||
#[derive(Accounts)]
|
||||
pub struct IsRealized<'info> {
|
||||
#[account(
|
||||
"&member.balances.spt == member_spt.to_account_info().key",
|
||||
"&member.balances_locked.spt == member_spt_locked.to_account_info().key"
|
||||
constraint = &member.balances.spt == member_spt.to_account_info().key,
|
||||
constraint = &member.balances_locked.spt == member_spt_locked.to_account_info().key
|
||||
)]
|
||||
member: ProgramAccount<'info, Member>,
|
||||
member_spt: CpiAccount<'info, TokenAccount>,
|
||||
|
@ -692,15 +692,15 @@ pub struct Deposit<'info> {
|
|||
member: ProgramAccount<'info, Member>,
|
||||
#[account(signer)]
|
||||
beneficiary: AccountInfo<'info>,
|
||||
#[account(mut, "vault.to_account_info().key == &member.balances.vault")]
|
||||
#[account(mut, constraint = vault.to_account_info().key == &member.balances.vault)]
|
||||
vault: CpiAccount<'info, TokenAccount>,
|
||||
// Depositor.
|
||||
#[account(mut)]
|
||||
depositor: AccountInfo<'info>,
|
||||
#[account(signer, "depositor_authority.key == &member.beneficiary")]
|
||||
#[account(signer, constraint = depositor_authority.key == &member.beneficiary)]
|
||||
depositor_authority: AccountInfo<'info>,
|
||||
// Misc.
|
||||
#[account("token_program.key == &token::ID")]
|
||||
#[account(constraint = token_program.key == &token::ID)]
|
||||
token_program: AccountInfo<'info>,
|
||||
}
|
||||
|
||||
|
@ -708,29 +708,26 @@ pub struct Deposit<'info> {
|
|||
pub struct DepositLocked<'info> {
|
||||
// Lockup whitelist relay interface.
|
||||
#[account(
|
||||
"vesting.to_account_info().owner == ®istry.lockup_program",
|
||||
"vesting.beneficiary == member.beneficiary"
|
||||
constraint = vesting.to_account_info().owner == ®istry.lockup_program,
|
||||
constraint = vesting.beneficiary == member.beneficiary
|
||||
)]
|
||||
vesting: CpiAccount<'info, Vesting>,
|
||||
#[account(mut, "vesting_vault.key == &vesting.vault")]
|
||||
#[account(mut, constraint = vesting_vault.key == &vesting.vault)]
|
||||
vesting_vault: AccountInfo<'info>,
|
||||
// Note: no need to verify the depositor_authority since the SPL program
|
||||
// will fail the transaction if it's not correct.
|
||||
#[account(signer)]
|
||||
depositor_authority: AccountInfo<'info>,
|
||||
#[account("token_program.key == &token::ID")]
|
||||
#[account(constraint = token_program.key == &token::ID)]
|
||||
token_program: AccountInfo<'info>,
|
||||
#[account(
|
||||
mut,
|
||||
"member_vault.to_account_info().key == &member.balances_locked.vault"
|
||||
constraint = member_vault.to_account_info().key == &member.balances_locked.vault
|
||||
)]
|
||||
member_vault: CpiAccount<'info, TokenAccount>,
|
||||
#[account(
|
||||
seeds = [
|
||||
registrar.to_account_info().key.as_ref(),
|
||||
member.to_account_info().key.as_ref(),
|
||||
&[member.nonce],
|
||||
]
|
||||
seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
|
||||
bump = member.nonce,
|
||||
)]
|
||||
member_signer: AccountInfo<'info>,
|
||||
|
||||
|
@ -757,26 +754,26 @@ pub struct Stake<'info> {
|
|||
member: ProgramAccount<'info, Member>,
|
||||
#[account(signer)]
|
||||
beneficiary: AccountInfo<'info>,
|
||||
#[account("BalanceSandbox::from(&balances) == member.balances")]
|
||||
#[account(constraint = BalanceSandbox::from(&balances) == member.balances)]
|
||||
balances: BalanceSandboxAccounts<'info>,
|
||||
#[account("BalanceSandbox::from(&balances_locked) == member.balances_locked")]
|
||||
#[account(constraint = BalanceSandbox::from(&balances_locked) == member.balances_locked)]
|
||||
balances_locked: BalanceSandboxAccounts<'info>,
|
||||
|
||||
// Program signers.
|
||||
#[account(
|
||||
seeds = [
|
||||
registrar.to_account_info().key.as_ref(),
|
||||
member.to_account_info().key.as_ref(),
|
||||
&[member.nonce],
|
||||
]
|
||||
seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
|
||||
bump = member.nonce,
|
||||
)]
|
||||
member_signer: AccountInfo<'info>,
|
||||
#[account(seeds = [registrar.to_account_info().key.as_ref(), &[registrar.nonce]])]
|
||||
#[account(
|
||||
seeds = [registrar.to_account_info().key.as_ref()],
|
||||
bump = registrar.nonce,
|
||||
)]
|
||||
registrar_signer: AccountInfo<'info>,
|
||||
|
||||
// Misc.
|
||||
clock: Sysvar<'info, Clock>,
|
||||
#[account("token_program.key == &token::ID")]
|
||||
#[account(constraint = token_program.key == &token::ID)]
|
||||
token_program: AccountInfo<'info>,
|
||||
}
|
||||
|
||||
|
@ -796,23 +793,20 @@ pub struct StartUnstake<'info> {
|
|||
member: ProgramAccount<'info, Member>,
|
||||
#[account(signer)]
|
||||
beneficiary: AccountInfo<'info>,
|
||||
#[account("BalanceSandbox::from(&balances) == member.balances")]
|
||||
#[account(constraint = BalanceSandbox::from(&balances) == member.balances)]
|
||||
balances: BalanceSandboxAccounts<'info>,
|
||||
#[account("BalanceSandbox::from(&balances_locked) == member.balances_locked")]
|
||||
#[account(constraint = BalanceSandbox::from(&balances_locked) == member.balances_locked)]
|
||||
balances_locked: BalanceSandboxAccounts<'info>,
|
||||
|
||||
// Programmatic signers.
|
||||
#[account(
|
||||
seeds = [
|
||||
registrar.to_account_info().key.as_ref(),
|
||||
member.to_account_info().key.as_ref(),
|
||||
&[member.nonce],
|
||||
]
|
||||
seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
|
||||
bump = member.nonce,
|
||||
)]
|
||||
member_signer: AccountInfo<'info>,
|
||||
|
||||
// Misc.
|
||||
#[account("token_program.key == &token::ID")]
|
||||
#[account(constraint = token_program.key == &token::ID)]
|
||||
token_program: AccountInfo<'info>,
|
||||
clock: Sysvar<'info, Clock>,
|
||||
}
|
||||
|
@ -825,7 +819,7 @@ pub struct EndUnstake<'info> {
|
|||
member: ProgramAccount<'info, Member>,
|
||||
#[account(signer)]
|
||||
beneficiary: AccountInfo<'info>,
|
||||
#[account(mut, has_one = registrar, has_one = member, "!pending_withdrawal.burned")]
|
||||
#[account(mut, has_one = registrar, has_one = member, constraint = !pending_withdrawal.burned)]
|
||||
pending_withdrawal: ProgramAccount<'info, PendingWithdrawal>,
|
||||
|
||||
// If we had ordered maps implementing Accounts we could do a constraint like
|
||||
|
@ -838,16 +832,13 @@ pub struct EndUnstake<'info> {
|
|||
vault_pw: AccountInfo<'info>,
|
||||
|
||||
#[account(
|
||||
seeds = [
|
||||
registrar.to_account_info().key.as_ref(),
|
||||
member.to_account_info().key.as_ref(),
|
||||
&[member.nonce],
|
||||
]
|
||||
seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
|
||||
bump = member.nonce,
|
||||
)]
|
||||
member_signer: AccountInfo<'info>,
|
||||
|
||||
clock: Sysvar<'info, Clock>,
|
||||
#[account("token_program.key == &token::ID")]
|
||||
#[account(constraint = token_program.key == &token::ID)]
|
||||
token_program: AccountInfo<'info>,
|
||||
}
|
||||
|
||||
|
@ -860,21 +851,18 @@ pub struct Withdraw<'info> {
|
|||
member: ProgramAccount<'info, Member>,
|
||||
#[account(signer)]
|
||||
beneficiary: AccountInfo<'info>,
|
||||
#[account(mut, "vault.to_account_info().key == &member.balances.vault")]
|
||||
#[account(mut, constraint = vault.to_account_info().key == &member.balances.vault)]
|
||||
vault: CpiAccount<'info, TokenAccount>,
|
||||
#[account(
|
||||
seeds = [
|
||||
registrar.to_account_info().key.as_ref(),
|
||||
member.to_account_info().key.as_ref(),
|
||||
&[member.nonce],
|
||||
]
|
||||
seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
|
||||
bump = member.nonce,
|
||||
)]
|
||||
member_signer: AccountInfo<'info>,
|
||||
// Receiver.
|
||||
#[account(mut)]
|
||||
depositor: AccountInfo<'info>,
|
||||
// Misc.
|
||||
#[account("token_program.key == &token::ID")]
|
||||
#[account(constraint = token_program.key == &token::ID)]
|
||||
token_program: AccountInfo<'info>,
|
||||
}
|
||||
|
||||
|
@ -882,27 +870,24 @@ pub struct Withdraw<'info> {
|
|||
pub struct WithdrawLocked<'info> {
|
||||
// Lockup whitelist relay interface.
|
||||
#[account(
|
||||
"vesting.to_account_info().owner == ®istry.lockup_program",
|
||||
"vesting.beneficiary == member.beneficiary"
|
||||
constraint = vesting.to_account_info().owner == ®istry.lockup_program,
|
||||
constraint = vesting.beneficiary == member.beneficiary,
|
||||
)]
|
||||
vesting: CpiAccount<'info, Vesting>,
|
||||
#[account(mut, "vesting_vault.key == &vesting.vault")]
|
||||
#[account(mut, constraint = vesting_vault.key == &vesting.vault)]
|
||||
vesting_vault: AccountInfo<'info>,
|
||||
#[account(signer)]
|
||||
vesting_signer: AccountInfo<'info>,
|
||||
#[account("token_program.key == &token::ID")]
|
||||
#[account(constraint = token_program.key == &token::ID)]
|
||||
token_program: AccountInfo<'info>,
|
||||
#[account(
|
||||
mut,
|
||||
"member_vault.to_account_info().key == &member.balances_locked.vault"
|
||||
constraint = member_vault.to_account_info().key == &member.balances_locked.vault
|
||||
)]
|
||||
member_vault: CpiAccount<'info, TokenAccount>,
|
||||
#[account(
|
||||
seeds = [
|
||||
registrar.to_account_info().key.as_ref(),
|
||||
member.to_account_info().key.as_ref(),
|
||||
&[member.nonce],
|
||||
]
|
||||
seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
|
||||
bump = member.nonce,
|
||||
)]
|
||||
member_signer: AccountInfo<'info>,
|
||||
|
||||
|
@ -934,7 +919,7 @@ pub struct DropReward<'info> {
|
|||
#[account(signer)]
|
||||
depositor_authority: AccountInfo<'info>,
|
||||
// Misc.
|
||||
#[account("token_program.key == &token::ID")]
|
||||
#[account(constraint = token_program.key == &token::ID)]
|
||||
token_program: AccountInfo<'info>,
|
||||
clock: Sysvar<'info, Clock>,
|
||||
}
|
||||
|
@ -984,9 +969,9 @@ pub struct ClaimRewardCommon<'info> {
|
|||
member: ProgramAccount<'info, Member>,
|
||||
#[account(signer)]
|
||||
beneficiary: AccountInfo<'info>,
|
||||
#[account("BalanceSandbox::from(&balances) == member.balances")]
|
||||
#[account(constraint = BalanceSandbox::from(&balances) == member.balances)]
|
||||
balances: BalanceSandboxAccounts<'info>,
|
||||
#[account("BalanceSandbox::from(&balances_locked) == member.balances_locked")]
|
||||
#[account(constraint = BalanceSandbox::from(&balances_locked) == member.balances_locked)]
|
||||
balances_locked: BalanceSandboxAccounts<'info>,
|
||||
// Vendor.
|
||||
#[account(has_one = registrar, has_one = vault)]
|
||||
|
@ -994,15 +979,12 @@ pub struct ClaimRewardCommon<'info> {
|
|||
#[account(mut)]
|
||||
vault: AccountInfo<'info>,
|
||||
#[account(
|
||||
seeds = [
|
||||
registrar.to_account_info().key.as_ref(),
|
||||
vendor.to_account_info().key.as_ref(),
|
||||
&[vendor.nonce],
|
||||
]
|
||||
seeds = [registrar.to_account_info().key.as_ref(), vendor.to_account_info().key.as_ref()],
|
||||
bump = vendor.nonce,
|
||||
)]
|
||||
vendor_signer: AccountInfo<'info>,
|
||||
// Misc.
|
||||
#[account("token_program.key == &token::ID")]
|
||||
#[account(constraint = token_program.key == &token::ID)]
|
||||
token_program: AccountInfo<'info>,
|
||||
clock: Sysvar<'info, Clock>,
|
||||
}
|
||||
|
@ -1017,11 +999,8 @@ pub struct ExpireReward<'info> {
|
|||
#[account(mut)]
|
||||
vault: CpiAccount<'info, TokenAccount>,
|
||||
#[account(
|
||||
seeds = [
|
||||
registrar.to_account_info().key.as_ref(),
|
||||
vendor.to_account_info().key.as_ref(),
|
||||
&[vendor.nonce],
|
||||
]
|
||||
seeds = [registrar.to_account_info().key.as_ref(), vendor.to_account_info().key.as_ref()],
|
||||
bump = vendor.nonce
|
||||
)]
|
||||
vendor_signer: AccountInfo<'info>,
|
||||
// Receiver.
|
||||
|
@ -1030,7 +1009,7 @@ pub struct ExpireReward<'info> {
|
|||
#[account(mut)]
|
||||
expiry_receiver_token: AccountInfo<'info>,
|
||||
// Misc.
|
||||
#[account("token_program.key == &token::ID")]
|
||||
#[account(constraint = token_program.key == &token::ID)]
|
||||
token_program: AccountInfo<'info>,
|
||||
clock: Sysvar<'info, Clock>,
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ pub struct DataU16 {
|
|||
}
|
||||
|
||||
#[account]
|
||||
#[derive(Default)]
|
||||
pub struct DataI8 {
|
||||
pub data: i8,
|
||||
}
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
use crate::account::*;
|
||||
use crate::misc::MyState;
|
||||
use anchor_lang::prelude::*;
|
||||
use anchor_spl::token::{Mint, TokenAccount};
|
||||
use misc2::misc2::MyState as Misc2State;
|
||||
use std::mem::size_of;
|
||||
|
||||
#[derive(Accounts)]
|
||||
#[instruction(token_bump: u8, mint_bump: u8)]
|
||||
pub struct TestTokenSeedsInit<'info> {
|
||||
#[account(
|
||||
init,
|
||||
seeds = [b"my-mint-seed".as_ref(), &[mint_bump]],
|
||||
seeds = [b"my-mint-seed".as_ref()],
|
||||
bump = mint_bump,
|
||||
payer = authority,
|
||||
mint::decimals = 6,
|
||||
mint::authority = authority,
|
||||
|
@ -17,7 +18,8 @@ pub struct TestTokenSeedsInit<'info> {
|
|||
pub mint: CpiAccount<'info, Mint>,
|
||||
#[account(
|
||||
init,
|
||||
seeds = [b"my-token-seed".as_ref(), &[token_bump]],
|
||||
seeds = [b"my-token-seed".as_ref()],
|
||||
bump = token_bump,
|
||||
payer = authority,
|
||||
token::mint = mint,
|
||||
token::authority = authority,
|
||||
|
@ -32,7 +34,10 @@ pub struct TestTokenSeedsInit<'info> {
|
|||
#[derive(Accounts)]
|
||||
#[instruction(nonce: u8)]
|
||||
pub struct TestInstructionConstraint<'info> {
|
||||
#[account(seeds = [b"my-seed", my_account.key.as_ref(), &[nonce]])]
|
||||
#[account(
|
||||
seeds = [b"my-seed", my_account.key.as_ref()],
|
||||
bump = nonce,
|
||||
)]
|
||||
pub my_pda: AccountInfo<'info>,
|
||||
pub my_account: AccountInfo<'info>,
|
||||
}
|
||||
|
@ -42,7 +47,8 @@ pub struct TestInstructionConstraint<'info> {
|
|||
pub struct TestPdaInit<'info> {
|
||||
#[account(
|
||||
init,
|
||||
seeds = [b"my-seed", domain.as_bytes(), foo.key.as_ref(), &seed, &[bump]],
|
||||
seeds = [b"my-seed", domain.as_bytes(), foo.key.as_ref(), &seed],
|
||||
bump = bump,
|
||||
payer = my_payer,
|
||||
)]
|
||||
pub my_pda: ProgramAccount<'info, DataU16>,
|
||||
|
@ -54,7 +60,12 @@ pub struct TestPdaInit<'info> {
|
|||
#[derive(Accounts)]
|
||||
#[instruction(bump: u8)]
|
||||
pub struct TestPdaInitZeroCopy<'info> {
|
||||
#[account(init, seeds = [b"my-seed".as_ref(), &[bump]], payer = my_payer)]
|
||||
#[account(
|
||||
init,
|
||||
seeds = [b"my-seed".as_ref()],
|
||||
bump = bump,
|
||||
payer = my_payer,
|
||||
)]
|
||||
pub my_pda: Loader<'info, DataZeroCopy>,
|
||||
pub my_payer: AccountInfo<'info>,
|
||||
pub system_program: AccountInfo<'info>,
|
||||
|
@ -62,7 +73,11 @@ pub struct TestPdaInitZeroCopy<'info> {
|
|||
|
||||
#[derive(Accounts)]
|
||||
pub struct TestPdaMutZeroCopy<'info> {
|
||||
#[account(mut, seeds = [b"my-seed".as_ref(), &[my_pda.load()?.bump]])]
|
||||
#[account(
|
||||
mut,
|
||||
seeds = [b"my-seed".as_ref()],
|
||||
bump = my_pda.load()?.bump,
|
||||
)]
|
||||
pub my_pda: Loader<'info, DataZeroCopy>,
|
||||
pub my_payer: AccountInfo<'info>,
|
||||
}
|
||||
|
@ -126,6 +141,47 @@ pub struct TestSimulate {}
|
|||
|
||||
#[derive(Accounts)]
|
||||
pub struct TestI8<'info> {
|
||||
#[account(init)]
|
||||
#[account(zero)]
|
||||
pub data: ProgramAccount<'info, DataI8>,
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct TestInit<'info> {
|
||||
#[account(init, payer = payer)]
|
||||
pub data: ProgramAccount<'info, DataI8>,
|
||||
#[account(signer)]
|
||||
pub payer: AccountInfo<'info>,
|
||||
pub system_program: AccountInfo<'info>,
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct TestInitZeroCopy<'info> {
|
||||
#[account(init, payer = payer, space = 8 + size_of::<DataZeroCopy>())]
|
||||
pub data: Loader<'info, DataZeroCopy>,
|
||||
#[account(signer)]
|
||||
pub payer: AccountInfo<'info>,
|
||||
pub system_program: AccountInfo<'info>,
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct TestInitMint<'info> {
|
||||
#[account(init, mint::decimals = 6, mint::authority = payer, payer = payer)]
|
||||
pub mint: CpiAccount<'info, Mint>,
|
||||
#[account(signer)]
|
||||
pub payer: AccountInfo<'info>,
|
||||
pub rent: Sysvar<'info, Rent>,
|
||||
pub system_program: AccountInfo<'info>,
|
||||
pub token_program: AccountInfo<'info>,
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct TestInitToken<'info> {
|
||||
#[account(init, token::mint = mint, token::authority = payer, payer = payer)]
|
||||
pub token: CpiAccount<'info, TokenAccount>,
|
||||
pub mint: CpiAccount<'info, Mint>,
|
||||
#[account(signer)]
|
||||
pub payer: AccountInfo<'info>,
|
||||
pub rent: Sysvar<'info, Rent>,
|
||||
pub system_program: AccountInfo<'info>,
|
||||
pub token_program: AccountInfo<'info>,
|
||||
}
|
||||
|
|
|
@ -128,4 +128,26 @@ pub mod misc {
|
|||
) -> ProgramResult {
|
||||
Err(ProgramError::Custom(1234))
|
||||
}
|
||||
|
||||
pub fn test_init(ctx: Context<TestInit>) -> ProgramResult {
|
||||
ctx.accounts.data.data = 3;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn test_init_zero_copy(ctx: Context<TestInitZeroCopy>) -> ProgramResult {
|
||||
let mut data = ctx.accounts.data.load_init()?;
|
||||
data.data = 10;
|
||||
data.bump = 2;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn test_init_mint(ctx: Context<TestInitMint>) -> ProgramResult {
|
||||
assert!(ctx.accounts.mint.decimals == 6);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn test_init_token(ctx: Context<TestInitToken>) -> ProgramResult {
|
||||
assert!(ctx.accounts.token.mint == ctx.accounts.mint.key());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -370,4 +370,88 @@ describe("misc", () => {
|
|||
}
|
||||
);
|
||||
});
|
||||
|
||||
it("Can init a random account", async () => {
|
||||
const data = anchor.web3.Keypair.generate();
|
||||
await program.rpc.testInit({
|
||||
accounts: {
|
||||
data: data.publicKey,
|
||||
payer: program.provider.wallet.publicKey,
|
||||
systemProgram: anchor.web3.SystemProgram.programId,
|
||||
},
|
||||
signers: [data],
|
||||
});
|
||||
|
||||
const account = await program.account.dataI8.fetch(data.publicKey);
|
||||
assert.ok(account.data === 3);
|
||||
});
|
||||
|
||||
it("Can init a random zero copy account", async () => {
|
||||
const data = anchor.web3.Keypair.generate();
|
||||
await program.rpc.testInitZeroCopy({
|
||||
accounts: {
|
||||
data: data.publicKey,
|
||||
payer: program.provider.wallet.publicKey,
|
||||
systemProgram: anchor.web3.SystemProgram.programId,
|
||||
},
|
||||
signers: [data],
|
||||
});
|
||||
const account = await program.account.dataZeroCopy.fetch(data.publicKey);
|
||||
assert.ok(account.data === 10);
|
||||
assert.ok(account.bump === 2);
|
||||
});
|
||||
|
||||
let mint = undefined;
|
||||
|
||||
it("Can create a random mint account", async () => {
|
||||
mint = anchor.web3.Keypair.generate();
|
||||
await program.rpc.testInitMint({
|
||||
accounts: {
|
||||
mint: mint.publicKey,
|
||||
payer: program.provider.wallet.publicKey,
|
||||
systemProgram: anchor.web3.SystemProgram.programId,
|
||||
tokenProgram: TOKEN_PROGRAM_ID,
|
||||
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
|
||||
},
|
||||
signers: [mint],
|
||||
});
|
||||
const client = new Token(
|
||||
program.provider.connection,
|
||||
mint.publicKey,
|
||||
TOKEN_PROGRAM_ID,
|
||||
program.provider.wallet.payer
|
||||
);
|
||||
const mintAccount = await client.getMintInfo();
|
||||
assert.ok(mintAccount.decimals === 6);
|
||||
assert.ok(
|
||||
mintAccount.mintAuthority.equals(program.provider.wallet.publicKey)
|
||||
);
|
||||
});
|
||||
|
||||
it("Can create a random token account", async () => {
|
||||
const token = anchor.web3.Keypair.generate();
|
||||
await program.rpc.testInitToken({
|
||||
accounts: {
|
||||
token: token.publicKey,
|
||||
mint: mint.publicKey,
|
||||
payer: program.provider.wallet.publicKey,
|
||||
systemProgram: anchor.web3.SystemProgram.programId,
|
||||
tokenProgram: TOKEN_PROGRAM_ID,
|
||||
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
|
||||
},
|
||||
signers: [token],
|
||||
});
|
||||
const client = new Token(
|
||||
program.provider.connection,
|
||||
mint.publicKey,
|
||||
TOKEN_PROGRAM_ID,
|
||||
program.provider.wallet.payer
|
||||
);
|
||||
const account = await client.getAccountInfo(token.publicKey);
|
||||
assert.ok(account.state === 1);
|
||||
assert.ok(account.amount.toNumber() === 0);
|
||||
assert.ok(account.isInitialized);
|
||||
assert.ok(account.owner.equals(program.provider.wallet.publicKey));
|
||||
assert.ok(account.mint.equals(mint.publicKey));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -192,20 +192,21 @@ pub struct Approve<'info> {
|
|||
pub struct Auth<'info> {
|
||||
#[account(mut)]
|
||||
multisig: ProgramAccount<'info, Multisig>,
|
||||
#[account(signer, seeds = [
|
||||
multisig.to_account_info().key.as_ref(),
|
||||
&[multisig.nonce],
|
||||
])]
|
||||
#[account(
|
||||
signer,
|
||||
seeds = [multisig.to_account_info().key.as_ref()],
|
||||
bump = multisig.nonce,
|
||||
)]
|
||||
multisig_signer: AccountInfo<'info>,
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct ExecuteTransaction<'info> {
|
||||
multisig: ProgramAccount<'info, Multisig>,
|
||||
#[account(seeds = [
|
||||
multisig.to_account_info().key.as_ref(),
|
||||
&[multisig.nonce],
|
||||
])]
|
||||
#[account(
|
||||
seeds = [multisig.to_account_info().key.as_ref()],
|
||||
bump = multisig.nonce,
|
||||
)]
|
||||
multisig_signer: AccountInfo<'info>,
|
||||
#[account(mut, has_one = multisig)]
|
||||
transaction: ProgramAccount<'info, Transaction>,
|
||||
|
|
|
@ -39,7 +39,8 @@ use syn::parse_macro_input;
|
|||
/// |:--|:--|:--|
|
||||
/// | `#[account(signer)]` | On raw `AccountInfo` structs. | Checks the given account signed the transaction. |
|
||||
/// | `#[account(mut)]` | On `AccountInfo`, `ProgramAccount` or `CpiAccount` structs. | Marks the account as mutable and persists the state transition. |
|
||||
/// | `#[account(init)]` | On `ProgramAccount` structs. | Marks the account as being initialized, skipping the account discriminator check. |
|
||||
/// | `#[account(init)]` | On `ProgramAccount` structs. | Marks the account as being initialized, creating the account via the system program. |
|
||||
/// | `#[account(zero)]` | On `ProgramAccount` structs. | Asserts the account discriminator is zero. |
|
||||
/// | `#[account(close = <target>)]` | On `ProgramAccount` and `Loader` structs. | Marks the account as being closed at the end of the instruction's execution, sending the rent exemption lamports to the specified <target>. |
|
||||
/// | `#[account(has_one = <target>)]` | On `ProgramAccount` or `CpiAccount` structs | Checks the `target` field on the account matches the `target` field in the struct deriving `Accounts`. |
|
||||
/// | `#[account(seeds = [<seeds>], bump? = <target>, payer? = <target>, space? = <target>, owner? = <target>)]` | On `AccountInfo` structs | Seeds for the program derived address an `AccountInfo` struct represents. If bump is provided, then appends it to the seeds. On initialization, validates the given bump is the bump provided by `Pubkey::find_program_address`.|
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::error::ErrorCode;
|
||||
use crate::{Accounts, AccountsExit, AccountsInit, ToAccountInfo, ToAccountInfos, ToAccountMetas};
|
||||
use crate::{Accounts, AccountsExit, ToAccountInfo, ToAccountInfos, ToAccountMetas};
|
||||
use solana_program::account_info::AccountInfo;
|
||||
use solana_program::entrypoint::ProgramResult;
|
||||
use solana_program::instruction::AccountMeta;
|
||||
|
@ -21,31 +21,6 @@ impl<'info> Accounts<'info> for AccountInfo<'info> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'info> AccountsInit<'info> for AccountInfo<'info> {
|
||||
fn try_accounts_init(
|
||||
_program_id: &Pubkey,
|
||||
accounts: &mut &[AccountInfo<'info>],
|
||||
) -> Result<Self, ProgramError> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
|
||||
let account = &accounts[0];
|
||||
*accounts = &accounts[1..];
|
||||
|
||||
// The discriminator should be zero, since we're initializing.
|
||||
let data: &[u8] = &account.try_borrow_data()?;
|
||||
let mut disc_bytes = [0u8; 8];
|
||||
disc_bytes.copy_from_slice(&data[..8]);
|
||||
let discriminator = u64::from_le_bytes(disc_bytes);
|
||||
if discriminator != 0 {
|
||||
return Err(ErrorCode::AccountDiscriminatorAlreadySet.into());
|
||||
}
|
||||
|
||||
Ok(account.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'info> ToAccountMetas for AccountInfo<'info> {
|
||||
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
|
||||
let is_signer = is_signer.unwrap_or(self.is_signer);
|
||||
|
|
|
@ -30,7 +30,7 @@ impl<'a, T: AccountDeserialize + Clone> CpiAccount<'a, T> {
|
|||
))
|
||||
}
|
||||
|
||||
pub fn try_from_init(info: &AccountInfo<'a>) -> Result<CpiAccount<'a, T>, ProgramError> {
|
||||
pub fn try_from_unchecked(info: &AccountInfo<'a>) -> Result<CpiAccount<'a, T>, ProgramError> {
|
||||
Self::try_from(info)
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,8 @@ pub enum ErrorCode {
|
|||
ConstraintClose,
|
||||
#[msg("An address constraint was violated")]
|
||||
ConstraintAddress,
|
||||
#[msg("Expected zero account discriminant")]
|
||||
ConstraintZero,
|
||||
|
||||
// Accounts.
|
||||
#[msg("The account discriminator was already set on this account")]
|
||||
|
|
|
@ -56,7 +56,7 @@ pub struct IdlAccounts<'info> {
|
|||
// Accounts for creating an idl buffer.
|
||||
#[derive(Accounts)]
|
||||
pub struct IdlCreateBuffer<'info> {
|
||||
#[account(init)]
|
||||
#[account(zero)]
|
||||
pub buffer: ProgramAccount<'info, IdlAccount>,
|
||||
#[account(signer, constraint = authority.key != &Pubkey::new_from_array([0u8; 32]))]
|
||||
pub authority: AccountInfo<'info>,
|
||||
|
|
|
@ -105,17 +105,6 @@ pub trait AccountsClose<'info>: ToAccountInfos<'info> {
|
|||
fn close(&self, sol_destination: AccountInfo<'info>) -> ProgramResult;
|
||||
}
|
||||
|
||||
/// A data structure of accounts providing a one time deserialization upon
|
||||
/// account initialization, i.e., when the data array for a given account is
|
||||
/// zeroed. Any subsequent call to `try_accounts_init` should fail. For all
|
||||
/// subsequent deserializations, it's expected that [`Accounts`] is used.
|
||||
pub trait AccountsInit<'info>: ToAccountMetas + ToAccountInfos<'info> + Sized {
|
||||
fn try_accounts_init(
|
||||
program_id: &Pubkey,
|
||||
accounts: &mut &[AccountInfo<'info>],
|
||||
) -> Result<Self, ProgramError>;
|
||||
}
|
||||
|
||||
/// Transformation to
|
||||
/// [`AccountMeta`](../solana_program/instruction/struct.AccountMeta.html)
|
||||
/// structs.
|
||||
|
@ -234,10 +223,9 @@ impl Key for Pubkey {
|
|||
pub mod prelude {
|
||||
pub use super::{
|
||||
access_control, account, emit, error, event, interface, program, require, state, zero_copy,
|
||||
AccountDeserialize, AccountSerialize, Accounts, AccountsExit, AccountsInit,
|
||||
AnchorDeserialize, AnchorSerialize, Context, CpiAccount, CpiContext, CpiState,
|
||||
CpiStateContext, Key, Loader, ProgramAccount, ProgramState, Sysvar, ToAccountInfo,
|
||||
ToAccountInfos, ToAccountMetas,
|
||||
AccountDeserialize, AccountSerialize, Accounts, AccountsExit, AnchorDeserialize,
|
||||
AnchorSerialize, Context, CpiAccount, CpiContext, CpiState, CpiStateContext, Key, Loader,
|
||||
ProgramAccount, ProgramState, Sysvar, ToAccountInfo, ToAccountInfos, ToAccountMetas,
|
||||
};
|
||||
|
||||
pub use borsh;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use crate::error::ErrorCode;
|
||||
use crate::{
|
||||
Accounts, AccountsClose, AccountsExit, AccountsInit, ToAccountInfo, ToAccountInfos,
|
||||
ToAccountMetas, ZeroCopy,
|
||||
Accounts, AccountsClose, AccountsExit, ToAccountInfo, ToAccountInfos, ToAccountMetas, ZeroCopy,
|
||||
};
|
||||
use solana_program::account_info::AccountInfo;
|
||||
use solana_program::entrypoint::ProgramResult;
|
||||
|
@ -54,17 +53,9 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
|
|||
|
||||
/// Constructs a new `Loader` from an uninitialized account.
|
||||
#[inline(never)]
|
||||
pub fn try_from_init(acc_info: &AccountInfo<'info>) -> Result<Loader<'info, T>, ProgramError> {
|
||||
let data = acc_info.try_borrow_data()?;
|
||||
|
||||
// The discriminator should be zero, since we're initializing.
|
||||
let mut disc_bytes = [0u8; 8];
|
||||
disc_bytes.copy_from_slice(&data[..8]);
|
||||
let discriminator = u64::from_le_bytes(disc_bytes);
|
||||
if discriminator != 0 {
|
||||
return Err(ErrorCode::AccountDiscriminatorAlreadySet.into());
|
||||
}
|
||||
|
||||
pub fn try_from_unchecked(
|
||||
acc_info: &AccountInfo<'info>,
|
||||
) -> Result<Loader<'info, T>, ProgramError> {
|
||||
Ok(Loader::new(acc_info.clone()))
|
||||
}
|
||||
|
||||
|
@ -147,25 +138,6 @@ impl<'info, T: ZeroCopy> Accounts<'info> for Loader<'info, T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'info, T: ZeroCopy> AccountsInit<'info> for Loader<'info, T> {
|
||||
#[inline(never)]
|
||||
fn try_accounts_init(
|
||||
program_id: &Pubkey,
|
||||
accounts: &mut &[AccountInfo<'info>],
|
||||
) -> Result<Self, ProgramError> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
let account = &accounts[0];
|
||||
*accounts = &accounts[1..];
|
||||
let l = Loader::try_from_init(account)?;
|
||||
if l.acc_info.owner != program_id {
|
||||
return Err(ErrorCode::AccountNotProgramOwned.into());
|
||||
}
|
||||
Ok(l)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'info, T: ZeroCopy> AccountsExit<'info> for Loader<'info, T> {
|
||||
// The account *cannot* be loaded when this is called.
|
||||
fn exit(&self, _program_id: &Pubkey) -> ProgramResult {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::error::ErrorCode;
|
||||
use crate::{
|
||||
AccountDeserialize, AccountSerialize, Accounts, AccountsClose, AccountsExit, AccountsInit,
|
||||
CpiAccount, ToAccountInfo, ToAccountInfos, ToAccountMetas,
|
||||
AccountDeserialize, AccountSerialize, Accounts, AccountsClose, AccountsExit, CpiAccount,
|
||||
ToAccountInfo, ToAccountInfos, ToAccountMetas,
|
||||
};
|
||||
use solana_program::account_info::AccountInfo;
|
||||
use solana_program::entrypoint::ProgramResult;
|
||||
|
@ -45,17 +45,10 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Clone> ProgramAccount<'a, T>
|
|||
/// initialization (since the entire account data array is zeroed and thus
|
||||
/// no account type is set).
|
||||
#[inline(never)]
|
||||
pub fn try_from_init(info: &AccountInfo<'a>) -> Result<ProgramAccount<'a, T>, ProgramError> {
|
||||
pub fn try_from_unchecked(
|
||||
info: &AccountInfo<'a>,
|
||||
) -> Result<ProgramAccount<'a, T>, ProgramError> {
|
||||
let mut data: &[u8] = &info.try_borrow_data()?;
|
||||
|
||||
// The discriminator should be zero, since we're initializing.
|
||||
let mut disc_bytes = [0u8; 8];
|
||||
disc_bytes.copy_from_slice(&data[..8]);
|
||||
let discriminator = u64::from_le_bytes(disc_bytes);
|
||||
if discriminator != 0 {
|
||||
return Err(ErrorCode::AccountDiscriminatorAlreadySet.into());
|
||||
}
|
||||
|
||||
Ok(ProgramAccount::new(
|
||||
info.clone(),
|
||||
T::try_deserialize_unchecked(&mut data)?,
|
||||
|
@ -90,28 +83,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'info, T> AccountsInit<'info> for ProgramAccount<'info, T>
|
||||
where
|
||||
T: AccountSerialize + AccountDeserialize + Clone,
|
||||
{
|
||||
#[inline(never)]
|
||||
fn try_accounts_init(
|
||||
program_id: &Pubkey,
|
||||
accounts: &mut &[AccountInfo<'info>],
|
||||
) -> Result<Self, ProgramError> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
let account = &accounts[0];
|
||||
*accounts = &accounts[1..];
|
||||
let pa = ProgramAccount::try_from_init(account)?;
|
||||
if pa.inner.info.owner != program_id {
|
||||
return Err(ErrorCode::AccountNotProgramOwned.into());
|
||||
}
|
||||
Ok(pa)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsExit<'info>
|
||||
for ProgramAccount<'info, T>
|
||||
{
|
||||
|
|
|
@ -60,14 +60,14 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
|
|||
|
||||
let mut constraints = Vec::new();
|
||||
|
||||
if let Some(c) = seeds {
|
||||
constraints.push(Constraint::Seeds(c));
|
||||
if let Some(c) = zeroed {
|
||||
constraints.push(Constraint::Zeroed(c));
|
||||
}
|
||||
if let Some(c) = init {
|
||||
constraints.push(Constraint::Init(c));
|
||||
}
|
||||
if let Some(c) = zeroed {
|
||||
constraints.push(Constraint::Zeroed(c));
|
||||
if let Some(c) = seeds {
|
||||
constraints.push(Constraint::Seeds(c));
|
||||
}
|
||||
if let Some(c) = mutable {
|
||||
constraints.push(Constraint::Mut(c));
|
||||
|
@ -136,14 +136,27 @@ fn generate_constraint_address(f: &Field, c: &ConstraintAddress) -> proc_macro2:
|
|||
}
|
||||
}
|
||||
|
||||
pub fn generate_constraint_init(_f: &Field, _c: &ConstraintInit) -> proc_macro2::TokenStream {
|
||||
quote! {}
|
||||
pub fn generate_constraint_init(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream {
|
||||
generate_constraint_init_group(f, c)
|
||||
}
|
||||
|
||||
pub fn generate_constraint_zeroed(_f: &Field, _c: &ConstraintZeroed) -> proc_macro2::TokenStream {
|
||||
// No constraint. The zero discriminator is checked in `try_accounts_init`
|
||||
// currently.
|
||||
quote! {}
|
||||
pub fn generate_constraint_zeroed(f: &Field, _c: &ConstraintZeroed) -> proc_macro2::TokenStream {
|
||||
let field = &f.ident;
|
||||
let (account_ty, account_wrapper_ty, _) = parse_ty(f);
|
||||
quote! {
|
||||
let #field: #account_wrapper_ty<#account_ty> = {
|
||||
let mut __data: &[u8] = &#field.try_borrow_data()?;
|
||||
let mut __disc_bytes = [0u8; 8];
|
||||
__disc_bytes.copy_from_slice(&__data[..8]);
|
||||
let __discriminator = u64::from_le_bytes(__disc_bytes);
|
||||
if __discriminator != 0 {
|
||||
return Err(anchor_lang::__private::ErrorCode::ConstraintZero.into());
|
||||
}
|
||||
#account_wrapper_ty::try_from_unchecked(
|
||||
&#field,
|
||||
)?
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_constraint_close(f: &Field, c: &ConstraintClose) -> proc_macro2::TokenStream {
|
||||
|
@ -184,6 +197,8 @@ pub fn generate_constraint_signer(f: &Field, _c: &ConstraintSigner) -> proc_macr
|
|||
let info = match f.ty {
|
||||
Ty::AccountInfo => quote! { #ident },
|
||||
Ty::ProgramAccount(_) => quote! { #ident.to_account_info() },
|
||||
Ty::Loader(_) => quote! { #ident.to_account_info() },
|
||||
Ty::CpiAccount(_) => quote! { #ident.to_account_info() },
|
||||
_ => panic!("Invalid syntax: signer cannot be specified."),
|
||||
};
|
||||
quote! {
|
||||
|
@ -255,31 +270,19 @@ pub fn generate_constraint_rent_exempt(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2::TokenStream {
|
||||
if c.is_init {
|
||||
generate_constraint_seeds_init(f, c)
|
||||
} else {
|
||||
generate_constraint_seeds_address(f, c)
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_constraint_seeds_init(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2::TokenStream {
|
||||
fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream {
|
||||
let payer = {
|
||||
let p = &c.payer;
|
||||
quote! {
|
||||
let payer = #p.to_account_info();
|
||||
}
|
||||
};
|
||||
let seeds_constraint = generate_constraint_seeds_address(f, c);
|
||||
let seeds_with_nonce = {
|
||||
let s = &c.seeds;
|
||||
match c.bump.as_ref() {
|
||||
// Bump keyword not given. Just use the seeds.
|
||||
None => quote! {
|
||||
[#s]
|
||||
},
|
||||
// Bump keyword given.
|
||||
Some(bump) => match bump {
|
||||
|
||||
let seeds_with_nonce = match &c.seeds {
|
||||
None => quote! {},
|
||||
Some(c) => {
|
||||
let s = &c.seeds;
|
||||
let inner = match c.bump.as_ref() {
|
||||
// Bump target not given. Use the canonical bump.
|
||||
None => {
|
||||
quote! {
|
||||
|
@ -298,30 +301,23 @@ fn generate_constraint_seeds_init(f: &Field, c: &ConstraintSeedsGroup) -> proc_m
|
|||
Some(b) => quote! {
|
||||
[#s, &[#b]]
|
||||
},
|
||||
},
|
||||
};
|
||||
quote! {
|
||||
&#inner[..]
|
||||
}
|
||||
}
|
||||
};
|
||||
generate_pda(
|
||||
f,
|
||||
seeds_constraint,
|
||||
seeds_with_nonce,
|
||||
payer,
|
||||
&c.space,
|
||||
&c.kind,
|
||||
)
|
||||
generate_pda(f, seeds_with_nonce, payer, &c.space, &c.kind)
|
||||
}
|
||||
|
||||
fn generate_constraint_seeds_address(
|
||||
f: &Field,
|
||||
c: &ConstraintSeedsGroup,
|
||||
) -> proc_macro2::TokenStream {
|
||||
fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2::TokenStream {
|
||||
let name = &f.ident;
|
||||
let s = &c.seeds;
|
||||
|
||||
// If the bump is provided on *initialization*, then force it to be the
|
||||
// canonical nonce.
|
||||
if c.is_init && c.bump.is_some() && c.bump.as_ref().unwrap().is_some() {
|
||||
let b = c.bump.as_ref().unwrap().as_ref().unwrap();
|
||||
// If the bump is provided with init *and target*, then force it to be the
|
||||
// canonical bump.
|
||||
if c.is_init && c.bump.is_some() {
|
||||
let b = c.bump.as_ref().unwrap();
|
||||
quote! {
|
||||
let (__program_signer, __bump) = anchor_lang::solana_program::pubkey::Pubkey::find_program_address(
|
||||
&[#s],
|
||||
|
@ -336,35 +332,26 @@ fn generate_constraint_seeds_address(
|
|||
}
|
||||
} else {
|
||||
let seeds = match c.bump.as_ref() {
|
||||
// Bump keyword not given, so just use the seeds.
|
||||
// Bump target not given. Find it.
|
||||
None => {
|
||||
quote! {
|
||||
[#s]
|
||||
[
|
||||
#s,
|
||||
&[
|
||||
Pubkey::find_program_address(
|
||||
&[#s],
|
||||
program_id,
|
||||
).1
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
// Bump keyword given.
|
||||
Some(bump) => match bump {
|
||||
// Bump target not given. Find it.
|
||||
None => {
|
||||
quote! {
|
||||
[
|
||||
#s,
|
||||
&[
|
||||
Pubkey::find_program_address(
|
||||
&[#s],
|
||||
program_id,
|
||||
).1
|
||||
]
|
||||
]
|
||||
}
|
||||
// Bump target given. Use it.
|
||||
Some(b) => {
|
||||
quote! {
|
||||
[#s, &[#b]]
|
||||
}
|
||||
// Bump target given. Use it.
|
||||
Some(b) => {
|
||||
quote! {
|
||||
[#s, &[#b]]
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
quote! {
|
||||
let __program_signer = Pubkey::create_program_address(
|
||||
|
@ -429,11 +416,10 @@ fn parse_ty(f: &Field) -> (proc_macro2::TokenStream, proc_macro2::TokenStream, b
|
|||
|
||||
pub fn generate_pda(
|
||||
f: &Field,
|
||||
seeds_constraint: proc_macro2::TokenStream,
|
||||
seeds_with_nonce: proc_macro2::TokenStream,
|
||||
payer: proc_macro2::TokenStream,
|
||||
space: &Option<Expr>,
|
||||
kind: &PdaKind,
|
||||
kind: &InitKind,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let field = &f.ident;
|
||||
let (account_ty, account_wrapper_ty, is_zero_copy) = parse_ty(f);
|
||||
|
@ -452,7 +438,7 @@ pub fn generate_pda(
|
|||
#account_wrapper_ty<#account_ty>
|
||||
},
|
||||
quote! {
|
||||
#account_wrapper_ty::try_from_init(
|
||||
#account_wrapper_ty::try_from_unchecked(
|
||||
&#field.to_account_info(),
|
||||
)?
|
||||
},
|
||||
|
@ -460,10 +446,9 @@ pub fn generate_pda(
|
|||
};
|
||||
|
||||
match kind {
|
||||
PdaKind::Token { owner, mint } => quote! {
|
||||
InitKind::Token { owner, mint } => quote! {
|
||||
let #field: #combined_account_ty = {
|
||||
#payer
|
||||
#seeds_constraint
|
||||
|
||||
// Fund the account for rent exemption.
|
||||
let required_lamports = __anchor_rent
|
||||
|
@ -485,7 +470,7 @@ pub fn generate_pda(
|
|||
#field.to_account_info(),
|
||||
system_program.to_account_info().clone(),
|
||||
],
|
||||
&[&#seeds_with_nonce[..]],
|
||||
&[#seeds_with_nonce],
|
||||
)?;
|
||||
|
||||
// Initialize the token account.
|
||||
|
@ -498,15 +483,14 @@ pub fn generate_pda(
|
|||
};
|
||||
let cpi_ctx = CpiContext::new(cpi_program, accounts);
|
||||
anchor_spl::token::initialize_account(cpi_ctx)?;
|
||||
anchor_lang::CpiAccount::try_from_init(
|
||||
anchor_lang::CpiAccount::try_from_unchecked(
|
||||
&#field.to_account_info(),
|
||||
)?
|
||||
};
|
||||
},
|
||||
PdaKind::Mint { owner, decimals } => quote! {
|
||||
InitKind::Mint { owner, decimals } => quote! {
|
||||
let #field: #combined_account_ty = {
|
||||
#payer
|
||||
#seeds_constraint
|
||||
|
||||
// Fund the account for rent exemption.
|
||||
let required_lamports = rent
|
||||
|
@ -528,7 +512,7 @@ pub fn generate_pda(
|
|||
#field.to_account_info(),
|
||||
system_program.to_account_info().clone(),
|
||||
],
|
||||
&[&#seeds_with_nonce[..]],
|
||||
&[#seeds_with_nonce],
|
||||
)?;
|
||||
|
||||
// Initialize the mint account.
|
||||
|
@ -539,30 +523,30 @@ pub fn generate_pda(
|
|||
};
|
||||
let cpi_ctx = CpiContext::new(cpi_program, accounts);
|
||||
anchor_spl::token::initialize_mint(cpi_ctx, #decimals, &#owner.to_account_info().key, None)?;
|
||||
anchor_lang::CpiAccount::try_from_init(
|
||||
anchor_lang::CpiAccount::try_from_unchecked(
|
||||
&#field.to_account_info(),
|
||||
)?
|
||||
};
|
||||
},
|
||||
PdaKind::Program { owner } => {
|
||||
InitKind::Program { owner } => {
|
||||
let space = match space {
|
||||
// If no explicit space param was given, serialize the type to bytes
|
||||
// and take the length (with +8 for the discriminator.)
|
||||
None => match is_zero_copy {
|
||||
false => {
|
||||
quote! {
|
||||
let space = 8 + #account_ty::default().try_to_vec().unwrap().len();
|
||||
let space = 8 + #account_ty::default().try_to_vec().unwrap().len();
|
||||
}
|
||||
}
|
||||
true => {
|
||||
quote! {
|
||||
let space = 8 + anchor_lang::__private::bytemuck::bytes_of(&#account_ty::default()).len();
|
||||
let space = 8 + anchor_lang::__private::bytemuck::bytes_of(&#account_ty::default()).len();
|
||||
}
|
||||
}
|
||||
},
|
||||
// Explicit account size given. Use it.
|
||||
Some(s) => quote! {
|
||||
let space = #s;
|
||||
let space = #s;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -580,7 +564,6 @@ pub fn generate_pda(
|
|||
let #field = {
|
||||
#space
|
||||
#payer
|
||||
#seeds_constraint
|
||||
|
||||
let lamports = __anchor_rent.minimum_balance(space);
|
||||
let ix = anchor_lang::solana_program::system_instruction::create_account(
|
||||
|
@ -599,7 +582,7 @@ pub fn generate_pda(
|
|||
payer.to_account_info(),
|
||||
system_program.to_account_info(),
|
||||
],
|
||||
&[&#seeds_with_nonce[..]]
|
||||
&[#seeds_with_nonce],
|
||||
).map_err(|e| {
|
||||
anchor_lang::solana_program::msg!("Unable to create associated account");
|
||||
e
|
||||
|
|
|
@ -30,7 +30,10 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
}
|
||||
}
|
||||
AccountField::Field(f) => {
|
||||
if is_pda_init(af) {
|
||||
// `init` and `zero` acccounts are special cased as they are
|
||||
// deserialized by constraints. Here, we just take out the
|
||||
// AccountInfo for later use at constraint validation time.
|
||||
if is_init(af) || f.constraints.zeroed.is_some() {
|
||||
let name = &f.ident;
|
||||
quote!{
|
||||
let #name = &accounts[0];
|
||||
|
@ -38,17 +41,10 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
}
|
||||
} else {
|
||||
let name = typed_ident(f);
|
||||
match f.constraints.is_init() || f.constraints.is_zeroed() {
|
||||
false => quote! {
|
||||
#[cfg(feature = "anchor-debug")]
|
||||
::solana_program::log::sol_log(stringify!(#name));
|
||||
let #name = anchor_lang::Accounts::try_accounts(program_id, accounts, ix_data)?;
|
||||
},
|
||||
true => quote! {
|
||||
#[cfg(feature = "anchor-debug")]
|
||||
::solana_program::log::sol_log(stringify!(#name));
|
||||
let #name = anchor_lang::AccountsInit::try_accounts_init(program_id, accounts)?;
|
||||
},
|
||||
quote! {
|
||||
#[cfg(feature = "anchor-debug")]
|
||||
::solana_program::log::sol_log(stringify!(#name));
|
||||
let #name = anchor_lang::Accounts::try_accounts(program_id, accounts, ix_data)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -111,18 +107,6 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_pda_init(af: &AccountField) -> bool {
|
||||
match af {
|
||||
AccountField::CompositeField(_s) => false,
|
||||
AccountField::Field(f) => f
|
||||
.constraints
|
||||
.seeds
|
||||
.as_ref()
|
||||
.map(|f| f.is_init)
|
||||
.unwrap_or(false),
|
||||
}
|
||||
}
|
||||
|
||||
fn typed_ident(field: &Field) -> TokenStream {
|
||||
let name = &field.ident;
|
||||
|
||||
|
@ -183,17 +167,17 @@ fn typed_ident(field: &Field) -> TokenStream {
|
|||
}
|
||||
|
||||
pub fn generate_constraints(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
||||
let non_pda_fields: Vec<&AccountField> =
|
||||
accs.fields.iter().filter(|af| !is_pda_init(af)).collect();
|
||||
let non_init_fields: Vec<&AccountField> =
|
||||
accs.fields.iter().filter(|af| !is_init(af)).collect();
|
||||
|
||||
// Deserialization for each pda init field. This must be after
|
||||
// the inital extraction from the accounts slice and before access_checks.
|
||||
let init_pda_fields: Vec<proc_macro2::TokenStream> = accs
|
||||
let init_fields: Vec<proc_macro2::TokenStream> = accs
|
||||
.fields
|
||||
.iter()
|
||||
.filter_map(|af| match af {
|
||||
AccountField::CompositeField(_s) => None,
|
||||
AccountField::Field(f) => match is_pda_init(af) {
|
||||
AccountField::Field(f) => match is_init(af) {
|
||||
false => None,
|
||||
true => Some(f),
|
||||
},
|
||||
|
@ -202,7 +186,7 @@ pub fn generate_constraints(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
.collect();
|
||||
|
||||
// Constraint checks for each account fields.
|
||||
let access_checks: Vec<proc_macro2::TokenStream> = non_pda_fields
|
||||
let access_checks: Vec<proc_macro2::TokenStream> = non_init_fields
|
||||
.iter()
|
||||
.map(|af: &&AccountField| match af {
|
||||
AccountField::Field(f) => constraints::generate(f),
|
||||
|
@ -211,7 +195,7 @@ pub fn generate_constraints(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
.collect();
|
||||
|
||||
quote! {
|
||||
#(#init_pda_fields)*
|
||||
#(#init_fields)*
|
||||
#(#access_checks)*
|
||||
}
|
||||
}
|
||||
|
@ -239,3 +223,10 @@ pub fn generate_accounts_instance(accs: &AccountsStruct) -> proc_macro2::TokenSt
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_init(af: &AccountField) -> bool {
|
||||
match af {
|
||||
AccountField::CompositeField(_s) => false,
|
||||
AccountField::Field(f) => f.constraints.init.is_some(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -227,7 +227,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
)?;
|
||||
|
||||
// Zero copy deserialize.
|
||||
let loader: anchor_lang::Loader<#mod_name::#name> = anchor_lang::Loader::try_from_init(&ctor_accounts.to)?;
|
||||
let loader: anchor_lang::Loader<#mod_name::#name> = anchor_lang::Loader::try_from_unchecked(&ctor_accounts.to)?;
|
||||
|
||||
// Invoke the ctor in a new lexical scope so that
|
||||
// the zero-copy RefMut gets dropped. Required
|
||||
|
|
|
@ -262,7 +262,7 @@ pub struct ErrorCode {
|
|||
// All well formed constraints on a single `Accounts` field.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct ConstraintGroup {
|
||||
init: Option<ConstraintInit>,
|
||||
init: Option<ConstraintInitGroup>,
|
||||
zeroed: Option<ConstraintZeroed>,
|
||||
mutable: Option<ConstraintMut>,
|
||||
signer: Option<ConstraintSigner>,
|
||||
|
@ -279,10 +279,6 @@ pub struct ConstraintGroup {
|
|||
}
|
||||
|
||||
impl ConstraintGroup {
|
||||
pub fn is_init(&self) -> bool {
|
||||
self.init.is_some()
|
||||
}
|
||||
|
||||
pub fn is_zeroed(&self) -> bool {
|
||||
self.zeroed.is_some()
|
||||
}
|
||||
|
@ -306,7 +302,7 @@ impl ConstraintGroup {
|
|||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug)]
|
||||
pub enum Constraint {
|
||||
Init(ConstraintInit),
|
||||
Init(ConstraintInitGroup),
|
||||
Zeroed(ConstraintZeroed),
|
||||
Mut(ConstraintMut),
|
||||
Signer(ConstraintSigner),
|
||||
|
@ -398,15 +394,19 @@ pub enum ConstraintRentExempt {
|
|||
Skip,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConstraintInitGroup {
|
||||
pub seeds: Option<ConstraintSeedsGroup>,
|
||||
pub payer: Option<Ident>,
|
||||
pub space: Option<Expr>,
|
||||
pub kind: InitKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConstraintSeedsGroup {
|
||||
pub is_init: bool,
|
||||
pub seeds: Punctuated<Expr, Token![,]>,
|
||||
pub payer: Option<Ident>,
|
||||
pub space: Option<Expr>,
|
||||
pub kind: PdaKind,
|
||||
// Some(None) => bump was given without a target.
|
||||
pub bump: Option<Option<Expr>>,
|
||||
pub bump: Option<Expr>, // None => bump was given without a target.
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -434,7 +434,7 @@ pub struct ConstraintSpace {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum PdaKind {
|
||||
pub enum InitKind {
|
||||
Program { owner: Option<Expr> },
|
||||
Token { owner: Expr, mint: Expr },
|
||||
Mint { owner: Expr, decimals: Expr },
|
||||
|
|
|
@ -279,8 +279,9 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
|
|||
bump: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(mut self) -> ParseResult<ConstraintGroup> {
|
||||
// Init implies mutable and rent exempt.
|
||||
// Init.
|
||||
if let Some(i) = &self.init {
|
||||
match self.mutable {
|
||||
Some(m) => {
|
||||
|
@ -298,9 +299,22 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
|
|||
self.rent_exempt
|
||||
.replace(Context::new(i.span(), ConstraintRentExempt::Enforce));
|
||||
}
|
||||
if self.payer.is_none() {
|
||||
return Err(ParseError::new(
|
||||
i.span(),
|
||||
"payer must be provided when initializing an account",
|
||||
));
|
||||
}
|
||||
// When initializing a non-PDA account, the account being
|
||||
// initialized must sign to invoke the system program's create
|
||||
// account instruction.
|
||||
if self.signer.is_none() && self.seeds.is_none() {
|
||||
self.signer
|
||||
.replace(Context::new(i.span(), ConstraintSigner {}));
|
||||
}
|
||||
}
|
||||
|
||||
// Seeds.
|
||||
// Zero.
|
||||
if let Some(z) = &self.zeroed {
|
||||
match self.mutable {
|
||||
Some(m) => {
|
||||
|
@ -320,6 +334,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
|
|||
}
|
||||
}
|
||||
|
||||
// Seeds.
|
||||
if let Some(i) = &self.seeds {
|
||||
if self.init.is_some() && self.payer.is_none() {
|
||||
return Err(ParseError::new(
|
||||
|
@ -327,6 +342,12 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
|
|||
"payer must be provided when creating a program derived address",
|
||||
));
|
||||
}
|
||||
if self.bump.is_none() {
|
||||
return Err(ParseError::new(
|
||||
i.span(),
|
||||
"bump must be provided with seeds",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Token.
|
||||
|
@ -338,7 +359,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
|
|||
));
|
||||
}
|
||||
|
||||
if self.init.is_none() || self.seeds.is_none() {
|
||||
if self.init.is_none() {
|
||||
return Err(ParseError::new(
|
||||
token_mint.span(),
|
||||
"init is required for a pda token",
|
||||
|
@ -434,9 +455,46 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
|
|||
}
|
||||
};
|
||||
|
||||
let is_init = init.is_some();
|
||||
let seeds = seeds.map(|c| ConstraintSeedsGroup {
|
||||
is_init: init.is_some(),
|
||||
seeds: c.seeds.clone(),
|
||||
bump: into_inner!(bump)
|
||||
.map(|b| b.bump)
|
||||
.expect("bump must be provided with seeds"),
|
||||
});
|
||||
Ok(ConstraintGroup {
|
||||
init: into_inner!(init),
|
||||
init: init.as_ref().map(|_| Ok(ConstraintInitGroup {
|
||||
seeds: seeds.clone(),
|
||||
payer: into_inner!(payer.clone()).map(|a| a.target),
|
||||
space: space.clone().map(|s| s.space.clone()),
|
||||
kind: if let Some(tm) = &token_mint {
|
||||
InitKind::Token {
|
||||
mint: tm.clone().into_inner().mint,
|
||||
owner: match &token_authority {
|
||||
Some(a) => a.clone().into_inner().auth,
|
||||
None => return Err(ParseError::new(
|
||||
tm.span(),
|
||||
"authority must be provided to initialize a token program derived address"
|
||||
)),
|
||||
},
|
||||
}
|
||||
} else if let Some(d) = &mint_decimals {
|
||||
InitKind::Mint {
|
||||
decimals: d.clone().into_inner().decimals,
|
||||
owner: match &mint_authority {
|
||||
Some(a) => a.clone().into_inner().mint_auth,
|
||||
None => return Err(ParseError::new(
|
||||
d.span(),
|
||||
"authority must be provided to initialize a mint program derived address"
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
InitKind::Program {
|
||||
owner: pda_owner.clone(),
|
||||
}
|
||||
},
|
||||
})).transpose()?,
|
||||
zeroed: into_inner!(zeroed),
|
||||
mutable: into_inner!(mutable),
|
||||
signer: into_inner!(signer),
|
||||
|
@ -449,45 +507,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
|
|||
state: into_inner!(state),
|
||||
close: into_inner!(close),
|
||||
address: into_inner!(address),
|
||||
seeds: seeds
|
||||
.map(|c| {
|
||||
Ok(ConstraintSeedsGroup {
|
||||
is_init,
|
||||
seeds: c.into_inner().seeds,
|
||||
payer: into_inner!(payer.clone()).map(|a| a.target),
|
||||
space: space.clone().map(|s| s.space.clone()),
|
||||
kind: if let Some(tm) = &token_mint {
|
||||
PdaKind::Token {
|
||||
mint: tm.clone().into_inner().mint,
|
||||
owner: match &token_authority {
|
||||
Some(a) => a.clone().into_inner().auth,
|
||||
None => return Err(ParseError::new(
|
||||
tm.span(),
|
||||
"authority must be provided to initialize a token program derived address"
|
||||
)),
|
||||
},
|
||||
}
|
||||
} else if let Some(d) = &mint_decimals {
|
||||
PdaKind::Mint {
|
||||
decimals: d.clone().into_inner().decimals,
|
||||
owner: match &mint_authority {
|
||||
Some(a) => a.clone().into_inner().mint_auth,
|
||||
None => return Err(ParseError::new(
|
||||
d.span(),
|
||||
"authority must be provided to initialize a mint program derived address"
|
||||
))
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PdaKind::Program {
|
||||
owner: pda_owner.clone(),
|
||||
}
|
||||
},
|
||||
bump: into_inner!(bump).map(|b| b.bump),
|
||||
})
|
||||
})
|
||||
.transpose()?,
|
||||
seeds,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -723,10 +743,10 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
|
|||
}
|
||||
|
||||
fn add_payer(&mut self, c: Context<ConstraintPayer>) -> ParseResult<()> {
|
||||
if self.seeds.is_none() {
|
||||
if self.init.is_none() {
|
||||
return Err(ParseError::new(
|
||||
c.span(),
|
||||
"seeds must be provided before payer",
|
||||
"init must be provided before payer",
|
||||
));
|
||||
}
|
||||
if self.payer.is_some() {
|
||||
|
@ -737,7 +757,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
|
|||
}
|
||||
|
||||
fn add_space(&mut self, c: Context<ConstraintSpace>) -> ParseResult<()> {
|
||||
if self.seeds.is_none() {
|
||||
if self.init.is_none() {
|
||||
return Err(ParseError::new(
|
||||
c.span(),
|
||||
"init must be provided before space",
|
||||
|
|
Loading…
Reference in New Issue