Explorar o código

Static owner and program ID checks (#686)

Armani Ferrante %!s(int64=4) %!d(string=hai) anos
pai
achega
3958533750
Modificáronse 60 ficheiros con 1184 adicións e 425 borrados
  1. 6 0
      CHANGELOG.md
  2. 17 9
      Cargo.lock
  3. 23 7
      cli/src/config.rs
  4. 38 45
      cli/src/lib.rs
  5. 16 15
      client/example/run-test.sh
  6. 3 0
      examples/tutorial/basic-1/Anchor.toml
  7. 4 2
      examples/tutorial/basic-1/programs/basic-1/src/lib.rs
  8. 3 0
      examples/tutorial/basic-2/Anchor.toml
  9. 3 7
      examples/tutorial/basic-2/programs/basic-2/src/lib.rs
  10. 4 0
      examples/tutorial/basic-3/Anchor.toml
  11. 4 2
      examples/tutorial/basic-3/programs/puppet-master/src/lib.rs
  12. 4 2
      examples/tutorial/basic-3/programs/puppet/src/lib.rs
  13. 3 0
      examples/tutorial/basic-4/Anchor.toml
  14. 2 0
      examples/tutorial/basic-4/programs/basic-4/src/lib.rs
  15. 2 0
      lang/attribute/account/Cargo.toml
  16. 296 0
      lang/attribute/account/src/id.rs
  17. 29 0
      lang/attribute/account/src/lib.rs
  18. 164 0
      lang/src/account.rs
  19. 10 3
      lang/src/cpi_account.rs
  20. 14 6
      lang/src/lib.rs
  21. 28 99
      lang/syn/src/codegen/accounts/constraints.rs
  22. 2 62
      lang/syn/src/codegen/accounts/try_accounts.rs
  23. 2 3
      lang/syn/src/codegen/program/cpi.rs
  24. 181 0
      lang/syn/src/lib.rs
  25. 2 1
      lang/syn/src/parser/accounts/constraints.rs
  26. 57 5
      lang/syn/src/parser/accounts/mod.rs
  27. 3 0
      lang/tests/generics_test.rs
  28. 27 0
      spl/src/token.rs
  29. 3 0
      tests/cashiers-check/Anchor.toml
  30. 10 8
      tests/cashiers-check/programs/cashiers-check/src/lib.rs
  31. 1 0
      tests/cfo/Anchor.toml
  32. 25 21
      tests/cfo/programs/cfo/src/lib.rs
  33. 3 0
      tests/chat/Anchor.toml
  34. 4 2
      tests/chat/programs/chat/src/lib.rs
  35. 3 0
      tests/composite/Anchor.toml
  36. 6 4
      tests/composite/programs/composite/src/lib.rs
  37. 3 0
      tests/errors/Anchor.toml
  38. 3 1
      tests/errors/programs/errors/src/lib.rs
  39. 3 0
      tests/escrow/Anchor.toml
  40. 12 10
      tests/escrow/programs/escrow/src/lib.rs
  41. 3 0
      tests/events/Anchor.toml
  42. 2 0
      tests/events/programs/events/src/lib.rs
  43. 3 0
      tests/ido-pool/Anchor.toml
  44. 27 25
      tests/ido-pool/programs/ido-pool/src/lib.rs
  45. 4 0
      tests/interface/Anchor.toml
  46. 2 0
      tests/interface/programs/counter-auth/src/lib.rs
  47. 2 0
      tests/interface/programs/counter/src/lib.rs
  48. 4 0
      tests/lockup/Anchor.toml
  49. 10 8
      tests/lockup/programs/lockup/src/lib.rs
  50. 53 51
      tests/lockup/programs/registry/src/lib.rs
  51. 4 0
      tests/misc/Anchor.toml
  52. 12 12
      tests/misc/programs/misc/src/context.rs
  53. 2 0
      tests/misc/programs/misc/src/lib.rs
  54. 2 0
      tests/misc/programs/misc2/src/lib.rs
  55. 7 5
      tests/misc/tests/misc.js
  56. 3 0
      tests/multisig/Anchor.toml
  57. 2 0
      tests/multisig/programs/multisig/src/lib.rs
  58. 6 0
      tests/tictactoe/Anchor.toml
  59. 11 10
      tests/tictactoe/programs/tictactoe/src/lib.rs
  60. 2 0
      tests/zero-copy/programs/zero-copy/src/lib.rs

+ 6 - 0
CHANGELOG.md

@@ -13,8 +13,14 @@ incremented for features.
 
 ### Features
 
+* lang: Add new `Account` type to replace `ProgramAccount` and `CpiAccount`, both of which are deprecated ([#686](https://github.com/project-serum/anchor/pull/686)).
+* lang: Add `Owner` trait, which is automatically implemented by all `#[account]` structs ([#686](https://github.com/project-serum/anchor/pull/686)).
 * lang: Check that ProgramAccount writable before mut borrow (`anchor-debug` only) ([#681](https://github.com/project-serum/anchor/pull/681)).
 
+### Breaking Changes
+
+* lang: All programs must now define their program id in source via `declare_id!` ([#686](https://github.com/project-serum/anchor/pull/686)).
+
 ## [0.14.0] - 2021-09-02
 
 ### Features

+ 17 - 9
Cargo.lock

@@ -72,8 +72,10 @@ version = "0.14.0"
 dependencies = [
  "anchor-syn",
  "anyhow",
+ "bs58 0.4.0",
  "proc-macro2 1.0.29",
  "quote 1.0.9",
+ "rustversion",
  "syn 1.0.75",
 ]
 
@@ -222,7 +224,7 @@ name = "anchor-syn"
 version = "0.14.0"
 dependencies = [
  "anyhow",
- "bs58",
+ "bs58 0.3.1",
  "heck",
  "proc-macro2 1.0.29",
  "proc-macro2-diagnostics",
@@ -477,6 +479,12 @@ version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb"
 
+[[package]]
+name = "bs58"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3"
+
 [[package]]
 name = "bumpalo"
 version = "3.7.0"
@@ -2725,7 +2733,7 @@ dependencies = [
  "anyhow",
  "arrayref",
  "bincode",
- "bs58",
+ "bs58 0.3.1",
  "rand 0.7.3",
  "serde",
  "serde_json",
@@ -2861,7 +2869,7 @@ dependencies = [
  "Inflector",
  "base64 0.12.3",
  "bincode",
- "bs58",
+ "bs58 0.3.1",
  "bv",
  "lazy_static",
  "serde",
@@ -2914,7 +2922,7 @@ checksum = "779f90ee9f77c831426af58c9732902051314bb8f2607473ffd6089a3b008133"
 dependencies = [
  "base64 0.13.0",
  "bincode",
- "bs58",
+ "bs58 0.3.1",
  "clap 2.33.3",
  "indicatif",
  "jsonrpc-core",
@@ -3008,7 +3016,7 @@ version = "1.7.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "21ddfc2b65a555c0e0156c043bce092d473bc4f00daa7ca3c223d97d92d2e807"
 dependencies = [
- "bs58",
+ "bs58 0.3.1",
  "bv",
  "generic-array 0.14.4",
  "log",
@@ -3102,7 +3110,7 @@ dependencies = [
  "blake3",
  "borsh",
  "borsh-derive 0.9.1",
- "bs58",
+ "bs58 0.3.1",
  "bv",
  "curve25519-dalek 2.1.3",
  "hex",
@@ -3217,7 +3225,7 @@ checksum = "95179bc7d87c5b61c86f3bbbac4e52a5d909432473593d33546e4f20dc582052"
 dependencies = [
  "assert_matches",
  "bincode",
- "bs58",
+ "bs58 0.3.1",
  "bv",
  "byteorder",
  "chrono",
@@ -3264,7 +3272,7 @@ version = "1.7.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b453dca160617b1676c47e3cfd4361f455dc5bb1c93659ec84b0c5d566b5c039"
 dependencies = [
- "bs58",
+ "bs58 0.3.1",
  "proc-macro2 1.0.29",
  "quote 1.0.9",
  "rustversion",
@@ -3311,7 +3319,7 @@ dependencies = [
  "Inflector",
  "base64 0.12.3",
  "bincode",
- "bs58",
+ "bs58 0.3.1",
  "lazy_static",
  "serde",
  "serde_derive",

+ 23 - 7
cli/src/config.rs

@@ -4,7 +4,7 @@ use anyhow::{anyhow, Error, Result};
 use clap::Clap;
 use serde::{Deserialize, Serialize};
 use solana_sdk::pubkey::Pubkey;
-use solana_sdk::signature::Keypair;
+use solana_sdk::signature::{Keypair, Signer};
 use std::collections::BTreeMap;
 use std::convert::TryFrom;
 use std::fs::{self, File};
@@ -471,13 +471,29 @@ pub struct Program {
 }
 
 impl Program {
-    pub fn anchor_keypair_path(&self) -> PathBuf {
-        std::env::current_dir()
+    pub fn pubkey(&self) -> Result<Pubkey> {
+        self.keypair().map(|kp| kp.pubkey())
+    }
+
+    pub fn keypair(&self) -> Result<Keypair> {
+        let file = self.keypair_file()?;
+        solana_sdk::signature::read_keypair_file(file.path())
+            .map_err(|_| anyhow!("failed to read keypair for program: {}", self.lib_name))
+    }
+
+    // Lazily initializes the keypair file with a new key if it doesn't exist.
+    pub fn keypair_file(&self) -> Result<WithPath<File>> {
+        fs::create_dir_all("target/deploy/")?;
+        let path = std::env::current_dir()
             .expect("Must have current dir")
-            .join(format!(
-                "target/deploy/anchor-{}-keypair.json",
-                self.lib_name
-            ))
+            .join(format!("target/deploy/{}-keypair.json", self.lib_name));
+        if path.exists() {
+            return Ok(WithPath::new(File::open(&path)?, path));
+        }
+        let program_kp = Keypair::generate(&mut rand::rngs::OsRng);
+        let mut file = File::create(&path)?;
+        file.write_all(format!("{:?}", &program_kp.to_bytes()).as_bytes())?;
+        Ok(WithPath::new(file, path))
     }
 
     pub fn binary_path(&self) -> PathBuf {

+ 38 - 45
cli/src/lib.rs

@@ -1,6 +1,4 @@
-use crate::config::{
-    AnchorPackage, Config, ConfigOverride, Manifest, Program, ProgramWorkspace, WithPath,
-};
+use crate::config::{AnchorPackage, Config, ConfigOverride, Manifest, ProgramWorkspace, WithPath};
 use anchor_client::Cluster;
 use anchor_lang::idl::{IdlAccount, IdlInstruction};
 use anchor_lang::{AccountDeserialize, AnchorDeserialize, AnchorSerialize};
@@ -154,6 +152,16 @@ pub enum Command {
         /// The name of the program to publish.
         program: String,
     },
+    /// Keypair commands.
+    Keys {
+        #[clap(subcommand)]
+        subcmd: KeysCommand,
+    },
+}
+
+#[derive(Debug, Clap)]
+pub enum KeysCommand {
+    List,
 }
 
 #[derive(Debug, Clap)]
@@ -283,6 +291,7 @@ pub fn entry(opts: Opts) -> Result<()> {
         Command::Run { script } => run(&opts.cfg_override, script),
         Command::Login { token } => login(&opts.cfg_override, token),
         Command::Publish { program } => publish(&opts.cfg_override, program),
+        Command::Keys { subcmd } => keys(&opts.cfg_override, subcmd),
     }
 }
 
@@ -1374,13 +1383,12 @@ fn genesis_flags(cfg: &WithPath<Config>) -> Result<Vec<String>> {
     for mut program in cfg.read_all_programs()? {
         let binary_path = program.binary_path().display().to_string();
 
+        // Use the [programs.cluster] override and fallback to the keypair
+        // files if no override is given.
         let address = programs
             .and_then(|m| m.get(&program.lib_name))
-            .map(|deployment| deployment.address.to_string())
-            .unwrap_or_else(|| {
-                let kp = Keypair::generate(&mut OsRng);
-                kp.pubkey().to_string()
-            });
+            .map(|deployment| Ok(deployment.address.to_string()))
+            .unwrap_or_else(|| program.pubkey().map(|p| p.to_string()))?;
 
         flags.push("--bpf-program".to_string());
         flags.push(address.clone());
@@ -1507,14 +1515,7 @@ fn start_test_validator(cfg: &Config, flags: Option<Vec<String>>) -> Result<Chil
     Ok(validator_handle)
 }
 
-fn deploy(cfg_override: &ConfigOverride, program_name: Option<String>) -> Result<()> {
-    _deploy(cfg_override, program_name).map(|_| ())
-}
-
-fn _deploy(
-    cfg_override: &ConfigOverride,
-    program_str: Option<String>,
-) -> Result<Vec<(Pubkey, Program)>> {
+fn deploy(cfg_override: &ConfigOverride, program_str: Option<String>) -> Result<()> {
     with_workspace(cfg_override, |cfg| {
         let url = cfg.provider.cluster.url().to_string();
         let keypair = cfg.provider.wallet.to_string();
@@ -1523,8 +1524,6 @@ fn _deploy(
         println!("Deploying workspace: {}", url);
         println!("Upgrade authority: {}", keypair);
 
-        let mut programs = Vec::new();
-
         for mut program in cfg.read_all_programs()? {
             if let Some(single_prog_str) = &program_str {
                 let program_name = program.path.file_name().unwrap().to_str().unwrap();
@@ -1540,11 +1539,7 @@ fn _deploy(
             );
             println!("Program path: {}...", binary_path);
 
-            // Write the program's keypair filepath. This forces a new deploy
-            // address.
-            let program_kp = Keypair::generate(&mut OsRng);
-            let mut file = File::create(program.anchor_keypair_path())?;
-            file.write_all(format!("{:?}", &program_kp.to_bytes()).as_bytes())?;
+            let file = program.keypair_file()?;
 
             // Send deploy transactions.
             let exit = std::process::Command::new("solana")
@@ -1555,7 +1550,7 @@ fn _deploy(
                 .arg("--keypair")
                 .arg(&keypair)
                 .arg("--program-id")
-                .arg(program.anchor_keypair_path().display().to_string())
+                .arg(file.path().display().to_string())
                 .arg(&binary_path)
                 .stdout(Stdio::inherit())
                 .stderr(Stdio::inherit())
@@ -1566,10 +1561,11 @@ fn _deploy(
                 std::process::exit(exit.status.code().unwrap_or(1));
             }
 
+            let program_pubkey = program.pubkey()?;
             if let Some(mut idl) = program.idl.as_mut() {
                 // Add program address to the IDL.
                 idl.metadata = Some(serde_json::to_value(IdlTestMetadata {
-                    address: program_kp.pubkey().to_string(),
+                    address: program_pubkey.to_string(),
                 })?);
 
                 // Persist it.
@@ -1578,13 +1574,11 @@ fn _deploy(
                     .with_extension("json");
                 write_idl(idl, OutFile::File(idl_out))?;
             }
-
-            programs.push((program_kp.pubkey(), program))
         }
 
         println!("Deploy success");
 
-        Ok(programs)
+        Ok(())
     })
 }
 
@@ -1619,20 +1613,6 @@ fn upgrade(
     })
 }
 
-// The Solana CLI doesn't redeploy a program if this file exists.
-// So remove it to make all commands explicit.
-fn clear_program_keys(cfg_override: &ConfigOverride) -> Result<()> {
-    let config = Config::discover(cfg_override).unwrap_or_default().unwrap();
-
-    for program in config.read_all_programs()? {
-        let anchor_keypair_path = program.anchor_keypair_path();
-        if Path::exists(&anchor_keypair_path) {
-            std::fs::remove_file(anchor_keypair_path).expect("Always remove");
-        }
-    }
-    Ok(())
-}
-
 fn create_idl_account(
     cfg: &Config,
     keypair_path: &str,
@@ -1902,6 +1882,7 @@ fn shell(cfg_override: &ConfigOverride) -> Result<()> {
                     })
                     .collect::<Vec<_>>();
             }
+
             // Finalize program list with all programs with IDLs.
             match cfg.programs.get(&cfg.provider.cluster) {
                 None => Vec::new(),
@@ -2130,6 +2111,21 @@ fn registry_api_token(_cfg_override: &ConfigOverride) -> Result<String> {
     Ok(credentials_toml.registry.token)
 }
 
+fn keys(cfg_override: &ConfigOverride, cmd: KeysCommand) -> Result<()> {
+    match cmd {
+        KeysCommand::List => keys_list(cfg_override),
+    }
+}
+
+fn keys_list(cfg_override: &ConfigOverride) -> Result<()> {
+    let cfg = Config::discover(cfg_override)?.expect("Not in workspace.");
+    for program in cfg.read_all_programs()? {
+        let pubkey = program.pubkey()?;
+        println!("{}: {}", program.lib_name, pubkey.to_string());
+    }
+    Ok(())
+}
+
 // with_workspace ensures the current working directory is always the top level
 // workspace directory, i.e., where the `Anchor.toml` file is located, before
 // and after the closure invocation.
@@ -2139,8 +2135,6 @@ fn registry_api_token(_cfg_override: &ConfigOverride) -> Result<String> {
 fn with_workspace<R>(cfg_override: &ConfigOverride, f: impl FnOnce(&WithPath<Config>) -> R) -> R {
     set_workspace_dir_or_exit();
 
-    clear_program_keys(cfg_override).unwrap();
-
     let cfg = Config::discover(cfg_override)
         .expect("Previously set the workspace dir")
         .expect("Anchor.toml must always exist");
@@ -2148,7 +2142,6 @@ fn with_workspace<R>(cfg_override: &ConfigOverride, f: impl FnOnce(&WithPath<Con
     let r = f(&cfg);
 
     set_workspace_dir_or_exit();
-    clear_program_keys(cfg_override).unwrap();
 
     r
 }

+ 16 - 15
client/example/run-test.sh

@@ -20,35 +20,36 @@ set -euox pipefail
 
 main() {
     #
-    # Bootup validator.
-    #
-    solana-test-validator > test-validator.log &
-    sleep 5
-
-    #
-    # Deploy programs.
+    # Build programs.
     #
     pushd ../../tests/composite/
     anchor build
-    anchor deploy
-    local composite_pid=$(cat target/idl/composite.json | jq -r .metadata.address)
+    local composite_pid="EHthziFziNoac9LBGxEaVN47Y3uUiRoXvqAiR6oes4iU"
     popd
     pushd ../../examples/tutorial/basic-2/
     anchor build
-    anchor deploy
-    local basic_2_pid=$(cat target/idl/basic_2.json | jq -r .metadata.address)
+    local basic_2_pid="Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
     popd
     pushd ../../examples/tutorial/basic-4/
     anchor build
-    anchor deploy
-    local basic_4_pid=$(cat target/idl/basic_4.json | jq -r .metadata.address)
+    local basic_4_pid="CwrqeMj2U8tFr1Rhkgwc84tpAsqbt9pTt2a4taoTADPr"
     popd
     pushd ../../tests/events
     anchor build
-    anchor deploy
-    local events_pid=$(cat target/idl/events.json | jq -r .metadata.address)
+    local events_pid="2dhGsWUzy5YKUsjZdLHLmkNpUDAXkNa9MYWsPc4Ziqzy"
     popd
 
+    #
+    # Bootup validator.
+    #
+    solana-test-validator \
+				--bpf-program $composite_pid ../../tests/composite/target/deploy/composite.so \
+				--bpf-program $basic_2_pid ../../examples/tutorial/basic-2/target/deploy/basic_2.so \
+				--bpf-program $basic_4_pid ../../examples/tutorial/basic-4/target/deploy/basic_4.so \
+				--bpf-program $events_pid ../../tests/events/target/deploy/events.so \
+				> test-validator.log &
+    sleep 5
+
     #
     # Run Test.
     #

+ 3 - 0
examples/tutorial/basic-1/Anchor.toml

@@ -2,5 +2,8 @@
 cluster = "localnet"
 wallet = "~/.config/solana/id.json"
 
+[programs.localnet]
+basic_1 = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
+
 [scripts]
 test = "mocha -t 1000000 tests/"

+ 4 - 2
examples/tutorial/basic-1/programs/basic-1/src/lib.rs

@@ -1,5 +1,7 @@
 use anchor_lang::prelude::*;
 
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+
 #[program]
 mod basic_1 {
     use super::*;
@@ -20,7 +22,7 @@ mod basic_1 {
 #[derive(Accounts)]
 pub struct Initialize<'info> {
     #[account(init, payer = user, space = 8 + 8)]
-    pub my_account: ProgramAccount<'info, MyAccount>,
+    pub my_account: Account<'info, MyAccount>,
     pub user: AccountInfo<'info>,
     pub system_program: AccountInfo<'info>,
 }
@@ -28,7 +30,7 @@ pub struct Initialize<'info> {
 #[derive(Accounts)]
 pub struct Update<'info> {
     #[account(mut)]
-    pub my_account: ProgramAccount<'info, MyAccount>,
+    pub my_account: Account<'info, MyAccount>,
 }
 
 #[account]

+ 3 - 0
examples/tutorial/basic-2/Anchor.toml

@@ -2,5 +2,8 @@
 cluster = "localnet"
 wallet = "~/.config/solana/id.json"
 
+[programs.localnet]
+basic_2 = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
+
 [scripts]
 test = "mocha -t 1000000 tests/"

+ 3 - 7
examples/tutorial/basic-2/programs/basic-2/src/lib.rs

@@ -1,7 +1,7 @@
 use anchor_lang::prelude::*;
 use anchor_lang::solana_program::system_program;
 
-// Define the program's instruction handlers.
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
 
 #[program]
 mod basic_2 {
@@ -21,12 +21,10 @@ mod basic_2 {
     }
 }
 
-// Define the validated accounts for each handler.
-
 #[derive(Accounts)]
 pub struct Create<'info> {
     #[account(init, payer = user, space = 8 + 40)]
-    pub counter: ProgramAccount<'info, Counter>,
+    pub counter: Account<'info, Counter>,
     #[account(signer)]
     pub user: AccountInfo<'info>,
     #[account(address = system_program::ID)]
@@ -36,13 +34,11 @@ pub struct Create<'info> {
 #[derive(Accounts)]
 pub struct Increment<'info> {
     #[account(mut, has_one = authority)]
-    pub counter: ProgramAccount<'info, Counter>,
+    pub counter: Account<'info, Counter>,
     #[account(signer)]
     pub authority: AccountInfo<'info>,
 }
 
-// Define the program owned accounts.
-
 #[account]
 pub struct Counter {
     pub authority: Pubkey,

+ 4 - 0
examples/tutorial/basic-3/Anchor.toml

@@ -2,5 +2,9 @@
 cluster = "localnet"
 wallet = "~/.config/solana/id.json"
 
+[programs.localnet]
+puppet = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
+puppet_master = "HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L"
+
 [scripts]
 test = "mocha -t 1000000 tests/"

+ 4 - 2
examples/tutorial/basic-3/programs/puppet-master/src/lib.rs

@@ -2,13 +2,15 @@
 use anchor_lang::prelude::*;
 use puppet::{Puppet, SetData};
 
+declare_id!("HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L");
+
 #[program]
 mod puppet_master {
     use super::*;
     pub fn pull_strings(ctx: Context<PullStrings>, data: u64) -> ProgramResult {
         let cpi_program = ctx.accounts.puppet_program.clone();
         let cpi_accounts = SetData {
-            puppet: ctx.accounts.puppet.clone().into(),
+            puppet: ctx.accounts.puppet.clone(),
         };
         let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
         puppet::cpi::set_data(cpi_ctx, data)
@@ -18,7 +20,7 @@ mod puppet_master {
 #[derive(Accounts)]
 pub struct PullStrings<'info> {
     #[account(mut, owner = puppet_program)]
-    pub puppet: CpiAccount<'info, Puppet>,
+    pub puppet: Account<'info, Puppet>,
     pub puppet_program: AccountInfo<'info>,
 }
 // #endregion core

+ 4 - 2
examples/tutorial/basic-3/programs/puppet/src/lib.rs

@@ -1,6 +1,8 @@
 use anchor_lang::prelude::*;
 use anchor_lang::solana_program::system_program;
 
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+
 #[program]
 pub mod puppet {
     use super::*;
@@ -18,7 +20,7 @@ pub mod puppet {
 #[derive(Accounts)]
 pub struct Initialize<'info> {
     #[account(init, payer = user, space = 8 + 8)]
-    pub puppet: ProgramAccount<'info, Puppet>,
+    pub puppet: Account<'info, Puppet>,
     #[account(signer)]
     pub user: AccountInfo<'info>,
     #[account(address = system_program::ID)]
@@ -28,7 +30,7 @@ pub struct Initialize<'info> {
 #[derive(Accounts)]
 pub struct SetData<'info> {
     #[account(mut)]
-    pub puppet: ProgramAccount<'info, Puppet>,
+    pub puppet: Account<'info, Puppet>,
 }
 
 #[account]

+ 3 - 0
examples/tutorial/basic-4/Anchor.toml

@@ -2,5 +2,8 @@
 cluster = "localnet"
 wallet = "~/.config/solana/id.json"
 
+[programs.localnet]
+basic_4 = "CwrqeMj2U8tFr1Rhkgwc84tpAsqbt9pTt2a4taoTADPr"
+
 [scripts]
 test = "mocha -t 1000000 tests/"

+ 2 - 0
examples/tutorial/basic-4/programs/basic-4/src/lib.rs

@@ -1,6 +1,8 @@
 // #region code
 use anchor_lang::prelude::*;
 
+declare_id!("CwrqeMj2U8tFr1Rhkgwc84tpAsqbt9pTt2a4taoTADPr");
+
 #[program]
 pub mod basic_4 {
     use super::*;

+ 2 - 0
lang/attribute/account/Cargo.toml

@@ -19,3 +19,5 @@ quote = "1.0"
 syn = { version = "1.0.60", features = ["full"] }
 anyhow = "1.0.32"
 anchor-syn = { path = "../../syn", version = "0.14.0", features = ["hash"] }
+rustversion = "1.0.3"
+bs58 = "0.4.0"

+ 296 - 0
lang/attribute/account/src/id.rs

@@ -0,0 +1,296 @@
+//! Copied from solana/sdk/macro so that Anchor programs don't need to specify
+//! `solana_program` as an additional crate dependency, but instead can access
+//! it via `anchor_lang::declare_id`.
+//!
+//! Convenience macro to declare a static public key and functions to interact with it
+//!
+//! Input: a single literal base58 string representation of a program's id
+
+extern crate proc_macro;
+
+use proc_macro2::{Delimiter, Span, TokenTree};
+use quote::{quote, ToTokens};
+use std::convert::TryFrom;
+use syn::{
+    bracketed,
+    parse::{Parse, ParseStream, Result},
+    punctuated::Punctuated,
+    token::Bracket,
+    Expr, Ident, LitByte, LitStr, Path, Token,
+};
+
+fn parse_id(
+    input: ParseStream,
+    pubkey_type: proc_macro2::TokenStream,
+) -> Result<proc_macro2::TokenStream> {
+    let id = if input.peek(syn::LitStr) {
+        let id_literal: LitStr = input.parse()?;
+        parse_pubkey(&id_literal, &pubkey_type)?
+    } else {
+        let expr: Expr = input.parse()?;
+        quote! { #expr }
+    };
+
+    if !input.is_empty() {
+        let stream: proc_macro2::TokenStream = input.parse()?;
+        return Err(syn::Error::new_spanned(stream, "unexpected token"));
+    }
+    Ok(id)
+}
+
+fn id_to_tokens(
+    id: &proc_macro2::TokenStream,
+    pubkey_type: proc_macro2::TokenStream,
+    tokens: &mut proc_macro2::TokenStream,
+) {
+    tokens.extend(quote! {
+        /// The static program ID
+        pub static ID: #pubkey_type = #id;
+
+        /// Confirms that a given pubkey is equivalent to the program ID
+        pub fn check_id(id: &#pubkey_type) -> bool {
+            id == &ID
+        }
+
+        /// Returns the program ID
+        pub fn id() -> #pubkey_type {
+            ID
+        }
+
+        #[cfg(test)]
+        #[test]
+        fn test_id() {
+            assert!(check_id(&id()));
+        }
+    });
+}
+
+fn deprecated_id_to_tokens(
+    id: &proc_macro2::TokenStream,
+    pubkey_type: proc_macro2::TokenStream,
+    tokens: &mut proc_macro2::TokenStream,
+) {
+    tokens.extend(quote! {
+        /// The static program ID
+        pub static ID: #pubkey_type = #id;
+
+        /// Confirms that a given pubkey is equivalent to the program ID
+        #[deprecated()]
+        pub fn check_id(id: &#pubkey_type) -> bool {
+            id == &ID
+        }
+
+        /// Returns the program ID
+        #[deprecated()]
+        pub fn id() -> #pubkey_type {
+            ID
+        }
+
+        #[cfg(test)]
+        #[test]
+            fn test_id() {
+            #[allow(deprecated)]
+            assert!(check_id(&id()));
+        }
+    });
+}
+
+pub struct Id(proc_macro2::TokenStream);
+
+impl Parse for Id {
+    fn parse(input: ParseStream) -> Result<Self> {
+        parse_id(
+            input,
+            quote! { anchor_lang::solana_program::pubkey::Pubkey },
+        )
+        .map(Self)
+    }
+}
+
+impl ToTokens for Id {
+    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+        id_to_tokens(
+            &self.0,
+            quote! { anchor_lang::solana_program::pubkey::Pubkey },
+            tokens,
+        )
+    }
+}
+
+struct IdDeprecated(proc_macro2::TokenStream);
+
+impl Parse for IdDeprecated {
+    fn parse(input: ParseStream) -> Result<Self> {
+        parse_id(
+            input,
+            quote! { anchor_lang::solana_program::pubkey::Pubkey },
+        )
+        .map(Self)
+    }
+}
+
+impl ToTokens for IdDeprecated {
+    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+        deprecated_id_to_tokens(
+            &self.0,
+            quote! { anchor_lang::solana_program::pubkey::Pubkey },
+            tokens,
+        )
+    }
+}
+
+struct ProgramSdkId(proc_macro2::TokenStream);
+impl Parse for ProgramSdkId {
+    fn parse(input: ParseStream) -> Result<Self> {
+        parse_id(
+            input,
+            quote! { anchor_lang::solana_program::pubkey::Pubkey },
+        )
+        .map(Self)
+    }
+}
+
+impl ToTokens for ProgramSdkId {
+    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+        id_to_tokens(
+            &self.0,
+            quote! { anchor_lang::solana_program::pubkey::Pubkey },
+            tokens,
+        )
+    }
+}
+
+struct ProgramSdkIdDeprecated(proc_macro2::TokenStream);
+impl Parse for ProgramSdkIdDeprecated {
+    fn parse(input: ParseStream) -> Result<Self> {
+        parse_id(
+            input,
+            quote! { anchor_lang::solana_program::pubkey::Pubkey },
+        )
+        .map(Self)
+    }
+}
+
+impl ToTokens for ProgramSdkIdDeprecated {
+    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+        deprecated_id_to_tokens(
+            &self.0,
+            quote! { anchor_lang::solana_program::pubkey::Pubkey },
+            tokens,
+        )
+    }
+}
+
+#[allow(dead_code)] // `respan` may be compiled out
+struct RespanInput {
+    to_respan: Path,
+    respan_using: Span,
+}
+
+impl Parse for RespanInput {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let to_respan: Path = input.parse()?;
+        let _comma: Token![,] = input.parse()?;
+        let respan_tree: TokenTree = input.parse()?;
+        match respan_tree {
+            TokenTree::Group(g) if g.delimiter() == Delimiter::None => {
+                let ident: Ident = syn::parse2(g.stream())?;
+                Ok(RespanInput {
+                    to_respan,
+                    respan_using: ident.span(),
+                })
+            }
+            val => Err(syn::Error::new_spanned(
+                val,
+                "expected None-delimited group",
+            )),
+        }
+    }
+}
+
+fn parse_pubkey(
+    id_literal: &LitStr,
+    pubkey_type: &proc_macro2::TokenStream,
+) -> Result<proc_macro2::TokenStream> {
+    let id_vec = bs58::decode(id_literal.value())
+        .into_vec()
+        .map_err(|_| syn::Error::new_spanned(&id_literal, "failed to decode base58 string"))?;
+    let id_array = <[u8; 32]>::try_from(<&[u8]>::clone(&&id_vec[..])).map_err(|_| {
+        syn::Error::new_spanned(
+            &id_literal,
+            format!("pubkey array is not 32 bytes long: len={}", id_vec.len()),
+        )
+    })?;
+    let bytes = id_array.iter().map(|b| LitByte::new(*b, Span::call_site()));
+    Ok(quote! {
+        #pubkey_type::new_from_array(
+            [#(#bytes,)*]
+        )
+    })
+}
+
+struct Pubkeys {
+    method: Ident,
+    num: usize,
+    pubkeys: proc_macro2::TokenStream,
+}
+impl Parse for Pubkeys {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let pubkey_type = quote! {
+            anchor_lang::solana_program::pubkey::Pubkey
+        };
+
+        let method = input.parse()?;
+        let _comma: Token![,] = input.parse()?;
+        let (num, pubkeys) = if input.peek(syn::LitStr) {
+            let id_literal: LitStr = input.parse()?;
+            (1, parse_pubkey(&id_literal, &pubkey_type)?)
+        } else if input.peek(Bracket) {
+            let pubkey_strings;
+            bracketed!(pubkey_strings in input);
+            let punctuated: Punctuated<LitStr, Token![,]> =
+                Punctuated::parse_terminated(&pubkey_strings)?;
+            let mut pubkeys: Punctuated<proc_macro2::TokenStream, Token![,]> = Punctuated::new();
+            for string in punctuated.iter() {
+                pubkeys.push(parse_pubkey(string, &pubkey_type)?);
+            }
+            (pubkeys.len(), quote! {#pubkeys})
+        } else {
+            let stream: proc_macro2::TokenStream = input.parse()?;
+            return Err(syn::Error::new_spanned(stream, "unexpected token"));
+        };
+
+        Ok(Pubkeys {
+            method,
+            num,
+            pubkeys,
+        })
+    }
+}
+
+impl ToTokens for Pubkeys {
+    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+        let Pubkeys {
+            method,
+            num,
+            pubkeys,
+        } = self;
+
+        let pubkey_type = quote! {
+            anchor_lang::solana_program::pubkey::Pubkey
+        };
+        if *num == 1 {
+            tokens.extend(quote! {
+                pub fn #method() -> #pubkey_type {
+                    #pubkeys
+                }
+            });
+        } else {
+            tokens.extend(quote! {
+                pub fn #method() -> ::std::vec::Vec<#pubkey_type> {
+                    vec![#pubkeys]
+                }
+            });
+        }
+    }
+}

+ 29 - 0
lang/attribute/account/src/lib.rs

@@ -3,6 +3,8 @@ extern crate proc_macro;
 use quote::quote;
 use syn::parse_macro_input;
 
+mod id;
+
 /// A data structure representing a Solana account, implementing various traits:
 ///
 /// - [`AccountSerialize`](./trait.AccountSerialize.html)
@@ -98,6 +100,21 @@ pub fn account(
         format!("{:?}", discriminator).parse().unwrap()
     };
 
+    let owner_impl = {
+        if namespace.is_empty() {
+            quote! {
+                #[automatically_derived]
+                impl #impl_gen anchor_lang::Owner for #account_name #type_gen #where_clause {
+                    fn owner() -> Pubkey {
+                        crate::ID
+                    }
+                }
+            }
+        } else {
+            quote! {}
+        }
+    };
+
     proc_macro::TokenStream::from({
         if is_zero_copy {
             quote! {
@@ -142,6 +159,8 @@ pub fn account(
                         Ok(*account)
                     }
                 }
+
+                #owner_impl
             }
         } else {
             quote! {
@@ -187,6 +206,8 @@ pub fn account(
                         #discriminator
                     }
                 }
+
+                #owner_impl
             }
         }
     })
@@ -270,3 +291,11 @@ pub fn zero_copy(
         #account_strct
     })
 }
+
+/// Defines the program's ID. This should be used at the root of all Anchor
+/// based programs.
+#[proc_macro]
+pub fn declare_id(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+    let id = parse_macro_input!(input as id::Id);
+    proc_macro::TokenStream::from(quote! {#id})
+}

+ 164 - 0
lang/src/account.rs

@@ -0,0 +1,164 @@
+use crate::error::ErrorCode;
+use crate::*;
+use solana_program::account_info::AccountInfo;
+use solana_program::entrypoint::ProgramResult;
+use solana_program::instruction::AccountMeta;
+use solana_program::program_error::ProgramError;
+use solana_program::pubkey::Pubkey;
+use std::ops::{Deref, DerefMut};
+
+/// Account container that checks ownership on deserialization.
+#[derive(Clone)]
+pub struct Account<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> {
+    account: T,
+    info: AccountInfo<'info>,
+}
+
+impl<'a, T: AccountSerialize + AccountDeserialize + Owner + Clone> Account<'a, T> {
+    fn new(info: AccountInfo<'a>, account: T) -> Account<'a, T> {
+        Self { info, account }
+    }
+
+    /// Deserializes the given `info` into a `Account`.
+    #[inline(never)]
+    pub fn try_from(info: &AccountInfo<'a>) -> Result<Account<'a, T>, ProgramError> {
+        if info.owner != &T::owner() {
+            return Err(ErrorCode::AccountNotProgramOwned.into());
+        }
+        let mut data: &[u8] = &info.try_borrow_data()?;
+        Ok(Account::new(info.clone(), T::try_deserialize(&mut data)?))
+    }
+
+    /// Deserializes the given `info` into a `Account` without checking
+    /// the account discriminator. Be careful when using this and avoid it if
+    /// possible.
+    #[inline(never)]
+    pub fn try_from_unchecked(info: &AccountInfo<'a>) -> Result<Account<'a, T>, ProgramError> {
+        if info.owner != &T::owner() {
+            return Err(ErrorCode::AccountNotProgramOwned.into());
+        }
+        let mut data: &[u8] = &info.try_borrow_data()?;
+        Ok(Account::new(
+            info.clone(),
+            T::try_deserialize_unchecked(&mut data)?,
+        ))
+    }
+
+    /// Reloads the account from storage. This is useful, for example, when
+    /// observing side effects after CPI.
+    pub fn reload(&mut self) -> ProgramResult {
+        let mut data: &[u8] = &self.info.try_borrow_data()?;
+        self.account = T::try_deserialize(&mut data)?;
+        Ok(())
+    }
+
+    pub fn into_inner(self) -> T {
+        self.account
+    }
+}
+
+impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> Accounts<'info>
+    for Account<'info, T>
+where
+    T: AccountSerialize + AccountDeserialize + Owner + Clone,
+{
+    #[inline(never)]
+    fn try_accounts(
+        _program_id: &Pubkey,
+        accounts: &mut &[AccountInfo<'info>],
+        _ix_data: &[u8],
+    ) -> Result<Self, ProgramError> {
+        if accounts.is_empty() {
+            return Err(ErrorCode::AccountNotEnoughKeys.into());
+        }
+        let account = &accounts[0];
+        *accounts = &accounts[1..];
+        Account::try_from(account)
+    }
+}
+
+impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AccountsExit<'info>
+    for Account<'info, T>
+{
+    fn exit(&self, program_id: &Pubkey) -> ProgramResult {
+        // Only persist if the owner is the current program.
+        if &T::owner() == program_id {
+            let info = self.to_account_info();
+            let mut data = info.try_borrow_mut_data()?;
+            let dst: &mut [u8] = &mut data;
+            let mut cursor = std::io::Cursor::new(dst);
+            self.account.try_serialize(&mut cursor)?;
+        }
+        Ok(())
+    }
+}
+
+impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AccountsClose<'info>
+    for Account<'info, T>
+{
+    fn close(&self, sol_destination: AccountInfo<'info>) -> ProgramResult {
+        crate::common::close(self.to_account_info(), sol_destination)
+    }
+}
+
+impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> ToAccountMetas
+    for Account<'info, T>
+{
+    fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
+        let is_signer = is_signer.unwrap_or(self.info.is_signer);
+        let meta = match self.info.is_writable {
+            false => AccountMeta::new_readonly(*self.info.key, is_signer),
+            true => AccountMeta::new(*self.info.key, is_signer),
+        };
+        vec![meta]
+    }
+}
+
+impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> ToAccountInfos<'info>
+    for Account<'info, T>
+{
+    fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
+        vec![self.info.clone()]
+    }
+}
+
+impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> ToAccountInfo<'info>
+    for Account<'info, T>
+{
+    fn to_account_info(&self) -> AccountInfo<'info> {
+        self.info.clone()
+    }
+}
+
+impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AsRef<AccountInfo<'info>>
+    for Account<'info, T>
+{
+    fn as_ref(&self) -> &AccountInfo<'info> {
+        &self.info
+    }
+}
+
+impl<'a, T: AccountSerialize + AccountDeserialize + Owner + Clone> Deref for Account<'a, T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &(*self).account
+    }
+}
+
+impl<'a, T: AccountSerialize + AccountDeserialize + Owner + Clone> DerefMut for Account<'a, T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        #[cfg(feature = "anchor-debug")]
+        if !self.info.is_writable {
+            solana_program::msg!("The given Account is not mutable");
+            panic!();
+        }
+        &mut self.account
+    }
+}
+
+impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> Key for Account<'info, T> {
+    fn key(&self) -> Pubkey {
+        *self.info.key
+    }
+}

+ 10 - 3
lang/src/cpi_account.rs

@@ -1,7 +1,5 @@
 use crate::error::ErrorCode;
-use crate::{
-    AccountDeserialize, Accounts, AccountsExit, Key, ToAccountInfo, ToAccountInfos, ToAccountMetas,
-};
+use crate::*;
 use solana_program::account_info::AccountInfo;
 use solana_program::entrypoint::ProgramResult;
 use solana_program::instruction::AccountMeta;
@@ -119,3 +117,12 @@ impl<'info, T: AccountDeserialize + Clone> Key for CpiAccount<'info, T> {
         *self.info.key
     }
 }
+
+impl<'info, T> From<Account<'info, T>> for CpiAccount<'info, T>
+where
+    T: AccountSerialize + AccountDeserialize + Owner + Clone,
+{
+    fn from(a: Account<'info, T>) -> Self {
+        Self::new(a.to_account_info(), Box::new(a.into_inner()))
+    }
+}

+ 14 - 6
lang/src/lib.rs

@@ -31,6 +31,7 @@ use solana_program::program_error::ProgramError;
 use solana_program::pubkey::Pubkey;
 use std::io::Write;
 
+mod account;
 mod account_info;
 mod account_meta;
 mod boxed;
@@ -48,6 +49,7 @@ pub mod state;
 mod sysvar;
 mod vec;
 
+pub use crate::account::Account;
 pub use crate::context::{Context, CpiContext, CpiStateContext};
 pub use crate::cpi_account::CpiAccount;
 pub use crate::cpi_state::CpiState;
@@ -56,7 +58,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, zero_copy};
+pub use anchor_attribute_account::{account, declare_id, zero_copy};
 pub use anchor_attribute_error::error;
 pub use anchor_attribute_event::{emit, event};
 pub use anchor_attribute_interface::interface;
@@ -199,6 +201,12 @@ pub trait Bump {
     fn seed(&self) -> u8;
 }
 
+/// Defines an address expected to own an account.
+pub trait Owner {
+    fn owner() -> Pubkey;
+}
+
+/// Defines the Pubkey of an account.
 pub trait Key {
     fn key(&self) -> Pubkey;
 }
@@ -213,15 +221,15 @@ impl Key for Pubkey {
 /// All programs should include it via `anchor_lang::prelude::*;`.
 pub mod prelude {
     pub use super::{
-        access_control, account, emit, error, event, interface, program, require, state, zero_copy,
-        AccountDeserialize, AccountSerialize, Accounts, AccountsExit, AnchorDeserialize,
-        AnchorSerialize, Context, CpiAccount, CpiContext, CpiState, CpiStateContext, Key, Loader,
-        ProgramAccount, ProgramState, Sysvar, ToAccountInfo, ToAccountInfos, ToAccountMetas,
+        access_control, account, declare_id, emit, error, event, interface, program, require,
+        state, zero_copy, Account, AccountDeserialize, AccountSerialize, Accounts, AccountsExit,
+        AnchorDeserialize, AnchorSerialize, Context, CpiAccount, CpiContext, CpiState,
+        CpiStateContext, Key, Loader, ProgramAccount, ProgramState, Sysvar, ToAccountInfo,
+        ToAccountInfos, ToAccountMetas,
     };
 
     pub use borsh;
     pub use solana_program::account_info::{next_account_info, AccountInfo};
-    pub use solana_program::declare_id;
     pub use solana_program::entrypoint::ProgramResult;
     pub use solana_program::instruction::AccountMeta;
     pub use solana_program::msg;

+ 28 - 99
lang/syn/src/codegen/accounts/constraints.rs

@@ -142,9 +142,10 @@ pub fn generate_constraint_init(f: &Field, c: &ConstraintInitGroup) -> proc_macr
 
 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);
+    let ty_decl = f.ty_decl();
+    let from_account_info = f.from_account_info(None);
     quote! {
-        let #field: #account_wrapper_ty<#account_ty> = {
+        let #field: #ty_decl = {
             let mut __data: &[u8] = &#field.try_borrow_data()?;
             let mut __disc_bytes = [0u8; 8];
             __disc_bytes.copy_from_slice(&__data[..8]);
@@ -152,10 +153,7 @@ pub fn generate_constraint_zeroed(f: &Field, _c: &ConstraintZeroed) -> proc_macr
             if __discriminator != 0 {
                 return Err(anchor_lang::__private::ErrorCode::ConstraintZero.into());
             }
-            #account_wrapper_ty::try_from_unchecked(
-                program_id,
-                &#field,
-            )?
+            #from_account_info
         };
     }
 }
@@ -198,6 +196,7 @@ 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::Account(_) => 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."),
@@ -308,7 +307,7 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
             }
         }
     };
-    generate_pda(f, seeds_with_nonce, payer, &c.space, &c.kind)
+    generate_init(f, seeds_with_nonce, payer, &c.space, &c.kind)
 }
 
 fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2::TokenStream {
@@ -366,56 +365,7 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2
     }
 }
 
-fn parse_ty(f: &Field) -> (proc_macro2::TokenStream, proc_macro2::TokenStream, bool) {
-    match &f.ty {
-        Ty::ProgramAccount(ty) => {
-            let ident = &ty.account_type_path;
-            (
-                quote! {
-                    #ident
-                },
-                quote! {
-                    anchor_lang::ProgramAccount
-                },
-                false,
-            )
-        }
-        Ty::Loader(ty) => {
-            let ident = &ty.account_type_path;
-            (
-                quote! {
-                    #ident
-                },
-                quote! {
-                    anchor_lang::Loader
-                },
-                true,
-            )
-        }
-        Ty::CpiAccount(ty) => {
-            let ident = &ty.account_type_path;
-            (
-                quote! {
-                    #ident
-                },
-                quote! {
-                    anchor_lang::CpiAccount
-                },
-                false,
-            )
-        }
-        Ty::AccountInfo => (
-            quote! {
-                AccountInfo
-            },
-            quote! {},
-            false,
-        ),
-        _ => panic!("Invalid type for initializing a program derived address"),
-    }
-}
-
-pub fn generate_pda(
+pub fn generate_init(
     f: &Field,
     seeds_with_nonce: proc_macro2::TokenStream,
     payer: proc_macro2::TokenStream,
@@ -423,30 +373,8 @@ pub fn generate_pda(
     kind: &InitKind,
 ) -> proc_macro2::TokenStream {
     let field = &f.ident;
-    let (account_ty, account_wrapper_ty, is_zero_copy) = parse_ty(f);
-
-    let (combined_account_ty, try_from) = match f.ty {
-        Ty::AccountInfo => (
-            quote! {
-                AccountInfo
-            },
-            quote! {
-                #field.to_account_info()
-            },
-        ),
-        _ => (
-            quote! {
-                #account_wrapper_ty<#account_ty>
-            },
-            quote! {
-                #account_wrapper_ty::try_from_unchecked(
-                    program_id,
-                    &#field.to_account_info(),
-                )?
-            },
-        ),
-    };
-
+    let ty_decl = f.ty_decl();
+    let from_account_info = f.from_account_info(Some(kind));
     match kind {
         InitKind::Token { owner, mint } => {
             let create_account = generate_create_account(
@@ -456,7 +384,7 @@ pub fn generate_pda(
                 seeds_with_nonce,
             );
             quote! {
-                let #field: #combined_account_ty = {
+                let #field: #ty_decl = {
                     // Define payer variable.
                     #payer
 
@@ -473,9 +401,8 @@ pub fn generate_pda(
                     };
                     let cpi_ctx = CpiContext::new(cpi_program, accounts);
                     anchor_spl::token::initialize_account(cpi_ctx)?;
-                    anchor_lang::CpiAccount::try_from_unchecked(
-                        &#field.to_account_info(),
-                    )?
+                    let mut pa: #ty_decl = #from_account_info;
+                    pa
                 };
             }
         }
@@ -487,7 +414,7 @@ pub fn generate_pda(
                 seeds_with_nonce,
             );
             quote! {
-                let #field: #combined_account_ty = {
+                let #field: #ty_decl = {
                     // Define payer variable.
                     #payer
 
@@ -502,9 +429,8 @@ 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_unchecked(
-                        &#field.to_account_info(),
-                    )?
+                    let mut pa: #ty_decl = #from_account_info;
+                    pa
                 };
             }
         }
@@ -512,18 +438,21 @@ pub fn generate_pda(
             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();
+                None => {
+                    let account_ty = f.account_ty();
+                    match matches!(f.ty, Ty::Loader(_)) {
+                        false => {
+                            quote! {
+                                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();
+                        true => {
+                            quote! {
+                                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;
@@ -547,7 +476,7 @@ pub fn generate_pda(
                     #space
                     #payer
                     #create_account
-                    let mut pa: #combined_account_ty = #try_from;
+                    let mut pa: #ty_decl = #from_account_info;
                     pa
                 };
             }

+ 2 - 62
lang/syn/src/codegen/accounts/try_accounts.rs

@@ -1,6 +1,5 @@
 use crate::codegen::accounts::{constraints, generics, ParsedGenerics};
-use crate::{AccountField, AccountsStruct, Field, SysvarTy, Ty};
-use proc_macro2::TokenStream;
+use crate::{AccountField, AccountsStruct};
 use quote::quote;
 use syn::Expr;
 
@@ -40,7 +39,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
                             *accounts = &accounts[1..];
                         }
                     } else {
-                        let name = typed_ident(f);
+                        let name = f.typed_ident();
                         quote! {
                             #[cfg(feature = "anchor-debug")]
                             ::solana_program::log::sol_log(stringify!(#name));
@@ -107,65 +106,6 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
     }
 }
 
-fn typed_ident(field: &Field) -> TokenStream {
-    let name = &field.ident;
-
-    let ty = match &field.ty {
-        Ty::AccountInfo => quote! { AccountInfo },
-        Ty::ProgramState(ty) => {
-            let account = &ty.account_type_path;
-            quote! {
-                ProgramState<#account>
-            }
-        }
-        Ty::CpiState(ty) => {
-            let account = &ty.account_type_path;
-            quote! {
-                CpiState<#account>
-            }
-        }
-        Ty::ProgramAccount(ty) => {
-            let account = &ty.account_type_path;
-            quote! {
-                ProgramAccount<#account>
-            }
-        }
-        Ty::Loader(ty) => {
-            let account = &ty.account_type_path;
-            quote! {
-                Loader<#account>
-            }
-        }
-        Ty::CpiAccount(ty) => {
-            let account = &ty.account_type_path;
-            quote! {
-                CpiAccount<#account>
-            }
-        }
-        Ty::Sysvar(ty) => {
-            let account = match ty {
-                SysvarTy::Clock => quote! {Clock},
-                SysvarTy::Rent => quote! {Rent},
-                SysvarTy::EpochSchedule => quote! {EpochSchedule},
-                SysvarTy::Fees => quote! {Fees},
-                SysvarTy::RecentBlockhashes => quote! {RecentBlockhashes},
-                SysvarTy::SlotHashes => quote! {SlotHashes},
-                SysvarTy::SlotHistory => quote! {SlotHistory},
-                SysvarTy::StakeHistory => quote! {StakeHistory},
-                SysvarTy::Instructions => quote! {Instructions},
-                SysvarTy::Rewards => quote! {Rewards},
-            };
-            quote! {
-                Sysvar<#account>
-            }
-        }
-    };
-
-    quote! {
-        #name: #ty
-    }
-}
-
 pub fn generate_constraints(accs: &AccountsStruct) -> proc_macro2::TokenStream {
     let non_init_fields: Vec<&AccountField> =
         accs.fields.iter().filter(|af| !is_init(af)).collect();

+ 2 - 3
lang/syn/src/codegen/program/cpi.rs

@@ -36,7 +36,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
                                         let data = anchor_lang::InstructionData::data(&ix);
                                         let accounts = ctx.to_account_metas(None);
                                         anchor_lang::solana_program::instruction::Instruction {
-                                            program_id: *ctx.program().key,
+                                            program_id: crate::ID,
                                             accounts,
                                             data,
                                         }
@@ -82,13 +82,12 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
                             data.append(&mut ix_data);
                             let accounts = ctx.to_account_metas(None);
                             anchor_lang::solana_program::instruction::Instruction {
-                                program_id: *ctx.program.key,
+                                program_id: crate::ID,
                                 accounts,
                                 data,
                             }
                         };
                         let mut acc_infos = ctx.to_account_infos();
-                        acc_infos.push(ctx.program.clone());
                         anchor_lang::solana_program::program::invoke_signed(
                             &ix,
                             &acc_infos,

+ 181 - 0
lang/syn/src/lib.rs

@@ -3,6 +3,7 @@ use codegen::program as program_codegen;
 use parser::accounts as accounts_parser;
 use parser::program as program_parser;
 use proc_macro2::{Span, TokenStream};
+use quote::quote;
 use quote::ToTokens;
 use std::ops::Deref;
 use syn::ext::IdentExt;
@@ -161,6 +162,177 @@ pub struct Field {
     pub ty: Ty,
 }
 
+impl Field {
+    pub fn typed_ident(&self) -> proc_macro2::TokenStream {
+        let name = &self.ident;
+        let ty_decl = self.ty_decl();
+        quote! {
+            #name: #ty_decl
+        }
+    }
+
+    pub fn ty_decl(&self) -> proc_macro2::TokenStream {
+        let account_ty = self.account_ty();
+        let container_ty = self.container_ty();
+        match &self.ty {
+            Ty::AccountInfo => quote! {
+                AccountInfo
+            },
+            Ty::Account(AccountTy { boxed, .. }) => {
+                if *boxed {
+                    quote! {
+                        Box<#container_ty<#account_ty>>
+                    }
+                } else {
+                    quote! {
+                        #container_ty<#account_ty>
+                    }
+                }
+            }
+            Ty::Sysvar(ty) => {
+                let account = match ty {
+                    SysvarTy::Clock => quote! {Clock},
+                    SysvarTy::Rent => quote! {Rent},
+                    SysvarTy::EpochSchedule => quote! {EpochSchedule},
+                    SysvarTy::Fees => quote! {Fees},
+                    SysvarTy::RecentBlockhashes => quote! {RecentBlockhashes},
+                    SysvarTy::SlotHashes => quote! {SlotHashes},
+                    SysvarTy::SlotHistory => quote! {SlotHistory},
+                    SysvarTy::StakeHistory => quote! {StakeHistory},
+                    SysvarTy::Instructions => quote! {Instructions},
+                    SysvarTy::Rewards => quote! {Rewards},
+                };
+                quote! {
+                    Sysvar<#account>
+                }
+            }
+            _ => quote! {
+                #container_ty<#account_ty>
+            },
+        }
+    }
+
+    // TODO: remove the option once `CpiAccount` is completely removed (not
+    //       just deprecated).
+    pub fn from_account_info(&self, kind: Option<&InitKind>) -> proc_macro2::TokenStream {
+        let field = &self.ident;
+        let container_ty = self.container_ty();
+        match &self.ty {
+            Ty::AccountInfo => quote! { #field.to_account_info() },
+            Ty::Account(AccountTy { boxed, .. }) => {
+                if *boxed {
+                    quote! {
+                        Box::new(#container_ty::try_from_unchecked(
+                            &#field,
+                        )?)
+                    }
+                } else {
+                    quote! {
+                        #container_ty::try_from_unchecked(
+                            &#field,
+                        )?
+                    }
+                }
+            }
+            _ => {
+                let owner_addr = match &kind {
+                    None => quote! { program_id },
+                    Some(InitKind::Program { .. }) => quote! {
+                        program_id
+                    },
+                    _ => quote! {
+                        &anchor_spl::token::ID
+                    },
+                };
+                quote! {
+                    #container_ty::try_from_unchecked(
+                        #owner_addr,
+                        &#field,
+                    )?
+                }
+            }
+        }
+    }
+
+    pub fn container_ty(&self) -> proc_macro2::TokenStream {
+        match &self.ty {
+            Ty::ProgramAccount(_) => quote! {
+                anchor_lang::ProgramAccount
+            },
+            Ty::Account(_) => quote! {
+                anchor_lang::Account
+            },
+            Ty::Loader(_) => quote! {
+                anchor_lang::Loader
+            },
+            Ty::CpiAccount(_) => quote! {
+                anchor_lang::CpiAccount
+            },
+            Ty::Sysvar(_) => quote! { anchor_lang::Sysvar },
+            Ty::CpiState(_) => quote! { anchor_lang::CpiState },
+            Ty::ProgramState(_) => quote! { anchor_lang::ProgramState },
+            Ty::AccountInfo => quote! {},
+        }
+    }
+
+    // Returns the inner account struct type.
+    pub fn account_ty(&self) -> proc_macro2::TokenStream {
+        match &self.ty {
+            Ty::AccountInfo => quote! {
+                AccountInfo
+            },
+            Ty::ProgramAccount(ty) => {
+                let ident = &ty.account_type_path;
+                quote! {
+                    #ident
+                }
+            }
+            Ty::Account(ty) => {
+                let ident = &ty.account_type_path;
+                quote! {
+                    #ident
+                }
+            }
+            Ty::Loader(ty) => {
+                let ident = &ty.account_type_path;
+                quote! {
+                    #ident
+                }
+            }
+            Ty::CpiAccount(ty) => {
+                let ident = &ty.account_type_path;
+                quote! {
+                    #ident
+                }
+            }
+            Ty::ProgramState(ty) => {
+                let account = &ty.account_type_path;
+                quote! {
+                    #account
+                }
+            }
+            Ty::CpiState(ty) => {
+                let account = &ty.account_type_path;
+                quote! {
+                    #account
+                }
+            }
+            Ty::Sysvar(ty) => match ty {
+                SysvarTy::Clock => quote! {Clock},
+                SysvarTy::Rent => quote! {Rent},
+                SysvarTy::EpochSchedule => quote! {EpochSchedule},
+                SysvarTy::Fees => quote! {Fees},
+                SysvarTy::RecentBlockhashes => quote! {RecentBlockhashes},
+                SysvarTy::SlotHashes => quote! {SlotHashes},
+                SysvarTy::SlotHistory => quote! {SlotHistory},
+                SysvarTy::StakeHistory => quote! {StakeHistory},
+                SysvarTy::Instructions => quote! {Instructions},
+                SysvarTy::Rewards => quote! {Rewards},
+            },
+        }
+    }
+}
+
 #[derive(Debug)]
 pub struct CompositeField {
     pub ident: Ident,
@@ -180,6 +352,7 @@ pub enum Ty {
     Loader(LoaderTy),
     CpiAccount(CpiAccountTy),
     Sysvar(SysvarTy),
+    Account(AccountTy),
 }
 
 #[derive(Debug, PartialEq)]
@@ -224,6 +397,14 @@ pub struct LoaderTy {
     pub account_type_path: TypePath,
 }
 
+#[derive(Debug, PartialEq)]
+pub struct AccountTy {
+    // The struct type of the account.
+    pub account_type_path: TypePath,
+    // True if the account has been boxed via `Box<T>`.
+    pub boxed: bool,
+}
+
 #[derive(Debug)]
 pub struct Error {
     pub name: String,

+ 2 - 1
lang/syn/src/parser/accounts/constraints.rs

@@ -561,11 +561,12 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
 
     fn add_close(&mut self, c: Context<ConstraintClose>) -> ParseResult<()> {
         if !matches!(self.f_ty, Some(Ty::ProgramAccount(_)))
+            && !matches!(self.f_ty, Some(Ty::Account(_)))
             && !matches!(self.f_ty, Some(Ty::Loader(_)))
         {
             return Err(ParseError::new(
                 c.span(),
-                "close must be on a ProgramAccount",
+                "close must be on an Account, ProgramAccount, or Loader",
             ));
         }
         if self.mutable.is_none() {

+ 57 - 5
lang/syn/src/parser/accounts/mod.rs

@@ -1,7 +1,4 @@
-use crate::{
-    AccountField, AccountsStruct, CompositeField, CpiAccountTy, CpiStateTy, Field, LoaderTy,
-    ProgramAccountTy, ProgramStateTy, SysvarTy, Ty,
-};
+use crate::*;
 use syn::parse::{Error as ParseError, Result as ParseResult};
 use syn::punctuated::Punctuated;
 use syn::spanned::Spanned;
@@ -76,6 +73,7 @@ fn is_field_primitive(f: &syn::Field) -> ParseResult<bool> {
             | "AccountInfo"
             | "CpiState"
             | "Loader"
+            | "Account"
     );
     Ok(r)
 }
@@ -93,6 +91,7 @@ fn parse_ty(f: &syn::Field) -> ParseResult<Ty> {
         "Sysvar" => Ty::Sysvar(parse_sysvar(&path)?),
         "AccountInfo" => Ty::AccountInfo,
         "Loader" => Ty::Loader(parse_program_account_zero_copy(&path)?),
+        "Account" => Ty::Account(parse_account_ty(&path)?),
         _ => return Err(ParseError::new(f.ty.span(), "invalid account type given")),
     };
 
@@ -104,6 +103,12 @@ fn ident_string(f: &syn::Field) -> ParseResult<String> {
         syn::Type::Path(ty_path) => ty_path.path.clone(),
         _ => return Err(ParseError::new(f.ty.span(), "invalid type")),
     };
+    if parser::tts_to_string(&path)
+        .replace(" ", "")
+        .starts_with("Box<Account<")
+    {
+        return Ok("Account".to_string());
+    }
     // TODO: allow segmented paths.
     if path.segments.len() != 1 {
         return Err(ParseError::new(
@@ -151,7 +156,54 @@ fn parse_program_account_zero_copy(path: &syn::Path) -> ParseResult<LoaderTy> {
     })
 }
 
-fn parse_account(path: &syn::Path) -> ParseResult<syn::TypePath> {
+fn parse_account_ty(path: &syn::Path) -> ParseResult<AccountTy> {
+    let account_type_path = parse_account(path)?;
+    let boxed = parser::tts_to_string(&path)
+        .replace(" ", "")
+        .starts_with("Box<Account<");
+    Ok(AccountTy {
+        account_type_path,
+        boxed,
+    })
+}
+
+// TODO: this whole method is a hack. Do something more idiomatic.
+fn parse_account(mut path: &syn::Path) -> ParseResult<syn::TypePath> {
+    if parser::tts_to_string(path)
+        .replace(" ", "")
+        .starts_with("Box<Account<")
+    {
+        let segments = &path.segments[0];
+        match &segments.arguments {
+            syn::PathArguments::AngleBracketed(args) => {
+                // Expected: <'info, MyType>.
+                if args.args.len() != 1 {
+                    return Err(ParseError::new(
+                        args.args.span(),
+                        "bracket arguments must be the lifetime and type",
+                    ));
+                }
+                match &args.args[0] {
+                    syn::GenericArgument::Type(syn::Type::Path(ty_path)) => {
+                        path = &ty_path.path;
+                    }
+                    _ => {
+                        return Err(ParseError::new(
+                            args.args[1].span(),
+                            "first bracket argument must be a lifetime",
+                        ))
+                    }
+                }
+            }
+            _ => {
+                return Err(ParseError::new(
+                    segments.arguments.span(),
+                    "expected angle brackets with a lifetime and type",
+                ))
+            }
+        }
+    }
+
     let segments = &path.segments[0];
     match &segments.arguments {
         syn::PathArguments::AngleBracketed(args) => {

+ 3 - 0
lang/tests/generics_test.rs

@@ -4,6 +4,9 @@ use anchor_lang::prelude::borsh::maybestd::io::Write;
 use anchor_lang::prelude::*;
 use borsh::{BorshDeserialize, BorshSerialize};
 
+// Needed to declare accounts.
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+
 #[derive(Accounts)]
 pub struct GenericsTest<'info, T, U, const N: usize>
 where

+ 27 - 0
spl/src/token.rs

@@ -5,6 +5,7 @@ use anchor_lang::solana_program::program_error::ProgramError;
 use anchor_lang::solana_program::program_pack::Pack;
 use anchor_lang::solana_program::pubkey::Pubkey;
 use anchor_lang::{Accounts, CpiContext};
+use std::io::Write;
 use std::ops::Deref;
 
 pub use spl_token::ID;
@@ -245,6 +246,19 @@ impl anchor_lang::AccountDeserialize for TokenAccount {
     }
 }
 
+impl anchor_lang::AccountSerialize for TokenAccount {
+    fn try_serialize<W: Write>(&self, _writer: &mut W) -> Result<(), ProgramError> {
+        // no-op
+        Ok(())
+    }
+}
+
+impl anchor_lang::Owner for TokenAccount {
+    fn owner() -> Pubkey {
+        ID
+    }
+}
+
 impl Deref for TokenAccount {
     type Target = spl_token::state::Account;
 
@@ -270,6 +284,19 @@ impl anchor_lang::AccountDeserialize for Mint {
     }
 }
 
+impl anchor_lang::AccountSerialize for Mint {
+    fn try_serialize<W: Write>(&self, _writer: &mut W) -> Result<(), ProgramError> {
+        // no-op
+        Ok(())
+    }
+}
+
+impl anchor_lang::Owner for Mint {
+    fn owner() -> Pubkey {
+        ID
+    }
+}
+
 impl Deref for Mint {
     type Target = spl_token::state::Mint;
 

+ 3 - 0
tests/cashiers-check/Anchor.toml

@@ -2,5 +2,8 @@
 cluster = "localnet"
 wallet = "~/.config/solana/id.json"
 
+[programs.localnet]
+cashiers_check = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
+
 [scripts]
 test = "mocha -t 1000000 tests/"

+ 10 - 8
tests/cashiers-check/programs/cashiers-check/src/lib.rs

@@ -7,6 +7,8 @@ use anchor_lang::prelude::*;
 use anchor_spl::token::{self, TokenAccount, Transfer};
 use std::convert::Into;
 
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+
 #[program]
 pub mod cashiers_check {
     use super::*;
@@ -84,18 +86,18 @@ pub mod cashiers_check {
 pub struct CreateCheck<'info> {
     // Check being created.
     #[account(zero)]
-    check: ProgramAccount<'info, Check>,
+    check: Account<'info, Check>,
     // Check's token vault.
     #[account(mut, constraint = &vault.owner == check_signer.key)]
-    vault: CpiAccount<'info, TokenAccount>,
+    vault: Account<'info, TokenAccount>,
     // Program derived address for the check.
     check_signer: AccountInfo<'info>,
     // Token account the check is made from.
     #[account(mut, has_one = owner)]
-    from: CpiAccount<'info, TokenAccount>,
+    from: Account<'info, TokenAccount>,
     // Token account the check is made to.
     #[account(constraint = from.mint == to.mint)]
-    to: CpiAccount<'info, TokenAccount>,
+    to: Account<'info, TokenAccount>,
     // Owner of the `from` token account.
     owner: AccountInfo<'info>,
     token_program: AccountInfo<'info>,
@@ -118,7 +120,7 @@ impl<'info> CreateCheck<'info> {
 #[derive(Accounts)]
 pub struct CashCheck<'info> {
     #[account(mut, has_one = vault, has_one = to)]
-    check: ProgramAccount<'info, Check>,
+    check: Account<'info, Check>,
     #[account(mut)]
     vault: AccountInfo<'info>,
     #[account(
@@ -127,7 +129,7 @@ pub struct CashCheck<'info> {
     )]
     check_signer: AccountInfo<'info>,
     #[account(mut, has_one = owner)]
-    to: CpiAccount<'info, TokenAccount>,
+    to: Account<'info, TokenAccount>,
     #[account(signer)]
     owner: AccountInfo<'info>,
     token_program: AccountInfo<'info>,
@@ -136,7 +138,7 @@ pub struct CashCheck<'info> {
 #[derive(Accounts)]
 pub struct CancelCheck<'info> {
     #[account(mut, has_one = vault, has_one = from)]
-    check: ProgramAccount<'info, Check>,
+    check: Account<'info, Check>,
     #[account(mut)]
     vault: AccountInfo<'info>,
     #[account(
@@ -145,7 +147,7 @@ pub struct CancelCheck<'info> {
     )]
     check_signer: AccountInfo<'info>,
     #[account(mut, has_one = owner)]
-    from: CpiAccount<'info, TokenAccount>,
+    from: Account<'info, TokenAccount>,
     #[account(signer)]
     owner: AccountInfo<'info>,
     token_program: AccountInfo<'info>,

+ 1 - 0
tests/cfo/Anchor.toml

@@ -3,6 +3,7 @@ cluster = "localnet"
 wallet = "~/.config/solana/id.json"
 
 [programs.localnet]
+cfo = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
 registry = { address = "GrAkKfEpTKQuVHG2Y97Y2FF4i7y7Q5AHLK94JBy7Y5yv", idl = "./deps/stake/target/idl/registry.json" }
 lockup = { address = "6ebQNeTPZ1j7k3TtkCCtEPRvG7GQsucQrZ7sSEDQi9Ks", idl = "./deps/stake/target/idl/lockup.json" }
 

+ 25 - 21
tests/cfo/programs/cfo/src/lib.rs

@@ -8,6 +8,8 @@ use anchor_spl::{dex, mint};
 use registry::{Registrar, RewardVendorKind};
 use std::convert::TryInto;
 
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+
 /// CFO is the program representing the Serum chief financial officer. It is
 /// the program responsible for collecting and distributing fees from the Serum
 /// DEX.
@@ -303,7 +305,7 @@ pub struct CreateOfficer<'info> {
         bump = bumps.bump,
         payer = authority,
     )]
-    officer: ProgramAccount<'info, Officer>,
+    officer: Box<Account<'info, Officer>>,
     #[account(
         init,
         seeds = [b"vault", officer.key().as_ref()],
@@ -312,7 +314,7 @@ pub struct CreateOfficer<'info> {
         token::mint = mint,
         token::authority = officer,
     )]
-    srm_vault: CpiAccount<'info, TokenAccount>,
+    srm_vault: Box<Account<'info, TokenAccount>>,
     #[account(
         init,
         seeds = [b"stake", officer.key().as_ref()],
@@ -321,7 +323,7 @@ pub struct CreateOfficer<'info> {
         token::mint = mint,
         token::authority = officer,
     )]
-    stake: CpiAccount<'info, TokenAccount>,
+    stake: Box<Account<'info, TokenAccount>>,
     #[account(
         init,
         seeds = [b"treasury", officer.key().as_ref()],
@@ -330,7 +332,7 @@ pub struct CreateOfficer<'info> {
         token::mint = mint,
         token::authority = officer,
     )]
-    treasury: CpiAccount<'info, TokenAccount>,
+    treasury: Box<Account<'info, TokenAccount>>,
     #[account(signer)]
     authority: AccountInfo<'info>,
     #[cfg_attr(
@@ -352,7 +354,7 @@ pub struct CreateOfficer<'info> {
 #[derive(Accounts)]
 #[instruction(bump: u8)]
 pub struct CreateOfficerToken<'info> {
-    officer: ProgramAccount<'info, Officer>,
+    officer: Account<'info, Officer>,
     #[account(
         init,
         seeds = [officer.key().as_ref(), mint.key().as_ref()],
@@ -361,7 +363,7 @@ pub struct CreateOfficerToken<'info> {
         token::authority = officer,
         payer = payer,
     )]
-    token: CpiAccount<'info, TokenAccount>,
+    token: Account<'info, TokenAccount>,
     #[account(owner = token_program)]
     mint: AccountInfo<'info>,
     #[account(mut, signer)]
@@ -376,7 +378,7 @@ pub struct CreateOfficerToken<'info> {
 #[derive(Accounts)]
 pub struct SetDistribution<'info> {
     #[account(has_one = authority)]
-    officer: ProgramAccount<'info, Officer>,
+    officer: Account<'info, Officer>,
     #[account(signer)]
     authority: AccountInfo<'info>,
 }
@@ -387,14 +389,14 @@ pub struct SweepFees<'info> {
         seeds = [dex.dex_program.key.as_ref()],
         bump = officer.bumps.bump,
     )]
-    officer: ProgramAccount<'info, Officer>,
+    officer: Account<'info, Officer>,
     #[account(
         mut,
         owner = dex.token_program,
         seeds = [officer.key().as_ref(), mint.key().as_ref()],
         bump,
     )]
-    sweep_vault: CpiAccount<'info, TokenAccount>,
+    sweep_vault: Account<'info, TokenAccount>,
     mint: AccountInfo<'info>,
     dex: Dex<'info>,
 }
@@ -418,7 +420,7 @@ pub struct SwapToUsdc<'info> {
         seeds = [dex_program.key().as_ref()],
         bump = officer.bumps.bump,
     )]
-    officer: ProgramAccount<'info, Officer>,
+    officer: Account<'info, Officer>,
     market: DexMarketAccounts<'info>,
     #[account(
         owner = token_program,
@@ -447,7 +449,7 @@ pub struct SwapToSrm<'info> {
         seeds = [dex_program.key().as_ref()],
         bump = officer.bumps.bump,
     )]
