Explorar el Código

lang, ts, examples: Remove associated keyword (#612)

Armani Ferrante hace 4 años
padre
commit
6e55078ff3

+ 0 - 1
.travis.yml

@@ -81,4 +81,3 @@ jobs:
         - pushd examples/tutorial/basic-2 && anchor test && popd
         - pushd examples/tutorial/basic-3 && anchor test && popd
         - pushd examples/tutorial/basic-4 && anchor test && popd
-        - pushd examples/tutorial/basic-5 && anchor test && popd

+ 1 - 0
CHANGELOG.md

@@ -19,6 +19,7 @@ incremented for features.
 ### 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.
 
 ## [0.13.2] - 2021-08-11
 

+ 0 - 113
docs/src/tutorials/tutorial-6.md

@@ -1,113 +0,0 @@
-# Associated Accounts
-
-No Solana program should be written without understanding associated accounts.
-Every program using an account in a way that maps to a user, which is almost all
-programs, should use them.
-
-The TLDR. Associated accounts are accounts whose address are deterministically defined by
-a program and some associated data. Usually that data is both a user wallet and some account
-instance, for example, a token mint.
-
-Why should you care? UX.
-
-Consider a wallet. Would you rather have a wallet with a single SOL address, which you
-can use to receive *all* SPL tokens, or would you rather have a wallet with a different
-address for every SPL token? Now generalize this. For every program you use, do you
-want a single account, i.e. your SOL wallet, to define your application state? Or do
-you want to keep track of all your account addresses, separately, for every program in existance?
-
-Associated accounts allow your users to reason about a single address, their main SOL wallet.  This is
-a huge improvement on the account model introduced thus far.
-
-Luckily, Anchor provides the ability to easily create associated program accounts for your program.
-
-::: details
-If you've explored Solana, you may have come across the [Associated Token Account Program](https://spl.solana.com/associated-token-account) which uniquely and deterministically defines
-a token account for a given wallet and a given mint. That is, if you have a SOL address,
-then you will have, at most, a single "token account" for every SPL mint in existence
-if you only use associated token addresses.
-
-Unfortunately, the SPL token program doesn't do this, strictly. It was built *before* the existance
-of associated token accounts (associated token accounts were built as an add-on).
-So in reality, there are non associated token accounts floating around Solanaland.
-However, for new programs, this isn't necessary, and it's recommended to only use associated
-accounts when creating accounts on behalf of users, such as a token account.
-:::
-
-## Clone the Repo
-
-To get started, clone the repo.
-
-```bash
-git clone https://github.com/project-serum/anchor
-```
-
-And change directories to the [example](https://github.com/project-serum/anchor/tree/master/examples/tutorial/basic-5).
-
-```bash
-cd anchor/examples/tutorial/basic-5
-```
-
-## Defining a Program to Create Associated Accounts
-
-The following program is an *extremely* simplified version of the SPL token program that
-does nothing other than create a mint and *associated* token account.
-
-<<< @/../examples/tutorial/basic-5/programs/basic-5/src/lib.rs#code
-
-### Deriving Accounts
-
-Two new keywords are introduced to the `CreateToken` account context:
-
-* `associated = <target>`
-* `with = <target>`
-
-Both of these allow you to define input "seeds" that
-uniquely define the associated account. By convention, `associated` is used to define
-the main address to associate, i.e., the wallet, while `with` is used to define an
-auxilliary piece of metadata which has the effect of namespacing the associated account.
-This can be used, for example, to create multiple different associated accounts, each of
-which is associated *with* a new piece of metadata. In the token program, these pieces
-of metadata are mints, i.e., different token types.
-
-Lastly, notice the `system_program` account at the bottom of account context.
-
-```rust
-    system_program: AccountInfo<'info>,
-```
-
-The `system-program` must be provided when creating an associated
-account. By convention, the name must be as given here.
-
-For more details on how to use `#[account(associated)]`, see [docs.rs](https://docs.rs/anchor-lang/latest/anchor_lang/derive.Accounts.html).
-
-### Defining an Associated Account
-
-The new `#[acount(associated)]` attribute will allow you to create a new associated account similar to `#[account(init)]`, but
-to actually define an account as associated, you must use the `#[associated]` attribute *instead* of the `#[account]` attribute.
-
-This new `#[associated]` attribute extends `#[account]` to include two things
-
-* A `Default` implementation, which is required for automatic size detection (performed when `#[account(space = "<size>")]` is omitted from the accounts context).
-* The implementation of the [Bump](https://docs.rs/anchor-lang/latest/anchor_lang/trait.Bump.html) trait, which is a bit of an implementation but is required for program derived addresses on Solana.
-
-## Using the Client
-
-The client can be used similarly to all other examples. Additionally, we introduce
-two new apis.
-
-* `<program>.account.<account-name>.associatedAddress` returns an associated token address, given seeds.
-* `<program>.account.<account-name>.associated` returns the deserialized associated account, given seeds.
-
-
-We can use them with the example above as follows
-
-<<< @/../examples/tutorial/basic-5/tests/basic-5.js#test
-
-Notice that, in both apis, the "seeds" given match what is expected by the `#[account(associated = <target, with = <target>)]` attribute, where order matters. The `associated` target must come before the `with` target.
-
-## Conclusion
-
-Here, we introduced associated accounts from the perspective of simplifying UX for
-a user wallet. However, deterministic addressing can be used beyond this and is a convenient
-tool to have in your Solana toolbox. For more, it's recommended to see the Solana [docs](https://docs.solana.com/developing/programming-model/calling-between-programs#program-derived-addresses).

+ 87 - 57
examples/cfo/programs/cfo/src/lib.rs

@@ -1,9 +1,8 @@
 // WIP. This program has been checkpointed and is not production ready.
 
-use anchor_lang::associated_seeds;
 use anchor_lang::prelude::*;
+use anchor_lang::solana_program::system_program;
 use anchor_lang::solana_program::sysvar::instructions as tx_instructions;
-use anchor_lang::solana_program::{system_instruction, system_program};
 use anchor_spl::token::{self, Mint, TokenAccount};
 use anchor_spl::{dex, mint};
 use registry::{Registrar, RewardVendorKind};
@@ -20,6 +19,7 @@ pub mod cfo {
     #[access_control(is_distribution_valid(&d))]
     pub fn create_officer(
         ctx: Context<CreateOfficer>,
+        bumps: OfficerBumps,
         d: Distribution,
         registrar: Pubkey,
         msrm_registrar: Pubkey,
@@ -34,6 +34,7 @@ pub mod cfo {
         officer.stake = *ctx.accounts.stake.to_account_info().key;
         officer.treasury = *ctx.accounts.treasury.to_account_info().key;
         officer.srm_vault = *ctx.accounts.srm_vault.to_account_info().key;
+        officer.bumps = bumps;
         emit!(OfficerDidCreate {
             pubkey: *officer.to_account_info().key,
         });
@@ -43,7 +44,7 @@ pub mod cfo {
     /// Creates a deterministic token account owned by the CFO.
     /// This should be used when a new mint is used for collecting fees.
     /// Can only be called once per token CFO and token mint.
-    pub fn create_officer_token(_ctx: Context<CreateOfficerToken>) -> Result<()> {
+    pub fn create_officer_token(_ctx: Context<CreateOfficerToken>, _bump: u8) -> Result<()> {
         Ok(())
     }
 
@@ -57,12 +58,12 @@ pub mod cfo {
 
     /// Transfers fees from the dex to the CFO.
     pub fn sweep_fees<'info>(ctx: Context<'_, '_, '_, 'info, SweepFees<'info>>) -> Result<()> {
-        let seeds = associated_seeds! {
-            account = ctx.accounts.officer,
-            associated = ctx.accounts.dex.dex_program
-        };
-        let cpi_ctx: CpiContext<'_, '_, '_, 'info, dex::SweepFees<'info>> = (&*ctx.accounts).into();
-        dex::sweep_fees(cpi_ctx.with_signer(&[seeds]))?;
+        let seeds = [
+            ctx.accounts.dex.dex_program.key.as_ref(),
+            &[ctx.accounts.officer.bumps.bump],
+        ];
+        let cpi_ctx = CpiContext::from(&*ctx.accounts);
+        dex::sweep_fees(cpi_ctx.with_signer(&[&seeds[..]]))?;
         Ok(())
     }
 
@@ -73,13 +74,13 @@ pub mod cfo {
         ctx: Context<'_, '_, '_, 'info, SwapToUsdc<'info>>,
         min_exchange_rate: ExchangeRate,
     ) -> Result<()> {
-        let seeds = associated_seeds! {
-            account = ctx.accounts.officer,
-            associated = ctx.accounts.dex_program
-        };
-        let cpi_ctx: CpiContext<'_, '_, '_, 'info, swap::Swap<'info>> = (&*ctx.accounts).into();
+        let seeds = [
+            ctx.accounts.dex_program.key.as_ref(),
+            &[ctx.accounts.officer.bumps.bump],
+        ];
+        let cpi_ctx = CpiContext::from(&*ctx.accounts);
         swap::cpi::swap(
-            cpi_ctx.with_signer(&[seeds]),
+            cpi_ctx.with_signer(&[&seeds[..]]),
             swap::Side::Bid,
             token::accessor::amount(&ctx.accounts.from_vault)?,
             min_exchange_rate.into(),
@@ -94,13 +95,13 @@ pub mod cfo {
         ctx: Context<'_, '_, '_, 'info, SwapToSrm<'info>>,
         min_exchange_rate: ExchangeRate,
     ) -> Result<()> {
-        let seeds = associated_seeds! {
-            account = ctx.accounts.officer,
-            associated = ctx.accounts.dex_program
-        };
+        let seeds = [
+            ctx.accounts.dex_program.key.as_ref(),
+            &[ctx.accounts.officer.bumps.bump],
+        ];
         let cpi_ctx: CpiContext<'_, '_, '_, 'info, swap::Swap<'info>> = (&*ctx.accounts).into();
         swap::cpi::swap(
-            cpi_ctx.with_signer(&[seeds]),
+            cpi_ctx.with_signer(&[&seeds[..]]),
             swap::Side::Bid,
             token::accessor::amount(&ctx.accounts.from_vault)?,
             min_exchange_rate.into(),
@@ -113,10 +114,10 @@ pub mod cfo {
     #[access_control(is_distribution_ready(&ctx.accounts))]
     pub fn distribute<'info>(ctx: Context<'_, '_, '_, 'info, Distribute<'info>>) -> Result<()> {
         let total_fees = ctx.accounts.srm_vault.amount;
-        let seeds = associated_seeds! {
-            account = ctx.accounts.officer,
-            associated = ctx.accounts.dex_program
-        };
+        let seeds = [
+            ctx.accounts.dex_program.key.as_ref(),
+            &[ctx.accounts.officer.bumps.bump],
+        ];
 
         // Burn.
         let burn_amount: u64 = u128::from(total_fees)
@@ -126,7 +127,10 @@ pub mod cfo {
             .unwrap()
             .try_into()
             .map_err(|_| ErrorCode::U128CannotConvert)?;
-        token::burn(ctx.accounts.into_burn().with_signer(&[seeds]), burn_amount)?;
+        token::burn(
+            ctx.accounts.into_burn().with_signer(&[&seeds[..]]),
+            burn_amount,
+        )?;
 
         // Stake.
         let stake_amount: u64 = u128::from(total_fees)
@@ -137,7 +141,9 @@ pub mod cfo {
             .try_into()
             .map_err(|_| ErrorCode::U128CannotConvert)?;
         token::transfer(
-            ctx.accounts.into_stake_transfer().with_signer(&[seeds]),
+            ctx.accounts
+                .into_stake_transfer()
+                .with_signer(&[&seeds[..]]),
             stake_amount,
         )?;
 
@@ -150,7 +156,9 @@ pub mod cfo {
             .try_into()
             .map_err(|_| ErrorCode::U128CannotConvert)?;
         token::transfer(
-            ctx.accounts.into_treasury_transfer().with_signer(&[seeds]),
+            ctx.accounts
+                .into_treasury_transfer()
+                .with_signer(&[&seeds[..]]),
             treasury_amount,
         )?;
 
@@ -174,10 +182,10 @@ pub mod cfo {
                 period_count,
             }
         };
-        let seeds = associated_seeds! {
-            account = ctx.accounts.officer,
-            associated = ctx.accounts.dex_program
-        };
+        let seeds = [
+            ctx.accounts.dex_program.key.as_ref(),
+            &[ctx.accounts.officer.bumps.bump],
+        ];
 
         // Total amount staked denominated in SRM (i.e. MSRM is converted to
         // SRM)
@@ -231,7 +239,7 @@ pub mod cfo {
                 ctx.accounts.token_program.key,
             );
             registry::cpi::drop_reward(
-                ctx.accounts.into_srm_reward().with_signer(&[seeds]),
+                ctx.accounts.into_srm_reward().with_signer(&[&seeds[..]]),
                 locked_kind.clone(),
                 srm_amount.try_into().unwrap(),
                 expiry_ts,
@@ -241,7 +249,7 @@ pub mod cfo {
 
             // Drop unlocked reward.
             registry::cpi::drop_reward(
-                ctx.accounts.into_srm_reward().with_signer(&[seeds]),
+                ctx.accounts.into_srm_reward().with_signer(&[&seeds[..]]),
                 RewardVendorKind::Unlocked,
                 srm_amount,
                 expiry_ts,
@@ -261,7 +269,7 @@ pub mod cfo {
                 ctx.accounts.token_program.key,
             );
             registry::cpi::drop_reward(
-                ctx.accounts.into_msrm_reward().with_signer(&[seeds]),
+                ctx.accounts.into_msrm_reward().with_signer(&[&seeds[..]]),
                 locked_kind,
                 msrm_amount,
                 expiry_ts,
@@ -271,7 +279,7 @@ pub mod cfo {
 
             // Drop unlocked reward.
             registry::cpi::drop_reward(
-                ctx.accounts.into_msrm_reward().with_signer(&[seeds]),
+                ctx.accounts.into_msrm_reward().with_signer(&[&seeds[..]]),
                 RewardVendorKind::Unlocked,
                 msrm_amount,
                 expiry_ts,
@@ -287,32 +295,40 @@ pub mod cfo {
 // Context accounts.
 
 #[derive(Accounts)]
+#[instruction(bumps: OfficerBumps)]
 pub struct CreateOfficer<'info> {
-    #[account(init, associated = dex_program, payer = authority)]
+    #[account(
+        init,
+        seeds = [dex_program.key.as_ref()],
+        bump = bumps.bump,
+        payer = authority,
+    )]
     officer: ProgramAccount<'info, Officer>,
     #[account(
         init,
+        seeds = [b"vault", officer.key().as_ref()],
+        bump = bumps.srm,
+        payer = authority,
         token::mint = mint,
         token::authority = officer,
-        associated = officer, with = b"vault",
-        payer = authority,
     )]
     srm_vault: CpiAccount<'info, TokenAccount>,
     #[account(
         init,
+        seeds = [b"stake", officer.key().as_ref()],
+        bump = bumps.stake,
+        payer = authority,
         token::mint = mint,
         token::authority = officer,
-        associated = officer, with = b"stake",
-        space = TokenAccount::LEN,
-        payer = authority,
     )]
     stake: CpiAccount<'info, TokenAccount>,
     #[account(
         init,
+        seeds = [b"treasury", officer.key().as_ref()],
+        bump = bumps.treasury,
+        payer = authority,
         token::mint = mint,
         token::authority = officer,
-        associated = officer, with = b"treasury",
-        payer = authority,
     )]
     treasury: CpiAccount<'info, TokenAccount>,
     #[account(signer)]
@@ -334,14 +350,15 @@ pub struct CreateOfficer<'info> {
 }
 
 #[derive(Accounts)]
+#[instruction(bump: u8)]
 pub struct CreateOfficerToken<'info> {
     officer: ProgramAccount<'info, Officer>,
     #[account(
         init,
+        seeds = [officer.key().as_ref(), mint.key().as_ref()],
+        bump = bump,
         token::mint = mint,
         token::authority = officer,
-        associated = officer, with = mint,
-        space = TokenAccount::LEN,
         payer = payer,
     )]
     token: CpiAccount<'info, TokenAccount>,
@@ -366,14 +383,15 @@ pub struct SetDistribution<'info> {
 
 #[derive(Accounts)]
 pub struct SweepFees<'info> {
-    #[account(associated = dex.dex_program)]
+    #[account(seeds = [dex.dex_program.key.as_ref(), &[officer.bumps.bump]])]
     officer: ProgramAccount<'info, Officer>,
     #[account(
         mut,
         owner = dex.token_program,
-        associated = officer, with = mint,
+        seeds = [officer.key().as_ref(), mint.key().as_ref()],
+        bump,
     )]
-    sweep_vault: AccountInfo<'info>,
+    sweep_vault: CpiAccount<'info, TokenAccount>,
     mint: AccountInfo<'info>,
     dex: Dex<'info>,
 }
@@ -393,7 +411,7 @@ pub struct Dex<'info> {
 
 #[derive(Accounts)]
 pub struct SwapToUsdc<'info> {
-    #[account(associated = dex_program)]
+    #[account(seeds = [dex_program.key().as_ref(), &[officer.bumps.bump]])]
     officer: ProgramAccount<'info, Officer>,
     market: DexMarketAccounts<'info>,
     #[account(
@@ -404,7 +422,7 @@ pub struct SwapToUsdc<'info> {
     from_vault: AccountInfo<'info>,
     #[account(owner = token_program)]
     quote_vault: AccountInfo<'info>,
-    #[account(associated = officer, with = mint::USDC)]
+    #[account(seeds = [officer.key().as_ref(), mint::USDC.as_ref()], bump)]
     usdc_vault: AccountInfo<'info>,
     #[account(address = swap::ID)]
     swap_program: AccountInfo<'info>,
@@ -412,14 +430,14 @@ pub struct SwapToUsdc<'info> {
     dex_program: AccountInfo<'info>,
     #[account(address = token::ID)]
     token_program: AccountInfo<'info>,
-    rent: Sysvar<'info, Rent>,
     #[account(address = tx_instructions::ID)]
     instructions: AccountInfo<'info>,
+    rent: Sysvar<'info, Rent>,
 }
 
 #[derive(Accounts)]
 pub struct SwapToSrm<'info> {
-    #[account(associated = dex_program)]
+    #[account(seeds = [dex_program.key().as_ref(), &[officer.bumps.bump]])]
     officer: ProgramAccount<'info, Officer>,
     market: DexMarketAccounts<'info>,
     #[account(
@@ -431,8 +449,8 @@ pub struct SwapToSrm<'info> {
     #[account(owner = token_program)]
     quote_vault: AccountInfo<'info>,
     #[account(
-        associated = officer,
-        with = mint::SRM,
+        seeds = [officer.key().as_ref(), mint::SRM.as_ref()],
+        bump,
         constraint = &officer.treasury != from_vault.key,
         constraint = &officer.stake != from_vault.key,
     )]
@@ -443,9 +461,9 @@ pub struct SwapToSrm<'info> {
     dex_program: AccountInfo<'info>,
     #[account(address = token::ID)]
     token_program: AccountInfo<'info>,
-    rent: Sysvar<'info, Rent>,
     #[account(address = tx_instructions::ID)]
     instructions: AccountInfo<'info>,
+    rent: Sysvar<'info, Rent>,
 }
 
 #[derive(Accounts)]
@@ -510,7 +528,9 @@ pub struct DropStakeReward<'info> {
         constraint = msrm.registrar.key == &officer.msrm_registrar,
     )]
     officer: ProgramAccount<'info, Officer>,
-    #[account(associated = officer, with = b"stake", with = mint)]
+    #[account(
+        seeds = [b"stake", officer.key().as_ref(), &[officer.bumps.stake]]
+    )]
     stake: CpiAccount<'info, TokenAccount>,
     #[cfg_attr(
         not(feature = "test"),
@@ -546,7 +566,7 @@ pub struct DropStakeRewardPool<'info> {
 
 // Accounts.
 
-#[associated]
+#[account]
 #[derive(Default)]
 pub struct Officer {
     // Priviledged account.
@@ -567,6 +587,16 @@ pub struct Officer {
     pub registrar: Pubkey,
     // MSRM stake pool address.
     pub msrm_registrar: Pubkey,
+    // Bump seeds for pdas.
+    pub bumps: OfficerBumps,
+}
+
+#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)]
+pub struct OfficerBumps {
+    pub bump: u8,
+    pub srm: u8,
+    pub stake: u8,
+    pub treasury: u8,
 }
 
 #[derive(AnchorSerialize, AnchorDeserialize, Default, Clone)]

+ 55 - 41
examples/cfo/tests/cfo.js

@@ -97,39 +97,55 @@ describe("cfo", () => {
       stake: 20,
       treasury: 0,
     };
-    officer = await program.account.officer.associatedAddress(DEX_PID);
-    const srmVault = await anchor.utils.publicKey.associated(
-      program.programId,
-      officer,
-      anchor.utils.bytes.utf8.encode("vault"),
-    );
-    const stake = await anchor.utils.publicKey.associated(
-      program.programId,
-      officer,
-      anchor.utils.bytes.utf8.encode("stake"),
-    );
-    const treasury = await anchor.utils.publicKey.associated(
-      program.programId,
-      officer,
-      Buffer.from(anchor.utils.bytes.utf8.encode("treasury")),
-    );
-    await program.rpc.createOfficer(distribution, registrar, msrmRegistrar, {
-      accounts: {
-        officer,
-        srmVault,
-        stake,
-        treasury,
-        mint: ORDERBOOK_ENV.mintA,
-        authority: program.provider.wallet.publicKey,
-        dexProgram: DEX_PID,
-        swapProgram: SWAP_PID,
-        tokenProgram: TOKEN_PID,
-        systemProgram: SystemProgram.programId,
-        rent: SYSVAR_RENT_PUBKEY,
-      },
-    });
+    const [_officer, officerBump] = await PublicKey.findProgramAddress(
+      [DEX_PID.toBuffer()],
+      program.programId
+    );
+    officer = _officer;
+    const [srmVault, srmBump] = await PublicKey.findProgramAddress(
+      [anchor.utils.bytes.utf8.encode("vault"), officer.toBuffer()],
+      program.programId
+    );
+    const [stake, stakeBump] = await PublicKey.findProgramAddress(
+      [anchor.utils.bytes.utf8.encode("stake"), officer.toBuffer()],
+      program.programId
+    );
+    const [treasury, treasuryBump] = await PublicKey.findProgramAddress(
+      [
+        Buffer.from(anchor.utils.bytes.utf8.encode("treasury")),
+        officer.toBuffer(),
+      ],
+      program.programId
+    );
+    const bumps = {
+      bump: officerBump,
+      srm: srmBump,
+      stake: stakeBump,
+      treasury: treasuryBump,
+    };
+    await program.rpc.createOfficer(
+      bumps,
+      distribution,
+      registrar,
+      msrmRegistrar,
+      {
+        accounts: {
+          officer,
+          srmVault,
+          stake,
+          treasury,
+          mint: ORDERBOOK_ENV.mintA,
+          authority: program.provider.wallet.publicKey,
+          dexProgram: DEX_PID,
+          swapProgram: SWAP_PID,
+          tokenProgram: TOKEN_PID,
+          systemProgram: SystemProgram.programId,
+          rent: SYSVAR_RENT_PUBKEY,
+        },
+      }
+    );
 
-    officerAccount = await program.account.officer.associated(DEX_PID);
+    officerAccount = await program.account.officer.fetch(officer);
     assert.ok(
       officerAccount.authority.equals(program.provider.wallet.publicKey)
     );
@@ -140,12 +156,11 @@ describe("cfo", () => {
   });
 
   it("Creates a token account for the officer associated with the market", async () => {
-    const token = await anchor.utils.publicKey.associated(
-      program.programId,
-      officer,
-      ORDERBOOK_ENV.usdc
+    const [token, bump] = await PublicKey.findProgramAddress(
+      [officer.toBuffer(), ORDERBOOK_ENV.usdc.toBuffer()],
+      program.programId
     );
-    await program.rpc.createOfficerToken({
+    await program.rpc.createOfficerToken(bump, {
       accounts: {
         officer,
         token,
@@ -162,10 +177,9 @@ describe("cfo", () => {
   });
 
   it("Sweeps fees", async () => {
-    const sweepVault = await anchor.utils.publicKey.associated(
-      program.programId,
-      officer,
-      ORDERBOOK_ENV.usdc
+    const [sweepVault, bump] = await PublicKey.findProgramAddress(
+      [officer.toBuffer(), ORDERBOOK_ENV.usdc.toBuffer()],
+      program.programId
     );
     const beforeTokenAccount = await serumCmn.getTokenAccount(
       program.provider,

+ 64 - 56
examples/cfo/tests/utils/index.js

@@ -260,39 +260,43 @@ async function setupMarket({
   );
   for (let k = 0; k < asks.length; k += 1) {
     let ask = asks[k];
-    const { transaction, signers } =
-      await MARKET_A_USDC.makePlaceOrderTransaction(provider.connection, {
-        owner: marketMaker.account,
-        payer: marketMaker.baseToken,
-        side: "sell",
-        price: ask[0],
-        size: ask[1],
-        orderType: "postOnly",
-        clientId: undefined,
-        openOrdersAddressKey: undefined,
-        openOrdersAccount: undefined,
-        feeDiscountPubkey: null,
-        selfTradeBehavior: "abortTransaction",
-      });
+    const {
+      transaction,
+      signers,
+    } = await MARKET_A_USDC.makePlaceOrderTransaction(provider.connection, {
+      owner: marketMaker.account,
+      payer: marketMaker.baseToken,
+      side: "sell",
+      price: ask[0],
+      size: ask[1],
+      orderType: "postOnly",
+      clientId: undefined,
+      openOrdersAddressKey: undefined,
+      openOrdersAccount: undefined,
+      feeDiscountPubkey: null,
+      selfTradeBehavior: "abortTransaction",
+    });
     await provider.send(transaction, signers.concat(marketMaker.account));
   }
 
   for (let k = 0; k < bids.length; k += 1) {
     let bid = bids[k];
-    const { transaction, signers } =
-      await MARKET_A_USDC.makePlaceOrderTransaction(provider.connection, {
-        owner: marketMaker.account,
-        payer: marketMaker.quoteToken,
-        side: "buy",
-        price: bid[0],
-        size: bid[1],
-        orderType: "postOnly",
-        clientId: undefined,
-        openOrdersAddressKey: undefined,
-        openOrdersAccount: undefined,
-        feeDiscountPubkey: null,
-        selfTradeBehavior: "abortTransaction",
-      });
+    const {
+      transaction,
+      signers,
+    } = await MARKET_A_USDC.makePlaceOrderTransaction(provider.connection, {
+      owner: marketMaker.account,
+      payer: marketMaker.quoteToken,
+      side: "buy",
+      price: bid[0],
+      size: bid[1],
+      orderType: "postOnly",
+      clientId: undefined,
+      openOrdersAddressKey: undefined,
+      openOrdersAccount: undefined,
+      feeDiscountPubkey: null,
+      selfTradeBehavior: "abortTransaction",
+    });
     await provider.send(transaction, signers.concat(marketMaker.account));
   }
 
@@ -566,38 +570,42 @@ async function runTradeBot(market, provider, iterations = undefined) {
     }
 
     // Post ask.
-    const { transaction: tx_ask, signers: sigs_ask } =
-      await marketClient.makePlaceOrderTransaction(provider.connection, {
-        owner: maker,
-        payer: baseToken,
-        side: "sell",
-        price,
-        size,
-        orderType: "postOnly",
-        clientId,
-        openOrdersAddressKey: undefined,
-        openOrdersAccount: undefined,
-        feeDiscountPubkey: null,
-        selfTradeBehavior: "abortTransaction",
-      });
+    const {
+      transaction: tx_ask,
+      signers: sigs_ask,
+    } = await marketClient.makePlaceOrderTransaction(provider.connection, {
+      owner: maker,
+      payer: baseToken,
+      side: "sell",
+      price,
+      size,
+      orderType: "postOnly",
+      clientId,
+      openOrdersAddressKey: undefined,
+      openOrdersAccount: undefined,
+      feeDiscountPubkey: null,
+      selfTradeBehavior: "abortTransaction",
+    });
     let txSig = await provider.send(tx_ask, sigs_ask.concat(maker));
     console.log("Ask", txSig);
 
     // Take.
-    const { transaction: tx_bid, signers: sigs_bid } =
-      await marketClient.makePlaceOrderTransaction(provider.connection, {
-        owner: taker,
-        payer: quoteToken,
-        side: "buy",
-        price,
-        size,
-        orderType: "ioc",
-        clientId: undefined,
-        openOrdersAddressKey: undefined,
-        openOrdersAccount: undefined,
-        feeDiscountPubkey: null,
-        selfTradeBehavior: "abortTransaction",
-      });
+    const {
+      transaction: tx_bid,
+      signers: sigs_bid,
+    } = await marketClient.makePlaceOrderTransaction(provider.connection, {
+      owner: taker,
+      payer: quoteToken,
+      side: "buy",
+      price,
+      size,
+      orderType: "ioc",
+      clientId: undefined,
+      openOrdersAddressKey: undefined,
+      openOrdersAccount: undefined,
+      feeDiscountPubkey: null,
+      selfTradeBehavior: "abortTransaction",
+    });
     txSig = await provider.send(tx_bid, sigs_bid.concat(taker));
     console.log("Bid", txSig);
 

+ 17 - 14
examples/cfo/tests/utils/stake.js

@@ -32,11 +32,13 @@ const WHITELIST_SIZE = 10;
 
 async function setupStakePool(mint, god) {
   // Registry genesis.
-  const [_registrarSigner, _nonce] =
-    await anchor.web3.PublicKey.findProgramAddress(
-      [registrar.publicKey.toBuffer()],
-      registry.programId
-    );
+  const [
+    _registrarSigner,
+    _nonce,
+  ] = await anchor.web3.PublicKey.findProgramAddress(
+    [registrar.publicKey.toBuffer()],
+    registry.programId
+  );
   registrarSigner = _registrarSigner;
   nonce = _nonce;
   poolMint = await serumCmn.createMint(provider, registrarSigner);
@@ -94,11 +96,13 @@ async function setupStakePool(mint, god) {
     seed,
     registry.programId
   );
-  const [_memberSigner, nonce2] =
-    await anchor.web3.PublicKey.findProgramAddress(
-      [registrar.publicKey.toBuffer(), member.toBuffer()],
-      registry.programId
-    );
+  const [
+    _memberSigner,
+    nonce2,
+  ] = await anchor.web3.PublicKey.findProgramAddress(
+    [registrar.publicKey.toBuffer(), member.toBuffer()],
+    registry.programId
+  );
   memberSigner = _memberSigner;
   const [mainTx, _balances] = await utils.createBalanceSandbox(
     provider,
@@ -129,10 +133,9 @@ async function setupStakePool(mint, god) {
         newAccountPubkey: member,
         basePubkey: registry.provider.wallet.publicKey,
         seed,
-        lamports:
-          await registry.provider.connection.getMinimumBalanceForRentExemption(
-            registry.account.member.size
-          ),
+        lamports: await registry.provider.connection.getMinimumBalanceForRentExemption(
+          registry.account.member.size
+        ),
         space: registry.account.member.size,
         programId: registry.programId,
       }),

+ 16 - 4
examples/chat/programs/chat/src/lib.rs

@@ -6,9 +6,10 @@ use anchor_lang::prelude::*;
 pub mod chat {
     use super::*;
 
-    pub fn create_user(ctx: Context<CreateUser>, name: String) -> Result<()> {
+    pub fn create_user(ctx: Context<CreateUser>, name: String, bump: u8) -> Result<()> {
         ctx.accounts.user.name = name;
         ctx.accounts.user.authority = *ctx.accounts.authority.key;
+        ctx.accounts.user.bump = bump;
         Ok(())
     }
     pub fn create_chat_room(ctx: Context<CreateChatRoom>, name: String) -> Result<()> {
@@ -35,8 +36,15 @@ pub mod chat {
 }
 
 #[derive(Accounts)]
+#[instruction(name: String, bump: u8)]
 pub struct CreateUser<'info> {
-    #[account(init, associated = authority, space = 312)]
+    #[account(
+        init,
+        seeds = [authority.key().as_ref()],
+        bump = bump,
+        payer = authority,
+        space = 320,
+    )]
     user: ProgramAccount<'info, User>,
     #[account(signer)]
     authority: AccountInfo<'info>,
@@ -51,7 +59,10 @@ pub struct CreateChatRoom<'info> {
 
 #[derive(Accounts)]
 pub struct SendMessage<'info> {
-    #[account(associated = authority, has_one = authority)]
+    #[account(
+        seeds = [authority.key().as_ref(), &[user.bump]],
+        has_one = authority,
+    )]
     user: ProgramAccount<'info, User>,
     #[account(signer)]
     authority: AccountInfo<'info>,
@@ -59,10 +70,11 @@ pub struct SendMessage<'info> {
     chat_room: Loader<'info, ChatRoom>,
 }
 
-#[associated]
+#[account]
 pub struct User {
     name: String,
     authority: Pubkey,
+    bump: u8,
 }
 
 #[account(zero_copy)]

+ 14 - 7
examples/chat/tests/chat.js

@@ -1,5 +1,6 @@
 const anchor = require("@project-serum/anchor");
 const assert = require("assert");
+const { PublicKey } = anchor.web3;
 
 describe("chat", () => {
   // Configure the client to use the local cluster.
@@ -12,8 +13,6 @@ describe("chat", () => {
   const chatRoom = anchor.web3.Keypair.generate();
 
   it("Creates a chat room", async () => {
-    // Add your test here.
-
     await program.rpc.createChatRoom("Test Chat", {
       accounts: {
         chatRoom: chatRoom.publicKey,
@@ -35,22 +34,30 @@ describe("chat", () => {
 
   it("Creates a user", async () => {
     const authority = program.provider.wallet.publicKey;
-    await program.rpc.createUser("My User", {
+    const [user, bump] = await PublicKey.findProgramAddress(
+      [authority.toBuffer()],
+      program.programId
+    );
+    await program.rpc.createUser("My User", bump, {
       accounts: {
-        user: await program.account.user.associatedAddress(authority),
+        user,
         authority,
-        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
         systemProgram: anchor.web3.SystemProgram.programId,
       },
     });
-    const account = await program.account.user.associated(authority);
+    const account = await program.account.user.fetch(user);
     assert.ok(account.name === "My User");
     assert.ok(account.authority.equals(authority));
   });
 
   it("Sends messages", async () => {
     const authority = program.provider.wallet.publicKey;
-    const user = await program.account.user.associatedAddress(authority);
+    const user = (
+      await PublicKey.findProgramAddress(
+        [authority.toBuffer()],
+        program.programId
+      )
+    )[0];
 
     // Only send a couple messages so the test doesn't take an eternity.
     const numMessages = 10;

+ 0 - 6
examples/misc/programs/misc/src/account.rs

@@ -1,11 +1,5 @@
 use anchor_lang::prelude::*;
 
-#[associated]
-#[derive(Default)]
-pub struct TestData {
-    pub data: u64,
-}
-
 #[account]
 pub struct Data {
     pub udata: u128,

+ 0 - 38
examples/misc/programs/misc/src/context.rs

@@ -109,35 +109,6 @@ pub struct TestClose<'info> {
     sol_dest: AccountInfo<'info>,
 }
 
-// `my_account` is the associated token account being created.
-// `authority` must be a `mut` and `signer` since it will pay for the creation
-// of the associated token account. `state` is used as an association, i.e., one
-// can *optionally* identify targets to be used as seeds for the program
-// derived address by using `with` (and it doesn't have to be a state account).
-// For example, the SPL token program uses a `Mint` account. Lastly,
-// `system_program` are *required* by convention, since the account is needed
-// when creating the associated program address within the program.
-#[derive(Accounts)]
-pub struct TestInitAssociatedAccount<'info> {
-    #[account(init, associated = authority, with = state, with = data, with = b"my-seed")]
-    pub my_account: ProgramAccount<'info, TestData>,
-    #[account(mut, signer)]
-    pub authority: AccountInfo<'info>,
-    pub state: ProgramState<'info, MyState>,
-    pub data: ProgramAccount<'info, Data>,
-    pub system_program: AccountInfo<'info>,
-}
-
-#[derive(Accounts)]
-pub struct TestAssociatedAccount<'info> {
-    #[account(mut, associated = authority, with = state, with = data, with = b"my-seed")]
-    pub my_account: ProgramAccount<'info, TestData>,
-    #[account(mut, signer)]
-    pub authority: AccountInfo<'info>,
-    pub state: ProgramState<'info, MyState>,
-    pub data: ProgramAccount<'info, Data>,
-}
-
 #[derive(Accounts)]
 pub struct TestU16<'info> {
     #[account(init)]
@@ -153,15 +124,6 @@ pub struct TestI16<'info> {
 #[derive(Accounts)]
 pub struct TestSimulate {}
 
-#[derive(Accounts)]
-pub struct TestSimulateAssociatedAccount<'info> {
-    #[account(init, associated = authority)]
-    pub my_account: ProgramAccount<'info, TestData>,
-    #[account(mut, signer)]
-    pub authority: AccountInfo<'info>,
-    pub system_program: AccountInfo<'info>,
-}
-
 #[derive(Accounts)]
 pub struct TestI8<'info> {
     #[account(init)]

+ 0 - 30
examples/misc/programs/misc/src/lib.rs

@@ -57,22 +57,6 @@ pub mod misc {
         misc2::cpi::state::set_data(ctx, data)
     }
 
-    pub fn test_init_associated_account(
-        ctx: Context<TestInitAssociatedAccount>,
-        data: u64,
-    ) -> ProgramResult {
-        ctx.accounts.my_account.data = data;
-        Ok(())
-    }
-
-    pub fn test_associated_account(
-        ctx: Context<TestAssociatedAccount>,
-        data: u64,
-    ) -> ProgramResult {
-        ctx.accounts.my_account.data = data;
-        Ok(())
-    }
-
     pub fn test_u16(ctx: Context<TestU16>, data: u16) -> ProgramResult {
         ctx.accounts.my_account.data = data;
         Ok(())
@@ -85,20 +69,6 @@ pub mod misc {
         Ok(())
     }
 
-    pub fn test_simulate_associated_account(
-        ctx: Context<TestSimulateAssociatedAccount>,
-        data: u32,
-    ) -> ProgramResult {
-        let associated_account = *ctx.accounts.my_account.to_account_info().key;
-        emit!(E1 { data });
-        emit!(E2 { data: 1234 });
-        emit!(E3 { data: 9 });
-        emit!(E4 {
-            data: associated_account
-        });
-        Ok(())
-    }
-
     pub fn test_i8(ctx: Context<TestI8>, data: i8) -> ProgramResult {
         ctx.accounts.data.data = data;
         Ok(())

+ 0 - 132
examples/misc/tests/misc.js

@@ -135,89 +135,6 @@ describe("misc", () => {
     assert.ok(stateAccount.auth.equals(program.provider.wallet.publicKey));
   });
 
-  it("Can init an associated program account", async () => {
-    const state = await program.state.address();
-
-    // Manual associated address calculation for test only. Clients should use
-    // the generated methods.
-    const [
-      associatedAccount,
-      nonce,
-    ] = await anchor.web3.PublicKey.findProgramAddress(
-      [
-        anchor.utils.bytes.utf8.encode("anchor"),
-        program.provider.wallet.publicKey.toBuffer(),
-        state.toBuffer(),
-        data.publicKey.toBuffer(),
-        anchor.utils.bytes.utf8.encode("my-seed"),
-      ],
-      program.programId
-    );
-    await assert.rejects(
-      async () => {
-        await program.account.testData.fetch(associatedAccount);
-      },
-      (err) => {
-        assert.ok(
-          err.toString() ===
-            `Error: Account does not exist ${associatedAccount.toString()}`
-        );
-        return true;
-      }
-    );
-    await program.rpc.testInitAssociatedAccount(new anchor.BN(1234), {
-      accounts: {
-        myAccount: associatedAccount,
-        authority: program.provider.wallet.publicKey,
-        state,
-        data: data.publicKey,
-        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
-        systemProgram: anchor.web3.SystemProgram.programId,
-      },
-    });
-    // Try out the generated associated method.
-    const account = await program.account.testData.associated(
-      program.provider.wallet.publicKey,
-      state,
-      data.publicKey,
-      anchor.utils.bytes.utf8.encode("my-seed")
-    );
-    assert.ok(account.data.toNumber() === 1234);
-  });
-
-  it("Can use an associated program account", async () => {
-    const state = await program.state.address();
-    const [
-      associatedAccount,
-      nonce,
-    ] = await anchor.web3.PublicKey.findProgramAddress(
-      [
-        anchor.utils.bytes.utf8.encode("anchor"),
-        program.provider.wallet.publicKey.toBuffer(),
-        state.toBuffer(),
-        data.publicKey.toBuffer(),
-        anchor.utils.bytes.utf8.encode("my-seed"),
-      ],
-      program.programId
-    );
-    await program.rpc.testAssociatedAccount(new anchor.BN(5), {
-      accounts: {
-        myAccount: associatedAccount,
-        authority: program.provider.wallet.publicKey,
-        state,
-        data: data.publicKey,
-      },
-    });
-    // Try out the generated associated method.
-    const account = await program.account.testData.associated(
-      program.provider.wallet.publicKey,
-      state,
-      data.publicKey,
-      anchor.utils.bytes.utf8.encode("my-seed")
-    );
-    assert.ok(account.data.toNumber() === 5);
-  });
-
   it("Can retrieve events when simulating a transaction", async () => {
     const resp = await program.simulate.testSimulate(44);
     const expectedRaw = [
@@ -238,55 +155,6 @@ describe("misc", () => {
     assert.ok(resp.events[2].data.data === 9);
   });
 
-  it("Can retrieve events when associated account is initialized in simulated transaction", async () => {
-    const myAccount = await program.account.testData.associatedAddress(
-      program.provider.wallet.publicKey
-    );
-    await assert.rejects(
-      async () => {
-        await program.account.testData.fetch(myAccount);
-      },
-      (err) => {
-        assert.ok(
-          err.toString() ===
-            `Error: Account does not exist ${myAccount.toString()}`
-        );
-        return true;
-      }
-    );
-
-    const resp = await program.simulate.testSimulateAssociatedAccount(44, {
-      accounts: {
-        myAccount,
-        authority: program.provider.wallet.publicKey,
-        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
-        systemProgram: anchor.web3.SystemProgram.programId,
-      },
-    });
-
-    const expectedRaw = [
-      "Program Fv6oRfzWETatiMymBvTs1JpRspZz3DbBfjZJEvUTDL1g invoke [1]",
-      "Program 11111111111111111111111111111111 invoke [2]",
-      "Program 11111111111111111111111111111111 success",
-      "Program log: NgyCA9omwbMsAAAA",
-      "Program log: fPhuIELK/k7SBAAA",
-      "Program log: jvbowsvlmkcJAAAA",
-      "Program log: mg+zq/K0sXRV+N/AsG9XLERDZ+J6eQAnnzoQVHlicBQBnGr65KE5Kw==",
-      "Program Fv6oRfzWETatiMymBvTs1JpRspZz3DbBfjZJEvUTDL1g consumed 20460 of 200000 compute units",
-      "Program Fv6oRfzWETatiMymBvTs1JpRspZz3DbBfjZJEvUTDL1g success",
-    ];
-
-    assert.ok(JSON.stringify(expectedRaw), resp.raw);
-    assert.ok(resp.events[0].name === "E1");
-    assert.ok(resp.events[0].data.data === 44);
-    assert.ok(resp.events[1].name === "E2");
-    assert.ok(resp.events[1].data.data === 1234);
-    assert.ok(resp.events[2].name === "E3");
-    assert.ok(resp.events[2].data.data === 9);
-    assert.ok(resp.events[3].name === "E4");
-    assert.ok(resp.events[3].data.data.toBase58() === myAccount.toBase58());
-  });
-
   it("Can use i8 in the idl", async () => {
     const data = anchor.web3.Keypair.generate();
     await program.rpc.testI8(-3, {

+ 0 - 9
examples/tutorial/basic-5/.gitignore

@@ -1,9 +0,0 @@
-# ignore Mac OS noise
-.DS_Store
-
-# ignore the build directory for Rust/Anchor
-target
-
-# Ignore backup files creates by cargo fmt.
-**/*.rs.bk
-    

+ 0 - 6
examples/tutorial/basic-5/Anchor.toml

@@ -1,6 +0,0 @@
-[provider]
-cluster = "localnet"
-wallet = "~/.config/solana/id.json"
-
-[scripts]
-test = "mocha -t 1000000 tests/"

+ 0 - 4
examples/tutorial/basic-5/Cargo.toml

@@ -1,4 +0,0 @@
-[workspace]
-members = [
-    "programs/*"
-]

+ 0 - 13
examples/tutorial/basic-5/migrations/deploy.js

@@ -1,13 +0,0 @@
-
-// Migrations are an early feature. Currently, they're nothing more than this
-// single deploy script that's invoked from the CLI, injecting a provider
-// configured from the workspace's Anchor.toml.
-
-const anchor = require("@project-serum/anchor");
-
-module.exports = async function (provider) {
-  // Configure client to use the provider.
-  anchor.setProvider(provider);
-
-  // Add your deploy script here.
-}

+ 0 - 18
examples/tutorial/basic-5/programs/basic-5/Cargo.toml

@@ -1,18 +0,0 @@
-[package]
-name = "basic-5"
-version = "0.1.0"
-description = "Created with Anchor"
-edition = "2018"
-
-[lib]
-crate-type = ["cdylib", "lib"]
-name = "basic_5"
-
-[features]
-no-entrypoint = []
-no-idl = []
-cpi = ["no-entrypoint"]
-default = []
-
-[dependencies]
-anchor-lang = { path = "../../../../../lang" }

+ 0 - 2
examples/tutorial/basic-5/programs/basic-5/Xargo.toml

@@ -1,2 +0,0 @@
-[target.bpfel-unknown-unknown.dependencies.std]
-features = []

+ 0 - 56
examples/tutorial/basic-5/programs/basic-5/src/lib.rs

@@ -1,56 +0,0 @@
-//! This example displays the use of associated program accounts.
-//!
-//! This program is an *extremely* simplified version of the SPL token program
-//! that does nothing other than create mint and token accounts, where all token
-//! accounts are associated accounts.
-
-// #region code
-use anchor_lang::prelude::*;
-
-#[program]
-pub mod basic_5 {
-    use super::*;
-
-    pub fn create_mint(ctx: Context<CreateMint>) -> ProgramResult {
-        ctx.accounts.mint.supply = 0;
-        Ok(())
-    }
-
-    pub fn create_token(ctx: Context<CreateToken>) -> ProgramResult {
-        let token = &mut ctx.accounts.token;
-        token.amount = 0;
-        token.authority = *ctx.accounts.authority.key;
-        token.mint = *ctx.accounts.mint.to_account_info().key;
-        Ok(())
-    }
-}
-
-#[derive(Accounts)]
-pub struct CreateMint<'info> {
-    #[account(init)]
-    mint: ProgramAccount<'info, Mint>,
-}
-
-#[derive(Accounts)]
-pub struct CreateToken<'info> {
-    #[account(init, associated = authority, with = mint)]
-    token: ProgramAccount<'info, Token>,
-    #[account(mut, signer)]
-    authority: AccountInfo<'info>,
-    mint: ProgramAccount<'info, Mint>,
-    system_program: AccountInfo<'info>,
-}
-
-#[account]
-pub struct Mint {
-    pub supply: u32,
-}
-
-#[associated]
-#[derive(Default)]
-pub struct Token {
-    pub amount: u32,
-    pub authority: Pubkey,
-    pub mint: Pubkey,
-}
-// #endregion code

+ 0 - 57
examples/tutorial/basic-5/tests/basic-5.js

@@ -1,57 +0,0 @@
-const anchor = require("@project-serum/anchor");
-const assert = require("assert");
-
-describe("basic-5", () => {
-  // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
-
-  const program = anchor.workspace.Basic5;
-
-  const mint = anchor.web3.Keypair.generate();
-
-  // Setup. Not important for the point of the example.
-  it("Sets up the test", async () => {
-    // Create the mint account.
-    await program.rpc.createMint({
-      accounts: {
-        mint: mint.publicKey,
-        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
-      },
-      instructions: [await program.account.mint.createInstruction(mint)],
-      signers: [mint],
-    });
-  });
-
-  it("Creates an associated token account", async () => {
-    // #region test
-    // Calculate the associated token address.
-    const authority = program.provider.wallet.publicKey;
-    const associatedToken = await program.account.token.associatedAddress(
-      authority,
-      mint.publicKey
-    );
-
-    // Execute the transaction to create the associated token account.
-    await program.rpc.createToken({
-      accounts: {
-        token: associatedToken,
-        authority,
-        mint: mint.publicKey,
-        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
-        systemProgram: anchor.web3.SystemProgram.programId,
-      },
-    });
-
-    // Fetch the new associated account.
-    const account = await program.account.token.associated(
-      authority,
-      mint.publicKey
-    );
-    // #endregion test
-
-    // Check it was created correctly.
-    assert.ok(account.amount === 0);
-    assert.ok(account.authority.equals(authority));
-    assert.ok(account.mint.equals(mint.publicKey));
-  });
-});

+ 17 - 4
examples/zero-copy/programs/zero-copy/src/lib.rs

@@ -125,7 +125,10 @@ pub struct UpdateFoo<'info> {
 
 #[derive(Accounts)]
 pub struct UpdateFooSecond<'info> {
-    #[account(mut, "&foo.load()?.get_second_authority() == second_authority.key")]
+    #[account(
+        mut,
+        constraint = &foo.load()?.get_second_authority() == second_authority.key,
+    )]
     foo: Loader<'info, Foo>,
     #[account(signer)]
     second_authority: AccountInfo<'info>,
@@ -133,7 +136,12 @@ pub struct UpdateFooSecond<'info> {
 
 #[derive(Accounts)]
 pub struct CreateBar<'info> {
-    #[account(init, associated = authority, with = foo)]
+    #[account(
+        init,
+        seeds = [authority.key().as_ref(), foo.key().as_ref()],
+        bump,
+        payer = authority,
+    )]
     bar: Loader<'info, Bar>,
     #[account(signer)]
     authority: AccountInfo<'info>,
@@ -143,7 +151,12 @@ pub struct CreateBar<'info> {
 
 #[derive(Accounts)]
 pub struct UpdateBar<'info> {
-    #[account(mut, associated = authority, with = foo, has_one = authority)]
+    #[account(
+        mut,
+        has_one = authority,
+        seeds = [authority.key().as_ref(), foo.key().as_ref()],
+        bump,
+    )]
     bar: Loader<'info, Bar>,
     #[account(signer)]
     authority: AccountInfo<'info>,
@@ -173,7 +186,7 @@ pub struct Foo {
     pub second_authority: [u8; 32],
 }
 
-#[associated(zero_copy)]
+#[account(zero_copy)]
 #[derive(Default)]
 pub struct Bar {
     pub authority: Pubkey,

+ 34 - 21
examples/zero-copy/tests/zero-copy.js

@@ -120,42 +120,55 @@ describe("zero-copy", () => {
   it("Creates an associated zero copy account", async () => {
     await program.rpc.createBar({
       accounts: {
-        bar: await program.account.bar.associatedAddress(
-          program.provider.wallet.publicKey,
-          foo.publicKey
-        ),
+        bar: (
+          await PublicKey.findProgramAddress(
+            [
+              program.provider.wallet.publicKey.toBuffer(),
+              foo.publicKey.toBuffer(),
+            ],
+            program.programId
+          )
+        )[0],
         authority: program.provider.wallet.publicKey,
         foo: foo.publicKey,
-        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
         systemProgram: anchor.web3.SystemProgram.programId,
       },
     });
 
-    const bar = await program.account.bar.associated(
-      program.provider.wallet.publicKey,
-      foo.publicKey
-    );
-    assert.ok(bar.authority.equals(program.provider.wallet.publicKey));
-    assert.ok(bar.data.toNumber() === 0);
+    const bar = (
+      await PublicKey.findProgramAddress(
+        [
+          program.provider.wallet.publicKey.toBuffer(),
+          foo.publicKey.toBuffer(),
+        ],
+        program.programId
+      )
+    )[0];
+    const barAccount = await program.account.bar.fetch(bar);
+    assert.ok(barAccount.authority.equals(program.provider.wallet.publicKey));
+    assert.ok(barAccount.data.toNumber() === 0);
   });
 
   it("Updates an associated zero copy account", async () => {
+    const bar = (
+      await PublicKey.findProgramAddress(
+        [
+          program.provider.wallet.publicKey.toBuffer(),
+          foo.publicKey.toBuffer(),
+        ],
+        program.programId
+      )
+    )[0];
     await program.rpc.updateBar(new BN(99), {
       accounts: {
-        bar: await program.account.bar.associatedAddress(
-          program.provider.wallet.publicKey,
-          foo.publicKey
-        ),
+        bar,
         authority: program.provider.wallet.publicKey,
         foo: foo.publicKey,
       },
     });
-    const bar = await program.account.bar.associated(
-      program.provider.wallet.publicKey,
-      foo.publicKey
-    );
-    assert.ok(bar.authority.equals(program.provider.wallet.publicKey));
-    assert.ok(bar.data.toNumber() === 99);
+    const barAccount = await program.account.bar.fetch(bar);
+    assert.ok(barAccount.authority.equals(program.provider.wallet.publicKey));
+    assert.ok(barAccount.data.toNumber() === 99);
   });
 
   const eventQ = anchor.web3.Keypair.generate();

+ 1 - 73
lang/attribute/account/src/lib.rs

@@ -1,7 +1,7 @@
 extern crate proc_macro;
 
 use quote::quote;
-use syn::{parse_macro_input, parse_quote};
+use syn::parse_macro_input;
 
 /// A data structure representing a Solana account, implementing various traits:
 ///
@@ -192,78 +192,6 @@ pub fn account(
     })
 }
 
-/// Extends the `#[account]` attribute to allow one to create associated
-/// accounts. This includes a `Default` implementation, which means all fields
-/// in an `#[associated]` struct must implement `Default` and an
-/// `anchor_lang::Bump` trait implementation, which allows the account to be
-/// used as a program derived address.
-///
-/// # Zero Copy Deserialization
-///
-/// Similar to the `#[account]` attribute one can enable zero copy
-/// deserialization by using the `zero_copy` argument:
-///
-/// ```ignore
-/// #[associated(zero_copy)]
-/// ```
-///
-/// For more, see the [`account`](./attr.account.html) attribute.
-#[proc_macro_attribute]
-pub fn associated(
-    args: proc_macro::TokenStream,
-    input: proc_macro::TokenStream,
-) -> proc_macro::TokenStream {
-    let mut account_strct = parse_macro_input!(input as syn::ItemStruct);
-    let account_name = &account_strct.ident;
-    let (impl_gen, ty_gen, where_clause) = account_strct.generics.split_for_impl();
-
-    // Add a `__nonce: u8` field to the struct to hold the bump seed for
-    // the program dervied address.
-    match &mut account_strct.fields {
-        syn::Fields::Named(fields) => {
-            let mut segments = syn::punctuated::Punctuated::new();
-            segments.push(syn::PathSegment {
-                ident: syn::Ident::new("u8", proc_macro2::Span::call_site()),
-                arguments: syn::PathArguments::None,
-            });
-            fields.named.push(syn::Field {
-                attrs: Vec::new(),
-                vis: syn::Visibility::Restricted(syn::VisRestricted {
-                    pub_token: syn::token::Pub::default(),
-                    paren_token: syn::token::Paren::default(),
-                    in_token: None,
-                    path: Box::new(parse_quote!(crate)),
-                }),
-                ident: Some(syn::Ident::new("__nonce", proc_macro2::Span::call_site())),
-                colon_token: Some(syn::token::Colon {
-                    spans: [proc_macro2::Span::call_site()],
-                }),
-                ty: syn::Type::Path(syn::TypePath {
-                    qself: None,
-                    path: syn::Path {
-                        leading_colon: None,
-                        segments,
-                    },
-                }),
-            });
-        }
-        _ => panic!("Fields must be named"),
-    }
-
-    let args: proc_macro2::TokenStream = args.into();
-    proc_macro::TokenStream::from(quote! {
-        #[anchor_lang::account(#args)]
-        #account_strct
-
-        #[automatically_derived]
-        impl #impl_gen anchor_lang::Bump for #account_name #ty_gen #where_clause {
-            fn seed(&self) -> u8 {
-                self.__nonce
-            }
-        }
-    })
-}
-
 #[proc_macro_derive(ZeroCopyAccessor, attributes(accessor))]
 pub fn derive_zero_copy_accessor(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
     let account_strct = parse_macro_input!(item as syn::ItemStruct);

+ 5 - 26
lang/src/lib.rs

@@ -56,7 +56,7 @@ pub use crate::program_account::ProgramAccount;
 pub use crate::state::ProgramState;
 pub use crate::sysvar::Sysvar;
 pub use anchor_attribute_access_control::access_control;
-pub use anchor_attribute_account::{account, associated, zero_copy};
+pub use anchor_attribute_account::{account, zero_copy};
 pub use anchor_attribute_error::error;
 pub use anchor_attribute_event::{emit, event};
 pub use anchor_attribute_interface::interface;
@@ -233,10 +233,10 @@ impl Key for Pubkey {
 /// All programs should include it via `anchor_lang::prelude::*;`.
 pub mod prelude {
     pub use super::{
-        access_control, account, associated, emit, error, event, interface, program, require,
-        state, zero_copy, AccountDeserialize, AccountSerialize, Accounts, AccountsExit,
-        AccountsInit, AnchorDeserialize, AnchorSerialize, Context, CpiAccount, CpiContext,
-        CpiState, CpiStateContext, Loader, ProgramAccount, ProgramState, Sysvar, ToAccountInfo,
+        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,
     };
 
@@ -307,27 +307,6 @@ pub mod __private {
     pub const CLOSED_ACCOUNT_DISCRIMINATOR: [u8; 8] = [255, 255, 255, 255, 255, 255, 255, 255];
 }
 
-/// Returns the program-derived-address seeds used for creating the associated
-/// account.
-#[macro_export]
-macro_rules! associated_seeds {
-    (account = $pda:expr, associated = $associated:expr) => {
-        &[
-            b"anchor".as_ref(),
-            $associated.to_account_info().key.as_ref(),
-            &[anchor_lang::Bump::seed(&*$pda)],
-        ]
-    };
-    (account = $pda:expr, associated = $associated:expr, $(with = $with:expr),+) => {
-        &[
-            b"anchor".as_ref(),
-            $associated.to_account_info().key.as_ref(),
-            $($with.to_account_info().key.as_ref()),+,
-            &[anchor_lang::Bump::seed(&*$pda)][..],
-        ]
-    };
-}
-
 /// Ensures a condition is true, otherwise returns the given error.
 /// Use this with a custom error type.
 ///

+ 52 - 187
lang/syn/src/codegen/accounts/constraints.rs

@@ -1,8 +1,8 @@
 use crate::{
-    CompositeField, Constraint, ConstraintAddress, ConstraintAssociatedGroup, ConstraintClose,
-    ConstraintExecutable, ConstraintGroup, ConstraintHasOne, ConstraintInit, ConstraintLiteral,
-    ConstraintMut, ConstraintOwner, ConstraintRaw, ConstraintRentExempt, ConstraintSeedsGroup,
-    ConstraintSigner, ConstraintState, Field, PdaKind, Ty,
+    CompositeField, Constraint, ConstraintAddress, ConstraintClose, ConstraintExecutable,
+    ConstraintGroup, ConstraintHasOne, ConstraintInit, ConstraintLiteral, ConstraintMut,
+    ConstraintOwner, ConstraintRaw, ConstraintRentExempt, ConstraintSeedsGroup, ConstraintSigner,
+    ConstraintState, Field, PdaKind, Ty,
 };
 use proc_macro2_diagnostics::SpanDiagnosticExt;
 use quote::quote;
@@ -45,9 +45,6 @@ pub fn generate_composite(f: &CompositeField) -> proc_macro2::TokenStream {
 
 // Linearizes the constraint group so that constraints with dependencies
 // run after those without.
-//
-// The associated cosntraint should always be first since it may also create
-// an account with a PDA.
 pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
     let ConstraintGroup {
         init,
@@ -61,16 +58,12 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
         seeds,
         executable,
         state,
-        associated,
         close,
         address,
     } = c_group.clone();
 
     let mut constraints = Vec::new();
 
-    if let Some(c) = associated {
-        constraints.push(Constraint::AssociatedGroup(c));
-    }
     if let Some(c) = seeds {
         constraints.push(Constraint::Seeds(c));
     }
@@ -120,7 +113,6 @@ fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream {
         Constraint::Seeds(c) => generate_constraint_seeds(f, c),
         Constraint::Executable(c) => generate_constraint_executable(f, c),
         Constraint::State(c) => generate_constraint_state(f, c),
-        Constraint::AssociatedGroup(c) => generate_constraint_associated(f, c),
         Constraint::Close(c) => generate_constraint_close(f, c),
         Constraint::Address(c) => generate_constraint_address(f, c),
     }
@@ -276,11 +268,30 @@ fn generate_constraint_seeds_init(f: &Field, c: &ConstraintSeedsGroup) -> proc_m
     let seeds_with_nonce = {
         let s = &c.seeds;
         match c.bump.as_ref() {
+            // Bump keyword not given. Just use the seeds.
             None => quote! {
                 [#s]
             },
-            Some(b) => quote! {
-                [#s, &[#b]]
+            // Bump keyword given.
+            Some(bump) => match bump {
+                // Bump target not given. Use the canonical bump.
+                None => {
+                    quote! {
+                        [
+                            #s,
+                            &[
+                                Pubkey::find_program_address(
+                                    &[#s],
+                                    program_id,
+                                ).1
+                            ]
+                        ]
+                    }
+                }
+                // Bump target given. Use it.
+                Some(b) => quote! {
+                    [#s, &[#b]]
+                },
             },
         }
     };
@@ -290,7 +301,6 @@ fn generate_constraint_seeds_init(f: &Field, c: &ConstraintSeedsGroup) -> proc_m
         seeds_with_nonce,
         payer,
         &c.space,
-        false,
         &c.kind,
     )
 }
@@ -300,12 +310,12 @@ fn generate_constraint_seeds_address(
     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() {
-        let s = &c.seeds;
-        let b = c.bump.as_ref().unwrap();
+    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();
         quote! {
             let (__program_signer, __bump) = anchor_lang::solana_program::pubkey::Pubkey::find_program_address(
                 &[#s],
@@ -320,22 +330,39 @@ fn generate_constraint_seeds_address(
         }
     } else {
         let seeds = match c.bump.as_ref() {
+            // Bump keyword not given, so just use the seeds.
             None => {
-                let s = &c.seeds;
                 quote! {
                     [#s]
                 }
             }
-            Some(b) => {
-                let s = &c.seeds;
-                quote! {
-                    [#s, &[#b]]
+            // 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]]
+                    }
+                }
+            },
         };
         quote! {
             let __program_signer = Pubkey::create_program_address(
-                &#seeds,
+                &#seeds[..],
                 program_id,
             ).map_err(|_| anchor_lang::__private::ErrorCode::ConstraintSeeds)?;
             if #name.to_account_info().key != &__program_signer {
@@ -345,64 +372,6 @@ fn generate_constraint_seeds_address(
     }
 }
 
-pub fn generate_constraint_associated(
-    f: &Field,
-    c: &ConstraintAssociatedGroup,
-) -> proc_macro2::TokenStream {
-    if c.is_init {
-        generate_constraint_associated_init(f, c)
-    } else {
-        generate_constraint_associated_seeds(f, c)
-    }
-}
-
-pub fn generate_constraint_associated_init(
-    f: &Field,
-    c: &ConstraintAssociatedGroup,
-) -> proc_macro2::TokenStream {
-    let associated_target = c.associated_target.clone();
-    let payer = match &c.payer {
-        None => quote! {
-            let payer = #associated_target.to_account_info();
-        },
-        Some(p) => quote! {
-            let payer = #p.to_account_info();
-        },
-    };
-    let seeds_constraint = generate_constraint_associated_seeds(f, c);
-    let seeds_with_nonce = {
-        if c.associated_seeds.is_empty() {
-            quote! {
-                [
-                    &b"anchor"[..],
-                    #associated_target.to_account_info().key.as_ref(),
-                    &[nonce],
-                ]
-            }
-        } else {
-            let seeds = to_seeds_tts(&c.associated_seeds);
-            quote! {
-                [
-                    &b"anchor"[..],
-                    #associated_target.to_account_info().key.as_ref(),
-                    #seeds
-                    &[nonce],
-                ]
-            }
-        }
-    };
-
-    generate_pda(
-        f,
-        seeds_constraint,
-        seeds_with_nonce,
-        payer,
-        &c.space,
-        true,
-        &c.kind,
-    )
-}
-
 fn parse_ty(f: &Field) -> (proc_macro2::TokenStream, proc_macro2::TokenStream, bool) {
     match &f.ty {
         Ty::ProgramAccount(ty) => {
@@ -458,28 +427,11 @@ pub fn generate_pda(
     seeds_with_nonce: proc_macro2::TokenStream,
     payer: proc_macro2::TokenStream,
     space: &Option<Expr>,
-    assign_nonce: bool,
     kind: &PdaKind,
 ) -> proc_macro2::TokenStream {
     let field = &f.ident;
     let (account_ty, account_wrapper_ty, is_zero_copy) = parse_ty(f);
 
-    let nonce_assignment = match assign_nonce {
-        false => quote! {},
-        true => match &f.ty {
-            Ty::CpiAccount(_) => quote! {},
-            _ => match is_zero_copy {
-                false => quote! {
-                    pa.__nonce = nonce;
-                },
-                // Zero copy is not deserialized, so the data must be lazy loaded.
-                true => quote! {
-                    pa.load_init()?.__nonce = nonce;
-                },
-            },
-        },
-    };
-
     let (combined_account_ty, try_from) = match f.ty {
         Ty::AccountInfo => (
             quote! {
@@ -647,11 +599,7 @@ pub fn generate_pda(
                         e
                     })?;
 
-                    // For now, we assume all accounts created with the `associated`
-                    // attribute have a `nonce` field in their account.
                     let mut pa: #combined_account_ty = #try_from;
-
-                    #nonce_assignment
                     pa
                 };
             }
@@ -659,62 +607,6 @@ pub fn generate_pda(
     }
 }
 
-pub fn generate_constraint_associated_seeds(
-    f: &Field,
-    c: &ConstraintAssociatedGroup,
-) -> proc_macro2::TokenStream {
-    let field = &f.ident;
-    let associated_target = c.associated_target.clone();
-    let seeds_no_nonce = if c.associated_seeds.is_empty() {
-        quote! {
-            &b"anchor"[..],
-            #associated_target.to_account_info().key.as_ref(),
-        }
-    } else {
-        let seeds = to_seeds_tts(&c.associated_seeds);
-        quote! {
-            &b"anchor"[..],
-            #associated_target.to_account_info().key.as_ref(),
-            #seeds
-        }
-    };
-
-    let is_find_nonce = match &f.ty {
-        Ty::CpiAccount(_) => true,
-        Ty::AccountInfo => true,
-        _ => c.is_init,
-    };
-    let associated_field = if is_find_nonce {
-        quote! {
-            let (__associated_field, nonce) = Pubkey::find_program_address(
-                &[#seeds_no_nonce],
-                program_id,
-            );
-        }
-    } else {
-        let nonce = match &f.ty {
-            Ty::ProgramAccount(_) => quote! { #field.__nonce },
-            Ty::Loader(_) => {
-                // Zero copy is not deserialized, so the data must be lazy loaded.
-                quote! { #field.load()?.__nonce }
-            }
-            _ => panic!("Invalid type for initializing a program derived address"),
-        };
-        quote! {
-            let __associated_field = Pubkey::create_program_address(
-                &[#seeds_no_nonce &[#nonce]],
-                program_id,
-            )?;
-        }
-    };
-    quote! {
-        #associated_field
-        if &__associated_field != #field.to_account_info().key {
-            return Err(anchor_lang::__private::ErrorCode::ConstraintAssociatedInit.into());
-        }
-    }
-}
-
 pub fn generate_constraint_executable(
     f: &Field,
     _c: &ConstraintExecutable,
@@ -745,30 +637,3 @@ pub fn generate_constraint_state(f: &Field, c: &ConstraintState) -> proc_macro2:
         }
     }
 }
-
-// Returns the inner part of the seeds slice as a token stream.
-fn to_seeds_tts(seeds: &[syn::Expr]) -> proc_macro2::TokenStream {
-    assert!(!seeds.is_empty());
-    let seed_0 = &seeds[0];
-    let mut tts = match seed_0 {
-        syn::Expr::Path(_) => quote! {
-            anchor_lang::Key::key(&#seed_0).as_ref(),
-        },
-        _ => quote! {
-            #seed_0,
-        },
-    };
-    for seed in &seeds[1..] {
-        tts = match seed {
-            syn::Expr::Path(_) => quote! {
-                #tts
-                anchor_lang::Key::key(&#seed).as_ref(),
-            },
-            _ => quote! {
-                #tts
-                #seed,
-            },
-        };
-    }
-    tts
-}

+ 11 - 22
lang/syn/src/codegen/accounts/try_accounts.rs

@@ -30,9 +30,6 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
                     }
                 }
                 AccountField::Field(f) => {
-                    // Associated fields are *first* deserialized into
-                    // AccountInfos, and then later deserialized into
-                    // ProgramAccounts in the "constraint check" phase.
                     if is_pda_init(af) {
                         let name = &f.ident;
                         quote!{
@@ -114,22 +111,15 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
     }
 }
 
-// Returns true if the given AccountField has an associated init constraint.
 fn is_pda_init(af: &AccountField) -> bool {
     match af {
         AccountField::CompositeField(_s) => false,
-        AccountField::Field(f) => {
-            f.constraints
-                .associated
-                .as_ref()
-                .map(|f| f.is_init)
-                .unwrap_or(false)
-                || f.constraints
-                    .seeds
-                    .as_ref()
-                    .map(|f| f.is_init)
-                    .unwrap_or(false)
-        }
+        AccountField::Field(f) => f
+            .constraints
+            .seeds
+            .as_ref()
+            .map(|f| f.is_init)
+            .unwrap_or(false),
     }
 }
 
@@ -193,13 +183,12 @@ fn typed_ident(field: &Field) -> TokenStream {
 }
 
 pub fn generate_constraints(accs: &AccountsStruct) -> proc_macro2::TokenStream {
-    // All fields without an `#[account(associated)]` attribute.
-    let non_associated_fields: Vec<&AccountField> =
+    let non_pda_fields: Vec<&AccountField> =
         accs.fields.iter().filter(|af| !is_pda_init(af)).collect();
 
-    // Deserialization for each *associated* field. This must be after
+    // Deserialization for each pda init field. This must be after
     // the inital extraction from the accounts slice and before access_checks.
-    let init_associated_fields: Vec<proc_macro2::TokenStream> = accs
+    let init_pda_fields: Vec<proc_macro2::TokenStream> = accs
         .fields
         .iter()
         .filter_map(|af| match af {
@@ -213,7 +202,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_associated_fields
+    let access_checks: Vec<proc_macro2::TokenStream> = non_pda_fields
         .iter()
         .map(|af: &&AccountField| match af {
             AccountField::Field(f) => constraints::generate(f),
@@ -222,7 +211,7 @@ pub fn generate_constraints(accs: &AccountsStruct) -> proc_macro2::TokenStream {
         .collect();
 
     quote! {
-        #(#init_associated_fields)*
+        #(#init_pda_fields)*
         #(#access_checks)*
     }
 }

+ 8 - 31
lang/syn/src/lib.rs

@@ -270,7 +270,6 @@ pub struct ConstraintGroup {
     seeds: Option<ConstraintSeedsGroup>,
     executable: Option<ConstraintExecutable>,
     state: Option<ConstraintState>,
-    associated: Option<ConstraintAssociatedGroup>,
     has_one: Vec<ConstraintHasOne>,
     literal: Vec<ConstraintLiteral>,
     raw: Vec<ConstraintRaw>,
@@ -297,7 +296,7 @@ impl ConstraintGroup {
 }
 
 // A single account constraint *after* merging all tokens into a well formed
-// constraint. Some constraints like "associated" are defined by multiple
+// constraint. Some constraints like "seeds" are defined by multiple
 // tokens, so a merging phase is required.
 #[allow(clippy::large_enum_variant)]
 #[derive(Debug)]
@@ -313,7 +312,6 @@ pub enum Constraint {
     Seeds(ConstraintSeedsGroup),
     Executable(ConstraintExecutable),
     State(ConstraintState),
-    AssociatedGroup(ConstraintAssociatedGroup),
     Close(ConstraintClose),
     Address(ConstraintAddress),
 }
@@ -334,10 +332,8 @@ pub enum ConstraintToken {
     Executable(Context<ConstraintExecutable>),
     State(Context<ConstraintState>),
     Close(Context<ConstraintClose>),
-    Associated(Context<ConstraintAssociated>),
-    AssociatedPayer(Context<ConstraintAssociatedPayer>),
-    AssociatedSpace(Context<ConstraintAssociatedSpace>),
-    AssociatedWith(Context<ConstraintAssociatedWith>),
+    Payer(Context<ConstraintPayer>),
+    Space(Context<ConstraintSpace>),
     Address(Context<ConstraintAddress>),
     TokenMint(Context<ConstraintTokenMint>),
     TokenAuthority(Context<ConstraintTokenAuthority>),
@@ -399,7 +395,8 @@ pub struct ConstraintSeedsGroup {
     pub payer: Option<Ident>,
     pub space: Option<Expr>,
     pub kind: PdaKind,
-    pub bump: Option<Expr>,
+    // Some(None) => bump was given without a target.
+    pub bump: Option<Option<Expr>>,
 }
 
 #[derive(Debug, Clone)]
@@ -416,32 +413,12 @@ pub struct ConstraintState {
 }
 
 #[derive(Debug, Clone)]
-pub struct ConstraintAssociatedGroup {
-    pub is_init: bool,
-    pub associated_target: Expr,
-    pub associated_seeds: Vec<Expr>,
-    pub payer: Option<Ident>,
-    pub space: Option<Expr>,
-    pub kind: PdaKind,
-}
-
-#[derive(Debug, Clone)]
-pub struct ConstraintAssociated {
-    pub target: Expr,
-}
-
-#[derive(Debug, Clone)]
-pub struct ConstraintAssociatedPayer {
+pub struct ConstraintPayer {
     pub target: Ident,
 }
 
 #[derive(Debug, Clone)]
-pub struct ConstraintAssociatedWith {
-    pub target: Expr,
-}
-
-#[derive(Debug, Clone)]
-pub struct ConstraintAssociatedSpace {
+pub struct ConstraintSpace {
     pub space: Expr,
 }
 
@@ -480,7 +457,7 @@ pub struct ConstraintMintDecimals {
 
 #[derive(Debug, Clone)]
 pub struct ConstraintTokenBump {
-    bump: Expr,
+    bump: Option<Expr>,
 }
 
 // Syntaxt context object for preserving metadata about the inner item.

+ 43 - 106
lang/syn/src/parser/accounts/constraints.rs

@@ -121,6 +121,17 @@ pub fn parse_token(stream: ParseStream) -> ParseResult<ConstraintToken> {
                 _ => return Err(ParseError::new(ident.span(), "Invalid attribute")),
             }
         }
+        "bump" => {
+            let bump = {
+                if stream.peek(Token![=]) {
+                    stream.parse::<Token![=]>()?;
+                    Some(stream.parse()?)
+                } else {
+                    None
+                }
+            };
+            ConstraintToken::Bump(Context::new(ident.span(), ConstraintTokenBump { bump }))
+        }
         _ => {
             stream.parse::<Token![=]>()?;
             let span = ident
@@ -166,27 +177,15 @@ pub fn parse_token(stream: ParseStream) -> ParseResult<ConstraintToken> {
                         program_target: stream.parse()?,
                     },
                 )),
-                "associated" => ConstraintToken::Associated(Context::new(
-                    span,
-                    ConstraintAssociated {
-                        target: stream.parse()?,
-                    },
-                )),
-                "payer" => ConstraintToken::AssociatedPayer(Context::new(
-                    span,
-                    ConstraintAssociatedPayer {
-                        target: stream.parse()?,
-                    },
-                )),
-                "with" => ConstraintToken::AssociatedWith(Context::new(
+                "payer" => ConstraintToken::Payer(Context::new(
                     span,
-                    ConstraintAssociatedWith {
+                    ConstraintPayer {
                         target: stream.parse()?,
                     },
                 )),
-                "space" => ConstraintToken::AssociatedSpace(Context::new(
+                "space" => ConstraintToken::Space(Context::new(
                     span,
-                    ConstraintAssociatedSpace {
+                    ConstraintSpace {
                         space: stream.parse()?,
                     },
                 )),
@@ -218,12 +217,6 @@ pub fn parse_token(stream: ParseStream) -> ParseResult<ConstraintToken> {
                         address: stream.parse()?,
                     },
                 )),
-                "bump" => ConstraintToken::Bump(Context::new(
-                    ident.span(),
-                    ConstraintTokenBump {
-                        bump: stream.parse()?,
-                    },
-                )),
                 _ => return Err(ParseError::new(ident.span(), "Invalid attribute")),
             }
         }
@@ -246,10 +239,8 @@ pub struct ConstraintGroupBuilder<'ty> {
     pub seeds: Option<Context<ConstraintSeeds>>,
     pub executable: Option<Context<ConstraintExecutable>>,
     pub state: Option<Context<ConstraintState>>,
-    pub associated: Option<Context<ConstraintAssociated>>,
-    pub associated_payer: Option<Context<ConstraintAssociatedPayer>>,
-    pub associated_space: Option<Context<ConstraintAssociatedSpace>>,
-    pub associated_with: Vec<Context<ConstraintAssociatedWith>>,
+    pub payer: Option<Context<ConstraintPayer>>,
+    pub space: Option<Context<ConstraintSpace>>,
     pub close: Option<Context<ConstraintClose>>,
     pub address: Option<Context<ConstraintAddress>>,
     pub token_mint: Option<Context<ConstraintTokenMint>>,
@@ -274,10 +265,8 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
             seeds: None,
             executable: None,
             state: None,
-            associated: None,
-            associated_payer: None,
-            associated_space: None,
-            associated_with: Vec::new(),
+            payer: None,
+            space: None,
             close: None,
             address: None,
             token_mint: None,
@@ -309,7 +298,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
 
         // Seeds.
         if let Some(i) = &self.seeds {
-            if self.init.is_some() && self.associated_payer.is_none() {
+            if self.init.is_some() && self.payer.is_none() {
                 return Err(ParseError::new(
                     i.span(),
                     "payer must be provided when creating a program derived address",
@@ -326,7 +315,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
                 ));
             }
 
-            if self.init.is_none() || (self.associated.is_none() && self.seeds.is_none()) {
+            if self.init.is_none() || self.seeds.is_none() {
                 return Err(ParseError::new(
                     token_mint.span(),
                     "init is required for a pda token",
@@ -365,10 +354,10 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
             && self.seeds.is_some()
             && self.token_mint.is_some()
             && (self.mint_authority.is_some() || self.token_authority.is_some())
-            && self.associated_space.is_some()
+            && self.space.is_some()
         {
             return Err(ParseError::new(
-                self.associated_space.as_ref().unwrap().span(),
+                self.space.as_ref().unwrap().span(),
                 "space is not required for initializing an spl account",
             ));
         }
@@ -386,10 +375,8 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
             seeds,
             executable,
             state,
-            associated,
-            associated_payer,
-            associated_space,
-            associated_with,
+            payer,
+            space,
             close,
             address,
             token_mint,
@@ -416,7 +403,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
         }
 
         let (owner, pda_owner) = {
-            if seeds.is_some() || associated.is_some() {
+            if seeds.is_some() {
                 (None, owner.map(|o| o.owner_target.clone()))
             } else {
                 (owner, None)
@@ -433,13 +420,17 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
             raw: into_inner_vec!(raw),
             owner: into_inner!(owner),
             rent_exempt: into_inner!(rent_exempt),
+            executable: into_inner!(executable),
+            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!(associated_payer.clone()).map(|a| a.target),
-                        space: associated_space.clone().map(|s| s.space.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 {
                                 PdaKind::Token {
                                     mint: tm.clone().into_inner().mint,
@@ -472,27 +463,6 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
                     })
                 })
                 .transpose()?,
-            executable: into_inner!(executable),
-            state: into_inner!(state),
-            associated: associated.map(|associated| ConstraintAssociatedGroup {
-                is_init,
-                associated_target: associated.target.clone(),
-                associated_seeds: associated_with.iter().map(|s| s.target.clone()).collect(),
-                payer: associated_payer.map(|p| p.target.clone()),
-                space: associated_space.map(|s| s.space.clone()),
-                kind: match token_mint {
-                    None => PdaKind::Program { owner: pda_owner },
-                    Some(tm) => PdaKind::Token {
-                        mint: tm.into_inner().mint,
-                        owner: match token_authority {
-                            Some(a) => a.into_inner().auth,
-                            None => associated.target.clone(),
-                        },
-                    },
-                },
-            }),
-            close: into_inner!(close),
-            address: into_inner!(address),
         })
     }
 
@@ -509,10 +479,8 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
             ConstraintToken::Seeds(c) => self.add_seeds(c),
             ConstraintToken::Executable(c) => self.add_executable(c),
             ConstraintToken::State(c) => self.add_state(c),
-            ConstraintToken::Associated(c) => self.add_associated(c),
-            ConstraintToken::AssociatedPayer(c) => self.add_associated_payer(c),
-            ConstraintToken::AssociatedSpace(c) => self.add_associated_space(c),
-            ConstraintToken::AssociatedWith(c) => self.add_associated_with(c),
+            ConstraintToken::Payer(c) => self.add_payer(c),
+            ConstraintToken::Space(c) => self.add_space(c),
             ConstraintToken::Close(c) => self.add_close(c),
             ConstraintToken::Address(c) => self.add_address(c),
             ConstraintToken::TokenAuthority(c) => self.add_token_authority(c),
@@ -694,12 +662,6 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
         if self.seeds.is_some() {
             return Err(ParseError::new(c.span(), "seeds already provided"));
         }
-        if self.associated.is_some() {
-            return Err(ParseError::new(
-                c.span(),
-                "both seeds and associated cannot be defined together",
-            ));
-        }
         self.seeds.replace(c);
         Ok(())
     }
@@ -720,56 +682,31 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
         Ok(())
     }
 
-    fn add_associated(&mut self, c: Context<ConstraintAssociated>) -> ParseResult<()> {
-        if self.associated.is_some() {
-            return Err(ParseError::new(c.span(), "associated already provided"));
-        }
-        if self.seeds.is_some() {
-            return Err(ParseError::new(
-                c.span(),
-                "both seeds and associated cannot be defined together",
-            ));
-        }
-        self.associated.replace(c);
-        Ok(())
-    }
-
-    fn add_associated_payer(&mut self, c: Context<ConstraintAssociatedPayer>) -> ParseResult<()> {
-        if self.associated.is_none() && self.seeds.is_none() {
+    fn add_payer(&mut self, c: Context<ConstraintPayer>) -> ParseResult<()> {
+        if self.seeds.is_none() {
             return Err(ParseError::new(
                 c.span(),
-                "associated or seeds must be provided before payer",
+                "seeds must be provided before payer",
             ));
         }
-        if self.associated_payer.is_some() {
+        if self.payer.is_some() {
             return Err(ParseError::new(c.span(), "payer already provided"));
         }
-        self.associated_payer.replace(c);
+        self.payer.replace(c);
         Ok(())
     }
 
-    fn add_associated_space(&mut self, c: Context<ConstraintAssociatedSpace>) -> ParseResult<()> {
-        if self.associated.is_none() && self.seeds.is_none() {
+    fn add_space(&mut self, c: Context<ConstraintSpace>) -> ParseResult<()> {
+        if self.seeds.is_none() {
             return Err(ParseError::new(
                 c.span(),
                 "associated or seeds must be provided before space",
             ));
         }
-        if self.associated_space.is_some() {
+        if self.space.is_some() {
             return Err(ParseError::new(c.span(), "space already provided"));
         }
-        self.associated_space.replace(c);
-        Ok(())
-    }
-
-    fn add_associated_with(&mut self, c: Context<ConstraintAssociatedWith>) -> ParseResult<()> {
-        if self.associated.is_none() {
-            return Err(ParseError::new(
-                c.span(),
-                "associated must be provided before with",
-            ));
-        }
-        self.associated_with.push(c);
+        self.space.replace(c);
         Ok(())
     }
 }

+ 1 - 1
lang/tests/generics_test.rs

@@ -21,7 +21,7 @@ pub struct Account<const N: usize> {
     pub data: WrappedU8Array<N>,
 }
 
-#[associated]
+#[account]
 #[derive(Default)]
 pub struct Associated<T>
 where

+ 0 - 19
ts/src/program/namespace/account.ts

@@ -241,25 +241,6 @@ export class AccountClient {
       programId: this._programId,
     });
   }
-
-  /**
-   * Function returning the associated account. Args are keys to associate.
-   * Order matters.
-   */
-  async associated(...args: Array<PublicKey | Buffer>): Promise<any> {
-    const addr = await this.associatedAddress(...args);
-    return await this.fetch(addr);
-  }
-
-  /**
-   * Function returning the associated address. Args are keys to associate.
-   * Order matters.
-   */
-  async associatedAddress(
-    ...args: Array<PublicKey | Buffer>
-  ): Promise<PublicKey> {
-    return await pubkeyUtil.associated(this._programId, ...args);
-  }
 }
 
 /**