Static owner and program ID checks (#686)

This commit is contained in:
Armani Ferrante 2021-09-07 13:06:15 -07:00 committed by GitHub
parent 675c7cd81d
commit 3958533750
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
60 changed files with 1185 additions and 426 deletions

View File

@ -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

26
Cargo.lock generated
View File

@ -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",

View File

@ -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 {

View File

@ -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
}

View File

@ -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.
#

View File

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

View File

@ -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]

View File

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

View File

@ -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,

View File

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

View File

@ -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

View File

@ -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]

View File

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

View File

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

View File

@ -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"

View File

@ -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]
}
});
}
}
}

View File

@ -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
lang/src/account.rs Normal file
View File

@ -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
}
}

View File

@ -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()))
}
}

View File

@ -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;

View File

@ -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
};
}

View File

@ -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();

View File

@ -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,

View File

@ -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,

View File

@ -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() {

View File

@ -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) => {

View File

@ -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

View File

@ -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;

View File

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

View File

@ -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>,

View File

@ -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" }

View File

@ -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(),

View File

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

View File

@ -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)]

View File

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

View File

@ -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]

View File

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

View File

@ -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>,
}

View File

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

View File

@ -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>,
}

View File

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

View File

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

View File

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

View File

@ -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) {

View File

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

View File

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

View File

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

View File

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

View File

@ -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>,
}

View File

@ -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<()> {

View File

@ -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"

View File

@ -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>,

View File

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

View File

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

View File

@ -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);
});

View File

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

View File

@ -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::*;

View File

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

View File

@ -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> {

View File

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