-    officer: ProgramAccount<'info, Officer>,
+    officer: Account<'info, Officer>,
     market: DexMarketAccounts<'info>,
     #[account(
         owner = token_program,
@@ -513,14 +515,14 @@ pub struct DexMarketAccounts<'info> {
 #[derive(Accounts)]
 pub struct Distribute<'info> {
     #[account(has_one = treasury, has_one = stake)]
-    officer: ProgramAccount<'info, Officer>,
+    officer: Account<'info, Officer>,
     treasury: AccountInfo<'info>,
     stake: AccountInfo<'info>,
     #[account(
         owner = token_program,
         constraint = srm_vault.mint == mint::SRM,
     )]
-    srm_vault: CpiAccount<'info, TokenAccount>,
+    srm_vault: Account<'info, TokenAccount>,
     #[account(address = mint::SRM)]
     mint: AccountInfo<'info>,
     #[account(address = spl_token::ID)]
@@ -536,12 +538,12 @@ pub struct DropStakeReward<'info> {
         constraint = srm.registrar.key == &officer.registrar,
         constraint = msrm.registrar.key == &officer.msrm_registrar,
     )]
-    officer: ProgramAccount<'info, Officer>,
+    officer: Box<Account<'info, Officer>>,
     #[account(
         seeds = [b"stake", officer.key().as_ref()],
         bump = officer.bumps.stake,
     )]
-    stake: CpiAccount<'info, TokenAccount>,
+    stake: Box<Account<'info, TokenAccount>>,
     #[cfg_attr(
         not(feature = "test"),
         account(address = mint::SRM),
@@ -550,7 +552,7 @@ pub struct DropStakeReward<'info> {
     srm: DropStakeRewardPool<'info>,
     msrm: DropStakeRewardPool<'info>,
     #[account(owner = registry_program)]
-    msrm_registrar: CpiAccount<'info, Registrar>,
+    msrm_registrar: Box<Account<'info, Registrar>>,
     #[account(address = token::ID)]
     token_program: AccountInfo<'info>,
     #[account(address = registry::ID)]
@@ -569,7 +571,7 @@ pub struct DropStakeReward<'info> {
 pub struct DropStakeRewardPool<'info> {
     registrar: AccountInfo<'info>,
     reward_event_q: AccountInfo<'info>,
-    pool_mint: CpiAccount<'info, Mint>,
+    pool_mint: Account<'info, Mint>,
     vendor: AccountInfo<'info>,
     vendor_vault: AccountInfo<'info>,
 }
@@ -704,8 +706,9 @@ impl<'info> DropStakeReward<'info> {
         let program = self.registry_program.clone();
         let accounts = registry::DropReward {
             registrar: ProgramAccount::try_from(program.key, &self.srm.registrar).unwrap(),
-            reward_event_q: ProgramAccount::try_from(program.key, &self.srm.reward_event_q).unwrap(),
-            pool_mint: self.srm.pool_mint.clone(),
+            reward_event_q: ProgramAccount::try_from(program.key, &self.srm.reward_event_q)
+                .unwrap(),
+            pool_mint: self.srm.pool_mint.clone().into(),
             vendor: ProgramAccount::try_from(program.key, &self.srm.vendor).unwrap(),
             vendor_vault: CpiAccount::try_from(&self.srm.vendor_vault).unwrap(),
             depositor: self.stake.to_account_info(),
@@ -721,8 +724,9 @@ impl<'info> DropStakeReward<'info> {
         let program = self.registry_program.clone();
         let accounts = registry::DropReward {
             registrar: ProgramAccount::try_from(program.key, &self.msrm.registrar).unwrap(),
-            reward_event_q: ProgramAccount::try_from(program.key, &self.msrm.reward_event_q).unwrap(),
-            pool_mint: self.msrm.pool_mint.clone(),
+            reward_event_q: ProgramAccount::try_from(program.key, &self.msrm.reward_event_q)
+                .unwrap(),
+            pool_mint: self.msrm.pool_mint.clone().into(),
             vendor: ProgramAccount::try_from(program.key, &self.msrm.vendor).unwrap(),
             vendor_vault: CpiAccount::try_from(&self.msrm.vendor_vault).unwrap(),
             depositor: self.stake.to_account_info(),

+ 3 - 0
tests/chat/Anchor.toml

@@ -2,5 +2,8 @@
 cluster = "localnet"
 wallet = "~/.config/solana/id.json"
 
+[programs.localnet]
+chat = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
+
 [scripts]
 test = "mocha -t 1000000 tests/"

+ 4 - 2
tests/chat/programs/chat/src/lib.rs

@@ -2,6 +2,8 @@
 
 use anchor_lang::prelude::*;
 
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+
 #[program]
 pub mod chat {
     use super::*;
@@ -45,7 +47,7 @@ pub struct CreateUser<'info> {
         payer = authority,
         space = 320,
     )]
-    user: ProgramAccount<'info, User>,
+    user: Account<'info, User>,
     #[account(signer)]
     authority: AccountInfo<'info>,
     system_program: AccountInfo<'info>,
@@ -64,7 +66,7 @@ pub struct SendMessage<'info> {
         bump = user.bump,
         has_one = authority,
     )]
-    user: ProgramAccount<'info, User>,
+    user: Account<'info, User>,
     #[account(signer)]
     authority: AccountInfo<'info>,
     #[account(mut)]

+ 3 - 0
tests/composite/Anchor.toml

@@ -2,5 +2,8 @@
 cluster = "localnet"
 wallet = "~/.config/solana/id.json"
 
+[programs.localnet]
+composite = "EHthziFziNoac9LBGxEaVN47Y3uUiRoXvqAiR6oes4iU"
+
 [scripts]
 test = "mocha -t 1000000 tests/"

+ 6 - 4
tests/composite/programs/composite/src/lib.rs

@@ -3,6 +3,8 @@
 
 use anchor_lang::prelude::*;
 
+declare_id!("EHthziFziNoac9LBGxEaVN47Y3uUiRoXvqAiR6oes4iU");
+
 #[program]
 mod composite {
     use super::*;
@@ -28,9 +30,9 @@ mod composite {
 #[derive(Accounts)]
 pub struct Initialize<'info> {
     #[account(zero)]
-    pub dummy_a: ProgramAccount<'info, DummyA>,
+    pub dummy_a: Account<'info, DummyA>,
     #[account(zero)]
-    pub dummy_b: ProgramAccount<'info, DummyB>,
+    pub dummy_b: Account<'info, DummyB>,
 }
 
 #[derive(Accounts)]
@@ -42,13 +44,13 @@ pub struct CompositeUpdate<'info> {
 #[derive(Accounts)]
 pub struct Foo<'info> {
     #[account(mut)]
-    pub dummy_a: ProgramAccount<'info, DummyA>,
+    pub dummy_a: Account<'info, DummyA>,
 }
 
 #[derive(Accounts)]
 pub struct Bar<'info> {
     #[account(mut)]
-    pub dummy_b: ProgramAccount<'info, DummyB>,
+    pub dummy_b: Account<'info, DummyB>,
 }
 
 #[account]

+ 3 - 0
tests/errors/Anchor.toml

@@ -2,5 +2,8 @@
 cluster = "localnet"
 wallet = "~/.config/solana/id.json"
 
+[programs.localnet]
+errors = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
+
 [scripts]
 test = "mocha -t 1000000 tests/"

+ 3 - 1
tests/errors/programs/errors/src/lib.rs

@@ -3,6 +3,8 @@
 
 use anchor_lang::prelude::*;
 
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+
 #[program]
 mod errors {
     use super::*;
@@ -44,7 +46,7 @@ pub struct MutError<'info> {
 #[derive(Accounts)]
 pub struct HasOneError<'info> {
     #[account(zero, has_one = owner)]
-    my_account: ProgramAccount<'info, HasOneAccount>,
+    my_account: Account<'info, HasOneAccount>,
     owner: AccountInfo<'info>,
 }
 

+ 3 - 0
tests/escrow/Anchor.toml

@@ -2,5 +2,8 @@
 cluster = "localnet"
 wallet = "~/.config/solana/id.json"
 
+[programs.localnet]
+escrow = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
+
 [scripts]
 test = "mocha -t 1000000 tests/"

+ 12 - 10
tests/escrow/programs/escrow/src/lib.rs

@@ -19,6 +19,8 @@ use anchor_lang::prelude::*;
 use anchor_spl::token::{self, SetAuthority, TokenAccount, Transfer};
 use spl_token::instruction::AuthorityType;
 
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+
 #[program]
 pub mod escrow {
     use super::*;
@@ -104,10 +106,10 @@ pub struct InitializeEscrow<'info> {
         mut,
         constraint = initializer_deposit_token_account.amount >= initializer_amount
     )]
-    pub initializer_deposit_token_account: CpiAccount<'info, TokenAccount>,
-    pub initializer_receive_token_account: CpiAccount<'info, TokenAccount>,
+    pub initializer_deposit_token_account: Account<'info, TokenAccount>,
+    pub initializer_receive_token_account: Account<'info, TokenAccount>,
     #[account(zero)]
-    pub escrow_account: ProgramAccount<'info, EscrowAccount>,
+    pub escrow_account: Account<'info, EscrowAccount>,
     pub token_program: AccountInfo<'info>,
 }
 
@@ -116,13 +118,13 @@ pub struct Exchange<'info> {
     #[account(signer)]
     pub taker: AccountInfo<'info>,
     #[account(mut)]
-    pub taker_deposit_token_account: CpiAccount<'info, TokenAccount>,
+    pub taker_deposit_token_account: Account<'info, TokenAccount>,
     #[account(mut)]
-    pub taker_receive_token_account: CpiAccount<'info, TokenAccount>,
+    pub taker_receive_token_account: Account<'info, TokenAccount>,
     #[account(mut)]
-    pub pda_deposit_token_account: CpiAccount<'info, TokenAccount>,
+    pub pda_deposit_token_account: Account<'info, TokenAccount>,
     #[account(mut)]
-    pub initializer_receive_token_account: CpiAccount<'info, TokenAccount>,
+    pub initializer_receive_token_account: Account<'info, TokenAccount>,
     #[account(mut)]
     pub initializer_main_account: AccountInfo<'info>,
     #[account(
@@ -133,7 +135,7 @@ pub struct Exchange<'info> {
         constraint = escrow_account.initializer_key == *initializer_main_account.key,
         close = initializer_main_account
     )]
-    pub escrow_account: ProgramAccount<'info, EscrowAccount>,
+    pub escrow_account: Account<'info, EscrowAccount>,
     pub pda_account: AccountInfo<'info>,
     pub token_program: AccountInfo<'info>,
 }
@@ -142,7 +144,7 @@ pub struct Exchange<'info> {
 pub struct CancelEscrow<'info> {
     pub initializer: AccountInfo<'info>,
     #[account(mut)]
-    pub pda_deposit_token_account: CpiAccount<'info, TokenAccount>,
+    pub pda_deposit_token_account: Account<'info, TokenAccount>,
     pub pda_account: AccountInfo<'info>,
     #[account(
         mut,
@@ -150,7 +152,7 @@ pub struct CancelEscrow<'info> {
         constraint = escrow_account.initializer_deposit_token_account == *pda_deposit_token_account.to_account_info().key,
         close = initializer
     )]
-    pub escrow_account: ProgramAccount<'info, EscrowAccount>,
+    pub escrow_account: Account<'info, EscrowAccount>,
     pub token_program: AccountInfo<'info>,
 }
 

+ 3 - 0
tests/events/Anchor.toml

@@ -2,5 +2,8 @@
 cluster = "localnet"
 wallet = "~/.config/solana/id.json"
 
+[programs.localnet]
+events = "2dhGsWUzy5YKUsjZdLHLmkNpUDAXkNa9MYWsPc4Ziqzy"
+
 [scripts]
 test = "mocha -t 1000000 tests/"

+ 2 - 0
tests/events/programs/events/src/lib.rs

@@ -3,6 +3,8 @@
 
 use anchor_lang::prelude::*;
 
+declare_id!("2dhGsWUzy5YKUsjZdLHLmkNpUDAXkNa9MYWsPc4Ziqzy");
+
 #[program]
 pub mod events {
     use super::*;

+ 3 - 0
tests/ido-pool/Anchor.toml

@@ -2,5 +2,8 @@
 cluster = "localnet"
 wallet = "~/.config/solana/id.json"
 
+[programs.localnet]
+ido_pool = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
+
 [scripts]
 test = "mocha -t 1000000 tests/"

+ 27 - 25
tests/ido-pool/programs/ido-pool/src/lib.rs

@@ -5,6 +5,8 @@ use anchor_lang::prelude::*;
 use anchor_lang::solana_program::program_option::COption;
 use anchor_spl::token::{self, Burn, Mint, MintTo, TokenAccount, Transfer};
 
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+
 #[program]
 pub mod ido_pool {
     use super::*;
@@ -192,23 +194,23 @@ pub mod ido_pool {
 #[derive(Accounts)]
 pub struct InitializePool<'info> {
     #[account(zero)]
-    pub pool_account: ProgramAccount<'info, PoolAccount>,
+    pub pool_account: Box<Account<'info, PoolAccount>>,
     pub pool_signer: AccountInfo<'info>,
     #[account(
         constraint = redeemable_mint.mint_authority == COption::Some(*pool_signer.key),
         constraint = redeemable_mint.supply == 0
     )]
-    pub redeemable_mint: CpiAccount<'info, Mint>,
+    pub redeemable_mint: Account<'info, Mint>,
     #[account(constraint = usdc_mint.decimals == redeemable_mint.decimals)]
-    pub usdc_mint: CpiAccount<'info, Mint>,
+    pub usdc_mint: Account<'info, Mint>,
     #[account(mut, constraint = pool_watermelon.owner == *pool_signer.key)]
-    pub pool_watermelon: CpiAccount<'info, TokenAccount>,
+    pub pool_watermelon: Account<'info, TokenAccount>,
     #[account(constraint = pool_usdc.owner == *pool_signer.key)]
-    pub pool_usdc: CpiAccount<'info, TokenAccount>,
+    pub pool_usdc: Account<'info, TokenAccount>,
     #[account(signer)]
     pub distribution_authority: AccountInfo<'info>,
     #[account(mut, constraint = creator_watermelon.owner == *distribution_authority.key)]
-    pub creator_watermelon: CpiAccount<'info, TokenAccount>,
+    pub creator_watermelon: Account<'info, TokenAccount>,
     #[account(constraint = token_program.key == &token::ID)]
     pub token_program: AccountInfo<'info>,
     pub clock: Sysvar<'info, Clock>,
@@ -231,7 +233,7 @@ impl<'info> InitializePool<'info> {
 #[derive(Accounts)]
 pub struct ExchangeUsdcForRedeemable<'info> {
     #[account(has_one = redeemable_mint, has_one = pool_usdc)]
-    pub pool_account: ProgramAccount<'info, PoolAccount>,
+    pub pool_account: Account<'info, PoolAccount>,
     #[account(
         seeds = [pool_account.watermelon_mint.as_ref()],
         bump = pool_account.nonce,
@@ -241,15 +243,15 @@ pub struct ExchangeUsdcForRedeemable<'info> {
         mut,
         constraint = redeemable_mint.mint_authority == COption::Some(*pool_signer.key)
     )]
-    pub redeemable_mint: CpiAccount<'info, Mint>,
+    pub redeemable_mint: Account<'info, Mint>,
     #[account(mut, constraint = pool_usdc.owner == *pool_signer.key)]
-    pub pool_usdc: CpiAccount<'info, TokenAccount>,
+    pub pool_usdc: Account<'info, TokenAccount>,
     #[account(signer)]
     pub user_authority: AccountInfo<'info>,
     #[account(mut, constraint = user_usdc.owner == *user_authority.key)]
-    pub user_usdc: CpiAccount<'info, TokenAccount>,
+    pub user_usdc: Account<'info, TokenAccount>,
     #[account(mut, constraint = user_redeemable.owner == *user_authority.key)]
-    pub user_redeemable: CpiAccount<'info, TokenAccount>,
+    pub user_redeemable: Account<'info, TokenAccount>,
     #[account(constraint = token_program.key == &token::ID)]
     pub token_program: AccountInfo<'info>,
     pub clock: Sysvar<'info, Clock>,
@@ -258,7 +260,7 @@ pub struct ExchangeUsdcForRedeemable<'info> {
 #[derive(Accounts)]
 pub struct ExchangeRedeemableForUsdc<'info> {
     #[account(has_one = redeemable_mint, has_one = pool_usdc)]
-    pub pool_account: ProgramAccount<'info, PoolAccount>,
+    pub pool_account: Account<'info, PoolAccount>,
     #[account(
         seeds = [pool_account.watermelon_mint.as_ref()],
         bump = pool_account.nonce,
@@ -268,15 +270,15 @@ pub struct ExchangeRedeemableForUsdc<'info> {
         mut,
         constraint = redeemable_mint.mint_authority == COption::Some(*pool_signer.key)
     )]
-    pub redeemable_mint: CpiAccount<'info, Mint>,
+    pub redeemable_mint: Account<'info, Mint>,
     #[account(mut, constraint = pool_usdc.owner == *pool_signer.key)]
-    pub pool_usdc: CpiAccount<'info, TokenAccount>,
+    pub pool_usdc: Account<'info, TokenAccount>,
     #[account(signer)]
     pub user_authority: AccountInfo<'info>,
     #[account(mut, constraint = user_usdc.owner == *user_authority.key)]
-    pub user_usdc: CpiAccount<'info, TokenAccount>,
+    pub user_usdc: Account<'info, TokenAccount>,
     #[account(mut, constraint = user_redeemable.owner == *user_authority.key)]
-    pub user_redeemable: CpiAccount<'info, TokenAccount>,
+    pub user_redeemable: Account<'info, TokenAccount>,
     #[account(constraint = token_program.key == &token::ID)]
     pub token_program: AccountInfo<'info>,
     pub clock: Sysvar<'info, Clock>,
@@ -285,7 +287,7 @@ pub struct ExchangeRedeemableForUsdc<'info> {
 #[derive(Accounts)]
 pub struct ExchangeRedeemableForWatermelon<'info> {
     #[account(has_one = redeemable_mint, has_one = pool_watermelon)]
-    pub pool_account: ProgramAccount<'info, PoolAccount>,
+    pub pool_account: Account<'info, PoolAccount>,
     #[account(
         seeds = [pool_account.watermelon_mint.as_ref()],
         bump = pool_account.nonce,
@@ -295,15 +297,15 @@ pub struct ExchangeRedeemableForWatermelon<'info> {
         mut,
         constraint = redeemable_mint.mint_authority == COption::Some(*pool_signer.key)
     )]
-    pub redeemable_mint: CpiAccount<'info, Mint>,
+    pub redeemable_mint: Account<'info, Mint>,
     #[account(mut, constraint = pool_watermelon.owner == *pool_signer.key)]
-    pub pool_watermelon: CpiAccount<'info, TokenAccount>,
+    pub pool_watermelon: Account<'info, TokenAccount>,
     #[account(signer)]
     pub user_authority: AccountInfo<'info>,
     #[account(mut, constraint = user_watermelon.owner == *user_authority.key)]
-    pub user_watermelon: CpiAccount<'info, TokenAccount>,
+    pub user_watermelon: Account<'info, TokenAccount>,
     #[account(mut, constraint = user_redeemable.owner == *user_authority.key)]
-    pub user_redeemable: CpiAccount<'info, TokenAccount>,
+    pub user_redeemable: Account<'info, TokenAccount>,
     #[account(constraint = token_program.key == &token::ID)]
     pub token_program: AccountInfo<'info>,
     pub clock: Sysvar<'info, Clock>,
@@ -312,18 +314,18 @@ pub struct ExchangeRedeemableForWatermelon<'info> {
 #[derive(Accounts)]
 pub struct WithdrawPoolUsdc<'info> {
     #[account(has_one = pool_usdc, has_one = distribution_authority)]
-    pub pool_account: ProgramAccount<'info, PoolAccount>,
+    pub pool_account: Account<'info, PoolAccount>,
     #[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>,
+    pub pool_usdc: Account<'info, TokenAccount>,
     #[account(signer)]
     pub distribution_authority: AccountInfo<'info>,
     #[account(mut, constraint = creator_usdc.owner == *distribution_authority.key)]
-    pub creator_usdc: CpiAccount<'info, TokenAccount>,
+    pub creator_usdc: Account<'info, TokenAccount>,
     #[account(constraint = token_program.key == &token::ID)]
     pub token_program: AccountInfo<'info>,
     pub clock: Sysvar<'info, Clock>,
@@ -399,7 +401,7 @@ fn withdraw_only_phase(ctx: &Context<ExchangeRedeemableForUsdc>) -> Result<()> {
 
 // Asserts the IDO sale period has ended, based on the current timestamp.
 fn ido_over<'info>(
-    pool_account: &ProgramAccount<'info, PoolAccount>,
+    pool_account: &Account<'info, PoolAccount>,
     clock: &Sysvar<'info, Clock>,
 ) -> Result<()> {
     if !(pool_account.end_ido_ts < clock.unix_timestamp) {

+ 4 - 0
tests/interface/Anchor.toml

@@ -2,5 +2,9 @@
 cluster = "localnet"
 wallet = "~/.config/solana/id.json"
 
+[programs.localnet]
+counter = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
+counter_auth = "Aws2XRVHjNqCUbMmaU245ojT2DBJFYX58KVo2YySEeeP"
+
 [scripts]
 test = "mocha -t 1000000 tests/"

+ 2 - 0
tests/interface/programs/counter-auth/src/lib.rs

@@ -6,6 +6,8 @@
 use anchor_lang::prelude::*;
 use counter::Auth;
 
+declare_id!("Aws2XRVHjNqCUbMmaU245ojT2DBJFYX58KVo2YySEeeP");
+
 #[program]
 pub mod counter_auth {
     use super::*;

+ 2 - 0
tests/interface/programs/counter/src/lib.rs

@@ -8,6 +8,8 @@
 
 use anchor_lang::prelude::*;
 
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+
 #[program]
 pub mod counter {
     use super::*;

+ 4 - 0
tests/lockup/Anchor.toml

@@ -2,5 +2,9 @@
 cluster = "localnet"
 wallet = "~/.config/solana/id.json"
 
+[programs.localnet]
+lockup = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
+registry = "HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L"
+
 [scripts]
 test = "mocha -t 1000000 tests/"

+ 10 - 8
tests/lockup/programs/lockup/src/lib.rs

@@ -8,6 +8,8 @@ use anchor_spl::token::{self, TokenAccount, Transfer};
 
 mod calculator;
 
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+
 #[program]
 pub mod lockup {
     use super::*;
@@ -208,9 +210,9 @@ pub struct Auth<'info> {
 pub struct CreateVesting<'info> {
     // Vesting.
     #[account(zero)]
-    vesting: ProgramAccount<'info, Vesting>,
+    vesting: Account<'info, Vesting>,
     #[account(mut)]
-    vault: CpiAccount<'info, TokenAccount>,
+    vault: Account<'info, TokenAccount>,
     // Depositor.
     #[account(mut)]
     depositor: AccountInfo<'info>,
@@ -246,11 +248,11 @@ impl<'info> CreateVesting<'info> {
 pub struct Withdraw<'info> {
     // Vesting.
     #[account(mut, has_one = beneficiary, has_one = vault)]
-    vesting: ProgramAccount<'info, Vesting>,
+    vesting: Account<'info, Vesting>,
     #[account(signer)]
     beneficiary: AccountInfo<'info>,
     #[account(mut)]
-    vault: CpiAccount<'info, TokenAccount>,
+    vault: Account<'info, TokenAccount>,
     #[account(
         seeds = [vesting.to_account_info().key.as_ref()],
         bump = vesting.nonce,
@@ -258,7 +260,7 @@ pub struct Withdraw<'info> {
     vesting_signer: AccountInfo<'info>,
     // Withdraw receiving target..
     #[account(mut)]
-    token: CpiAccount<'info, TokenAccount>,
+    token: Account<'info, TokenAccount>,
     // Misc.
     #[account(constraint = token_program.key == &token::ID)]
     token_program: AccountInfo<'info>,
@@ -284,9 +286,9 @@ pub struct WhitelistTransfer<'info> {
 
     // Whitelist interface.
     #[account(mut, has_one = beneficiary, has_one = vault)]
-    vesting: ProgramAccount<'info, Vesting>,
+    vesting: Account<'info, Vesting>,
     #[account(mut, constraint = &vault.owner == vesting_signer.key)]
-    vault: CpiAccount<'info, TokenAccount>,
+    vault: Account<'info, TokenAccount>,
     #[account(
         seeds = [vesting.to_account_info().key.as_ref()],
         bump = vesting.nonce,
@@ -301,7 +303,7 @@ pub struct WhitelistTransfer<'info> {
 
 #[derive(Accounts)]
 pub struct AvailableForWithdrawal<'info> {
-    vesting: ProgramAccount<'info, Vesting>,
+    vesting: Account<'info, Vesting>,
     clock: Sysvar<'info, Clock>,
 }
 

+ 53 - 51
tests/lockup/programs/registry/src/lib.rs

@@ -7,6 +7,8 @@ use anchor_spl::token::{self, Mint, TokenAccount, Transfer};
 use lockup::{CreateVesting, RealizeLock, Realizor, Vesting};
 use std::convert::Into;
 
+declare_id!("HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L");
+
 #[program]
 mod registry {
     use super::*;
@@ -557,11 +559,11 @@ mod registry {
 #[derive(Accounts)]
 pub struct Initialize<'info> {
     #[account(zero)]
-    registrar: ProgramAccount<'info, Registrar>,
+    registrar: Account<'info, Registrar>,
     #[account(zero)]
-    reward_event_q: ProgramAccount<'info, RewardQueue>,
+    reward_event_q: Account<'info, RewardQueue>,
     #[account("pool_mint.decimals == 0")]
-    pool_mint: CpiAccount<'info, Mint>,
+    pool_mint: Account<'info, Mint>,
 }
 
 impl<'info> Initialize<'info> {
@@ -585,7 +587,7 @@ impl<'info> Initialize<'info> {
 #[derive(Accounts)]
 pub struct UpdateRegistrar<'info> {
     #[account(mut, has_one = authority)]
-    registrar: ProgramAccount<'info, Registrar>,
+    registrar: Account<'info, Registrar>,
     #[account(signer)]
     authority: AccountInfo<'info>,
 }
@@ -593,10 +595,10 @@ pub struct UpdateRegistrar<'info> {
 #[derive(Accounts)]
 pub struct CreateMember<'info> {
     // Stake instance.
-    registrar: ProgramAccount<'info, Registrar>,
+    registrar: Box<Account<'info, Registrar>>,
     // Member.
     #[account(zero)]
-    member: ProgramAccount<'info, Member>,
+    member: Box<Account<'info, Member>>,
     #[account(signer)]
     beneficiary: AccountInfo<'info>,
     #[account(
@@ -642,17 +644,17 @@ impl<'info> CreateMember<'info> {
 #[derive(Accounts, Clone)]
 pub struct BalanceSandboxAccounts<'info> {
     #[account(mut)]
-    spt: CpiAccount<'info, TokenAccount>,
+    spt: Box<Account<'info, TokenAccount>>,
     #[account(mut, constraint = vault.owner == spt.owner)]
-    vault: CpiAccount<'info, TokenAccount>,
+    vault: Box<Account<'info, TokenAccount>>,
     #[account(
         mut,
         constraint = vault_stake.owner == spt.owner,
         constraint = vault_stake.mint == vault.mint
     )]
-    vault_stake: CpiAccount<'info, TokenAccount>,
+    vault_stake: Box<Account<'info, TokenAccount>>,
     #[account(mut, constraint = vault_pw.owner == spt.owner, constraint = vault_pw.mint == vault.mint)]
-    vault_pw: CpiAccount<'info, TokenAccount>,
+    vault_pw: Box<Account<'info, TokenAccount>>,
 }
 
 #[derive(Accounts)]
@@ -672,15 +674,15 @@ pub struct IsRealized<'info> {
         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>,
-    member_spt_locked: CpiAccount<'info, TokenAccount>,
+    member: Account<'info, Member>,
+    member_spt: Account<'info, TokenAccount>,
+    member_spt_locked: Account<'info, TokenAccount>,
 }
 
 #[derive(Accounts)]
 pub struct UpdateMember<'info> {
     #[account(mut, has_one = beneficiary)]
-    member: ProgramAccount<'info, Member>,
+    member: Account<'info, Member>,
     #[account(signer)]
     beneficiary: AccountInfo<'info>,
 }
@@ -689,11 +691,11 @@ pub struct UpdateMember<'info> {
 pub struct Deposit<'info> {
     // Member.
     #[account(has_one = beneficiary)]
-    member: ProgramAccount<'info, Member>,
+    member: Account<'info, Member>,
     #[account(signer)]
     beneficiary: AccountInfo<'info>,
     #[account(mut, constraint = vault.to_account_info().key == &member.balances.vault)]
-    vault: CpiAccount<'info, TokenAccount>,
+    vault: Account<'info, TokenAccount>,
     // Depositor.
     #[account(mut)]
     depositor: AccountInfo<'info>,
@@ -711,7 +713,7 @@ pub struct DepositLocked<'info> {
         constraint = vesting.to_account_info().owner == &registry.lockup_program,
         constraint = vesting.beneficiary == member.beneficiary
     )]
-    vesting: CpiAccount<'info, Vesting>,
+    vesting: Box<Account<'info, Vesting>>,
     #[account(mut, constraint = vesting_vault.key == &vesting.vault)]
     vesting_vault: AccountInfo<'info>,
     // Note: no need to verify the depositor_authority since the SPL program
@@ -724,7 +726,7 @@ pub struct DepositLocked<'info> {
         mut,
         constraint = member_vault.to_account_info().key == &member.balances_locked.vault
     )]
-    member_vault: CpiAccount<'info, TokenAccount>,
+    member_vault: Box<Account<'info, TokenAccount>>,
     #[account(
         seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
         bump = member.nonce,
@@ -733,9 +735,9 @@ pub struct DepositLocked<'info> {
 
     // Program specific.
     registry: ProgramState<'info, Registry>,
-    registrar: ProgramAccount<'info, Registrar>,
+    registrar: Box<Account<'info, Registrar>>,
     #[account(has_one = registrar, has_one = beneficiary)]
-    member: ProgramAccount<'info, Member>,
+    member: Box<Account<'info, Member>>,
     #[account(signer)]
     beneficiary: AccountInfo<'info>,
 }
@@ -744,14 +746,14 @@ pub struct DepositLocked<'info> {
 pub struct Stake<'info> {
     // Global accounts for the staking instance.
     #[account(has_one = pool_mint, has_one = reward_event_q)]
-    registrar: ProgramAccount<'info, Registrar>,
-    reward_event_q: ProgramAccount<'info, RewardQueue>,
+    registrar: Account<'info, Registrar>,
+    reward_event_q: Account<'info, RewardQueue>,
     #[account(mut)]
-    pool_mint: CpiAccount<'info, Mint>,
+    pool_mint: Account<'info, Mint>,
 
     // Member.
     #[account(mut, has_one = beneficiary, has_one = registrar)]
-    member: ProgramAccount<'info, Member>,
+    member: Account<'info, Member>,
     #[account(signer)]
     beneficiary: AccountInfo<'info>,
     #[account(constraint = BalanceSandbox::from(&balances) == member.balances)]
@@ -781,16 +783,16 @@ pub struct Stake<'info> {
 pub struct StartUnstake<'info> {
     // Stake instance globals.
     #[account(has_one = reward_event_q)]
-    registrar: ProgramAccount<'info, Registrar>,
-    reward_event_q: ProgramAccount<'info, RewardQueue>,
+    registrar: Account<'info, Registrar>,
+    reward_event_q: Account<'info, RewardQueue>,
     #[account(mut)]
     pool_mint: AccountInfo<'info>,
 
     // Member.
     #[account(zero)]
-    pending_withdrawal: ProgramAccount<'info, PendingWithdrawal>,
+    pending_withdrawal: Account<'info, PendingWithdrawal>,
     #[account(has_one = beneficiary, has_one = registrar)]
-    member: ProgramAccount<'info, Member>,
+    member: Account<'info, Member>,
     #[account(signer)]
     beneficiary: AccountInfo<'info>,
     #[account(constraint = BalanceSandbox::from(&balances) == member.balances)]
@@ -813,14 +815,14 @@ pub struct StartUnstake<'info> {
 
 #[derive(Accounts)]
 pub struct EndUnstake<'info> {
-    registrar: ProgramAccount<'info, Registrar>,
+    registrar: Account<'info, Registrar>,
 
     #[account(has_one = registrar, has_one = beneficiary)]
-    member: ProgramAccount<'info, Member>,
+    member: Account<'info, Member>,
     #[account(signer)]
     beneficiary: AccountInfo<'info>,
     #[account(mut, has_one = registrar, has_one = member, constraint = !pending_withdrawal.burned)]
-    pending_withdrawal: ProgramAccount<'info, PendingWithdrawal>,
+    pending_withdrawal: Account<'info, PendingWithdrawal>,
 
     // If we had ordered maps implementing Accounts we could do a constraint like
     // balances.get(pending_withdrawal.balance_id).vault == vault.key.
@@ -845,14 +847,14 @@ pub struct EndUnstake<'info> {
 #[derive(Accounts)]
 pub struct Withdraw<'info> {
     // Stake instance.
-    registrar: ProgramAccount<'info, Registrar>,
+    registrar: Account<'info, Registrar>,
     // Member.
     #[account(has_one = registrar, has_one = beneficiary)]
-    member: ProgramAccount<'info, Member>,
+    member: Account<'info, Member>,
     #[account(signer)]
     beneficiary: AccountInfo<'info>,
     #[account(mut, constraint = vault.to_account_info().key == &member.balances.vault)]
-    vault: CpiAccount<'info, TokenAccount>,
+    vault: Account<'info, TokenAccount>,
     #[account(
         seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
         bump = member.nonce,
@@ -873,7 +875,7 @@ pub struct WithdrawLocked<'info> {
         constraint = vesting.to_account_info().owner == &registry.lockup_program,
         constraint = vesting.beneficiary == member.beneficiary,
     )]
-    vesting: CpiAccount<'info, Vesting>,
+    vesting: Box<Account<'info, Vesting>>,
     #[account(mut, constraint = vesting_vault.key == &vesting.vault)]
     vesting_vault: AccountInfo<'info>,
     #[account(signer)]
@@ -884,7 +886,7 @@ pub struct WithdrawLocked<'info> {
         mut,
         constraint = member_vault.to_account_info().key == &member.balances_locked.vault
     )]
-    member_vault: CpiAccount<'info, TokenAccount>,
+    member_vault: Box<Account<'info, TokenAccount>>,
     #[account(
         seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
         bump = member.nonce,
@@ -893,9 +895,9 @@ pub struct WithdrawLocked<'info> {
 
     // Program specific.
     registry: ProgramState<'info, Registry>,
-    registrar: ProgramAccount<'info, Registrar>,
+    registrar: Box<Account<'info, Registrar>>,
     #[account(has_one = registrar, has_one = beneficiary)]
-    member: ProgramAccount<'info, Member>,
+    member: Box<Account<'info, Member>>,
     #[account(signer)]
     beneficiary: AccountInfo<'info>,
 }
@@ -904,15 +906,15 @@ pub struct WithdrawLocked<'info> {
 pub struct DropReward<'info> {
     // Staking instance.
     #[account(has_one = reward_event_q, has_one = pool_mint)]
-    registrar: ProgramAccount<'info, Registrar>,
+    registrar: Account<'info, Registrar>,
     #[account(mut)]
-    reward_event_q: ProgramAccount<'info, RewardQueue>,
-    pool_mint: CpiAccount<'info, Mint>,
+    reward_event_q: Account<'info, RewardQueue>,
+    pool_mint: Account<'info, Mint>,
     // Vendor.
     #[account(zero)]
-    vendor: ProgramAccount<'info, RewardVendor>,
+    vendor: Account<'info, RewardVendor>,
     #[account(mut)]
-    vendor_vault: CpiAccount<'info, TokenAccount>,
+    vendor_vault: Account<'info, TokenAccount>,
     // Depositor.
     #[account(mut)]
     depositor: AccountInfo<'info>,
@@ -963,10 +965,10 @@ pub struct ClaimRewardLocked<'info> {
 #[derive(Accounts)]
 pub struct ClaimRewardCommon<'info> {
     // Stake instance.
-    registrar: ProgramAccount<'info, Registrar>,
+    registrar: Account<'info, Registrar>,
     // Member.
     #[account(mut, has_one = registrar, has_one = beneficiary)]
-    member: ProgramAccount<'info, Member>,
+    member: Account<'info, Member>,
     #[account(signer)]
     beneficiary: AccountInfo<'info>,
     #[account(constraint = BalanceSandbox::from(&balances) == member.balances)]
@@ -975,7 +977,7 @@ pub struct ClaimRewardCommon<'info> {
     balances_locked: BalanceSandboxAccounts<'info>,
     // Vendor.
     #[account(has_one = registrar, has_one = vault)]
-    vendor: ProgramAccount<'info, RewardVendor>,
+    vendor: Account<'info, RewardVendor>,
     #[account(mut)]
     vault: AccountInfo<'info>,
     #[account(
@@ -992,12 +994,12 @@ pub struct ClaimRewardCommon<'info> {
 #[derive(Accounts)]
 pub struct ExpireReward<'info> {
     // Staking instance globals.
-    registrar: ProgramAccount<'info, Registrar>,
+    registrar: Account<'info, Registrar>,
     // Vendor.
     #[account(mut, has_one = registrar, has_one = vault, has_one = expiry_receiver)]
-    vendor: ProgramAccount<'info, RewardVendor>,
+    vendor: Account<'info, RewardVendor>,
     #[account(mut)]
-    vault: CpiAccount<'info, TokenAccount>,
+    vault: Account<'info, TokenAccount>,
     #[account(
         seeds = [registrar.to_account_info().key.as_ref(), vendor.to_account_info().key.as_ref()],
         bump = vendor.nonce
@@ -1303,8 +1305,8 @@ fn reward_eligible(cmn: &ClaimRewardCommon) -> Result<()> {
 // Asserts the user calling the `Stake` instruction has no rewards available
 // in the reward queue.
 pub fn no_available_rewards<'info>(
-    reward_q: &ProgramAccount<'info, RewardQueue>,
-    member: &ProgramAccount<'info, Member>,
+    reward_q: &Account<'info, RewardQueue>,
+    member: &Account<'info, Member>,
     balances: &BalanceSandboxAccounts<'info>,
     balances_locked: &BalanceSandboxAccounts<'info>,
 ) -> Result<()> {

+ 4 - 0
tests/misc/Anchor.toml

@@ -2,6 +2,10 @@
 cluster = "localnet"
 wallet = "~/.config/solana/id.json"
 
+[programs.localnet]
+misc = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
+misc2 = "HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L"
+
 [[test.genesis]]
 address = "FtMNMKp9DZHKWUyVAsj3Q5QV8ow4P3fUPP7ZrWEQJzKr"
 program = "./target/deploy/misc.so"

+ 12 - 12
tests/misc/programs/misc/src/context.rs

@@ -15,7 +15,7 @@ pub struct TestTokenSeedsInit<'info> {
         mint::decimals = 6,
         mint::authority = authority,
     )]
-    pub mint: CpiAccount<'info, Mint>,
+    pub mint: Account<'info, Mint>,
     #[account(
         init,
         seeds = [b"my-token-seed".as_ref()],
@@ -24,7 +24,7 @@ pub struct TestTokenSeedsInit<'info> {
         token::mint = mint,
         token::authority = authority,
     )]
-    pub my_pda: CpiAccount<'info, TokenAccount>,
+    pub my_pda: Account<'info, TokenAccount>,
     pub authority: AccountInfo<'info>,
     pub system_program: AccountInfo<'info>,
     pub rent: Sysvar<'info, Rent>,
@@ -51,7 +51,7 @@ pub struct TestPdaInit<'info> {
         bump = bump,
         payer = my_payer,
     )]
-    pub my_pda: ProgramAccount<'info, DataU16>,
+    pub my_pda: Account<'info, DataU16>,
     pub my_payer: AccountInfo<'info>,
     pub foo: AccountInfo<'info>,
     pub system_program: AccountInfo<'info>,
@@ -91,7 +91,7 @@ pub struct RemainingAccounts {}
 #[derive(Accounts)]
 pub struct Initialize<'info> {
     #[account(zero)]
-    pub data: ProgramAccount<'info, Data>,
+    pub data: Account<'info, Data>,
 }
 
 #[derive(Accounts)]
@@ -120,20 +120,20 @@ pub struct TestStateCpi<'info> {
 #[derive(Accounts)]
 pub struct TestClose<'info> {
     #[account(mut, close = sol_dest)]
-    pub data: ProgramAccount<'info, Data>,
+    pub data: Account<'info, Data>,
     sol_dest: AccountInfo<'info>,
 }
 
 #[derive(Accounts)]
 pub struct TestU16<'info> {
     #[account(zero)]
-    pub my_account: ProgramAccount<'info, DataU16>,
+    pub my_account: Account<'info, DataU16>,
 }
 
 #[derive(Accounts)]
 pub struct TestI16<'info> {
     #[account(zero)]
-    pub data: ProgramAccount<'info, DataI16>,
+    pub data: Account<'info, DataI16>,
 }
 
 #[derive(Accounts)]
@@ -142,13 +142,13 @@ pub struct TestSimulate {}
 #[derive(Accounts)]
 pub struct TestI8<'info> {
     #[account(zero)]
-    pub data: ProgramAccount<'info, DataI8>,
+    pub data: Account<'info, DataI8>,
 }
 
 #[derive(Accounts)]
 pub struct TestInit<'info> {
     #[account(init, payer = payer)]
-    pub data: ProgramAccount<'info, DataI8>,
+    pub data: Account<'info, DataI8>,
     #[account(signer)]
     pub payer: AccountInfo<'info>,
     pub system_program: AccountInfo<'info>,
@@ -166,7 +166,7 @@ pub struct TestInitZeroCopy<'info> {
 #[derive(Accounts)]
 pub struct TestInitMint<'info> {
     #[account(init, mint::decimals = 6, mint::authority = payer, payer = payer)]
-    pub mint: CpiAccount<'info, Mint>,
+    pub mint: Account<'info, Mint>,
     #[account(signer)]
     pub payer: AccountInfo<'info>,
     pub rent: Sysvar<'info, Rent>,
@@ -177,8 +177,8 @@ pub struct TestInitMint<'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>,
+    pub token: Account<'info, TokenAccount>,
+    pub mint: Account<'info, Mint>,
     #[account(signer)]
     pub payer: AccountInfo<'info>,
     pub rent: Sysvar<'info, Rent>,

+ 2 - 0
tests/misc/programs/misc/src/lib.rs

@@ -10,6 +10,8 @@ mod account;
 mod context;
 mod event;
 
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+
 #[program]
 pub mod misc {
     use super::*;

+ 2 - 0
tests/misc/programs/misc2/src/lib.rs

@@ -1,5 +1,7 @@
 use anchor_lang::prelude::*;
 
+declare_id!("HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L");
+
 #[program]
 pub mod misc2 {
     use super::*;

+ 7 - 5
tests/misc/tests/misc.js

@@ -155,17 +155,19 @@ describe("misc", () => {
     assert.ok(resp.events[2].data.data === 9);
   });
 
+	let dataI8;
+
   it("Can use i8 in the idl", async () => {
-    const data = anchor.web3.Keypair.generate();
+    dataI8 = anchor.web3.Keypair.generate();
     await program.rpc.testI8(-3, {
       accounts: {
-        data: data.publicKey,
+        data: dataI8.publicKey,
         rent: anchor.web3.SYSVAR_RENT_PUBKEY,
       },
-      instructions: [await program.account.dataI8.createInstruction(data)],
-      signers: [data],
+      instructions: [await program.account.dataI8.createInstruction(dataI8)],
+      signers: [dataI8],
     });
-    const dataAccount = await program.account.dataI8.fetch(data.publicKey);
+    const dataAccount = await program.account.dataI8.fetch(dataI8.publicKey);
     assert.ok(dataAccount.data === -3);
   });
 

+ 3 - 0
tests/multisig/Anchor.toml

@@ -2,5 +2,8 @@
 cluster = "localnet"
 wallet = "~/.config/solana/id.json"
 
+[programs.localnet]
+multisig = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
+
 [scripts]
 test = "mocha -t 1000000 tests/"

+ 2 - 0
tests/multisig/programs/multisig/src/lib.rs

@@ -22,6 +22,8 @@ use anchor_lang::solana_program;
 use anchor_lang::solana_program::instruction::Instruction;
 use std::convert::Into;
 
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+
 #[program]
 pub mod multisig {
     use super::*;

+ 6 - 0
tests/tictactoe/Anchor.toml

@@ -1,3 +1,9 @@
 [provider]
 cluster = "localnet"
 wallet = "~/.config/solana/id.json"
+
+[programs.localnet]
+tictactoe = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
+
+[scripts]
+test = "mocha -t 1000000 tests/"

+ 11 - 10
tests/tictactoe/programs/tictactoe/src/lib.rs

@@ -1,10 +1,11 @@
 use anchor_lang::prelude::*;
-use std::str::FromStr;
 
 const BOARD_ITEM_FREE: u8 = 0; // Free slot
 const BOARD_ITEM_X: u8 = 1; // Player X
 const BOARD_ITEM_O: u8 = 2; // Player O
 
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+
 /// Game State
 /// 0 - Waiting
 /// 1 - XMove
@@ -55,14 +56,14 @@ pub mod tictactoe {
 
 #[derive(Accounts)]
 pub struct Status<'info> {
-    dashboard: ProgramAccount<'info, Dashboard>,
-    game: ProgramAccount<'info, Game>,
+    dashboard: Account<'info, Dashboard>,
+    game: Account<'info, Game>,
 }
 
 #[derive(Accounts)]
 pub struct Initializedashboard<'info> {
-    #[account(init)]
-    dashboard: ProgramAccount<'info, Dashboard>,
+    #[account(zero)]
+    dashboard: Account<'info, Dashboard>,
     #[account(signer)]
     authority: AccountInfo<'info>,
 }
@@ -72,9 +73,9 @@ pub struct Initialize<'info> {
     #[account(signer)]
     player_x: AccountInfo<'info>,
     #[account(mut)]
-    dashboard: ProgramAccount<'info, Dashboard>,
-    #[account(init)]
-    game: ProgramAccount<'info, Game>,
+    dashboard: Account<'info, Dashboard>,
+    #[account(zero)]
+    game: Account<'info, Game>,
 }
 
 #[derive(Accounts)]
@@ -82,7 +83,7 @@ pub struct Playerjoin<'info> {
     #[account(signer)]
     player_o: AccountInfo<'info>,
     #[account(mut, constraint = game.game_state != 0 && game.player_x != Pubkey::default())]
-    game: ProgramAccount<'info, Game>,
+    game: Account<'info, Game>,
 }
 
 #[derive(Accounts)]
@@ -90,7 +91,7 @@ pub struct Playermove<'info> {
     #[account(signer)]
     player: AccountInfo<'info>,
     #[account(mut)]
-    game: ProgramAccount<'info, Game>,
+    game: Account<'info, Game>,
 }
 
 impl<'info> Playermove<'info> {

+ 2 - 0
tests/zero-copy/programs/zero-copy/src/lib.rs

@@ -7,6 +7,8 @@
 
 use anchor_lang::prelude::*;
 
+declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
+
 #[program]
 pub mod zero_copy {
     use super::*;