Merge branch 'master' into armani/versioning
This commit is contained in:
commit
288005882d
|
@ -15,6 +15,7 @@ jobs:
|
|||
test-core:
|
||||
name: Core Tests
|
||||
runs-on: ubuntu-18.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/actions/setup/
|
||||
|
@ -49,6 +50,7 @@ jobs:
|
|||
setup-anchor-cli:
|
||||
name: Setup Anchor cli
|
||||
runs-on: ubuntu-18.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/actions/setup/
|
||||
|
@ -74,6 +76,7 @@ jobs:
|
|||
needs: setup-anchor-cli
|
||||
name: Examples Test
|
||||
runs-on: ubuntu-18.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/download-artifact@v2
|
||||
|
@ -120,7 +123,9 @@ jobs:
|
|||
needs: setup-anchor-cli
|
||||
name: Setup Client Example Test
|
||||
runs-on: ubuntu-18.04
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node:
|
||||
- path: tests/events/
|
||||
|
@ -141,7 +146,7 @@ jobs:
|
|||
name: anchor-binary
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +rwx ~/.cargo/bin/anchor
|
||||
- run: cd ${{ matrix.node.path }} && anchor build
|
||||
- run: cd ${{ matrix.node.path }} && anchor build --skip-lint
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ matrix.node.name }}
|
||||
|
@ -151,6 +156,7 @@ jobs:
|
|||
needs: setup-client-example
|
||||
name: Client Example Test
|
||||
runs-on: ubuntu-18.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/actions/setup/
|
||||
|
@ -189,6 +195,7 @@ jobs:
|
|||
needs: setup-anchor-cli
|
||||
name: Test tests/bpf-upgradeable-state
|
||||
runs-on: ubuntu-18.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/actions/setup/
|
||||
|
@ -223,60 +230,62 @@ jobs:
|
|||
name: start validator
|
||||
- run: cd tests/bpf-upgradeable-state && yarn
|
||||
- run: cd tests/bpf-upgradeable-state && yarn link @project-serum/anchor
|
||||
- run: cd tests/bpf-upgradeable-state && anchor build
|
||||
- run: cd tests/bpf-upgradeable-state && anchor build --skip-lint
|
||||
- run: cd tests/bpf-upgradeable-state && solana program deploy --program-id program_with_different_programdata.json target/deploy/bpf_upgradeable_state.so
|
||||
- run: cd tests/bpf-upgradeable-state && cp bpf_upgradeable_state-keypair.json target/deploy/bpf_upgradeable_state-keypair.json && anchor deploy && anchor test --skip-deploy --skip-build
|
||||
- run: cd tests/bpf-upgradeable-state && cp bpf_upgradeable_state-keypair.json target/deploy/bpf_upgradeable_state-keypair.json && anchor deploy && anchor test --skip-deploy --skip-build --skip-lint
|
||||
|
||||
test-programs:
|
||||
needs: setup-anchor-cli
|
||||
name: Test ${{ matrix.node.path }}
|
||||
runs-on: ubuntu-18.04
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node:
|
||||
- cmd: cd tests/sysvars && anchor test
|
||||
- cmd: cd tests/sysvars && anchor test --skip-lint
|
||||
path: tests/sysvars
|
||||
- cmd: cd tests/composite && anchor test
|
||||
- cmd: cd tests/composite && anchor test --skip-lint
|
||||
path: tests/composite
|
||||
- cmd: cd tests/errors && anchor test
|
||||
- cmd: cd tests/errors && anchor test --skip-lint
|
||||
path: tests/errors
|
||||
- cmd: cd tests/spl/token-proxy && anchor test
|
||||
- cmd: cd tests/spl/token-proxy && anchor test --skip-lint
|
||||
path: spl/token-proxy
|
||||
- cmd: cd tests/multisig && anchor test
|
||||
- cmd: cd tests/multisig && anchor test --skip-lint
|
||||
path: tests/multisig
|
||||
- cmd: cd tests/interface && anchor test
|
||||
- cmd: cd tests/interface && anchor test --skip-lint
|
||||
path: tests/interface
|
||||
- cmd: cd tests/lockup && anchor test
|
||||
- cmd: cd tests/lockup && anchor test --skip-lint
|
||||
path: tests/lockup
|
||||
- cmd: cd tests/swap/deps/serum-dex/dex && cargo build-bpf && cd ../../../ && anchor test
|
||||
- cmd: cd tests/swap/deps/serum-dex/dex && cargo build-bpf && cd ../../../ && anchor test --skip-lint
|
||||
path: tests/swap
|
||||
- cmd: cd tests/escrow && anchor test
|
||||
- cmd: cd tests/escrow && anchor test --skip-lint
|
||||
path: tests/escrow
|
||||
- cmd: cd tests/pyth && anchor test
|
||||
- cmd: cd tests/pyth && anchor test --skip-lint
|
||||
path: tests/pyth
|
||||
- cmd: cd tests/system-accounts && anchor test
|
||||
- cmd: cd tests/system-accounts && anchor test --skip-lint
|
||||
path: tests/system-accounts
|
||||
- cmd: cd tests/misc && anchor test
|
||||
- cmd: cd tests/misc && anchor test --skip-lint
|
||||
path: tests/misc
|
||||
- cmd: cd tests/events && anchor test
|
||||
- cmd: cd tests/events && anchor test --skip-lint
|
||||
path: tests/events
|
||||
- cmd: cd tests/cashiers-check && anchor test
|
||||
- cmd: cd tests/cashiers-check && anchor test --skip-lint
|
||||
path: tests/cashiers-check
|
||||
- cmd: cd tests/declare-id && anchor test
|
||||
- cmd: cd tests/declare-id && anchor test --skip-lint
|
||||
path: tests/declare-id
|
||||
- cmd: cd tests/typescript && anchor test
|
||||
- cmd: cd tests/typescript && anchor test --skip-lint
|
||||
path: tests/typescript
|
||||
- cmd: cd tests/zero-copy && anchor test && cd programs/zero-copy && cargo test-bpf
|
||||
- cmd: cd tests/zero-copy && anchor test --skip-lint && cd programs/zero-copy && cargo test-bpf
|
||||
path: tests/zero-copy
|
||||
- cmd: cd tests/chat && anchor test
|
||||
- cmd: cd tests/chat && anchor test --skip-lint
|
||||
path: tests/chat
|
||||
- cmd: cd tests/ido-pool && anchor test
|
||||
- cmd: cd tests/ido-pool && anchor test --skip-lint
|
||||
path: tests/ido-pool
|
||||
- cmd: cd tests/cfo && anchor run test-with-build
|
||||
path: tests/cfo
|
||||
- cmd: cd tests/auction-house && yarn && anchor test
|
||||
- cmd: cd tests/auction-house && yarn && anchor test --skip-lint
|
||||
path: tests/auction-house
|
||||
- cmd: cd tests/floats && yarn && anchor test
|
||||
- cmd: cd tests/floats && yarn && anchor test --skip-lint
|
||||
path: tests/floats
|
||||
- cmd: cd tests/safety-checks && ./test.sh
|
||||
path: tests/safety-checks
|
||||
|
|
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -11,10 +11,17 @@ incremented for features.
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixes
|
||||
|
||||
* cli: Fix rust template ([#1488](https://github.com/project-serum/anchor/pull/1488)).
|
||||
|
||||
## [0.22.0] - 2022-02-20
|
||||
|
||||
### Features
|
||||
|
||||
* lang: add check that declared id == program id ([#1451](https://github.com/project-serum/anchor/pull/1451))
|
||||
* lang: Add check that declared id == program id ([#1451](https://github.com/project-serum/anchor/pull/1451)).
|
||||
* ts: Added float types support ([#1425](https://github.com/project-serum/anchor/pull/1425)).
|
||||
* cli: Add `--skip-lint` option to disable check linting introduced in ([#1452](https://github.com/project-serum/anchor/pull/1452)) for rapid prototyping ([#1482](https://github.com/project-serum/anchor/pull/1482)).
|
||||
|
||||
### Fixes
|
||||
|
||||
|
@ -27,6 +34,11 @@ incremented for features.
|
|||
* lang: The `AccountLoader` and `Loader`'s `load_init` method has been removed. Instead, use `load_mut` ([#1415](https://github.com/project-serum/anchor/pull/1415)).
|
||||
* lang: All error-related code is now in the error module ([#1426](https://github.com/project-serum/anchor/pull/1426)).
|
||||
* lang: Require doc comments when using AccountInfo or UncheckedAccount types ([#1452](https://github.com/project-serum/anchor/pull/1452)).
|
||||
* lang: add [`error!`](https://docs.rs/anchor-lang/latest/anchor_lang/prelude/macro.error.html) and [`err!`](https://docs.rs/anchor-lang/latest/anchor_lang/prelude/macro.err.html) macro and `Result` type ([#1462](https://github.com/project-serum/anchor/pull/1462)).
|
||||
This change will break most programs. Do the following to upgrade:
|
||||
* change all `ProgramResult`'s to `Result<()>`
|
||||
* change `#[error]` to `#[error_code]`
|
||||
* change all `Err(MyError::SomeError.into())` to `Err(error!(MyError::SomeError))` and all `Err(ProgramError::SomeProgramError)` to `Err(ProgramError::SomeProgramError.into())` or `Err(Error::from(ProgramError::SomeProgramError).with_source(source!()))` to provide file and line source of the error (`with_source` is most useful with `ProgramError`s. `error!` already adds source information for custom and anchor internal errors).
|
||||
* ts: `BorshAccountsCoder.accountDiscriminator` method has been replaced with `BorshAccountHeader.discriminator` ([#1415](https://github.com/project-serum/anchor/pull/1415)).
|
||||
|
||||
## [0.21.0] - 2022-02-07
|
||||
|
|
|
@ -56,7 +56,7 @@ checksum = "6b2d54853319fd101b8dd81de382bcbf3e03410a64d8928bbee85a3e7dcde483"
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-access-control"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anyhow",
|
||||
|
@ -68,7 +68,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-account"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
dependencies = [
|
||||
"anchor-common",
|
||||
"anchor-syn",
|
||||
|
@ -82,7 +82,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-constant"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"proc-macro2 1.0.32",
|
||||
|
@ -91,7 +91,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-error"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"proc-macro2 1.0.32",
|
||||
|
@ -101,7 +101,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-event"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
dependencies = [
|
||||
"anchor-common",
|
||||
"anchor-syn",
|
||||
|
@ -113,7 +113,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-interface"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anyhow",
|
||||
|
@ -125,7 +125,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-program"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anyhow",
|
||||
|
@ -136,7 +136,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-state"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anyhow",
|
||||
|
@ -147,7 +147,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-cli"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
dependencies = [
|
||||
"anchor-client",
|
||||
"anchor-lang",
|
||||
|
@ -179,7 +179,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-client"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
dependencies = [
|
||||
"anchor-lang",
|
||||
"anyhow",
|
||||
|
@ -202,7 +202,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-derive-accounts"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anyhow",
|
||||
|
@ -213,7 +213,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-lang"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
dependencies = [
|
||||
"anchor-attribute-access-control",
|
||||
"anchor-attribute-account",
|
||||
|
@ -236,7 +236,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-spl"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
dependencies = [
|
||||
"anchor-lang",
|
||||
"serum_dex",
|
||||
|
@ -247,7 +247,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-syn"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arrayref",
|
||||
|
@ -317,7 +317,7 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
|||
|
||||
[[package]]
|
||||
name = "avm"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cfg-if 1.0.0",
|
||||
|
|
24
Makefile
24
Makefile
|
@ -53,27 +53,27 @@ clean:
|
|||
.PHONY: publish
|
||||
publish:
|
||||
cd lang/syn/ && cargo publish && cd ../../
|
||||
sleep 10
|
||||
sleep 25
|
||||
cd lang/derive/accounts/ && cargo publish && cd ../../../
|
||||
sleep 10
|
||||
sleep 25
|
||||
cd lang/attribute/access-control/ && cargo publish && cd ../../../
|
||||
sleep 10
|
||||
sleep 25
|
||||
cd lang/attribute/account/ && cargo publish && cd ../../../
|
||||
sleep 10
|
||||
sleep 25
|
||||
cd lang/attribute/constant/ && cargo publish && cd ../../../
|
||||
sleep 10
|
||||
sleep 25
|
||||
cd lang/attribute/error/ && cargo publish && cd ../../../
|
||||
sleep 10
|
||||
sleep 25
|
||||
cd lang/attribute/interface/ && cargo publish && cd ../../../
|
||||
sleep 10
|
||||
sleep 25
|
||||
cd lang/attribute/program/ && cargo publish && cd ../../..
|
||||
sleep 10
|
||||
sleep 25
|
||||
cd lang/attribute/state/ && cargo publish && cd ../../../
|
||||
sleep 10
|
||||
sleep 25
|
||||
cd lang/attribute/event/ && cargo publish && cd ../../../
|
||||
sleep 10
|
||||
sleep 25
|
||||
cd lang/ && cargo publish && cd../
|
||||
sleep 10
|
||||
sleep 25
|
||||
cd spl/ && cargo publish && cd ../
|
||||
sleep 10
|
||||
sleep 25
|
||||
cd client/ && cargo publish && cd ../
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "avm"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
edition = "2018"
|
||||
|
||||
[[bin]]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "anchor-cli"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
authors = ["armaniferrante <armaniferrante@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@project-serum/anchor-cli",
|
||||
"version": "0.21.0",
|
||||
"version": "0.22.0",
|
||||
"description": "Anchor CLI tool",
|
||||
"homepage": "https://github.com/project-serum/anchor#readme",
|
||||
"bugs": {
|
||||
|
|
|
@ -256,26 +256,10 @@ pub struct Config {
|
|||
pub test: Option<Test>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct FeaturesConfig {
|
||||
#[serde(default)]
|
||||
pub seeds: bool,
|
||||
#[serde(default = "default_safety_checks")]
|
||||
pub safety_checks: bool,
|
||||
}
|
||||
|
||||
impl Default for FeaturesConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
seeds: false,
|
||||
// Anchor safety checks on by default
|
||||
safety_checks: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn default_safety_checks() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
|
|
|
@ -70,6 +70,10 @@ pub enum Command {
|
|||
/// Output directory for the IDL.
|
||||
#[clap(short, long)]
|
||||
idl: Option<String>,
|
||||
/// True if the build should not fail even if there are
|
||||
/// no "CHECK" comments where normally required
|
||||
#[clap(long)]
|
||||
skip_lint: bool,
|
||||
/// Output directory for the TypeScript IDL.
|
||||
#[clap(short = 't', long)]
|
||||
idl_ts: Option<String>,
|
||||
|
@ -151,6 +155,10 @@ pub enum Command {
|
|||
/// programs.
|
||||
#[clap(long)]
|
||||
skip_deploy: bool,
|
||||
/// True if the build should not fail even if there are
|
||||
/// no "CHECK" comments where normally required
|
||||
#[clap(long)]
|
||||
skip_lint: bool,
|
||||
/// Flag to skip starting a local validator, if the configured cluster
|
||||
/// url is a localnet.
|
||||
#[clap(long)]
|
||||
|
@ -250,6 +258,10 @@ pub enum Command {
|
|||
/// programs.
|
||||
#[clap(long)]
|
||||
skip_deploy: bool,
|
||||
/// True if the build should not fail even if there are
|
||||
/// no "CHECK" comments where normally required
|
||||
#[clap(long)]
|
||||
skip_lint: bool,
|
||||
/// Arguments to pass to the underlying `cargo build-bpf` command.
|
||||
#[clap(
|
||||
required = false,
|
||||
|
@ -360,11 +372,13 @@ pub fn entry(opts: Opts) -> Result<()> {
|
|||
docker_image,
|
||||
bootstrap,
|
||||
cargo_args,
|
||||
skip_lint,
|
||||
} => build(
|
||||
&opts.cfg_override,
|
||||
idl,
|
||||
idl_ts,
|
||||
verifiable,
|
||||
skip_lint,
|
||||
program_name,
|
||||
solana_version,
|
||||
docker_image,
|
||||
|
@ -407,11 +421,13 @@ pub fn entry(opts: Opts) -> Result<()> {
|
|||
detach,
|
||||
args,
|
||||
cargo_args,
|
||||
skip_lint,
|
||||
} => test(
|
||||
&opts.cfg_override,
|
||||
skip_deploy,
|
||||
skip_local_validator,
|
||||
skip_build,
|
||||
skip_lint,
|
||||
detach,
|
||||
args,
|
||||
cargo_args,
|
||||
|
@ -430,8 +446,15 @@ pub fn entry(opts: Opts) -> Result<()> {
|
|||
Command::Localnet {
|
||||
skip_build,
|
||||
skip_deploy,
|
||||
skip_lint,
|
||||
cargo_args,
|
||||
} => localnet(&opts.cfg_override, skip_build, skip_deploy, cargo_args),
|
||||
} => localnet(
|
||||
&opts.cfg_override,
|
||||
skip_build,
|
||||
skip_deploy,
|
||||
skip_lint,
|
||||
cargo_args,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -685,6 +708,7 @@ pub fn build(
|
|||
idl: Option<String>,
|
||||
idl_ts: Option<String>,
|
||||
verifiable: bool,
|
||||
skip_lint: bool,
|
||||
program_name: Option<String>,
|
||||
solana_version: Option<String>,
|
||||
docker_image: Option<String>,
|
||||
|
@ -736,6 +760,7 @@ pub fn build(
|
|||
stdout,
|
||||
stderr,
|
||||
cargo_args,
|
||||
skip_lint,
|
||||
)?,
|
||||
// If the Cargo.toml is at the root, build the entire workspace.
|
||||
Some(cargo) if cargo.path().parent() == cfg.path().parent() => build_all(
|
||||
|
@ -747,6 +772,7 @@ pub fn build(
|
|||
stdout,
|
||||
stderr,
|
||||
cargo_args,
|
||||
skip_lint,
|
||||
)?,
|
||||
// Cargo.toml represents a single package. Build it.
|
||||
Some(cargo) => build_cwd(
|
||||
|
@ -758,6 +784,7 @@ pub fn build(
|
|||
stdout,
|
||||
stderr,
|
||||
cargo_args,
|
||||
skip_lint,
|
||||
)?,
|
||||
}
|
||||
|
||||
|
@ -776,6 +803,7 @@ fn build_all(
|
|||
stdout: Option<File>, // Used for the package registry server.
|
||||
stderr: Option<File>, // Used for the package registry server.
|
||||
cargo_args: Vec<String>,
|
||||
skip_lint: bool,
|
||||
) -> Result<()> {
|
||||
let cur_dir = std::env::current_dir()?;
|
||||
let r = match cfg_path.parent() {
|
||||
|
@ -791,6 +819,7 @@ fn build_all(
|
|||
stdout.as_ref().map(|f| f.try_clone()).transpose()?,
|
||||
stderr.as_ref().map(|f| f.try_clone()).transpose()?,
|
||||
cargo_args.clone(),
|
||||
skip_lint,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
|
@ -811,14 +840,23 @@ fn build_cwd(
|
|||
stdout: Option<File>,
|
||||
stderr: Option<File>,
|
||||
cargo_args: Vec<String>,
|
||||
skip_lint: bool,
|
||||
) -> Result<()> {
|
||||
match cargo_toml.parent() {
|
||||
None => return Err(anyhow!("Unable to find parent")),
|
||||
Some(p) => std::env::set_current_dir(&p)?,
|
||||
};
|
||||
match build_config.verifiable {
|
||||
false => _build_cwd(cfg, idl_out, idl_ts_out, cargo_args),
|
||||
true => build_cwd_verifiable(cfg, cargo_toml, build_config, stdout, stderr, cargo_args),
|
||||
false => _build_cwd(cfg, idl_out, idl_ts_out, skip_lint, cargo_args),
|
||||
true => build_cwd_verifiable(
|
||||
cfg,
|
||||
cargo_toml,
|
||||
build_config,
|
||||
stdout,
|
||||
stderr,
|
||||
skip_lint,
|
||||
cargo_args,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -830,6 +868,7 @@ fn build_cwd_verifiable(
|
|||
build_config: &BuildConfig,
|
||||
stdout: Option<File>,
|
||||
stderr: Option<File>,
|
||||
skip_lint: bool,
|
||||
cargo_args: Vec<String>,
|
||||
) -> Result<()> {
|
||||
// Create output dirs.
|
||||
|
@ -861,7 +900,7 @@ fn build_cwd_verifiable(
|
|||
Ok(_) => {
|
||||
// Build the idl.
|
||||
println!("Extracting the IDL");
|
||||
if let Ok(Some(idl)) = extract_idl(cfg, "src/lib.rs") {
|
||||
if let Ok(Some(idl)) = extract_idl(cfg, "src/lib.rs", skip_lint) {
|
||||
// Write out the JSON file.
|
||||
println!("Writing the IDL file");
|
||||
let out_file = workspace_dir.join(format!("target/idl/{}.json", idl.name));
|
||||
|
@ -1121,6 +1160,7 @@ fn _build_cwd(
|
|||
cfg: &WithPath<Config>,
|
||||
idl_out: Option<PathBuf>,
|
||||
idl_ts_out: Option<PathBuf>,
|
||||
skip_lint: bool,
|
||||
cargo_args: Vec<String>,
|
||||
) -> Result<()> {
|
||||
let exit = std::process::Command::new("cargo")
|
||||
|
@ -1135,7 +1175,7 @@ fn _build_cwd(
|
|||
}
|
||||
|
||||
// Always assume idl is located at src/lib.rs.
|
||||
if let Some(idl) = extract_idl(cfg, "src/lib.rs")? {
|
||||
if let Some(idl) = extract_idl(cfg, "src/lib.rs", skip_lint)? {
|
||||
// JSON out path.
|
||||
let out = match idl_out {
|
||||
None => PathBuf::from(".").join(&idl.name).with_extension("json"),
|
||||
|
@ -1192,6 +1232,7 @@ fn verify(
|
|||
None, // idl
|
||||
None, // idl ts
|
||||
true, // verifiable
|
||||
true, // skip lint
|
||||
None, // program name
|
||||
solana_version.or_else(|| cfg.solana_version.clone()), // solana version
|
||||
docker_image, // docker image
|
||||
|
@ -1219,7 +1260,7 @@ fn verify(
|
|||
}
|
||||
|
||||
// Verify IDL (only if it's not a buffer account).
|
||||
if let Some(local_idl) = extract_idl(&cfg, "src/lib.rs")? {
|
||||
if let Some(local_idl) = extract_idl(&cfg, "src/lib.rs", true)? {
|
||||
if bin_ver.state != BinVerificationState::Buffer {
|
||||
let deployed_idl = fetch_idl(cfg_override, program_id)?;
|
||||
if local_idl != deployed_idl {
|
||||
|
@ -1383,7 +1424,7 @@ fn fetch_idl(cfg_override: &ConfigOverride, idl_addr: Pubkey) -> Result<Idl> {
|
|||
serde_json::from_slice(&s[..]).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn extract_idl(cfg: &WithPath<Config>, file: &str) -> Result<Option<Idl>> {
|
||||
fn extract_idl(cfg: &WithPath<Config>, file: &str, skip_lint: bool) -> Result<Option<Idl>> {
|
||||
let file = shellexpand::tilde(file);
|
||||
let manifest_from_path = std::env::current_dir()?.join(PathBuf::from(&*file).parent().unwrap());
|
||||
let cargo = Manifest::discover_from_path(manifest_from_path)?
|
||||
|
@ -1393,6 +1434,7 @@ fn extract_idl(cfg: &WithPath<Config>, file: &str) -> Result<Option<Idl>> {
|
|||
&*file,
|
||||
cfg.features.seeds,
|
||||
cfg.features.safety_checks,
|
||||
!skip_lint,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1686,7 +1728,7 @@ fn idl_parse(
|
|||
out_ts: Option<String>,
|
||||
) -> Result<()> {
|
||||
let cfg = Config::discover(cfg_override)?.expect("Not in workspace.");
|
||||
let idl = extract_idl(&cfg, &file)?.ok_or_else(|| anyhow!("IDL not parsed"))?;
|
||||
let idl = extract_idl(&cfg, &file, true)?.ok_or_else(|| anyhow!("IDL not parsed"))?;
|
||||
let out = match out {
|
||||
None => OutFile::Stdout,
|
||||
Some(out) => OutFile::File(PathBuf::from(out)),
|
||||
|
@ -1726,11 +1768,13 @@ enum OutFile {
|
|||
}
|
||||
|
||||
// Builds, deploys, and tests all workspace programs in a single command.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn test(
|
||||
cfg_override: &ConfigOverride,
|
||||
skip_deploy: bool,
|
||||
skip_local_validator: bool,
|
||||
skip_build: bool,
|
||||
skip_lint: bool,
|
||||
detach: bool,
|
||||
extra_args: Vec<String>,
|
||||
cargo_args: Vec<String>,
|
||||
|
@ -1743,6 +1787,7 @@ fn test(
|
|||
None,
|
||||
None,
|
||||
false,
|
||||
skip_lint,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
|
@ -2667,6 +2712,7 @@ fn publish(
|
|||
None,
|
||||
None,
|
||||
true,
|
||||
false,
|
||||
Some(program_name),
|
||||
None,
|
||||
None,
|
||||
|
@ -2754,6 +2800,7 @@ fn localnet(
|
|||
cfg_override: &ConfigOverride,
|
||||
skip_build: bool,
|
||||
skip_deploy: bool,
|
||||
skip_lint: bool,
|
||||
cargo_args: Vec<String>,
|
||||
) -> Result<()> {
|
||||
with_workspace(cfg_override, |cfg| {
|
||||
|
@ -2764,6 +2811,7 @@ fn localnet(
|
|||
None,
|
||||
None,
|
||||
false,
|
||||
skip_lint,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
|
|
|
@ -176,7 +176,8 @@ declare_id!("{}");
|
|||
#[program]
|
||||
pub mod {} {{
|
||||
use super::*;
|
||||
pub fn initialize(ctx: Context<Initialize>) -> ProgramResult {{
|
||||
|
||||
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {{
|
||||
Ok(())
|
||||
}}
|
||||
}}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "anchor-client"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0"
|
||||
|
@ -11,7 +11,7 @@ debug = []
|
|||
deprecated-layout = ["anchor-lang/deprecated-layout"]
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = { path = "../lang", version = "0.21.0" }
|
||||
anchor-lang = { path = "../lang", version = "0.22.0" }
|
||||
anyhow = "1.0.32"
|
||||
regex = "1.4.5"
|
||||
serde = { version = "1.0.122", features = ["derive"] }
|
||||
|
|
|
@ -375,6 +375,8 @@ pub enum ClientError {
|
|||
#[error("Account not found")]
|
||||
AccountNotFound,
|
||||
#[error("{0}")]
|
||||
AnchorError(#[from] anchor_lang::error::Error),
|
||||
#[error("{0}")]
|
||||
ProgramError(#[from] ProgramError),
|
||||
#[error("{0}")]
|
||||
SolanaClientError(#[from] SolanaClientError),
|
||||
|
|
|
@ -44,7 +44,7 @@ npm i -g @project-serum/anchor-cli
|
|||
For now, we can use Cargo to install the CLI.
|
||||
|
||||
```bash
|
||||
cargo install --git https://github.com/project-serum/anchor --tag v0.21.0 anchor-cli --locked
|
||||
cargo install --git https://github.com/project-serum/anchor --tag v0.22.0 anchor-cli --locked
|
||||
```
|
||||
|
||||
On Linux systems you may need to install additional dependencies if `cargo install` fails. On Ubuntu,
|
||||
|
|
|
@ -30,7 +30,7 @@ have an `Anchor.toml` to define the build.
|
|||
An example `Anchor.toml` config looks as follows,
|
||||
|
||||
```toml
|
||||
anchor_version = "0.21.0"
|
||||
anchor_version = "0.22.0"
|
||||
|
||||
[workspace]
|
||||
members = ["programs/multisig"]
|
||||
|
|
|
@ -32,10 +32,10 @@ If the program has an IDL, it will also check the IDL deployed on chain matches.
|
|||
|
||||
## Images
|
||||
|
||||
A docker image for each version of Anchor is published on [Docker Hub](https://hub.docker.com/r/projectserum/build). They are tagged in the form `projectserum/build:<version>`. For example, to get the image for Anchor `v0.21.0` one can run
|
||||
A docker image for each version of Anchor is published on [Docker Hub](https://hub.docker.com/r/projectserum/build). They are tagged in the form `projectserum/build:<version>`. For example, to get the image for Anchor `v0.22.0` one can run
|
||||
|
||||
```
|
||||
docker pull projectserum/build:v0.21.0
|
||||
docker pull projectserum/build:v0.22.0
|
||||
```
|
||||
|
||||
## Removing an Image
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# Errors
|
||||
|
||||
If you've ever programmed on a blockchain, you've probably been frustrated by
|
||||
either non existant or opaque error codes. Anchor attempts to address this by
|
||||
providing the `#[error]` attribute, which can be used to create typed Errors with
|
||||
either non existent or opaque error codes. Anchor attempts to address this by
|
||||
providing the `#[error_code]` attribute, which can be used to create typed Errors with
|
||||
descriptive messages that automatically propagate to the client.
|
||||
|
||||
## Defining a Program
|
||||
|
@ -16,31 +16,26 @@ use anchor_lang::prelude::*;
|
|||
mod errors {
|
||||
use super::*;
|
||||
pub fn hello(_ctx: Context<Hello>) -> Result<()> {
|
||||
Err(ErrorCode::Hello.into())
|
||||
Err(error!(ErrorCode::Hello))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct Hello {}
|
||||
|
||||
#[error]
|
||||
#[error_code]
|
||||
pub enum ErrorCode {
|
||||
#[msg("This is an error message clients will automatically display")]
|
||||
Hello,
|
||||
}
|
||||
```
|
||||
|
||||
Observe the [#[error]](https://docs.rs/anchor-lang/latest/anchor_lang/attr.error.html) attribute on the `ErrorCode` enum. This macro generates two types: an `Error` and a `Result`, both of which can be used when returning from your program.
|
||||
Observe the [#[error_code]](https://docs.rs/anchor-lang/latest/anchor_lang/attr.error_code.html) attribute on the `ErrorCode` enum.
|
||||
This macro generates internal anchor code that helps anchor turn the error code into an error and display it properly.
|
||||
|
||||
To use the `Error`, you can simply use the user defined `ErrorCode` with Rust's [From](https://doc.rust-lang.org/std/convert/trait.From.html) trait. If you're unfamiliar with `From`, no worries. Just know that you need to either call
|
||||
`.into()` when using your `ErrorCode`. Or use Rust's `?` operator, when returning an error.
|
||||
Both of these will automatically convert *into* the correct `Error`.
|
||||
To create an error, use the [`error!`](https://docs.rs/anchor-lang/latest/anchor_lang/prelude/macro.error.html) macro together with an error code. This macro creates an [`AnchorError`](https://docs.rs/anchor-lang/latest/anchor_lang/error/struct.AnchorError.html) that includes helpful information like the file and line the error was created in.
|
||||
|
||||
::: details
|
||||
What's the deal with this From stuff? Well, because the Solana runtime expects a [ProgramError](https://docs.rs/solana-program/1.5.5/solana_program/program_error/enum.ProgramError.html) in the return value. The framework needs to wrap the user defined error code into a
|
||||
`ProgramError::Code` variant, before returning. The alternative would be to use the
|
||||
`ProgramError` directly.
|
||||
:::
|
||||
To make writing errors even easier, anchor also provides the [`err!`](https://docs.rs/anchor-lang/latest/anchor_lang/prelude/macro.err.html and the [`require!`](https://docs.rs/anchor-lang/latest/anchor_lang/prelude/macro.require.html macros.
|
||||
|
||||
## Using the Client
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "basic-0",
|
||||
"version": "0.21.0",
|
||||
"version": "0.22.0",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/project-serum/anchor#readme",
|
||||
"bugs": {
|
||||
|
@ -14,6 +14,6 @@
|
|||
"node": ">=11"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "anchor test"
|
||||
"test": "anchor test --skip-lint"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
|||
#[program]
|
||||
mod basic_0 {
|
||||
use super::*;
|
||||
pub fn initialize(_ctx: Context<Initialize>) -> ProgramResult {
|
||||
pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "basic-1",
|
||||
"version": "0.21.0",
|
||||
"version": "0.22.0",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/project-serum/anchor#readme",
|
||||
"bugs": {
|
||||
|
@ -14,6 +14,6 @@
|
|||
"node": ">=11"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "anchor test"
|
||||
"test": "anchor test --skip-lint"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,13 +6,13 @@ declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
|||
mod basic_1 {
|
||||
use super::*;
|
||||
|
||||
pub fn initialize(ctx: Context<Initialize>, data: u64) -> ProgramResult {
|
||||
pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
|
||||
let my_account = &mut ctx.accounts.my_account;
|
||||
my_account.data = data;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update(ctx: Context<Update>, data: u64) -> ProgramResult {
|
||||
pub fn update(ctx: Context<Update>, data: u64) -> Result<()> {
|
||||
let my_account = &mut ctx.accounts.my_account;
|
||||
my_account.data = data;
|
||||
Ok(())
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "basic-2",
|
||||
"version": "0.21.0",
|
||||
"version": "0.22.0",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/project-serum/anchor#readme",
|
||||
"bugs": {
|
||||
|
@ -14,6 +14,6 @@
|
|||
"node": ">=11"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "anchor test"
|
||||
"test": "anchor test --skip-lint"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,14 +6,14 @@ declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
|||
mod basic_2 {
|
||||
use super::*;
|
||||
|
||||
pub fn create(ctx: Context<Create>, authority: Pubkey) -> ProgramResult {
|
||||
pub fn create(ctx: Context<Create>, authority: Pubkey) -> Result<()> {
|
||||
let counter = &mut ctx.accounts.counter;
|
||||
counter.authority = authority;
|
||||
counter.count = 0;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn increment(ctx: Context<Increment>) -> ProgramResult {
|
||||
pub fn increment(ctx: Context<Increment>) -> Result<()> {
|
||||
let counter = &mut ctx.accounts.counter;
|
||||
counter.count += 1;
|
||||
Ok(())
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "basic-3",
|
||||
"version": "0.21.0",
|
||||
"version": "0.22.0",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/project-serum/anchor#readme",
|
||||
"bugs": {
|
||||
|
@ -14,6 +14,6 @@
|
|||
"node": ">=11"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "anchor test"
|
||||
"test": "anchor test --skip-lint"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ declare_id!("HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L");
|
|||
#[program]
|
||||
mod puppet_master {
|
||||
use super::*;
|
||||
pub fn pull_strings(ctx: Context<PullStrings>, data: u64) -> ProgramResult {
|
||||
pub fn pull_strings(ctx: Context<PullStrings>, data: u64) -> anchor_lang::Result<()> {
|
||||
let cpi_program = ctx.accounts.puppet_program.to_account_info();
|
||||
let cpi_accounts = SetData {
|
||||
puppet: ctx.accounts.puppet.to_account_info(),
|
||||
|
|
|
@ -5,11 +5,11 @@ declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
|||
#[program]
|
||||
pub mod puppet {
|
||||
use super::*;
|
||||
pub fn initialize(_ctx: Context<Initialize>) -> ProgramResult {
|
||||
pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_data(ctx: Context<SetData>, data: u64) -> ProgramResult {
|
||||
pub fn set_data(ctx: Context<SetData>, data: u64) -> Result<()> {
|
||||
let puppet = &mut ctx.accounts.puppet;
|
||||
puppet.data = data;
|
||||
Ok(())
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "basic-4",
|
||||
"version": "0.21.0",
|
||||
"version": "0.22.0",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/project-serum/anchor#readme",
|
||||
"bugs": {
|
||||
|
@ -14,6 +14,6 @@
|
|||
"node": ">=11"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "anchor test"
|
||||
"test": "anchor test --skip-lint"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,16 +14,16 @@ pub mod basic_4 {
|
|||
}
|
||||
|
||||
impl Counter {
|
||||
pub fn new(ctx: Context<Auth>) -> Result<Self> {
|
||||
pub fn new(ctx: Context<Auth>) -> anchor_lang::Result<Self> {
|
||||
Ok(Self {
|
||||
authority: *ctx.accounts.authority.key,
|
||||
count: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn increment(&mut self, ctx: Context<Auth>) -> Result<()> {
|
||||
pub fn increment(&mut self, ctx: Context<Auth>) -> anchor_lang::Result<()> {
|
||||
if &self.authority != ctx.accounts.authority.key {
|
||||
return Err(ErrorCode::Unauthorized.into());
|
||||
return Err(error!(ErrorCode::Unauthorized));
|
||||
}
|
||||
self.count += 1;
|
||||
Ok(())
|
||||
|
@ -37,7 +37,7 @@ pub struct Auth<'info> {
|
|||
}
|
||||
// #endregion code
|
||||
|
||||
#[error]
|
||||
#[error_code]
|
||||
pub enum ErrorCode {
|
||||
#[msg("You are not authorized to perform this action.")]
|
||||
Unauthorized,
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
"basic-4"
|
||||
],
|
||||
"dependencies": {
|
||||
"@project-serum/anchor": "^0.21.0"
|
||||
"@project-serum/anchor": "^0.22.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "^9.1.3",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "anchor-lang"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/project-serum/anchor"
|
||||
edition = "2018"
|
||||
|
@ -30,16 +30,16 @@ anchor-debug = [
|
|||
]
|
||||
|
||||
[dependencies]
|
||||
anchor-attribute-access-control = { path = "./attribute/access-control", version = "0.21.0" }
|
||||
anchor-attribute-account = { path = "./attribute/account", version = "0.21.0" }
|
||||
anchor-attribute-constant = { path = "./attribute/constant", version = "0.21.0" }
|
||||
anchor-attribute-error = { path = "./attribute/error", version = "0.21.0" }
|
||||
anchor-attribute-program = { path = "./attribute/program", version = "0.21.0" }
|
||||
anchor-attribute-state = { path = "./attribute/state", version = "0.21.0" }
|
||||
anchor-attribute-interface = { path = "./attribute/interface", version = "0.21.0" }
|
||||
anchor-attribute-event = { path = "./attribute/event", version = "0.21.0" }
|
||||
anchor-derive-accounts = { path = "./derive/accounts", version = "0.21.0" }
|
||||
anchor-common = { path = "./common", version = "0.21.0" }
|
||||
anchor-attribute-access-control = { path = "./attribute/access-control", version = "0.22.0" }
|
||||
anchor-attribute-account = { path = "./attribute/account", version = "0.22.0" }
|
||||
anchor-attribute-constant = { path = "./attribute/constant", version = "0.22.0" }
|
||||
anchor-attribute-error = { path = "./attribute/error", version = "0.22.0" }
|
||||
anchor-attribute-program = { path = "./attribute/program", version = "0.22.0" }
|
||||
anchor-attribute-state = { path = "./attribute/state", version = "0.22.0" }
|
||||
anchor-attribute-interface = { path = "./attribute/interface", version = "0.22.0" }
|
||||
anchor-attribute-event = { path = "./attribute/event", version = "0.22.0" }
|
||||
anchor-derive-accounts = { path = "./derive/accounts", version = "0.22.0" }
|
||||
anchor-common = { path = "./common", version = "0.22.0" }
|
||||
arrayref = "0.3.6"
|
||||
base64 = "0.13.0"
|
||||
borsh = "0.9"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "anchor-attribute-access-control"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/project-serum/anchor"
|
||||
license = "Apache-2.0"
|
||||
|
@ -18,5 +18,5 @@ proc-macro2 = "1.0"
|
|||
quote = "1.0"
|
||||
syn = { version = "1.0.60", features = ["full"] }
|
||||
anyhow = "1.0.32"
|
||||
anchor-syn = { path = "../../syn", version = "0.21.0" }
|
||||
anchor-syn = { path = "../../syn", version = "0.22.0" }
|
||||
regex = "1.0"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "anchor-attribute-account"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/project-serum/anchor"
|
||||
license = "Apache-2.0"
|
||||
|
@ -19,7 +19,7 @@ proc-macro2 = "1.0"
|
|||
quote = "1.0"
|
||||
syn = { version = "1.0.60", features = ["full"] }
|
||||
anyhow = "1.0.32"
|
||||
anchor-syn = { path = "../../syn", version = "0.21.0", features = ["hash"] }
|
||||
anchor-syn = { path = "../../syn", version = "0.22.0", features = ["hash"] }
|
||||
anchor-common = { path = "../../common", version = "0.21.0" }
|
||||
rustversion = "1.0.3"
|
||||
bs58 = "0.4.0"
|
||||
|
|
|
@ -149,7 +149,7 @@ pub fn account(
|
|||
// It's expected on-chain programs deserialize via zero-copy.
|
||||
#[automatically_derived]
|
||||
impl #impl_gen anchor_lang::AccountDeserialize for #account_name #type_gen #where_clause {
|
||||
fn try_deserialize(buf: &mut &[u8]) -> std::result::Result<Self, ProgramError> {
|
||||
fn try_deserialize(buf: &mut &[u8]) -> std::result::Result<Self> {
|
||||
// Header is always 8 bytes.
|
||||
if buf.len() < anchor_lang::accounts::header::HEADER_LEN {
|
||||
return Err(anchor_lang::error::ErrorCode::AccountDiscriminatorNotFound.into());
|
||||
|
@ -161,7 +161,7 @@ pub fn account(
|
|||
Self::try_deserialize_unchecked(buf)
|
||||
}
|
||||
|
||||
fn try_deserialize_unchecked(buf: &mut &[u8]) -> std::result::Result<Self, ProgramError> {
|
||||
fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
|
||||
let data: &[u8] = &buf[8..];
|
||||
// Re-interpret raw bytes into the POD data structure.
|
||||
let account = anchor_lang::__private::bytemuck::from_bytes(data);
|
||||
|
@ -179,7 +179,7 @@ pub fn account(
|
|||
|
||||
#[automatically_derived]
|
||||
impl #impl_gen anchor_lang::AccountSerialize for #account_name #type_gen #where_clause {
|
||||
fn try_serialize(&self, buf: &mut [u8]) -> std::result::Result<(), ProgramError> {
|
||||
fn try_serialize(&self, buf: &mut [u8]) -> anchor_lang::Result<()> {
|
||||
let dst = anchor_lang::accounts::header::read_data_mut(buf);
|
||||
let mut writer = std::io::Cursor::new(dst);
|
||||
AnchorSerialize::serialize(
|
||||
|
@ -193,7 +193,7 @@ pub fn account(
|
|||
|
||||
#[automatically_derived]
|
||||
impl #impl_gen anchor_lang::AccountDeserialize for #account_name #type_gen #where_clause {
|
||||
fn try_deserialize(buf: &mut &[u8]) -> std::result::Result<Self, ProgramError> {
|
||||
fn try_deserialize(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
|
||||
if buf.len() < #discriminator.len() {
|
||||
return Err(anchor_lang::error::ErrorCode::AccountDiscriminatorNotFound.into());
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ pub fn account(
|
|||
Self::try_deserialize_unchecked(buf)
|
||||
}
|
||||
|
||||
fn try_deserialize_unchecked(buf: &mut &[u8]) -> std::result::Result<Self, ProgramError> {
|
||||
fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
|
||||
let mut data: &[u8] = &buf[8..];
|
||||
AnchorDeserialize::deserialize(&mut data)
|
||||
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotDeserialize.into())
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "anchor-attribute-constant"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/project-serum/anchor"
|
||||
license = "Apache-2.0"
|
||||
|
@ -16,4 +16,4 @@ anchor-debug = ["anchor-syn/anchor-debug"]
|
|||
[dependencies]
|
||||
proc-macro2 = "1.0"
|
||||
syn = { version = "1.0.60", features = ["full"] }
|
||||
anchor-syn = { path = "../../syn", version = "0.21.0" }
|
||||
anchor-syn = { path = "../../syn", version = "0.22.0" }
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "anchor-attribute-error"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/project-serum/anchor"
|
||||
license = "Apache-2.0"
|
||||
|
@ -17,4 +17,4 @@ anchor-debug = ["anchor-syn/anchor-debug"]
|
|||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = { version = "1.0.60", features = ["full"] }
|
||||
anchor-syn = { path = "../../syn", version = "0.21.0" }
|
||||
anchor-syn = { path = "../../syn", version = "0.22.0" }
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
extern crate proc_macro;
|
||||
|
||||
use anchor_syn::codegen::error as error_codegen;
|
||||
use anchor_syn::parser::error as error_parser;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
use anchor_syn::parser::error::{self as error_parser, ErrorWithAccountNameInput};
|
||||
use anchor_syn::ErrorArgs;
|
||||
use syn::parse_macro_input;
|
||||
use anchor_syn::{codegen, parser::error::ErrorInput};
|
||||
use syn::{parse_macro_input, Expr};
|
||||
|
||||
/// Generates `Error` and `type Result<T> = Result<T, Error>` types to be
|
||||
/// used as return types from Anchor instruction handlers. Importantly, the
|
||||
|
@ -21,14 +24,14 @@ use syn::parse_macro_input;
|
|||
/// mod errors {
|
||||
/// use super::*;
|
||||
/// pub fn hello(_ctx: Context<Hello>) -> Result<()> {
|
||||
/// Err(MyError::Hello.into())
|
||||
/// Err(error!(MyError::Hello))
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Accounts)]
|
||||
/// pub struct Hello {}
|
||||
///
|
||||
/// #[error]
|
||||
/// #[error_code]
|
||||
/// pub enum MyError {
|
||||
/// #[msg("This is an error message clients will automatically display")]
|
||||
/// Hello,
|
||||
|
@ -40,14 +43,14 @@ use syn::parse_macro_input;
|
|||
/// [`ProgramError`](../solana_program/enum.ProgramError.html), which is used
|
||||
/// pervasively, throughout solana program crates. The generated `Error` type
|
||||
/// should almost never be used directly, as the user defined error is
|
||||
/// preferred. In the example above, `MyError::Hello.into()`.
|
||||
/// preferred. In the example above, `error!(MyError::Hello)`.
|
||||
///
|
||||
/// # Msg
|
||||
///
|
||||
/// The `#[msg(..)]` attribute is inert, and is used only as a marker so that
|
||||
/// parsers and IDLs can map error codes to error messages.
|
||||
#[proc_macro_attribute]
|
||||
pub fn error(
|
||||
pub fn error_code(
|
||||
args: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
|
@ -56,6 +59,69 @@ pub fn error(
|
|||
false => Some(parse_macro_input!(args as ErrorArgs)),
|
||||
};
|
||||
let mut error_enum = parse_macro_input!(input as syn::ItemEnum);
|
||||
let error = error_codegen::generate(error_parser::parse(&mut error_enum, args));
|
||||
let error = codegen::error::generate(error_parser::parse(&mut error_enum, args));
|
||||
proc_macro::TokenStream::from(error)
|
||||
}
|
||||
|
||||
/// Generates an [`Error::AnchorError`](../../anchor_lang/error/enum.Error.html) that includes file and line information.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust,ignore
|
||||
/// #[program]
|
||||
/// mod errors {
|
||||
/// use super::*;
|
||||
/// pub fn example(_ctx: Context<Example>) -> Result<()> {
|
||||
/// Err(error!(MyError::Hello))
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[error_code]
|
||||
/// pub enum MyError {
|
||||
/// #[msg("This is an error message clients will automatically display")]
|
||||
/// Hello,
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro]
|
||||
pub fn error(ts: proc_macro::TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(ts as ErrorInput);
|
||||
let error_code = input.error_code;
|
||||
create_error(error_code, true, None)
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn error_with_account_name(ts: proc_macro::TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(ts as ErrorWithAccountNameInput);
|
||||
let error_code = input.error_code;
|
||||
let account_name = input.account_name;
|
||||
create_error(error_code, false, Some(account_name))
|
||||
}
|
||||
|
||||
fn create_error(error_code: Expr, source: bool, account_name: Option<Expr>) -> TokenStream {
|
||||
let source = if source {
|
||||
quote! {
|
||||
Some(anchor_lang::error::Source {
|
||||
filename: file!(),
|
||||
line: line!()
|
||||
})
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
None
|
||||
}
|
||||
};
|
||||
let account_name = match account_name {
|
||||
Some(_) => quote! { Some(#account_name.to_string()) },
|
||||
None => quote! { None },
|
||||
};
|
||||
TokenStream::from(quote! {
|
||||
anchor_lang::error::Error::from(
|
||||
anchor_lang::error::AnchorError {
|
||||
error_name: #error_code.name(),
|
||||
error_code_number: #error_code.into(),
|
||||
error_msg: #error_code.to_string(),
|
||||
source: #source,
|
||||
account_name: #account_name
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "anchor-attribute-event"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/project-serum/anchor"
|
||||
license = "Apache-2.0"
|
||||
|
@ -19,5 +19,6 @@ proc-macro2 = "1.0"
|
|||
quote = "1.0"
|
||||
syn = { version = "1.0.60", features = ["full"] }
|
||||
anyhow = "1.0.32"
|
||||
anchor-syn = { path = "../../syn", version = "0.21.0", features = ["hash"] }
|
||||
anchor-common = { path = "../../common", version = "0.21.0" }
|
||||
anchor-syn = { path = "../../syn", version = "0.22.0", features = ["hash"] }
|
||||
anchor-common = { path = "../../common", version = "0.22.0" }
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "anchor-attribute-interface"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/project-serum/anchor"
|
||||
license = "Apache-2.0"
|
||||
|
@ -18,5 +18,5 @@ proc-macro2 = "1.0"
|
|||
quote = "1.0"
|
||||
syn = { version = "1.0.60", features = ["full"] }
|
||||
anyhow = "1.0.32"
|
||||
anchor-syn = { path = "../../syn", version = "0.21.0" }
|
||||
anchor-syn = { path = "../../syn", version = "0.22.0" }
|
||||
heck = "0.3.2"
|
||||
|
|
|
@ -26,7 +26,7 @@ use syn::parse_macro_input;
|
|||
///
|
||||
/// #[interface]
|
||||
/// pub trait Auth<'info, T: Accounts<'info>> {
|
||||
/// fn is_authorized(ctx: Context<T>, current: u64, new: u64) -> ProgramResult;
|
||||
/// fn is_authorized(ctx: Context<T>, current: u64, new: u64) -> anchor_lang::Result<()>;
|
||||
/// }
|
||||
///
|
||||
/// #[program]
|
||||
|
@ -74,13 +74,13 @@ use syn::parse_macro_input;
|
|||
/// impl<'info> SetCount<'info> {
|
||||
/// pub fn accounts(counter: &Counter, ctx: &Context<SetCount>) -> Result<()> {
|
||||
/// if ctx.accounts.auth_program.key != &counter.auth_program {
|
||||
/// return Err(ErrorCode::InvalidAuthProgram.into());
|
||||
/// return Err(error!(ErrorCode::InvalidAuthProgram));
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[error]
|
||||
/// #[error_code]
|
||||
/// pub enum ErrorCode {
|
||||
/// #[msg("Invalid auth program.")]
|
||||
/// InvalidAuthProgram,
|
||||
|
@ -104,14 +104,14 @@ use syn::parse_macro_input;
|
|||
/// pub struct CounterAuth;
|
||||
///
|
||||
/// impl<'info> Auth<'info, Empty> for CounterAuth {
|
||||
/// fn is_authorized(_ctx: Context<Empty>, current: u64, new: u64) -> ProgramResult {
|
||||
/// fn is_authorized(_ctx: Context<Empty>, current: u64, new: u64) -> Result<()> {
|
||||
/// if current % 2 == 0 {
|
||||
/// if new % 2 == 0 {
|
||||
/// return Err(ProgramError::Custom(50)); // Arbitrary error code.
|
||||
/// return Err(ProgramError::Custom(50).into()); // Arbitrary error code.
|
||||
/// }
|
||||
/// } else {
|
||||
/// if new % 2 == 1 {
|
||||
/// return Err(ProgramError::Custom(60)); // Arbitrary error code.
|
||||
/// return Err(ProgramError::Custom(60).into()); // Arbitrary error code.
|
||||
/// }
|
||||
/// }
|
||||
/// Ok(())
|
||||
|
@ -200,7 +200,7 @@ pub fn interface(
|
|||
pub fn #method_name<'a,'b, 'c, 'info, T: anchor_lang::Accounts<'info> + anchor_lang::ToAccountMetas + anchor_lang::ToAccountInfos<'info>>(
|
||||
ctx: anchor_lang::context::CpiContext<'a, 'b, 'c, 'info, T>,
|
||||
#(#args),*
|
||||
) -> anchor_lang::solana_program::entrypoint::ProgramResult {
|
||||
) -> anchor_lang::Result<()> {
|
||||
#args_struct
|
||||
|
||||
let ix = {
|
||||
|
@ -224,7 +224,7 @@ pub fn interface(
|
|||
&ix,
|
||||
&acc_infos,
|
||||
ctx.signer_seeds,
|
||||
)
|
||||
).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "anchor-attribute-program"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/project-serum/anchor"
|
||||
license = "Apache-2.0"
|
||||
|
@ -18,4 +18,4 @@ proc-macro2 = "1.0"
|
|||
quote = "1.0"
|
||||
syn = { version = "1.0.60", features = ["full"] }
|
||||
anyhow = "1.0.32"
|
||||
anchor-syn = { path = "../../syn", version = "0.21.0" }
|
||||
anchor-syn = { path = "../../syn", version = "0.22.0" }
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "anchor-attribute-state"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/project-serum/anchor"
|
||||
license = "Apache-2.0"
|
||||
|
@ -18,4 +18,4 @@ proc-macro2 = "1.0"
|
|||
quote = "1.0"
|
||||
syn = { version = "1.0.60", features = ["full"] }
|
||||
anyhow = "1.0.32"
|
||||
anchor-syn = { path = "../../syn", version = "0.21.0" }
|
||||
anchor-syn = { path = "../../syn", version = "0.22.0" }
|
||||
|
|
|
@ -42,18 +42,18 @@ pub fn state(
|
|||
// as the initialized value. Use the default implementation.
|
||||
quote! {
|
||||
impl anchor_lang::__private::AccountSize for #struct_ident {
|
||||
fn size(&self) -> std::result::Result<u64, anchor_lang::solana_program::program_error::ProgramError> {
|
||||
fn size(&self) -> anchor_lang::Result<u64> {
|
||||
Ok(8 + self
|
||||
.try_to_vec()
|
||||
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotSerialize)?
|
||||
.len() as u64)
|
||||
.try_to_vec()
|
||||
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotSerialize)?
|
||||
.len() as u64)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if is_zero_copy {
|
||||
quote! {
|
||||
impl anchor_lang::__private::AccountSize for #struct_ident {
|
||||
fn size(&self) -> std::result::Result<u64, anchor_lang::solana_program::program_error::ProgramError> {
|
||||
fn size(&self) -> anchor_lang::Result<u64> {
|
||||
let len = anchor_lang::__private::bytemuck::bytes_of(self).len() as u64;
|
||||
Ok(8 + len)
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ pub fn state(
|
|||
// Size override given to the macro. Use it.
|
||||
quote! {
|
||||
impl anchor_lang::__private::AccountSize for #struct_ident {
|
||||
fn size(&self) -> std::result::Result<u64, anchor_lang::solana_program::program_error::ProgramError> {
|
||||
fn size(&self) -> anchor_lang::Result<u64> {
|
||||
Ok(#size)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "anchor-derive-accounts"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/project-serum/anchor"
|
||||
license = "Apache-2.0"
|
||||
|
@ -20,4 +20,4 @@ proc-macro2 = "1.0"
|
|||
quote = "1.0"
|
||||
syn = { version = "1.0.60", features = ["full"] }
|
||||
anyhow = "1.0.32"
|
||||
anchor-syn = { path = "../../syn", version = "0.21.0" }
|
||||
anchor-syn = { path = "../../syn", version = "0.22.0" }
|
||||
|
|
|
@ -22,7 +22,7 @@ use syn::parse_macro_input;
|
|||
///
|
||||
/// ```ignore
|
||||
/// ...
|
||||
/// pub fn initialize(ctx: Context<Create>, bump: u8, authority: Pubkey, data: u64) -> ProgramResult {
|
||||
/// pub fn initialize(ctx: Context<Create>, bump: u8, authority: Pubkey, data: u64) -> anchor_lang::Result<()> {
|
||||
/// ...
|
||||
/// Ok(())
|
||||
/// }
|
||||
|
|
|
@ -3,9 +3,7 @@
|
|||
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::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
|
@ -44,7 +42,7 @@ use std::ops::{Deref, DerefMut};
|
|||
/// #[program]
|
||||
/// mod hello_anchor {
|
||||
/// use super::*;
|
||||
/// pub fn set_data(ctx: Context<SetData>, data: u64) -> ProgramResult {
|
||||
/// pub fn set_data(ctx: Context<SetData>, data: u64) -> Result<()> {
|
||||
/// if (*ctx.accounts.auth_account).authorized {
|
||||
/// (*ctx.accounts.my_account).data = data;
|
||||
/// }
|
||||
|
@ -104,7 +102,7 @@ use std::ops::{Deref, DerefMut};
|
|||
/// // "try_deserialize_unchecked" by default which is what we want here
|
||||
/// // because non-anchor accounts don't have a discriminator to check
|
||||
/// impl anchor_lang::AccountDeserialize for Mint {
|
||||
/// fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self, ProgramError> {
|
||||
/// fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self> {
|
||||
/// spl_token::state::Mint::unpack(buf).map(Mint)
|
||||
/// }
|
||||
/// }
|
||||
|
@ -161,7 +159,7 @@ use std::ops::{Deref, DerefMut};
|
|||
/// pub fn set_initial_admin(
|
||||
/// ctx: Context<SetInitialAdmin>,
|
||||
/// admin_key: Pubkey
|
||||
/// ) -> ProgramResult {
|
||||
/// ) -> Result<()> {
|
||||
/// ctx.accounts.admin_settings.admin_key = admin_key;
|
||||
/// Ok(())
|
||||
/// }
|
||||
|
@ -243,7 +241,7 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Owner + Clone> Account<'a, T
|
|||
|
||||
/// Deserializes the given `info` into a `Account`.
|
||||
#[inline(never)]
|
||||
pub fn try_from(info: &AccountInfo<'a>) -> Result<Account<'a, T>, ProgramError> {
|
||||
pub fn try_from(info: &AccountInfo<'a>) -> Result<Account<'a, T>> {
|
||||
if info.owner == &system_program::ID && info.lamports() == 0 {
|
||||
return Err(ErrorCode::AccountNotInitialized.into());
|
||||
}
|
||||
|
@ -258,7 +256,7 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Owner + Clone> Account<'a, T
|
|||
/// 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> {
|
||||
pub fn try_from_unchecked(info: &AccountInfo<'a>) -> Result<Account<'a, T>> {
|
||||
if info.owner == &system_program::ID && info.lamports() == 0 {
|
||||
return Err(ErrorCode::AccountNotInitialized.into());
|
||||
}
|
||||
|
@ -274,7 +272,7 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Owner + Clone> Account<'a, T
|
|||
|
||||
/// Reloads the account from storage. This is useful, for example, when
|
||||
/// observing side effects after CPI.
|
||||
pub fn reload(&mut self) -> ProgramResult {
|
||||
pub fn reload(&mut self) -> Result<()> {
|
||||
let mut data: &[u8] = &self.info.try_borrow_data()?;
|
||||
self.account = T::try_deserialize(&mut data)?;
|
||||
Ok(())
|
||||
|
@ -288,7 +286,7 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Owner + Clone> Account<'a, T
|
|||
///
|
||||
/// Instead of this:
|
||||
/// ```ignore
|
||||
/// pub fn new_user(ctx: Context<CreateUser>, new_user:User) -> ProgramResult {
|
||||
/// pub fn new_user(ctx: Context<CreateUser>, new_user:User) -> Result<()> {
|
||||
/// (*ctx.accounts.user_to_create).name = new_user.name;
|
||||
/// (*ctx.accounts.user_to_create).age = new_user.age;
|
||||
/// (*ctx.accounts.user_to_create).address = new_user.address;
|
||||
|
@ -296,7 +294,7 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Owner + Clone> Account<'a, T
|
|||
/// ```
|
||||
/// You can do this:
|
||||
/// ```ignore
|
||||
/// pub fn new_user(ctx: Context<CreateUser>, new_user:User) -> ProgramResult {
|
||||
/// pub fn new_user(ctx: Context<CreateUser>, new_user:User) -> Result<()> {
|
||||
/// ctx.accounts.user_to_create.set_inner(new_user);
|
||||
/// }
|
||||
/// ```
|
||||
|
@ -316,7 +314,7 @@ where
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
_ix_data: &[u8],
|
||||
_bumps: &mut BTreeMap<String, u8>,
|
||||
) -> Result<Self, ProgramError> {
|
||||
) -> Result<Self> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
|
@ -329,7 +327,7 @@ where
|
|||
impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AccountsExit<'info>
|
||||
for Account<'info, T>
|
||||
{
|
||||
fn exit(&self, program_id: &Pubkey) -> ProgramResult {
|
||||
fn exit(&self, program_id: &Pubkey) -> Result<()> {
|
||||
// Only persist if the owner is the current program.
|
||||
if &T::owner() == program_id {
|
||||
let info = self.to_account_info();
|
||||
|
@ -340,10 +338,17 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AccountsEx
|
|||
}
|
||||
}
|
||||
|
||||
/// This function is for INTERNAL USE ONLY.
|
||||
/// Do NOT use this function in a program.
|
||||
/// Manual closing of `Account<'info, T>` types is NOT supported.
|
||||
///
|
||||
/// Details: Using `close` with `Account<'info, T>` is not safe because
|
||||
/// it requires the `mut` constraint but for that type the constraint
|
||||
/// overwrites the "closed account" discriminator at the end of the instruction.
|
||||
impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AccountsClose<'info>
|
||||
for Account<'info, T>
|
||||
{
|
||||
fn close(&self, sol_destination: AccountInfo<'info>) -> ProgramResult {
|
||||
fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> {
|
||||
crate::common::close(self.to_account_info(), sol_destination)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,9 @@
|
|||
//! should be used instead.
|
||||
|
||||
use crate::error::ErrorCode;
|
||||
use crate::{Accounts, AccountsExit, ToAccountInfos, ToAccountMetas};
|
||||
use crate::{Accounts, AccountsExit, Result, ToAccountInfos, ToAccountMetas};
|
||||
use solana_program::account_info::AccountInfo;
|
||||
use solana_program::instruction::AccountMeta;
|
||||
use solana_program::program_error::ProgramError;
|
||||
use solana_program::pubkey::Pubkey;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
|
@ -16,7 +15,7 @@ impl<'info> Accounts<'info> for AccountInfo<'info> {
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
_ix_data: &[u8],
|
||||
_bumps: &mut BTreeMap<String, u8>,
|
||||
) -> Result<Self, ProgramError> {
|
||||
) -> Result<Self> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
|
|
|
@ -3,10 +3,9 @@
|
|||
use crate::accounts::header;
|
||||
use crate::error::ErrorCode;
|
||||
use crate::*;
|
||||
use arrayref::array_ref;
|
||||
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::cell::{Ref, RefMut};
|
||||
use std::collections::BTreeMap;
|
||||
|
@ -46,14 +45,14 @@ use std::ops::DerefMut;
|
|||
/// pub mod bar {
|
||||
/// use super::*;
|
||||
///
|
||||
/// pub fn create_bar(ctx: Context<CreateBar>, data: u64) -> ProgramResult {
|
||||
/// pub fn create_bar(ctx: Context<CreateBar>, data: u64) -> Result<()> {
|
||||
/// let bar = &mut ctx.accounts.bar.load_init()?;
|
||||
/// bar.authority = ctx.accounts.authority.key();
|
||||
/// bar.data = data;
|
||||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
/// pub fn update_bar(ctx: Context<UpdateBar>, data: u64) -> ProgramResult {
|
||||
/// pub fn update_bar(ctx: Context<UpdateBar>, data: u64) -> Result<()> {
|
||||
/// (*ctx.accounts.bar.load_mut()?).data = data;
|
||||
/// Ok(())
|
||||
/// }
|
||||
|
@ -113,9 +112,7 @@ impl<'info, T: ZeroCopy + Owner> AccountLoader<'info, T> {
|
|||
|
||||
/// Constructs a new `AccountLoader` from a previously initialized account.
|
||||
#[inline(never)]
|
||||
pub fn try_from(
|
||||
acc_info: &AccountInfo<'info>,
|
||||
) -> Result<AccountLoader<'info, T>, ProgramError> {
|
||||
pub fn try_from(acc_info: &AccountInfo<'info>) -> Result<AccountLoader<'info, T>> {
|
||||
if acc_info.owner != &T::owner() {
|
||||
return Err(ErrorCode::AccountOwnedByWrongProgram.into());
|
||||
}
|
||||
|
@ -133,14 +130,14 @@ impl<'info, T: ZeroCopy + Owner> AccountLoader<'info, T> {
|
|||
pub fn try_from_unchecked(
|
||||
_program_id: &Pubkey,
|
||||
acc_info: &AccountInfo<'info>,
|
||||
) -> Result<AccountLoader<'info, T>, ProgramError> {
|
||||
) -> Result<AccountLoader<'info, T>> {
|
||||
if acc_info.owner != &T::owner() {
|
||||
return Err(ErrorCode::AccountOwnedByWrongProgram.into());
|
||||
}
|
||||
Ok(AccountLoader::new(acc_info.clone()))
|
||||
}
|
||||
/// Returns a Ref to the account data structure for reading.
|
||||
pub fn load(&self) -> Result<Ref<T>, ProgramError> {
|
||||
pub fn load(&self) -> Result<Ref<T>> {
|
||||
let data = self.acc_info.try_borrow_data()?;
|
||||
let disc_bytes = header::read_discriminator(&data);
|
||||
if disc_bytes != &T::discriminator() {
|
||||
|
@ -152,7 +149,7 @@ impl<'info, T: ZeroCopy + Owner> AccountLoader<'info, T> {
|
|||
}
|
||||
|
||||
/// Returns a `RefMut` to the account data structure for reading or writing.
|
||||
pub fn load_mut(&self) -> Result<RefMut<T>, ProgramError> {
|
||||
pub fn load_mut(&self) -> Result<RefMut<T>> {
|
||||
// AccountInfo api allows you to borrow mut even if the account isn't
|
||||
// writable, so add this check for a better dev experience.
|
||||
if !self.acc_info.is_writable {
|
||||
|
@ -178,7 +175,7 @@ impl<'info, T: ZeroCopy + Owner> Accounts<'info> for AccountLoader<'info, T> {
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
_ix_data: &[u8],
|
||||
_bumps: &mut BTreeMap<String, u8>,
|
||||
) -> Result<Self, ProgramError> {
|
||||
) -> Result<Self> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
|
@ -197,8 +194,15 @@ impl<'info, T: ZeroCopy + Owner> AccountsExit<'info> for AccountLoader<'info, T>
|
|||
}
|
||||
}
|
||||
|
||||
/// This function is for INTERNAL USE ONLY.
|
||||
/// Do NOT use this function in a program.
|
||||
/// Manual closing of `AccountLoader<'info, T>` types is NOT supported.
|
||||
///
|
||||
/// Details: Using `close` with `AccountLoader<'info, T>` is not safe because
|
||||
/// it requires the `mut` constraint but for that type the constraint
|
||||
/// overwrites the "closed account" discriminator at the end of the instruction.
|
||||
impl<'info, T: ZeroCopy + Owner> AccountsClose<'info> for AccountLoader<'info, T> {
|
||||
fn close(&self, sol_destination: AccountInfo<'info>) -> ProgramResult {
|
||||
fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> {
|
||||
crate::common::close(self.to_account_info(), sol_destination)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,11 +13,9 @@
|
|||
//! }
|
||||
//! ```
|
||||
|
||||
use crate::{Accounts, AccountsClose, AccountsExit, ToAccountInfos, ToAccountMetas};
|
||||
use crate::{Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMetas};
|
||||
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::collections::BTreeMap;
|
||||
use std::ops::Deref;
|
||||
|
@ -28,13 +26,13 @@ impl<'info, T: Accounts<'info>> Accounts<'info> for Box<T> {
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
ix_data: &[u8],
|
||||
bumps: &mut BTreeMap<String, u8>,
|
||||
) -> Result<Self, ProgramError> {
|
||||
) -> Result<Self> {
|
||||
T::try_accounts(program_id, accounts, ix_data, bumps).map(Box::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'info, T: AccountsExit<'info>> AccountsExit<'info> for Box<T> {
|
||||
fn exit(&self, program_id: &Pubkey) -> ProgramResult {
|
||||
fn exit(&self, program_id: &Pubkey) -> Result<()> {
|
||||
T::exit(Deref::deref(self), program_id)
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +50,7 @@ impl<T: ToAccountMetas> ToAccountMetas for Box<T> {
|
|||
}
|
||||
|
||||
impl<'info, T: AccountsClose<'info>> AccountsClose<'info> for Box<T> {
|
||||
fn close(&self, sol_destination: AccountInfo<'info>) -> ProgramResult {
|
||||
fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> {
|
||||
T::close(self, sol_destination)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
use crate::*;
|
||||
use crate::{error::ErrorCode, prelude::Account};
|
||||
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::collections::BTreeMap;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
@ -23,7 +21,7 @@ impl<'a, T: AccountDeserialize + Clone> CpiAccount<'a, T> {
|
|||
}
|
||||
|
||||
/// Deserializes the given `info` into a `CpiAccount`.
|
||||
pub fn try_from(info: &AccountInfo<'a>) -> Result<CpiAccount<'a, T>, ProgramError> {
|
||||
pub fn try_from(info: &AccountInfo<'a>) -> Result<CpiAccount<'a, T>> {
|
||||
let mut data: &[u8] = &info.try_borrow_data()?;
|
||||
Ok(CpiAccount::new(
|
||||
info.clone(),
|
||||
|
@ -31,13 +29,13 @@ impl<'a, T: AccountDeserialize + Clone> CpiAccount<'a, T> {
|
|||
))
|
||||
}
|
||||
|
||||
pub fn try_from_unchecked(info: &AccountInfo<'a>) -> Result<CpiAccount<'a, T>, ProgramError> {
|
||||
pub fn try_from_unchecked(info: &AccountInfo<'a>) -> Result<CpiAccount<'a, T>> {
|
||||
Self::try_from(info)
|
||||
}
|
||||
|
||||
/// Reloads the account from storage. This is useful, for example, when
|
||||
/// observing side effects after CPI.
|
||||
pub fn reload(&mut self) -> ProgramResult {
|
||||
pub fn reload(&mut self) -> Result<()> {
|
||||
let mut data: &[u8] = &self.info.try_borrow_data()?;
|
||||
self.account = Box::new(T::try_deserialize(&mut data)?);
|
||||
Ok(())
|
||||
|
@ -55,7 +53,7 @@ where
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
_ix_data: &[u8],
|
||||
_bumps: &mut BTreeMap<String, u8>,
|
||||
) -> Result<Self, ProgramError> {
|
||||
) -> Result<Self> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@ use crate::error::ErrorCode;
|
|||
#[allow(deprecated)]
|
||||
use crate::{accounts::state::ProgramState, context::CpiStateContext};
|
||||
use crate::{
|
||||
AccountDeserialize, AccountSerialize, Accounts, AccountsExit, ToAccountInfos, ToAccountMetas,
|
||||
AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Result, ToAccountInfos,
|
||||
ToAccountMetas,
|
||||
};
|
||||
use solana_program::account_info::AccountInfo;
|
||||
use solana_program::instruction::AccountMeta;
|
||||
use solana_program::program_error::ProgramError;
|
||||
use solana_program::pubkey::Pubkey;
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
@ -35,7 +35,7 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> CpiState<'info, T>
|
|||
|
||||
/// Deserializes the given `info` into a `CpiState`.
|
||||
#[inline(never)]
|
||||
pub fn try_from(info: &AccountInfo<'info>) -> Result<CpiState<'info, T>, ProgramError> {
|
||||
pub fn try_from(info: &AccountInfo<'info>) -> Result<CpiState<'info, T>> {
|
||||
let mut data: &[u8] = &info.try_borrow_data()?;
|
||||
Ok(CpiState::new(info.clone(), T::try_deserialize(&mut data)?))
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ where
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
_ix_data: &[u8],
|
||||
_bumps: &mut BTreeMap<String, u8>,
|
||||
) -> Result<Self, ProgramError> {
|
||||
) -> Result<Self> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use crate::accounts::header;
|
||||
use crate::error::ErrorCode;
|
||||
use crate::*;
|
||||
use arrayref::array_ref;
|
||||
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::cell::{Ref, RefMut};
|
||||
use std::collections::BTreeMap;
|
||||
|
@ -54,7 +53,7 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
|
|||
pub fn try_from(
|
||||
program_id: &Pubkey,
|
||||
acc_info: &AccountInfo<'info>,
|
||||
) -> Result<Loader<'info, T>, ProgramError> {
|
||||
) -> Result<Loader<'info, T>> {
|
||||
if acc_info.owner != program_id {
|
||||
return Err(ErrorCode::AccountOwnedByWrongProgram.into());
|
||||
}
|
||||
|
@ -73,7 +72,7 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
|
|||
pub fn try_from_unchecked(
|
||||
program_id: &Pubkey,
|
||||
acc_info: &AccountInfo<'info>,
|
||||
) -> Result<Loader<'info, T>, ProgramError> {
|
||||
) -> Result<Loader<'info, T>> {
|
||||
if acc_info.owner != program_id {
|
||||
return Err(ErrorCode::AccountOwnedByWrongProgram.into());
|
||||
}
|
||||
|
@ -82,7 +81,7 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
|
|||
|
||||
/// Returns a Ref to the account data structure for reading.
|
||||
#[allow(deprecated)]
|
||||
pub fn load(&self) -> Result<Ref<T>, ProgramError> {
|
||||
pub fn load(&self) -> Result<Ref<T>> {
|
||||
let data = self.acc_info.try_borrow_data()?;
|
||||
let disc_bytes = header::read_discriminator(&data);
|
||||
if disc_bytes != &T::discriminator() {
|
||||
|
@ -94,7 +93,7 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
|
|||
|
||||
/// Returns a `RefMut` to the account data structure for reading or writing.
|
||||
#[allow(deprecated)]
|
||||
pub fn load_mut(&self) -> Result<RefMut<T>, ProgramError> {
|
||||
pub fn load_mut(&self) -> Result<RefMut<T>> {
|
||||
// AccountInfo api allows you to borrow mut even if the account isn't
|
||||
// writable, so add this check for a better dev experience.
|
||||
if !self.acc_info.is_writable {
|
||||
|
@ -121,7 +120,7 @@ impl<'info, T: ZeroCopy> Accounts<'info> for Loader<'info, T> {
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
_ix_data: &[u8],
|
||||
_bumps: &mut BTreeMap<String, u8>,
|
||||
) -> Result<Self, ProgramError> {
|
||||
) -> Result<Self> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
|
@ -141,9 +140,16 @@ impl<'info, T: ZeroCopy> AccountsExit<'info> for Loader<'info, T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// This function is for INTERNAL USE ONLY.
|
||||
/// Do NOT use this function in a program.
|
||||
/// Manual closing of `Loader<'info, T>` types is NOT supported.
|
||||
///
|
||||
/// Details: Using `close` with `Loader<'info, T>` is not safe because
|
||||
/// it requires the `mut` constraint but for that type the constraint
|
||||
/// overwrites the "closed account" discriminator at the end of the instruction.
|
||||
#[allow(deprecated)]
|
||||
impl<'info, T: ZeroCopy> AccountsClose<'info> for Loader<'info, T> {
|
||||
fn close(&self, sol_destination: AccountInfo<'info>) -> ProgramResult {
|
||||
fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> {
|
||||
crate::common::close(self.to_account_info(), sol_destination)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ use crate::*;
|
|||
use solana_program::account_info::AccountInfo;
|
||||
use solana_program::bpf_loader_upgradeable::{self, UpgradeableLoaderState};
|
||||
use solana_program::instruction::AccountMeta;
|
||||
use solana_program::program_error::ProgramError;
|
||||
use solana_program::pubkey::Pubkey;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
|
@ -85,7 +84,7 @@ impl<'a, T: Id + Clone> Program<'a, T> {
|
|||
|
||||
/// Deserializes the given `info` into a `Program`.
|
||||
#[inline(never)]
|
||||
pub fn try_from(info: &AccountInfo<'a>) -> Result<Program<'a, T>, ProgramError> {
|
||||
pub fn try_from(info: &AccountInfo<'a>) -> Result<Program<'a, T>> {
|
||||
if info.key != &T::id() {
|
||||
return Err(ErrorCode::InvalidProgramId.into());
|
||||
}
|
||||
|
@ -137,7 +136,7 @@ where
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
_ix_data: &[u8],
|
||||
_bumps: &mut BTreeMap<String, u8>,
|
||||
) -> Result<Self, ProgramError> {
|
||||
) -> Result<Self> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
|
|
|
@ -3,9 +3,7 @@ use crate::accounts::cpi_account::CpiAccount;
|
|||
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::collections::BTreeMap;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
@ -34,10 +32,7 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Clone> ProgramAccount<'a, T>
|
|||
|
||||
/// Deserializes the given `info` into a `ProgramAccount`.
|
||||
#[inline(never)]
|
||||
pub fn try_from(
|
||||
program_id: &Pubkey,
|
||||
info: &AccountInfo<'a>,
|
||||
) -> Result<ProgramAccount<'a, T>, ProgramError> {
|
||||
pub fn try_from(program_id: &Pubkey, info: &AccountInfo<'a>) -> Result<ProgramAccount<'a, T>> {
|
||||
if info.owner != program_id {
|
||||
return Err(ErrorCode::AccountOwnedByWrongProgram.into());
|
||||
}
|
||||
|
@ -55,7 +50,7 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Clone> ProgramAccount<'a, T>
|
|||
pub fn try_from_unchecked(
|
||||
program_id: &Pubkey,
|
||||
info: &AccountInfo<'a>,
|
||||
) -> Result<ProgramAccount<'a, T>, ProgramError> {
|
||||
) -> Result<ProgramAccount<'a, T>> {
|
||||
if info.owner != program_id {
|
||||
return Err(ErrorCode::AccountOwnedByWrongProgram.into());
|
||||
}
|
||||
|
@ -82,7 +77,7 @@ where
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
_ix_data: &[u8],
|
||||
_bumps: &mut BTreeMap<String, u8>,
|
||||
) -> Result<Self, ProgramError> {
|
||||
) -> Result<Self> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
|
@ -96,7 +91,7 @@ where
|
|||
impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsExit<'info>
|
||||
for ProgramAccount<'info, T>
|
||||
{
|
||||
fn exit(&self, _program_id: &Pubkey) -> ProgramResult {
|
||||
fn exit(&self, _program_id: &Pubkey) -> Result<()> {
|
||||
let info = self.to_account_info();
|
||||
let mut data = info.try_borrow_mut_data()?;
|
||||
self.inner.account.try_serialize(&mut data)?;
|
||||
|
@ -104,11 +99,18 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsExit<'info
|
|||
}
|
||||
}
|
||||
|
||||
/// This function is for INTERNAL USE ONLY.
|
||||
/// Do NOT use this function in a program.
|
||||
/// Manual closing of `ProgramAccount<'info, T>` types is NOT supported.
|
||||
///
|
||||
/// Details: Using `close` with `ProgramAccount<'info, T>` is not safe because
|
||||
/// it requires the `mut` constraint but for that type the constraint
|
||||
/// overwrites the "closed account" discriminator at the end of the instruction.
|
||||
#[allow(deprecated)]
|
||||
impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsClose<'info>
|
||||
for ProgramAccount<'info, T>
|
||||
{
|
||||
fn close(&self, sol_destination: AccountInfo<'info>) -> ProgramResult {
|
||||
fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> {
|
||||
crate::common::close(self.to_account_info(), sol_destination)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ use crate::error::ErrorCode;
|
|||
use crate::*;
|
||||
use solana_program::account_info::AccountInfo;
|
||||
use solana_program::instruction::AccountMeta;
|
||||
use solana_program::program_error::ProgramError;
|
||||
use solana_program::pubkey::Pubkey;
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::Deref;
|
||||
|
@ -47,7 +46,7 @@ impl<'info> Signer<'info> {
|
|||
|
||||
/// Deserializes the given `info` into a `Signer`.
|
||||
#[inline(never)]
|
||||
pub fn try_from(info: &AccountInfo<'info>) -> Result<Signer<'info>, ProgramError> {
|
||||
pub fn try_from(info: &AccountInfo<'info>) -> Result<Signer<'info>> {
|
||||
if !info.is_signer {
|
||||
return Err(ErrorCode::AccountNotSigner.into());
|
||||
}
|
||||
|
@ -62,7 +61,7 @@ impl<'info> Accounts<'info> for Signer<'info> {
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
_ix_data: &[u8],
|
||||
_bumps: &mut BTreeMap<String, u8>,
|
||||
) -> Result<Self, ProgramError> {
|
||||
) -> Result<Self> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
|
|
|
@ -2,13 +2,11 @@
|
|||
use crate::accounts::cpi_account::CpiAccount;
|
||||
use crate::error::ErrorCode;
|
||||
use crate::{
|
||||
AccountDeserialize, AccountSerialize, Accounts, AccountsExit, ToAccountInfo, ToAccountInfos,
|
||||
ToAccountMetas,
|
||||
AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Result, ToAccountInfo,
|
||||
ToAccountInfos, ToAccountMetas,
|
||||
};
|
||||
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::collections::BTreeMap;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
@ -39,10 +37,7 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Clone> ProgramState<'a, T> {
|
|||
|
||||
/// Deserializes the given `info` into a `ProgramState`.
|
||||
#[inline(never)]
|
||||
pub fn try_from(
|
||||
program_id: &Pubkey,
|
||||
info: &AccountInfo<'a>,
|
||||
) -> Result<ProgramState<'a, T>, ProgramError> {
|
||||
pub fn try_from(program_id: &Pubkey, info: &AccountInfo<'a>) -> Result<ProgramState<'a, T>> {
|
||||
if info.owner != program_id {
|
||||
return Err(ErrorCode::AccountOwnedByWrongProgram.into());
|
||||
}
|
||||
|
@ -77,7 +72,7 @@ where
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
_ix_data: &[u8],
|
||||
_bumps: &mut BTreeMap<String, u8>,
|
||||
) -> Result<Self, ProgramError> {
|
||||
) -> Result<Self> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
|
@ -149,7 +144,7 @@ where
|
|||
impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsExit<'info>
|
||||
for ProgramState<'info, T>
|
||||
{
|
||||
fn exit(&self, _program_id: &Pubkey) -> ProgramResult {
|
||||
fn exit(&self, _program_id: &Pubkey) -> Result<()> {
|
||||
let info = self.to_account_info();
|
||||
let mut data = info.try_borrow_mut_data()?;
|
||||
self.inner.account.try_serialize(&mut data)?;
|
||||
|
|
|
@ -4,7 +4,6 @@ use crate::error::ErrorCode;
|
|||
use crate::*;
|
||||
use solana_program::account_info::AccountInfo;
|
||||
use solana_program::instruction::AccountMeta;
|
||||
use solana_program::program_error::ProgramError;
|
||||
use solana_program::pubkey::Pubkey;
|
||||
use solana_program::system_program;
|
||||
use std::collections::BTreeMap;
|
||||
|
@ -26,7 +25,7 @@ impl<'info> SystemAccount<'info> {
|
|||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn try_from(info: &AccountInfo<'info>) -> Result<SystemAccount<'info>, ProgramError> {
|
||||
pub fn try_from(info: &AccountInfo<'info>) -> Result<SystemAccount<'info>> {
|
||||
if *info.owner != system_program::ID {
|
||||
return Err(ErrorCode::AccountNotSystemOwned.into());
|
||||
}
|
||||
|
@ -41,7 +40,7 @@ impl<'info> Accounts<'info> for SystemAccount<'info> {
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
_ix_data: &[u8],
|
||||
_bumps: &mut BTreeMap<String, u8>,
|
||||
) -> Result<Self, ProgramError> {
|
||||
) -> Result<Self> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
//! Type validating that the account is a sysvar and deserializing it
|
||||
|
||||
use crate::error::ErrorCode;
|
||||
use crate::{Accounts, AccountsExit, ToAccountInfos, ToAccountMetas};
|
||||
use crate::{Accounts, AccountsExit, Result, ToAccountInfos, ToAccountMetas};
|
||||
use solana_program::account_info::AccountInfo;
|
||||
use solana_program::instruction::AccountMeta;
|
||||
use solana_program::program_error::ProgramError;
|
||||
use solana_program::pubkey::Pubkey;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
|
@ -27,7 +26,7 @@ use std::ops::{Deref, DerefMut};
|
|||
/// pub clock: Sysvar<'info, Clock>
|
||||
/// }
|
||||
/// // BETTER - via syscall in the instruction function
|
||||
/// fn better(ctx: Context<Better>) -> ProgramResult {
|
||||
/// fn better(ctx: Context<Better>) -> Result<()> {
|
||||
/// let clock = Clock::get()?;
|
||||
/// }
|
||||
/// ```
|
||||
|
@ -46,9 +45,7 @@ impl<'info, T: solana_program::sysvar::Sysvar + fmt::Debug> fmt::Debug for Sysva
|
|||
}
|
||||
|
||||
impl<'info, T: solana_program::sysvar::Sysvar> Sysvar<'info, T> {
|
||||
pub fn from_account_info(
|
||||
acc_info: &AccountInfo<'info>,
|
||||
) -> Result<Sysvar<'info, T>, ProgramError> {
|
||||
pub fn from_account_info(acc_info: &AccountInfo<'info>) -> Result<Sysvar<'info, T>> {
|
||||
Ok(Sysvar {
|
||||
info: acc_info.clone(),
|
||||
account: T::from_account_info(acc_info)?,
|
||||
|
@ -71,7 +68,7 @@ impl<'info, T: solana_program::sysvar::Sysvar> Accounts<'info> for Sysvar<'info,
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
_ix_data: &[u8],
|
||||
_bumps: &mut BTreeMap<String, u8>,
|
||||
) -> Result<Self, ProgramError> {
|
||||
) -> Result<Self> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
//! that no checks are performed
|
||||
|
||||
use crate::error::ErrorCode;
|
||||
use crate::{Accounts, AccountsExit, ToAccountInfos, ToAccountMetas};
|
||||
use crate::{Accounts, AccountsExit, Result, ToAccountInfos, ToAccountMetas};
|
||||
use solana_program::account_info::AccountInfo;
|
||||
use solana_program::instruction::AccountMeta;
|
||||
use solana_program::program_error::ProgramError;
|
||||
use solana_program::pubkey::Pubkey;
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::Deref;
|
||||
|
@ -27,7 +26,7 @@ impl<'info> Accounts<'info> for UncheckedAccount<'info> {
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
_ix_data: &[u8],
|
||||
_bumps: &mut BTreeMap<String, u8>,
|
||||
) -> Result<Self, ProgramError> {
|
||||
) -> Result<Self> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::{AccountDeserialize, AccountSerialize, Owner};
|
||||
use crate::error::ErrorCode;
|
||||
use crate::{AccountDeserialize, AccountSerialize, Owner, Result};
|
||||
use solana_program::{
|
||||
bpf_loader_upgradeable::UpgradeableLoaderState, program_error::ProgramError, pubkey::Pubkey,
|
||||
};
|
||||
|
@ -10,27 +11,21 @@ pub struct ProgramData {
|
|||
}
|
||||
|
||||
impl AccountDeserialize for ProgramData {
|
||||
fn try_deserialize(
|
||||
buf: &mut &[u8],
|
||||
) -> Result<Self, solana_program::program_error::ProgramError> {
|
||||
fn try_deserialize(buf: &mut &[u8]) -> Result<Self> {
|
||||
ProgramData::try_deserialize_unchecked(buf)
|
||||
}
|
||||
|
||||
fn try_deserialize_unchecked(
|
||||
buf: &mut &[u8],
|
||||
) -> Result<Self, solana_program::program_error::ProgramError> {
|
||||
fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self> {
|
||||
let program_state = AccountDeserialize::try_deserialize_unchecked(buf)?;
|
||||
|
||||
match program_state {
|
||||
UpgradeableLoaderState::Uninitialized => {
|
||||
Err(anchor_lang::error::ErrorCode::AccountNotProgramData.into())
|
||||
}
|
||||
UpgradeableLoaderState::Uninitialized => Err(ErrorCode::AccountNotProgramData.into()),
|
||||
UpgradeableLoaderState::Buffer {
|
||||
authority_address: _,
|
||||
} => Err(anchor_lang::error::ErrorCode::AccountNotProgramData.into()),
|
||||
} => Err(ErrorCode::AccountNotProgramData.into()),
|
||||
UpgradeableLoaderState::Program {
|
||||
programdata_address: _,
|
||||
} => Err(anchor_lang::error::ErrorCode::AccountNotProgramData.into()),
|
||||
} => Err(ErrorCode::AccountNotProgramData.into()),
|
||||
UpgradeableLoaderState::ProgramData {
|
||||
slot,
|
||||
upgrade_authority_address,
|
||||
|
@ -59,11 +54,11 @@ impl Owner for UpgradeableLoaderState {
|
|||
impl AccountSerialize for UpgradeableLoaderState {}
|
||||
|
||||
impl AccountDeserialize for UpgradeableLoaderState {
|
||||
fn try_deserialize(buf: &mut &[u8]) -> Result<Self, ProgramError> {
|
||||
fn try_deserialize(buf: &mut &[u8]) -> Result<Self> {
|
||||
UpgradeableLoaderState::try_deserialize_unchecked(buf)
|
||||
}
|
||||
|
||||
fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self, ProgramError> {
|
||||
bincode::deserialize(buf).map_err(|_| ProgramError::InvalidAccountData)
|
||||
fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self> {
|
||||
bincode::deserialize(buf).map_err(|_| ProgramError::InvalidAccountData.into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
use crate::error::ErrorCode;
|
||||
use crate::prelude::error;
|
||||
use crate::Result;
|
||||
use solana_program::account_info::AccountInfo;
|
||||
use solana_program::entrypoint::ProgramResult;
|
||||
use std::io::Write;
|
||||
|
||||
pub fn close<'info>(
|
||||
info: AccountInfo<'info>,
|
||||
sol_destination: AccountInfo<'info>,
|
||||
) -> ProgramResult {
|
||||
pub fn close<'info>(info: AccountInfo<'info>, sol_destination: AccountInfo<'info>) -> Result<()> {
|
||||
// Transfer tokens from the account to the sol_destination.
|
||||
let dest_starting_lamports = sol_destination.lamports();
|
||||
**sol_destination.lamports.borrow_mut() =
|
||||
|
@ -19,6 +17,5 @@ pub fn close<'info>(
|
|||
let mut cursor = std::io::Cursor::new(dst);
|
||||
cursor
|
||||
.write_all(&crate::__private::CLOSED_ACCOUNT_DISCRIMINATOR)
|
||||
.map_err(|_| ErrorCode::AccountDidNotSerialize)?;
|
||||
Ok(())
|
||||
.map_err(|_| error!(ErrorCode::AccountDidNotSerialize))
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use std::fmt;
|
|||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// pub fn set_data(ctx: Context<SetData>, age: u64, other_data: u32) -> ProgramResult {
|
||||
/// pub fn set_data(ctx: Context<SetData>, age: u64, other_data: u32) -> Result<()> {
|
||||
/// // Set account data like this
|
||||
/// (*ctx.accounts.my_account).age = age;
|
||||
/// (*ctx.accounts.my_account).other_data = other_data;
|
||||
|
@ -76,12 +76,12 @@ impl<'a, 'b, 'c, 'info, T: Accounts<'info>> Context<'a, 'b, 'c, 'info, T> {
|
|||
/// #[program]
|
||||
/// pub mod callee {
|
||||
/// use super::*;
|
||||
/// pub fn init(ctx: Context<Init>) -> ProgramResult {
|
||||
/// pub fn init(ctx: Context<Init>) -> Result<()> {
|
||||
/// (*ctx.accounts.data).authority = ctx.accounts.authority.key();
|
||||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
/// pub fn set_data(ctx: Context<SetData>, data: u64) -> ProgramResult {
|
||||
/// pub fn set_data(ctx: Context<SetData>, data: u64) -> Result<()> {
|
||||
/// (*ctx.accounts.data_acc).data = data;
|
||||
/// Ok(())
|
||||
/// }
|
||||
|
@ -120,7 +120,7 @@ impl<'a, 'b, 'c, 'info, T: Accounts<'info>> Context<'a, 'b, 'c, 'info, T> {
|
|||
/// #[program]
|
||||
/// pub mod caller {
|
||||
/// use super::*;
|
||||
/// pub fn do_cpi(ctx: Context<DoCpi>, data: u64) -> ProgramResult {
|
||||
/// pub fn do_cpi(ctx: Context<DoCpi>, data: u64) -> Result<()> {
|
||||
/// let callee_id = ctx.accounts.callee.to_account_info();
|
||||
/// let callee_accounts = callee::cpi::accounts::SetData {
|
||||
/// data_acc: ctx.accounts.data_acc.to_account_info(),
|
||||
|
@ -130,7 +130,7 @@ impl<'a, 'b, 'c, 'info, T: Accounts<'info>> Context<'a, 'b, 'c, 'info, T> {
|
|||
/// callee::cpi::set_data(cpi_ctx, data)
|
||||
/// }
|
||||
///
|
||||
/// pub fn do_cpi_with_pda_authority(ctx: Context<DoCpiWithPDAAuthority>, bump: u8, data: u64) -> ProgramResult {
|
||||
/// pub fn do_cpi_with_pda_authority(ctx: Context<DoCpiWithPDAAuthority>, bump: u8, data: u64) -> Result<()> {
|
||||
/// let seeds = &[&[b"example_seed", bytemuck::bytes_of(&bump)][..]];
|
||||
/// let callee_id = ctx.accounts.callee.to_account_info();
|
||||
/// let callee_accounts = callee::cpi::accounts::SetData {
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use crate::error;
|
||||
use anchor_attribute_error::error_code;
|
||||
use borsh::maybestd::io::Error as BorshIoError;
|
||||
use solana_program::program_error::ProgramError;
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
/// The starting point for user defined error codes.
|
||||
pub const ERROR_CODE_OFFSET: u32 = 6000;
|
||||
|
@ -15,7 +18,7 @@ pub const ERROR_CODE_OFFSET: u32 = 6000;
|
|||
///
|
||||
/// The starting point for user-defined errors is defined
|
||||
/// by the [ERROR_CODE_OFFSET](crate::error::ERROR_CODE_OFFSET).
|
||||
#[error(offset = 0)]
|
||||
#[error_code(offset = 0)]
|
||||
pub enum ErrorCode {
|
||||
// Instructions
|
||||
/// 100 - 8 byte instruction identifier not provided
|
||||
|
@ -165,3 +168,187 @@ pub enum ErrorCode {
|
|||
#[msg("The API being used is deprecated and should no longer be used")]
|
||||
Deprecated = 5000,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
AnchorError(AnchorError),
|
||||
ProgramError(ProgramErrorWithOrigin),
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Error::AnchorError(ae) => Display::fmt(&ae, f),
|
||||
Error::ProgramError(pe) => Display::fmt(&pe, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for AnchorError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Debug::fmt(&self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AnchorError> for Error {
|
||||
fn from(ae: AnchorError) -> Self {
|
||||
Self::AnchorError(ae)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ProgramError> for Error {
|
||||
fn from(program_error: ProgramError) -> Self {
|
||||
Self::ProgramError(program_error.into())
|
||||
}
|
||||
}
|
||||
impl From<BorshIoError> for Error {
|
||||
fn from(error: BorshIoError) -> Self {
|
||||
Error::ProgramError(ProgramError::from(error).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ProgramErrorWithOrigin> for Error {
|
||||
fn from(pe: ProgramErrorWithOrigin) -> Self {
|
||||
Self::ProgramError(pe)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn log(&self) {
|
||||
match self {
|
||||
Error::ProgramError(program_error) => program_error.log(),
|
||||
Error::AnchorError(anchor_error) => anchor_error.log(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_account_name(mut self, account_name: impl ToString) -> Self {
|
||||
match &mut self {
|
||||
Error::AnchorError(ae) => ae.account_name = Some(account_name.to_string()),
|
||||
Error::ProgramError(pe) => pe.account_name = Some(account_name.to_string()),
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_source(mut self, source: Source) -> Self {
|
||||
match &mut self {
|
||||
Error::AnchorError(ae) => ae.source = Some(source),
|
||||
Error::ProgramError(pe) => pe.source = Some(source),
|
||||
};
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ProgramErrorWithOrigin {
|
||||
pub program_error: ProgramError,
|
||||
pub source: Option<Source>,
|
||||
pub account_name: Option<String>,
|
||||
}
|
||||
|
||||
impl Display for ProgramErrorWithOrigin {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Display::fmt(&self.program_error, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProgramErrorWithOrigin {
|
||||
pub fn log(&self) {
|
||||
if let Some(source) = &self.source {
|
||||
anchor_lang::solana_program::msg!(
|
||||
"ProgramError thrown in {}:{}. Error Code: {:?}. Error Number: {}. Error Message: {}.",
|
||||
source.filename,
|
||||
source.line,
|
||||
self.program_error,
|
||||
u64::from(self.program_error.clone()),
|
||||
self.program_error
|
||||
);
|
||||
} else if let Some(account_name) = &self.account_name {
|
||||
anchor_lang::solana_program::log::sol_log(&format!(
|
||||
"ProgramError caused by account: {}. Error Code: {:?}. Error Number: {}. Error Message: {}.",
|
||||
account_name,
|
||||
self.program_error,
|
||||
u64::from(self.program_error.clone()),
|
||||
self.program_error
|
||||
));
|
||||
} else {
|
||||
anchor_lang::solana_program::log::sol_log(&format!(
|
||||
"ProgramError occurred. Error Code: {:?}. Error Number: {}. Error Message: {}.",
|
||||
self.program_error,
|
||||
u64::from(self.program_error.clone()),
|
||||
self.program_error
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ProgramError> for ProgramErrorWithOrigin {
|
||||
fn from(program_error: ProgramError) -> Self {
|
||||
Self {
|
||||
program_error,
|
||||
source: None,
|
||||
account_name: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AnchorError {
|
||||
pub error_name: String,
|
||||
pub error_code_number: u32,
|
||||
pub error_msg: String,
|
||||
pub source: Option<Source>,
|
||||
pub account_name: Option<String>,
|
||||
}
|
||||
|
||||
impl AnchorError {
|
||||
pub fn log(&self) {
|
||||
if let Some(source) = &self.source {
|
||||
anchor_lang::solana_program::msg!(
|
||||
"AnchorError thrown in {}:{}. Error Code: {}. Error Number: {}. Error Message: {}.",
|
||||
source.filename,
|
||||
source.line,
|
||||
self.error_name,
|
||||
self.error_code_number,
|
||||
self.error_msg
|
||||
);
|
||||
} else if let Some(account_name) = &self.account_name {
|
||||
anchor_lang::solana_program::log::sol_log(&format!(
|
||||
"AnchorError caused by account: {}. Error Code: {}. Error Number: {}. Error Message: {}.",
|
||||
account_name,
|
||||
self.error_name,
|
||||
self.error_code_number,
|
||||
self.error_msg
|
||||
));
|
||||
} else {
|
||||
anchor_lang::solana_program::log::sol_log(&format!(
|
||||
"AnchorError occurred. Error Code: {}. Error Number: {}. Error Message: {}.",
|
||||
self.error_name, self.error_code_number, self.error_msg
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<Error> for anchor_lang::solana_program::program_error::ProgramError {
|
||||
fn from(e: Error) -> anchor_lang::solana_program::program_error::ProgramError {
|
||||
match e {
|
||||
Error::AnchorError(AnchorError {
|
||||
error_name: _,
|
||||
error_code_number,
|
||||
error_msg: _,
|
||||
source: _,
|
||||
account_name: _,
|
||||
}) => {
|
||||
anchor_lang::solana_program::program_error::ProgramError::Custom(error_code_number)
|
||||
}
|
||||
Error::ProgramError(program_error) => program_error.program_error,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Source {
|
||||
pub filename: &'static str,
|
||||
pub line: u32,
|
||||
}
|
||||
|
|
|
@ -25,9 +25,7 @@ extern crate self as anchor_lang;
|
|||
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
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::collections::BTreeMap;
|
||||
|
||||
|
@ -48,7 +46,7 @@ pub use crate::bpf_upgradeable_state::*;
|
|||
pub use anchor_attribute_access_control::access_control;
|
||||
pub use anchor_attribute_account::{account, declare_id, zero_copy};
|
||||
pub use anchor_attribute_constant::constant;
|
||||
pub use anchor_attribute_error::error;
|
||||
pub use anchor_attribute_error;
|
||||
pub use anchor_attribute_event::{emit, event};
|
||||
pub use anchor_attribute_interface::interface;
|
||||
pub use anchor_attribute_program::program;
|
||||
|
@ -58,6 +56,8 @@ pub use anchor_derive_accounts::Accounts;
|
|||
pub use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorSerialize};
|
||||
pub use solana_program;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, error::Error>;
|
||||
|
||||
/// A data structure of validated accounts that can be deserialized from the
|
||||
/// input to a Solana program. Implementations of this trait should perform any
|
||||
/// and all requisite constraint checks on accounts to ensure the accounts
|
||||
|
@ -81,14 +81,14 @@ pub trait Accounts<'info>: ToAccountMetas + ToAccountInfos<'info> + Sized {
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
ix_data: &[u8],
|
||||
bumps: &mut BTreeMap<String, u8>,
|
||||
) -> Result<Self, ProgramError>;
|
||||
) -> Result<Self>;
|
||||
}
|
||||
|
||||
/// The exit procedure for an account. Any cleanup or persistence to storage
|
||||
/// should be done here.
|
||||
pub trait AccountsExit<'info>: ToAccountMetas + ToAccountInfos<'info> {
|
||||
/// `program_id` is the currently executing program.
|
||||
fn exit(&self, _program_id: &Pubkey) -> ProgramResult {
|
||||
fn exit(&self, _program_id: &Pubkey) -> Result<()> {
|
||||
// no-op
|
||||
Ok(())
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ pub trait AccountsExit<'info>: ToAccountMetas + ToAccountInfos<'info> {
|
|||
/// The close procedure to initiate garabage collection of an account, allowing
|
||||
/// one to retrieve the rent exemption.
|
||||
pub trait AccountsClose<'info>: ToAccountInfos<'info> {
|
||||
fn close(&self, sol_destination: AccountInfo<'info>) -> ProgramResult;
|
||||
fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()>;
|
||||
}
|
||||
|
||||
/// Transformation to
|
||||
|
@ -164,14 +164,14 @@ pub trait AccountDeserialize: Sized {
|
|||
/// For example, if the SPL token program were to implement this trait,
|
||||
/// it should be impossible to deserialize a `Mint` account into a token
|
||||
/// `Account`.
|
||||
fn try_deserialize(buf: &mut &[u8]) -> Result<Self, ProgramError> {
|
||||
fn try_deserialize(buf: &mut &[u8]) -> Result<Self> {
|
||||
Self::try_deserialize_unchecked(buf)
|
||||
}
|
||||
|
||||
/// Deserializes account data without checking the account discriminator.
|
||||
/// This should only be used on account initialization, when the bytes of
|
||||
/// the account are zeroed.
|
||||
fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self, ProgramError>;
|
||||
fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self>;
|
||||
}
|
||||
|
||||
/// An account data structure capable of zero copy deserialization.
|
||||
|
@ -243,16 +243,16 @@ pub mod prelude {
|
|||
accounts::account_loader::AccountLoader, accounts::program::Program,
|
||||
accounts::signer::Signer, accounts::system_account::SystemAccount,
|
||||
accounts::sysvar::Sysvar, accounts::unchecked_account::UncheckedAccount, constant,
|
||||
context::Context, context::CpiContext, declare_id, emit, error, event, interface, program,
|
||||
require, solana_program::bpf_loader_upgradeable::UpgradeableLoaderState, state, zero_copy,
|
||||
AccountDeserialize, AccountSerialize, Accounts, AccountsExit, AnchorDeserialize,
|
||||
AnchorSerialize, Id, Key, Owner, ProgramData, System, ToAccountInfo, ToAccountInfos,
|
||||
ToAccountMetas,
|
||||
context::Context, context::CpiContext, declare_id, emit, err, error, event, interface,
|
||||
program, require, solana_program::bpf_loader_upgradeable::UpgradeableLoaderState, source,
|
||||
state, zero_copy, AccountDeserialize, AccountSerialize, Accounts, AccountsExit,
|
||||
AnchorDeserialize, AnchorSerialize, Id, Key, Owner, ProgramData, Result, System,
|
||||
ToAccountInfo, ToAccountInfos, ToAccountMetas,
|
||||
};
|
||||
|
||||
pub use anchor_attribute_error::*;
|
||||
pub use borsh;
|
||||
pub use error::*;
|
||||
pub use solana_program::account_info::{next_account_info, AccountInfo};
|
||||
pub use solana_program::entrypoint::ProgramResult;
|
||||
pub use solana_program::instruction::AccountMeta;
|
||||
pub use solana_program::msg;
|
||||
pub use solana_program::program_error::ProgramError;
|
||||
|
@ -274,6 +274,7 @@ pub mod prelude {
|
|||
/// Internal module used by macros and unstable apis.
|
||||
#[doc(hidden)]
|
||||
pub mod __private {
|
||||
use super::Result;
|
||||
/// The discriminator anchor uses to mark an account as closed.
|
||||
pub const CLOSED_ACCOUNT_DISCRIMINATOR: [u8; 8] = [255, 255, 255, 255, 255, 255, 255, 255];
|
||||
|
||||
|
@ -287,8 +288,6 @@ pub mod __private {
|
|||
|
||||
pub use bytemuck;
|
||||
|
||||
use solana_program::program_error::ProgramError;
|
||||
|
||||
use solana_program::pubkey::Pubkey;
|
||||
|
||||
pub mod state {
|
||||
|
@ -299,7 +298,7 @@ pub mod __private {
|
|||
// data in it. This trait is currently only used for `#[state]` accounts.
|
||||
#[doc(hidden)]
|
||||
pub trait AccountSize {
|
||||
fn size(&self) -> Result<u64, ProgramError>;
|
||||
fn size(&self) -> Result<u64>;
|
||||
}
|
||||
|
||||
// Very experimental trait.
|
||||
|
@ -323,20 +322,20 @@ pub mod __private {
|
|||
pub use crate::accounts::state::PROGRAM_STATE_SEED;
|
||||
}
|
||||
|
||||
/// Ensures a condition is true, otherwise returns the given error.
|
||||
/// Ensures a condition is true, otherwise returns with the given error.
|
||||
/// Use this with a custom error type.
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// // Instruction function
|
||||
/// pub fn set_data(ctx: Context<SetData>, data: u64) -> ProgramResult {
|
||||
/// pub fn set_data(ctx: Context<SetData>, data: u64) -> Result<()> {
|
||||
/// require!(ctx.accounts.data.mutation_allowed, MyError::MutationForbidden);
|
||||
/// ctx.accounts.data.data = data;
|
||||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
/// // An enum for custom error codes
|
||||
/// #[error]
|
||||
/// #[error_code]
|
||||
/// pub enum MyError {
|
||||
/// MutationForbidden
|
||||
/// }
|
||||
|
@ -360,12 +359,53 @@ pub mod __private {
|
|||
macro_rules! require {
|
||||
($invariant:expr, $error:tt $(,)?) => {
|
||||
if !($invariant) {
|
||||
return Err(crate::ErrorCode::$error.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error!(
|
||||
crate::ErrorCode::$error
|
||||
));
|
||||
}
|
||||
};
|
||||
($invariant:expr, $error:expr $(,)?) => {
|
||||
if !($invariant) {
|
||||
return Err($error.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error!($error));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns with the given error.
|
||||
/// Use this with a custom error type.
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// // Instruction function
|
||||
/// pub fn example(ctx: Context<Example>) -> Result<()> {
|
||||
/// err!(MyError::SomeError)
|
||||
/// }
|
||||
///
|
||||
/// // An enum for custom error codes
|
||||
/// #[error_code]
|
||||
/// pub enum MyError {
|
||||
/// SomeError
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! err {
|
||||
($error:tt $(,)?) => {
|
||||
Err(anchor_lang::anchor_attribute_error::error!(
|
||||
crate::ErrorCode::$error
|
||||
))
|
||||
};
|
||||
($error:expr $(,)?) => {
|
||||
Err(anchor_lang::anchor_attribute_error::error!($error))
|
||||
};
|
||||
}
|
||||
|
||||
/// Creates a [`Source`](crate::error::Source)
|
||||
#[macro_export]
|
||||
macro_rules! source {
|
||||
() => {
|
||||
anchor_lang::error::Source {
|
||||
filename: file!(),
|
||||
line: line!(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use crate::{Accounts, ToAccountInfos, ToAccountMetas};
|
||||
use crate::{Accounts, Result, ToAccountInfos, ToAccountMetas};
|
||||
use solana_program::account_info::AccountInfo;
|
||||
use solana_program::instruction::AccountMeta;
|
||||
use solana_program::program_error::ProgramError;
|
||||
use solana_program::pubkey::Pubkey;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
|
@ -27,7 +26,7 @@ impl<'info, T: Accounts<'info>> Accounts<'info> for Vec<T> {
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
ix_data: &[u8],
|
||||
bumps: &mut BTreeMap<String, u8>,
|
||||
) -> Result<Self, ProgramError> {
|
||||
) -> Result<Self> {
|
||||
let mut vec: Vec<T> = Vec::new();
|
||||
T::try_accounts(program_id, accounts, ix_data, bumps).map(|item| vec.push(item))?;
|
||||
Ok(vec)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "anchor-syn"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/project-serum/anchor"
|
||||
license = "Apache-2.0"
|
||||
|
|
|
@ -110,8 +110,8 @@ fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream {
|
|||
Constraint::Mut(c) => generate_constraint_mut(f, c),
|
||||
Constraint::HasOne(c) => generate_constraint_has_one(f, c),
|
||||
Constraint::Signer(c) => generate_constraint_signer(f, c),
|
||||
Constraint::Literal(c) => generate_constraint_literal(c),
|
||||
Constraint::Raw(c) => generate_constraint_raw(c),
|
||||
Constraint::Literal(c) => generate_constraint_literal(&f.ident, c),
|
||||
Constraint::Raw(c) => generate_constraint_raw(&f.ident, c),
|
||||
Constraint::Owner(c) => generate_constraint_owner(f, c),
|
||||
Constraint::RentExempt(c) => generate_constraint_rent_exempt(f, c),
|
||||
Constraint::Seeds(c) => generate_constraint_seeds(f, c),
|
||||
|
@ -123,10 +123,10 @@ fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream {
|
|||
}
|
||||
}
|
||||
|
||||
fn generate_constraint_composite(_f: &CompositeField, c: &Constraint) -> proc_macro2::TokenStream {
|
||||
fn generate_constraint_composite(f: &CompositeField, c: &Constraint) -> proc_macro2::TokenStream {
|
||||
match c {
|
||||
Constraint::Raw(c) => generate_constraint_raw(c),
|
||||
Constraint::Literal(c) => generate_constraint_literal(c),
|
||||
Constraint::Raw(c) => generate_constraint_raw(&f.ident, c),
|
||||
Constraint::Literal(c) => generate_constraint_literal(&f.ident, c),
|
||||
_ => panic!("Invariant violation"),
|
||||
}
|
||||
}
|
||||
|
@ -134,16 +134,17 @@ fn generate_constraint_composite(_f: &CompositeField, c: &Constraint) -> proc_ma
|
|||
fn generate_constraint_address(f: &Field, c: &ConstraintAddress) -> proc_macro2::TokenStream {
|
||||
let field = &f.ident;
|
||||
let addr = &c.address;
|
||||
let error = generate_custom_error(&c.error, quote! { ConstraintAddress });
|
||||
let error = generate_custom_error(field, &c.error, quote! { ConstraintAddress });
|
||||
quote! {
|
||||
if #field.key() != #addr {
|
||||
return Err(#error);
|
||||
return #error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_constraint_zeroed(f: &Field, _c: &ConstraintZeroed) -> proc_macro2::TokenStream {
|
||||
let field = &f.ident;
|
||||
let name_str = field.to_string();
|
||||
let ty_decl = f.ty_decl();
|
||||
let account_ty = f.account_ty();
|
||||
let from_account_info = f.from_account_info(None);
|
||||
|
@ -162,7 +163,7 @@ pub fn generate_constraint_zeroed(f: &Field, _c: &ConstraintZeroed) -> proc_macr
|
|||
__header_bytes.copy_from_slice(&__data[..8]);
|
||||
let __header = u64::from_le_bytes(__header_bytes);
|
||||
if __header != 0 {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintZero.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintZero, #name_str));
|
||||
}
|
||||
|
||||
#header_write
|
||||
|
@ -174,20 +175,21 @@ pub fn generate_constraint_zeroed(f: &Field, _c: &ConstraintZeroed) -> proc_macr
|
|||
|
||||
pub fn generate_constraint_close(f: &Field, c: &ConstraintClose) -> proc_macro2::TokenStream {
|
||||
let field = &f.ident;
|
||||
let name_str = field.to_string();
|
||||
let target = &c.sol_dest;
|
||||
quote! {
|
||||
if #field.key() == #target.key() {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintClose.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintClose, #name_str));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_constraint_mut(f: &Field, c: &ConstraintMut) -> proc_macro2::TokenStream {
|
||||
let ident = &f.ident;
|
||||
let error = generate_custom_error(&c.error, quote! { ConstraintMut });
|
||||
let error = generate_custom_error(ident, &c.error, quote! { ConstraintMut });
|
||||
quote! {
|
||||
if !#ident.to_account_info().is_writable {
|
||||
return Err(#error);
|
||||
return #error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -200,10 +202,10 @@ pub fn generate_constraint_has_one(f: &Field, c: &ConstraintHasOne) -> proc_macr
|
|||
Ty::AccountLoader(_) => quote! {#ident.load()?},
|
||||
_ => quote! {#ident},
|
||||
};
|
||||
let error = generate_custom_error(&c.error, quote! { ConstraintHasOne });
|
||||
let error = generate_custom_error(ident, &c.error, quote! { ConstraintHasOne });
|
||||
quote! {
|
||||
if #field.#target != #target.key() {
|
||||
return Err(#error);
|
||||
return #error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -219,15 +221,19 @@ pub fn generate_constraint_signer(f: &Field, c: &ConstraintSigner) -> proc_macro
|
|||
Ty::CpiAccount(_) => quote! { #ident.to_account_info() },
|
||||
_ => panic!("Invalid syntax: signer cannot be specified."),
|
||||
};
|
||||
let error = generate_custom_error(&c.error, quote! { ConstraintSigner });
|
||||
let error = generate_custom_error(ident, &c.error, quote! { ConstraintSigner });
|
||||
quote! {
|
||||
if !#info.is_signer {
|
||||
return Err(#error);
|
||||
return #error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_constraint_literal(c: &ConstraintLiteral) -> proc_macro2::TokenStream {
|
||||
pub fn generate_constraint_literal(
|
||||
ident: &Ident,
|
||||
c: &ConstraintLiteral,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let name_str = ident.to_string();
|
||||
let lit: proc_macro2::TokenStream = {
|
||||
let lit = &c.lit;
|
||||
let constraint = lit.value().replace('\"', "");
|
||||
|
@ -240,17 +246,17 @@ pub fn generate_constraint_literal(c: &ConstraintLiteral) -> proc_macro2::TokenS
|
|||
};
|
||||
quote! {
|
||||
if !(#lit) {
|
||||
return Err(anchor_lang::error::ErrorCode::Deprecated.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::Deprecated, #name_str));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_constraint_raw(c: &ConstraintRaw) -> proc_macro2::TokenStream {
|
||||
pub fn generate_constraint_raw(ident: &Ident, c: &ConstraintRaw) -> proc_macro2::TokenStream {
|
||||
let raw = &c.raw;
|
||||
let error = generate_custom_error(&c.error, quote! { ConstraintRaw });
|
||||
let error = generate_custom_error(ident, &c.error, quote! { ConstraintRaw });
|
||||
quote! {
|
||||
if !(#raw) {
|
||||
return Err(#error);
|
||||
return #error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -258,10 +264,10 @@ pub fn generate_constraint_raw(c: &ConstraintRaw) -> proc_macro2::TokenStream {
|
|||
pub fn generate_constraint_owner(f: &Field, c: &ConstraintOwner) -> proc_macro2::TokenStream {
|
||||
let ident = &f.ident;
|
||||
let owner_address = &c.owner_address;
|
||||
let error = generate_custom_error(&c.error, quote! { ConstraintOwner });
|
||||
let error = generate_custom_error(ident, &c.error, quote! { ConstraintOwner });
|
||||
quote! {
|
||||
if #ident.as_ref().owner != &#owner_address {
|
||||
return Err(#error);
|
||||
return #error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -271,6 +277,7 @@ pub fn generate_constraint_rent_exempt(
|
|||
c: &ConstraintRentExempt,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let ident = &f.ident;
|
||||
let name_str = ident.to_string();
|
||||
let info = quote! {
|
||||
#ident.to_account_info()
|
||||
};
|
||||
|
@ -278,7 +285,7 @@ pub fn generate_constraint_rent_exempt(
|
|||
ConstraintRentExempt::Skip => quote! {},
|
||||
ConstraintRentExempt::Enforce => quote! {
|
||||
if !__anchor_rent.is_exempt(#info.lamports(), #info.try_data_len()?) {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintRentExempt.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintRentExempt, #name_str));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -286,6 +293,7 @@ pub fn generate_constraint_rent_exempt(
|
|||
|
||||
fn generate_constraint_init(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream {
|
||||
let field = &f.ident;
|
||||
let name_str = f.ident.to_string();
|
||||
let ty_decl = f.ty_decl();
|
||||
let if_needed = if c.if_needed {
|
||||
quote! {true}
|
||||
|
@ -309,7 +317,6 @@ fn generate_constraint_init(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::
|
|||
let (find_pda, seeds_with_bump) = match &c.seeds {
|
||||
None => (quote! {}, quote! {}),
|
||||
Some(c) => {
|
||||
let name_str = f.ident.to_string();
|
||||
let seeds = &mut c.seeds.clone();
|
||||
|
||||
// If the seeds came with a trailing comma, we need to chop it off
|
||||
|
@ -375,10 +382,10 @@ fn generate_constraint_init(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::
|
|||
let pa: #ty_decl = #from_account_info;
|
||||
if #if_needed {
|
||||
if pa.mint != #mint.key() {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintTokenMint.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintTokenMint, #name_str));
|
||||
}
|
||||
if pa.owner != #owner.key() {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintTokenOwner.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintTokenOwner, #name_str));
|
||||
}
|
||||
}
|
||||
pa
|
||||
|
@ -410,14 +417,14 @@ fn generate_constraint_init(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::
|
|||
let pa: #ty_decl = #from_account_info;
|
||||
if #if_needed {
|
||||
if pa.mint != #mint.key() {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintTokenMint.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintTokenMint, #name_str));
|
||||
}
|
||||
if pa.owner != #owner.key() {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintTokenOwner.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintTokenOwner, #name_str));
|
||||
}
|
||||
|
||||
if pa.key() != anchor_spl::associated_token::get_associated_token_address(&#owner.key(), &#mint.key()) {
|
||||
return Err(anchor_lang::error::ErrorCode::AccountNotAssociatedTokenAccount.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::AccountNotAssociatedTokenAccount, #name_str));
|
||||
}
|
||||
}
|
||||
pa
|
||||
|
@ -463,16 +470,16 @@ fn generate_constraint_init(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::
|
|||
let pa: #ty_decl = #from_account_info;
|
||||
if #if_needed {
|
||||
if pa.mint_authority != anchor_lang::solana_program::program_option::COption::Some(#owner.key()) {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintMintMintAuthority.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintMintMintAuthority, #name_str));
|
||||
}
|
||||
if pa.freeze_authority
|
||||
.as_ref()
|
||||
.map(|fa| #freeze_authority.as_ref().map(|expected_fa| fa != *expected_fa).unwrap_or(true))
|
||||
.unwrap_or(#freeze_authority.is_some()) {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintMintFreezeAuthority.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintMintFreezeAuthority, #name_str));
|
||||
}
|
||||
if pa.decimals != #decimals {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintMintDecimals.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintMintDecimals, #name_str));
|
||||
}
|
||||
}
|
||||
pa
|
||||
|
@ -575,17 +582,17 @@ fn generate_constraint_init(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::
|
|||
// Assert the account was created correctly.
|
||||
if #if_needed {
|
||||
if space != actual_field.data_len() {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintSpace.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintSpace, #name_str));
|
||||
}
|
||||
|
||||
if actual_owner != #owner {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintOwner.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintOwner, #name_str));
|
||||
}
|
||||
|
||||
{
|
||||
let required_lamports = __anchor_rent.minimum_balance(space);
|
||||
if pa.to_account_info().lamports() < required_lamports {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintRentExempt.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintRentExempt, #name_str));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -627,10 +634,10 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2
|
|||
let b = c.bump.as_ref().unwrap();
|
||||
quote! {
|
||||
if #name.key() != __pda_address {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintSeeds.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintSeeds, #name_str));
|
||||
}
|
||||
if __bump != #b {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintSeeds.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintSeeds, #name_str));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -642,7 +649,7 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2
|
|||
else if c.is_init {
|
||||
quote! {
|
||||
if #name.key() != __pda_address {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintSeeds.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintSeeds, #name_str));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -651,6 +658,7 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2
|
|||
let maybe_seeds_plus_comma = (!s.is_empty()).then(|| {
|
||||
quote! { #s, }
|
||||
});
|
||||
|
||||
let define_pda = match c.bump.as_ref() {
|
||||
// Bump target not given. Find it.
|
||||
None => quote! {
|
||||
|
@ -665,7 +673,7 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2
|
|||
let __pda_address = Pubkey::create_program_address(
|
||||
&[#maybe_seeds_plus_comma &[#b][..]],
|
||||
&#deriving_program_id,
|
||||
).map_err(|_| anchor_lang::error::ErrorCode::ConstraintSeeds)?;
|
||||
).map_err(|_| anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintSeeds, #name_str))?;
|
||||
},
|
||||
};
|
||||
quote! {
|
||||
|
@ -674,7 +682,7 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2
|
|||
|
||||
// Check it.
|
||||
if #name.key() != __pda_address {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintSeeds.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintSeeds, #name_str));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -685,15 +693,16 @@ fn generate_constraint_associated_token(
|
|||
c: &ConstraintAssociatedToken,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let name = &f.ident;
|
||||
let name_str = name.to_string();
|
||||
let wallet_address = &c.wallet;
|
||||
let spl_token_mint_address = &c.mint;
|
||||
quote! {
|
||||
if #name.owner != #wallet_address.key() {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintTokenOwner.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintTokenOwner, #name_str));
|
||||
}
|
||||
let __associated_token_address = anchor_spl::associated_token::get_associated_token_address(&#wallet_address.key(), &#spl_token_mint_address.key());
|
||||
if #name.key() != __associated_token_address {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintAssociated.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintAssociated, #name_str));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -786,9 +795,10 @@ pub fn generate_constraint_executable(
|
|||
_c: &ConstraintExecutable,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let name = &f.ident;
|
||||
let name_str = name.to_string();
|
||||
quote! {
|
||||
if !#name.to_account_info().executable {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintExecutable.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintExecutable, #name_str));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -796,6 +806,7 @@ pub fn generate_constraint_executable(
|
|||
pub fn generate_constraint_state(f: &Field, c: &ConstraintState) -> proc_macro2::TokenStream {
|
||||
let program_target = c.program_target.clone();
|
||||
let ident = &f.ident;
|
||||
let name_str = ident.to_string();
|
||||
let account_ty = match &f.ty {
|
||||
Ty::CpiState(ty) => &ty.account_type_path,
|
||||
_ => panic!("Invalid state constraint"),
|
||||
|
@ -804,20 +815,26 @@ pub fn generate_constraint_state(f: &Field, c: &ConstraintState) -> proc_macro2:
|
|||
// Checks the given state account is the canonical state account for
|
||||
// the target program.
|
||||
if #ident.key() != anchor_lang::accounts::cpi_state::CpiState::<#account_ty>::address(&#program_target.key()) {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintState.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintState, #name_str));
|
||||
}
|
||||
if #ident.as_ref().owner != &#program_target.key() {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintState.into());
|
||||
return Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::ConstraintState, #name_str));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_custom_error(
|
||||
account_name: &Ident,
|
||||
custom_error: &Option<Expr>,
|
||||
error: proc_macro2::TokenStream,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let account_name = account_name.to_string();
|
||||
match custom_error {
|
||||
Some(error) => quote! { #error.into() },
|
||||
None => quote! { anchor_lang::error::ErrorCode::#error.into() },
|
||||
Some(error) => {
|
||||
quote! { Err(anchor_lang::anchor_attribute_error::error_with_account_name!(#error, #account_name)) }
|
||||
}
|
||||
None => {
|
||||
quote! { Err(anchor_lang::anchor_attribute_error::error_with_account_name!(anchor_lang::error::ErrorCode::#error, #account_name)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,25 +18,29 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
.map(|af: &AccountField| match af {
|
||||
AccountField::CompositeField(s) => {
|
||||
let name = &s.ident;
|
||||
let name_str = name.to_string();
|
||||
quote! {
|
||||
anchor_lang::AccountsExit::exit(&self.#name, program_id)?;
|
||||
anchor_lang::AccountsExit::exit(&self.#name, program_id)
|
||||
.map_err(|e| e.with_account_name(#name_str))?;
|
||||
}
|
||||
}
|
||||
AccountField::Field(f) => {
|
||||
let ident = &f.ident;
|
||||
let name_str = ident.to_string();
|
||||
if f.constraints.is_close() {
|
||||
let close_target = &f.constraints.close.as_ref().unwrap().sol_dest;
|
||||
quote! {
|
||||
anchor_lang::AccountsClose::close(
|
||||
&self.#ident,
|
||||
self.#close_target.to_account_info(),
|
||||
)?;
|
||||
).map_err(|e| e.with_account_name(#name_str))?;
|
||||
}
|
||||
} else {
|
||||
match f.constraints.is_mutable() {
|
||||
false => quote! {},
|
||||
true => quote! {
|
||||
anchor_lang::AccountsExit::exit(&self.#ident, program_id)?;
|
||||
anchor_lang::AccountsExit::exit(&self.#ident, program_id)
|
||||
.map_err(|e| e.with_account_name(#name_str))?;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +50,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
quote! {
|
||||
#[automatically_derived]
|
||||
impl<#combined_generics> anchor_lang::AccountsExit<#trait_generics> for #name<#struct_generics> #where_clause{
|
||||
fn exit(&self, program_id: &anchor_lang::solana_program::pubkey::Pubkey) -> anchor_lang::solana_program::entrypoint::ProgramResult {
|
||||
fn exit(&self, program_id: &anchor_lang::solana_program::pubkey::Pubkey) -> anchor_lang::Result<()> {
|
||||
#(#on_save)*
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -39,11 +39,13 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
*accounts = &accounts[1..];
|
||||
}
|
||||
} else {
|
||||
let name = f.typed_ident();
|
||||
let name = f.ident.to_string();
|
||||
let typed_name = f.typed_ident();
|
||||
quote! {
|
||||
#[cfg(feature = "anchor-debug")]
|
||||
::solana_program::log::sol_log(stringify!(#name));
|
||||
let #name = anchor_lang::Accounts::try_accounts(program_id, accounts, ix_data, __bumps)?;
|
||||
::solana_program::log::sol_log(stringify!(#typed_name));
|
||||
let #typed_name = anchor_lang::Accounts::try_accounts(program_id, accounts, ix_data, __bumps)
|
||||
.map_err(|e| e.with_account_name(#name))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +95,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
accounts: &mut &[anchor_lang::solana_program::account_info::AccountInfo<'info>],
|
||||
ix_data: &[u8],
|
||||
__bumps: &mut std::collections::BTreeMap<String, u8>,
|
||||
) -> std::result::Result<Self, anchor_lang::solana_program::program_error::ProgramError> {
|
||||
) -> anchor_lang::Result<Self> {
|
||||
// Deserialize instruction, if declared.
|
||||
#ix_de
|
||||
// Deserialize each account.
|
||||
|
|
|
@ -6,7 +6,7 @@ pub fn generate(error: Error) -> proc_macro2::TokenStream {
|
|||
let enum_name = &error.ident;
|
||||
// Each arm of the `match` statement for implementing `std::fmt::Display`
|
||||
// on the user defined error code.
|
||||
let variant_dispatch: Vec<proc_macro2::TokenStream> = error
|
||||
let display_variant_dispatch: Vec<proc_macro2::TokenStream> = error
|
||||
.raw_enum
|
||||
.variants
|
||||
.iter()
|
||||
|
@ -14,7 +14,7 @@ pub fn generate(error: Error) -> proc_macro2::TokenStream {
|
|||
.map(|(idx, variant)| {
|
||||
let ident = &variant.ident;
|
||||
let error_code = &error.codes[idx];
|
||||
let msg = match &error_code.msg {
|
||||
let display_msg = match &error_code.msg {
|
||||
None => {
|
||||
quote! {
|
||||
<Self as std::fmt::Debug>::fmt(self, fmt)
|
||||
|
@ -27,7 +27,22 @@ pub fn generate(error: Error) -> proc_macro2::TokenStream {
|
|||
}
|
||||
};
|
||||
quote! {
|
||||
#enum_name::#ident => #msg
|
||||
#enum_name::#ident => #display_msg
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Each arm of the `match` statement for implementing the `name` function
|
||||
// on the user defined error code.
|
||||
let name_variant_dispatch: Vec<proc_macro2::TokenStream> = error
|
||||
.raw_enum
|
||||
.variants
|
||||
.iter()
|
||||
.map(|variant| {
|
||||
let ident = &variant.ident;
|
||||
let ident_name = ident.to_string();
|
||||
quote! {
|
||||
#enum_name::#ident => #ident_name.to_string()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
@ -41,50 +56,44 @@ pub fn generate(error: Error) -> proc_macro2::TokenStream {
|
|||
};
|
||||
|
||||
quote! {
|
||||
/// Anchor generated Result to be used as the return type for the
|
||||
/// program.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Anchor generated error allowing one to easily return a
|
||||
/// `ProgramError` or a custom, user defined error code by utilizing
|
||||
/// its `From` implementation.
|
||||
#[doc(hidden)]
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
ProgramError(#[from] anchor_lang::solana_program::program_error::ProgramError),
|
||||
#[error(transparent)]
|
||||
ErrorCode(#[from] #enum_name),
|
||||
}
|
||||
|
||||
#[derive(std::fmt::Debug, Clone, Copy)]
|
||||
#[repr(u32)]
|
||||
#error_enum
|
||||
|
||||
impl #enum_name {
|
||||
pub fn name(&self) -> String {
|
||||
match self {
|
||||
#(#name_variant_dispatch),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<#enum_name> for u32 {
|
||||
fn from(e: #enum_name) -> u32 {
|
||||
e as u32 + #offset
|
||||
}
|
||||
}
|
||||
|
||||
impl From<#enum_name> for anchor_lang::error::Error {
|
||||
fn from(error_code: #enum_name) -> Error {
|
||||
anchor_lang::error::Error::from(
|
||||
anchor_lang::error::AnchorError {
|
||||
error_name: error_code.name(),
|
||||
error_code_number: error_code.into(),
|
||||
error_msg: error_code.to_string(),
|
||||
source: None,
|
||||
account_name: None
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for #enum_name {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||
match self {
|
||||
#(#variant_dispatch),*
|
||||
#(#display_variant_dispatch),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for #enum_name {}
|
||||
|
||||
impl std::convert::From<Error> for anchor_lang::solana_program::program_error::ProgramError {
|
||||
fn from(e: Error) -> anchor_lang::solana_program::program_error::ProgramError {
|
||||
match e {
|
||||
Error::ProgramError(e) => e,
|
||||
Error::ErrorCode(c) => anchor_lang::solana_program::program_error::ProgramError::Custom(c as u32 + #offset),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<#enum_name> for anchor_lang::solana_program::program_error::ProgramError {
|
||||
fn from(e: #enum_name) -> anchor_lang::solana_program::program_error::ProgramError {
|
||||
let err: Error = e.into();
|
||||
err.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
pub fn #method_name<'a, 'b, 'c, 'info>(
|
||||
ctx: anchor_lang::context::CpiStateContext<'a, 'b, 'c, 'info, #accounts_ident<'info>>,
|
||||
#(#args),*
|
||||
) -> ProgramResult {
|
||||
) -> anchor_lang::Result<()> {
|
||||
let ix = {
|
||||
let ix = instruction::state::#ix_variant;
|
||||
let data = anchor_lang::InstructionData::data(&ix);
|
||||
|
@ -47,7 +47,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
&ix,
|
||||
&acc_infos,
|
||||
ctx.signer_seeds(),
|
||||
)
|
||||
).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -74,7 +74,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
pub fn #method_name<'a, 'b, 'c, 'info>(
|
||||
ctx: anchor_lang::context::CpiContext<'a, 'b, 'c, 'info, #accounts_ident<'info>>,
|
||||
#(#args),*
|
||||
) -> ProgramResult {
|
||||
) -> anchor_lang::Result<()> {
|
||||
let ix = {
|
||||
let ix = instruction::#ix_variant;
|
||||
let mut ix_data = AnchorSerialize::try_to_vec(&ix)
|
||||
|
@ -93,7 +93,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
&ix,
|
||||
&acc_infos,
|
||||
ctx.signer_seeds,
|
||||
)
|
||||
).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -139,7 +139,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
data: &[u8],
|
||||
) -> ProgramResult {
|
||||
) -> anchor_lang::Result<()> {
|
||||
// Split the instruction data into the first 8 byte method
|
||||
// identifier (sighash) and the serialized instruction data.
|
||||
let mut ix_data: &[u8] = data;
|
||||
|
|
|
@ -6,7 +6,7 @@ use quote::quote;
|
|||
pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||
let name: proc_macro2::TokenStream = program.name.to_string().to_camel_case().parse().unwrap();
|
||||
let fallback_maybe = dispatch::gen_fallback(program).unwrap_or(quote! {
|
||||
Err(anchor_lang::error::ErrorCode::InstructionMissing.into());
|
||||
Err(anchor_lang::error::ErrorCode::InstructionMissing.into())
|
||||
});
|
||||
quote! {
|
||||
#[cfg(not(feature = "no-entrypoint"))]
|
||||
|
@ -51,7 +51,14 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
///
|
||||
/// The `entry` function here, defines the standard entry to a Solana
|
||||
/// program, where execution begins.
|
||||
pub fn entry(program_id: &Pubkey, accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
|
||||
pub fn entry(program_id: &Pubkey, accounts: &[AccountInfo], data: &[u8]) -> anchor_lang::solana_program::entrypoint::ProgramResult {
|
||||
try_entry(program_id, accounts, data).map_err(|e| {
|
||||
e.log();
|
||||
e.into()
|
||||
})
|
||||
}
|
||||
|
||||
fn try_entry(program_id: &Pubkey, accounts: &[AccountInfo], data: &[u8]) -> anchor_lang::Result<()> {
|
||||
#[cfg(feature = "anchor-debug")]
|
||||
{
|
||||
msg!("anchor-debug is active");
|
||||
|
@ -60,14 +67,10 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
return Err(anchor_lang::error::ErrorCode::DeclaredProgramIdMismatch.into());
|
||||
}
|
||||
if data.len() < 8 {
|
||||
return #fallback_maybe
|
||||
return #fallback_maybe;
|
||||
}
|
||||
|
||||
dispatch(program_id, accounts, data)
|
||||
.map_err(|e| {
|
||||
anchor_lang::solana_program::msg!(&e.to_string());
|
||||
e
|
||||
})
|
||||
}
|
||||
|
||||
pub mod program {
|
||||
|
|
|
@ -16,7 +16,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
// on chain.
|
||||
#[inline(never)]
|
||||
#[cfg(not(feature = "no-idl"))]
|
||||
pub fn __idl_dispatch(program_id: &Pubkey, accounts: &[AccountInfo], idl_ix_data: &[u8]) -> ProgramResult {
|
||||
pub fn __idl_dispatch(program_id: &Pubkey, accounts: &[AccountInfo], idl_ix_data: &[u8]) -> anchor_lang::Result<()> {
|
||||
let mut accounts = accounts;
|
||||
let mut data: &[u8] = idl_ix_data;
|
||||
|
||||
|
@ -65,7 +65,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
|
||||
#[inline(never)]
|
||||
#[cfg(feature = "no-idl")]
|
||||
pub fn __idl_dispatch(program_id: &Pubkey, accounts: &[AccountInfo], idl_ix_data: &[u8]) -> ProgramResult {
|
||||
pub fn __idl_dispatch(program_id: &Pubkey, accounts: &[AccountInfo], idl_ix_data: &[u8]) -> anchor_lang::Result<()> {
|
||||
Err(anchor_lang::error::ErrorCode::IdlInstructionStub.into())
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
program_id: &Pubkey,
|
||||
accounts: &mut anchor_lang::idl::IdlCreateAccounts,
|
||||
data_len: u64,
|
||||
) -> ProgramResult {
|
||||
) -> anchor_lang::Result<()> {
|
||||
#[cfg(not(feature = "no-log-ix-name"))]
|
||||
anchor_lang::prelude::msg!("Instruction: IdlCreateAccount");
|
||||
|
||||
|
@ -137,7 +137,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
pub fn __idl_create_buffer(
|
||||
program_id: &Pubkey,
|
||||
accounts: &mut anchor_lang::idl::IdlCreateBuffer,
|
||||
) -> ProgramResult {
|
||||
) -> anchor_lang::Result<()> {
|
||||
#[cfg(not(feature = "no-log-ix-name"))]
|
||||
anchor_lang::prelude::msg!("Instruction: IdlCreateBuffer");
|
||||
|
||||
|
@ -151,7 +151,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
program_id: &Pubkey,
|
||||
accounts: &mut anchor_lang::idl::IdlAccounts,
|
||||
idl_data: Vec<u8>,
|
||||
) -> ProgramResult {
|
||||
) -> anchor_lang::Result<()> {
|
||||
#[cfg(not(feature = "no-log-ix-name"))]
|
||||
anchor_lang::prelude::msg!("Instruction: IdlWrite");
|
||||
|
||||
|
@ -165,7 +165,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
program_id: &Pubkey,
|
||||
accounts: &mut anchor_lang::idl::IdlAccounts,
|
||||
new_authority: Pubkey,
|
||||
) -> ProgramResult {
|
||||
) -> anchor_lang::Result<()> {
|
||||
#[cfg(not(feature = "no-log-ix-name"))]
|
||||
anchor_lang::prelude::msg!("Instruction: IdlSetAuthority");
|
||||
|
||||
|
@ -177,7 +177,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
pub fn __idl_set_buffer(
|
||||
program_id: &Pubkey,
|
||||
accounts: &mut anchor_lang::idl::IdlSetBuffer,
|
||||
) -> ProgramResult {
|
||||
) -> anchor_lang::Result<()> {
|
||||
#[cfg(not(feature = "no-log-ix-name"))]
|
||||
anchor_lang::prelude::msg!("Instruction: IdlSetBuffer");
|
||||
|
||||
|
@ -214,7 +214,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
// One time state account initializer. Will faill on subsequent
|
||||
// invocations.
|
||||
#[inline(never)]
|
||||
pub fn __ctor(program_id: &Pubkey, accounts: &[AccountInfo], ix_data: &[u8]) -> ProgramResult {
|
||||
pub fn __ctor(program_id: &Pubkey, accounts: &[AccountInfo], ix_data: &[u8]) -> anchor_lang::Result<()> {
|
||||
#[cfg(not(feature = "no-log-ix-name"))]
|
||||
anchor_lang::prelude::msg!(#ix_name_log);
|
||||
|
||||
|
@ -296,7 +296,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
// One time state account initializer. Will faill on subsequent
|
||||
// invocations.
|
||||
#[inline(never)]
|
||||
pub fn __ctor(program_id: &Pubkey, accounts: &[AccountInfo], ix_data: &[u8]) -> ProgramResult {
|
||||
pub fn __ctor(program_id: &Pubkey, accounts: &[AccountInfo], ix_data: &[u8]) -> anchor_lang::Result<()> {
|
||||
#[cfg(not(feature = "no-log-ix-name"))]
|
||||
anchor_lang::prelude::msg!(#ix_name_log);
|
||||
|
||||
|
@ -405,7 +405,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
ix_data: &[u8],
|
||||
) -> ProgramResult {
|
||||
) -> anchor_lang::Result<()> {
|
||||
#[cfg(not(feature = "no-log-ix-name"))]
|
||||
anchor_lang::prelude::msg!(#ix_name_log);
|
||||
|
||||
|
@ -461,7 +461,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
ix_data: &[u8],
|
||||
) -> ProgramResult {
|
||||
) -> anchor_lang::Result<()> {
|
||||
#[cfg(not(feature = "no-log-ix-name"))]
|
||||
anchor_lang::prelude::msg!(#ix_name_log);
|
||||
|
||||
|
@ -589,7 +589,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
ix_data: &[u8],
|
||||
) -> ProgramResult {
|
||||
) -> anchor_lang::Result<()> {
|
||||
#[cfg(not(feature = "no-log-ix-name"))]
|
||||
anchor_lang::prelude::msg!(#ix_name_log);
|
||||
|
||||
|
@ -649,7 +649,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
ix_data: &[u8],
|
||||
) -> ProgramResult {
|
||||
) -> anchor_lang::Result<()> {
|
||||
#[cfg(not(feature = "no-log-ix-name"))]
|
||||
anchor_lang::prelude::msg!(#ix_name_log);
|
||||
|
||||
|
@ -691,6 +691,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
})
|
||||
.unwrap_or_default(),
|
||||
};
|
||||
|
||||
let non_inlined_handlers: Vec<proc_macro2::TokenStream> = program
|
||||
.ixs
|
||||
.iter()
|
||||
|
@ -707,7 +708,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
ix_data: &[u8],
|
||||
) -> ProgramResult {
|
||||
) -> anchor_lang::Result<()> {
|
||||
#[cfg(not(feature = "no-log-ix-name"))]
|
||||
anchor_lang::prelude::msg!(#ix_name_log);
|
||||
|
||||
|
|
|
@ -316,7 +316,7 @@ fn parse_error_enum(ctx: &CrateContext) -> Option<syn::ItemEnum> {
|
|||
.iter()
|
||||
.filter(|attr| {
|
||||
let segment = attr.path.segments.last().unwrap();
|
||||
segment.ident == "error"
|
||||
segment.ident == "error_code"
|
||||
})
|
||||
.count();
|
||||
match attrs_count {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use crate::{Error, ErrorArgs, ErrorCode};
|
||||
use syn::parse::{Parse, Result as ParseResult};
|
||||
use syn::{Expr, Token};
|
||||
|
||||
// Removes any internal #[msg] attributes, as they are inert.
|
||||
pub fn parse(error_enum: &mut syn::ItemEnum, args: Option<ErrorArgs>) -> Error {
|
||||
|
@ -75,3 +77,31 @@ fn parse_error_attribute(variant: &syn::Variant) -> Option<String> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ErrorInput {
|
||||
pub error_code: Expr,
|
||||
}
|
||||
|
||||
impl Parse for ErrorInput {
|
||||
fn parse(stream: syn::parse::ParseStream) -> ParseResult<Self> {
|
||||
let error_code = stream.call(Expr::parse)?;
|
||||
Ok(Self { error_code })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ErrorWithAccountNameInput {
|
||||
pub error_code: Expr,
|
||||
pub account_name: Expr,
|
||||
}
|
||||
|
||||
impl Parse for ErrorWithAccountNameInput {
|
||||
fn parse(stream: syn::parse::ParseStream) -> ParseResult<Self> {
|
||||
let error_code = stream.call(Expr::parse)?;
|
||||
let _ = stream.parse::<Token!(,)>();
|
||||
let account_name = stream.call(Expr::parse)?;
|
||||
Ok(Self {
|
||||
error_code,
|
||||
account_name,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "anchor-spl"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0"
|
||||
|
@ -17,7 +17,7 @@ devnet = []
|
|||
dex = ["serum_dex"]
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = { path = "../lang", version = "0.21.0", features = ["derive"] }
|
||||
anchor-lang = { path = "../lang", version = "0.22.0", features = ["derive"] }
|
||||
serum_dex = { git = "https://github.com/project-serum/serum-dex", rev = "1be91f2", version = "0.4.0", features = ["no-entrypoint"], optional = true }
|
||||
solana-program = "1.8.5"
|
||||
spl-token = { version = "3.1.1", features = ["no-entrypoint"], optional = true }
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use anchor_lang::solana_program::account_info::AccountInfo;
|
||||
use anchor_lang::solana_program::entrypoint::ProgramResult;
|
||||
use anchor_lang::solana_program::program_error::ProgramError;
|
||||
use anchor_lang::solana_program::pubkey::Pubkey;
|
||||
use anchor_lang::{context::CpiContext, Accounts, ToAccountInfos};
|
||||
use anchor_lang::{context::CpiContext, Accounts, Result, ToAccountInfos};
|
||||
use serum_dex::instruction::SelfTradeBehavior;
|
||||
use serum_dex::matching::{OrderType, Side};
|
||||
use std::num::NonZeroU64;
|
||||
|
@ -25,7 +25,7 @@ pub fn new_order_v3<'info>(
|
|||
order_type: OrderType,
|
||||
client_order_id: u64,
|
||||
limit: u16,
|
||||
) -> ProgramResult {
|
||||
) -> Result<()> {
|
||||
let referral = ctx.remaining_accounts.get(0);
|
||||
let ix = serum_dex::instruction::new_order(
|
||||
ctx.accounts.market.key,
|
||||
|
@ -50,7 +50,8 @@ pub fn new_order_v3<'info>(
|
|||
self_trade_behavior,
|
||||
limit,
|
||||
max_native_pc_qty_including_fees,
|
||||
)?;
|
||||
)
|
||||
.map_err(|pe| ProgramError::from(pe))?;
|
||||
solana_program::program::invoke_signed(
|
||||
&ix,
|
||||
&ToAccountInfos::to_account_infos(&ctx),
|
||||
|
@ -63,7 +64,7 @@ pub fn cancel_order_v2<'info>(
|
|||
ctx: CpiContext<'_, '_, '_, 'info, CancelOrderV2<'info>>,
|
||||
side: Side,
|
||||
order_id: u128,
|
||||
) -> ProgramResult {
|
||||
) -> Result<()> {
|
||||
let ix = serum_dex::instruction::cancel_order(
|
||||
&ID,
|
||||
ctx.accounts.market.key,
|
||||
|
@ -74,7 +75,8 @@ pub fn cancel_order_v2<'info>(
|
|||
ctx.accounts.event_queue.key,
|
||||
side,
|
||||
order_id,
|
||||
)?;
|
||||
)
|
||||
.map_err(|pe| ProgramError::from(pe))?;
|
||||
solana_program::program::invoke_signed(
|
||||
&ix,
|
||||
&ToAccountInfos::to_account_infos(&ctx),
|
||||
|
@ -83,9 +85,7 @@ pub fn cancel_order_v2<'info>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn settle_funds<'info>(
|
||||
ctx: CpiContext<'_, '_, '_, 'info, SettleFunds<'info>>,
|
||||
) -> ProgramResult {
|
||||
pub fn settle_funds<'info>(ctx: CpiContext<'_, '_, '_, 'info, SettleFunds<'info>>) -> Result<()> {
|
||||
let referral = ctx.remaining_accounts.get(0);
|
||||
let ix = serum_dex::instruction::settle_funds(
|
||||
&ID,
|
||||
|
@ -99,7 +99,8 @@ pub fn settle_funds<'info>(
|
|||
ctx.accounts.pc_wallet.key,
|
||||
referral.map(|r| r.key),
|
||||
ctx.accounts.vault_signer.key,
|
||||
)?;
|
||||
)
|
||||
.map_err(|pe| ProgramError::from(pe))?;
|
||||
solana_program::program::invoke_signed(
|
||||
&ix,
|
||||
&ToAccountInfos::to_account_infos(&ctx),
|
||||
|
@ -110,14 +111,15 @@ pub fn settle_funds<'info>(
|
|||
|
||||
pub fn init_open_orders<'info>(
|
||||
ctx: CpiContext<'_, '_, '_, 'info, InitOpenOrders<'info>>,
|
||||
) -> ProgramResult {
|
||||
) -> Result<()> {
|
||||
let ix = serum_dex::instruction::init_open_orders(
|
||||
&ID,
|
||||
ctx.accounts.open_orders.key,
|
||||
ctx.accounts.authority.key,
|
||||
ctx.accounts.market.key,
|
||||
ctx.remaining_accounts.first().map(|acc| acc.key),
|
||||
)?;
|
||||
)
|
||||
.map_err(|pe| ProgramError::from(pe))?;
|
||||
solana_program::program::invoke_signed(
|
||||
&ix,
|
||||
&ToAccountInfos::to_account_infos(&ctx),
|
||||
|
@ -128,14 +130,15 @@ pub fn init_open_orders<'info>(
|
|||
|
||||
pub fn close_open_orders<'info>(
|
||||
ctx: CpiContext<'_, '_, '_, 'info, CloseOpenOrders<'info>>,
|
||||
) -> ProgramResult {
|
||||
) -> Result<()> {
|
||||
let ix = serum_dex::instruction::close_open_orders(
|
||||
&ID,
|
||||
ctx.accounts.open_orders.key,
|
||||
ctx.accounts.authority.key,
|
||||
ctx.accounts.destination.key,
|
||||
ctx.accounts.market.key,
|
||||
)?;
|
||||
)
|
||||
.map_err(|pe| ProgramError::from(pe))?;
|
||||
solana_program::program::invoke_signed(
|
||||
&ix,
|
||||
&ToAccountInfos::to_account_infos(&ctx),
|
||||
|
@ -144,7 +147,7 @@ pub fn close_open_orders<'info>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn sweep_fees<'info>(ctx: CpiContext<'_, '_, '_, 'info, SweepFees<'info>>) -> ProgramResult {
|
||||
pub fn sweep_fees<'info>(ctx: CpiContext<'_, '_, '_, 'info, SweepFees<'info>>) -> Result<()> {
|
||||
let ix = serum_dex::instruction::sweep_fees(
|
||||
&ID,
|
||||
ctx.accounts.market.key,
|
||||
|
@ -153,7 +156,8 @@ pub fn sweep_fees<'info>(ctx: CpiContext<'_, '_, '_, 'info, SweepFees<'info>>) -
|
|||
ctx.accounts.sweep_receiver.key,
|
||||
ctx.accounts.vault_signer.key,
|
||||
ctx.accounts.token_program.key,
|
||||
)?;
|
||||
)
|
||||
.map_err(|pe| ProgramError::from(pe))?;
|
||||
solana_program::program::invoke_signed(
|
||||
&ix,
|
||||
&ToAccountInfos::to_account_infos(&ctx),
|
||||
|
@ -168,7 +172,7 @@ pub fn initialize_market<'info>(
|
|||
pc_lot_size: u64,
|
||||
vault_signer_nonce: u64,
|
||||
pc_dust_threshold: u64,
|
||||
) -> ProgramResult {
|
||||
) -> Result<()> {
|
||||
let authority = ctx.remaining_accounts.get(0);
|
||||
let prune_authority = ctx.remaining_accounts.get(1);
|
||||
let ix = serum_dex::instruction::initialize_market(
|
||||
|
@ -188,7 +192,8 @@ pub fn initialize_market<'info>(
|
|||
pc_lot_size,
|
||||
vault_signer_nonce,
|
||||
pc_dust_threshold,
|
||||
)?;
|
||||
)
|
||||
.map_err(|pe| ProgramError::from(pe))?;
|
||||
solana_program::program::invoke_signed(
|
||||
&ix,
|
||||
&ToAccountInfos::to_account_infos(&ctx),
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use anchor_lang::solana_program;
|
||||
use anchor_lang::solana_program::account_info::AccountInfo;
|
||||
use anchor_lang::solana_program::entrypoint::ProgramResult;
|
||||
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::{context::CpiContext, Accounts};
|
||||
use anchor_lang::{solana_program, Result};
|
||||
use std::ops::Deref;
|
||||
|
||||
pub use spl_token::ID;
|
||||
|
@ -12,7 +11,7 @@ pub use spl_token::ID;
|
|||
pub fn transfer<'a, 'b, 'c, 'info>(
|
||||
ctx: CpiContext<'a, 'b, 'c, 'info, Transfer<'info>>,
|
||||
amount: u64,
|
||||
) -> ProgramResult {
|
||||
) -> Result<()> {
|
||||
let ix = spl_token::instruction::transfer(
|
||||
&spl_token::ID,
|
||||
ctx.accounts.from.key,
|
||||
|
@ -30,12 +29,13 @@ pub fn transfer<'a, 'b, 'c, 'info>(
|
|||
],
|
||||
ctx.signer_seeds,
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn mint_to<'a, 'b, 'c, 'info>(
|
||||
ctx: CpiContext<'a, 'b, 'c, 'info, MintTo<'info>>,
|
||||
amount: u64,
|
||||
) -> ProgramResult {
|
||||
) -> Result<()> {
|
||||
let ix = spl_token::instruction::mint_to(
|
||||
&spl_token::ID,
|
||||
ctx.accounts.mint.key,
|
||||
|
@ -53,12 +53,13 @@ pub fn mint_to<'a, 'b, 'c, 'info>(
|
|||
],
|
||||
ctx.signer_seeds,
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn burn<'a, 'b, 'c, 'info>(
|
||||
ctx: CpiContext<'a, 'b, 'c, 'info, Burn<'info>>,
|
||||
amount: u64,
|
||||
) -> ProgramResult {
|
||||
) -> Result<()> {
|
||||
let ix = spl_token::instruction::burn(
|
||||
&spl_token::ID,
|
||||
ctx.accounts.to.key,
|
||||
|
@ -76,12 +77,13 @@ pub fn burn<'a, 'b, 'c, 'info>(
|
|||
],
|
||||
ctx.signer_seeds,
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn approve<'a, 'b, 'c, 'info>(
|
||||
ctx: CpiContext<'a, 'b, 'c, 'info, Approve<'info>>,
|
||||
amount: u64,
|
||||
) -> ProgramResult {
|
||||
) -> Result<()> {
|
||||
let ix = spl_token::instruction::approve(
|
||||
&spl_token::ID,
|
||||
ctx.accounts.to.key,
|
||||
|
@ -99,11 +101,12 @@ pub fn approve<'a, 'b, 'c, 'info>(
|
|||
],
|
||||
ctx.signer_seeds,
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn initialize_account<'a, 'b, 'c, 'info>(
|
||||
ctx: CpiContext<'a, 'b, 'c, 'info, InitializeAccount<'info>>,
|
||||
) -> ProgramResult {
|
||||
) -> Result<()> {
|
||||
let ix = spl_token::instruction::initialize_account(
|
||||
&spl_token::ID,
|
||||
ctx.accounts.account.key,
|
||||
|
@ -120,11 +123,12 @@ pub fn initialize_account<'a, 'b, 'c, 'info>(
|
|||
],
|
||||
ctx.signer_seeds,
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn close_account<'a, 'b, 'c, 'info>(
|
||||
ctx: CpiContext<'a, 'b, 'c, 'info, CloseAccount<'info>>,
|
||||
) -> ProgramResult {
|
||||
) -> Result<()> {
|
||||
let ix = spl_token::instruction::close_account(
|
||||
&spl_token::ID,
|
||||
ctx.accounts.account.key,
|
||||
|
@ -141,11 +145,12 @@ pub fn close_account<'a, 'b, 'c, 'info>(
|
|||
],
|
||||
ctx.signer_seeds,
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn freeze_account<'a, 'b, 'c, 'info>(
|
||||
ctx: CpiContext<'a, 'b, 'c, 'info, FreezeAccount<'info>>,
|
||||
) -> ProgramResult {
|
||||
) -> Result<()> {
|
||||
let ix = spl_token::instruction::freeze_account(
|
||||
&spl_token::ID,
|
||||
ctx.accounts.account.key,
|
||||
|
@ -162,11 +167,12 @@ pub fn freeze_account<'a, 'b, 'c, 'info>(
|
|||
],
|
||||
ctx.signer_seeds,
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn thaw_account<'a, 'b, 'c, 'info>(
|
||||
ctx: CpiContext<'a, 'b, 'c, 'info, ThawAccount<'info>>,
|
||||
) -> ProgramResult {
|
||||
) -> Result<()> {
|
||||
let ix = spl_token::instruction::thaw_account(
|
||||
&spl_token::ID,
|
||||
ctx.accounts.account.key,
|
||||
|
@ -183,6 +189,7 @@ pub fn thaw_account<'a, 'b, 'c, 'info>(
|
|||
],
|
||||
ctx.signer_seeds,
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn initialize_mint<'a, 'b, 'c, 'info>(
|
||||
|
@ -190,7 +197,7 @@ pub fn initialize_mint<'a, 'b, 'c, 'info>(
|
|||
decimals: u8,
|
||||
authority: &Pubkey,
|
||||
freeze_authority: Option<&Pubkey>,
|
||||
) -> ProgramResult {
|
||||
) -> Result<()> {
|
||||
let ix = spl_token::instruction::initialize_mint(
|
||||
&spl_token::ID,
|
||||
ctx.accounts.mint.key,
|
||||
|
@ -203,13 +210,14 @@ pub fn initialize_mint<'a, 'b, 'c, 'info>(
|
|||
&[ctx.accounts.mint.clone(), ctx.accounts.rent.clone()],
|
||||
ctx.signer_seeds,
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn set_authority<'a, 'b, 'c, 'info>(
|
||||
ctx: CpiContext<'a, 'b, 'c, 'info, SetAuthority<'info>>,
|
||||
authority_type: spl_token::instruction::AuthorityType,
|
||||
new_authority: Option<Pubkey>,
|
||||
) -> ProgramResult {
|
||||
) -> Result<()> {
|
||||
let mut spl_new_authority: Option<&Pubkey> = None;
|
||||
if new_authority.is_some() {
|
||||
spl_new_authority = new_authority.as_ref()
|
||||
|
@ -231,6 +239,7 @@ pub fn set_authority<'a, 'b, 'c, 'info>(
|
|||
],
|
||||
ctx.signer_seeds,
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
|
@ -310,8 +319,10 @@ impl TokenAccount {
|
|||
}
|
||||
|
||||
impl anchor_lang::AccountDeserialize for TokenAccount {
|
||||
fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self, ProgramError> {
|
||||
spl_token::state::Account::unpack(buf).map(TokenAccount)
|
||||
fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
|
||||
spl_token::state::Account::unpack(buf)
|
||||
.map(TokenAccount)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -339,8 +350,10 @@ impl Mint {
|
|||
}
|
||||
|
||||
impl anchor_lang::AccountDeserialize for Mint {
|
||||
fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self, ProgramError> {
|
||||
spl_token::state::Mint::unpack(buf).map(Mint)
|
||||
fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
|
||||
spl_token::state::Mint::unpack(buf)
|
||||
.map(Mint)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -374,21 +387,21 @@ impl anchor_lang::Id for Token {
|
|||
pub mod accessor {
|
||||
use super::*;
|
||||
|
||||
pub fn amount(account: &AccountInfo) -> Result<u64, ProgramError> {
|
||||
pub fn amount(account: &AccountInfo) -> Result<u64> {
|
||||
let bytes = account.try_borrow_data()?;
|
||||
let mut amount_bytes = [0u8; 8];
|
||||
amount_bytes.copy_from_slice(&bytes[64..72]);
|
||||
Ok(u64::from_le_bytes(amount_bytes))
|
||||
}
|
||||
|
||||
pub fn mint(account: &AccountInfo) -> Result<Pubkey, ProgramError> {
|
||||
pub fn mint(account: &AccountInfo) -> Result<Pubkey> {
|
||||
let bytes = account.try_borrow_data()?;
|
||||
let mut mint_bytes = [0u8; 32];
|
||||
mint_bytes.copy_from_slice(&bytes[..32]);
|
||||
Ok(Pubkey::new_from_array(mint_bytes))
|
||||
}
|
||||
|
||||
pub fn authority(account: &AccountInfo) -> Result<Pubkey, ProgramError> {
|
||||
pub fn authority(account: &AccountInfo) -> Result<Pubkey> {
|
||||
let bytes = account.try_borrow_data()?;
|
||||
let mut owner_bytes = [0u8; 32];
|
||||
owner_bytes.copy_from_slice(&bytes[32..64]);
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 2b1b1e04986106715ab53794bcb63d3641673f64
|
||||
Subproject commit 63e7bb81beb76f2722245a37c16a7b0b00d6905a
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"@project-serum/anchor": "^0.21.0"
|
||||
"@project-serum/anchor": "^0.22.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.3.4",
|
||||
|
|
|
@ -5,17 +5,17 @@ declare_id!("Cum9tTyj5HwcEiAmhgaS7Bbj4UczCwsucrCkxRECzM4e");
|
|||
#[program]
|
||||
pub mod bpf_upgradeable_state {
|
||||
use super::*;
|
||||
pub fn set_admin_settings(ctx: Context<SetAdminSettings>, admin_data: u64) -> ProgramResult {
|
||||
pub fn set_admin_settings(ctx: Context<SetAdminSettings>, admin_data: u64) -> Result<()> {
|
||||
match *ctx.accounts.program {
|
||||
UpgradeableLoaderState::Program {
|
||||
programdata_address,
|
||||
} => {
|
||||
if programdata_address != ctx.accounts.program_data.key() {
|
||||
return Err(CustomError::InvalidProgramDataAddress.into());
|
||||
return err!(CustomError::InvalidProgramDataAddress);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(CustomError::AccountNotProgram.into());
|
||||
return err!(CustomError::AccountNotProgram);
|
||||
}
|
||||
};
|
||||
ctx.accounts.settings.admin_data = admin_data;
|
||||
|
@ -25,7 +25,7 @@ pub mod bpf_upgradeable_state {
|
|||
pub fn set_admin_settings_use_program_state(
|
||||
ctx: Context<SetAdminSettingsUseProgramState>,
|
||||
admin_data: u64,
|
||||
) -> ProgramResult {
|
||||
) -> Result<()> {
|
||||
ctx.accounts.settings.admin_data = admin_data;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ pub struct Settings {
|
|||
admin_data: u64,
|
||||
}
|
||||
|
||||
#[error]
|
||||
#[error_code]
|
||||
pub enum CustomError {
|
||||
InvalidProgramDataAddress,
|
||||
AccountNotProgram,
|
||||
|
|
|
@ -9,4 +9,3 @@ cashiers_check = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
|
|||
test = "yarn run mocha -t 1000000 tests/"
|
||||
|
||||
[features]
|
||||
safety_checks = false
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "cashiers-check",
|
||||
"version": "0.21.0",
|
||||
"version": "0.22.0",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/project-serum/anchor#readme",
|
||||
"bugs": {
|
||||
|
|
|
@ -109,9 +109,9 @@ impl<'info> CreateCheck<'info> {
|
|||
&[ctx.accounts.check.to_account_info().key.as_ref(), &[nonce]],
|
||||
ctx.program_id,
|
||||
)
|
||||
.map_err(|_| ErrorCode::InvalidCheckNonce)?;
|
||||
.map_err(|_| error!(ErrorCode::InvalidCheckNonce))?;
|
||||
if &signer != ctx.accounts.check_signer.to_account_info().key {
|
||||
return Err(ErrorCode::InvalidCheckSigner.into());
|
||||
return err!(ErrorCode::InvalidCheckSigner);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ pub struct Check {
|
|||
burned: bool,
|
||||
}
|
||||
|
||||
#[error]
|
||||
#[error_code]
|
||||
pub enum ErrorCode {
|
||||
#[msg("The given nonce does not create a valid program derived address.")]
|
||||
InvalidCheckNonce,
|
||||
|
@ -174,7 +174,7 @@ pub enum ErrorCode {
|
|||
|
||||
fn not_burned(check: &Check) -> Result<()> {
|
||||
if check.burned {
|
||||
return Err(ErrorCode::AlreadyBurned.into());
|
||||
return err!(ErrorCode::AlreadyBurned);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -12,15 +12,15 @@ lockup = { address = "6ebQNeTPZ1j7k3TtkCCtEPRvG7GQsucQrZ7sSEDQi9Ks", idl = "./de
|
|||
# Testing.
|
||||
#
|
||||
test = "yarn run mocha -t 1000000 tests/"
|
||||
test-with-build = "anchor run build && anchor test --skip-build"
|
||||
test-with-build = "anchor run build && anchor test --skip-build --skip-lint"
|
||||
#
|
||||
# Build the program and all CPI dependencies.
|
||||
#
|
||||
build = "anchor run build-deps && anchor build"
|
||||
build = "anchor run build-deps && anchor build --skip-lint"
|
||||
build-deps = "anchor run build-dex && anchor run build-swap && anchor run build-stake"
|
||||
build-dex = "pushd deps/serum-dex/dex/ && cargo build-bpf && popd"
|
||||
build-swap = "cd deps/swap && pwd && anchor build && cd ../../"
|
||||
build-stake = "pushd deps/stake && anchor build && popd"
|
||||
build-swap = "cd deps/swap && pwd && anchor build --skip-lint && cd ../../"
|
||||
build-stake = "pushd deps/stake && anchor build --skip-lint && popd"
|
||||
#
|
||||
# Runs a localnet with all the programs deployed.
|
||||
#
|
||||
|
@ -43,4 +43,3 @@ address = "6ebQNeTPZ1j7k3TtkCCtEPRvG7GQsucQrZ7sSEDQi9Ks"
|
|||
program = "./deps/stake/target/deploy/lockup.so"
|
||||
|
||||
[features]
|
||||
safety_checks = false
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 990eaa7944c6682838fdaa6c14cc07ed680007f0
|
||||
Subproject commit 6a1c128e859b13b39812c0c75d202b2bf9ee1e8c
|
|
@ -1 +1 @@
|
|||
Subproject commit 96e3b1e2a53a95ef56e6ec2da68348ffd6a5c091
|
||||
Subproject commit 3da36aaae7af6ce901d68c0280aac34817fe7fd8
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "cfo",
|
||||
"version": "0.21.0",
|
||||
"version": "0.22.0",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/project-serum/anchor#readme",
|
||||
"bugs": {
|
||||
|
|
|
@ -155,7 +155,7 @@ pub mod cfo {
|
|||
.checked_div(100)
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.map_err(|_| ErrorCode::U128CannotConvert)?;
|
||||
.map_err(|_| error!(ErrorCode::U128CannotConvert))?;
|
||||
token::burn(ctx.accounts.into_burn().with_signer(&[&seeds]), burn_amount)?;
|
||||
|
||||
// Stake.
|
||||
|
@ -165,7 +165,7 @@ pub mod cfo {
|
|||
.checked_div(100)
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.map_err(|_| ErrorCode::U128CannotConvert)?;
|
||||
.map_err(|_| error!(ErrorCode::U128CannotConvert))?;
|
||||
token::transfer(
|
||||
ctx.accounts.into_stake_transfer().with_signer(&[&seeds]),
|
||||
stake_amount,
|
||||
|
@ -178,7 +178,7 @@ pub mod cfo {
|
|||
.checked_div(100)
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.map_err(|_| ErrorCode::U128CannotConvert)?;
|
||||
.map_err(|_| error!(ErrorCode::U128CannotConvert))?;
|
||||
token::transfer(
|
||||
ctx.accounts.into_treasury_transfer().with_signer(&[&seeds]),
|
||||
treasury_amount,
|
||||
|
@ -195,7 +195,7 @@ pub mod cfo {
|
|||
let expiry_ts = 1853942400; // 9/30/2028.
|
||||
let expiry_receiver = *ctx.accounts.officer.to_account_info().key;
|
||||
let locked_kind = {
|
||||
let start_ts = 1633017600; // 9/30.21.0.
|
||||
let start_ts = 1633017600; // 9/30.22.0.
|
||||
let end_ts = 1822320000; // 9/30/2027.
|
||||
let period_count = 2191;
|
||||
RewardVendorKind::Locked {
|
||||
|
@ -236,7 +236,7 @@ pub mod cfo {
|
|||
.checked_div(total_pool_value)
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.map_err(|_| ErrorCode::U128CannotConvert)?;
|
||||
.map_err(|_| error!(ErrorCode::U128CannotConvert))?;
|
||||
|
||||
// Proportion of the reward going to the msrm pool.
|
||||
//
|
||||
|
@ -248,7 +248,7 @@ pub mod cfo {
|
|||
.checked_div(total_pool_value)
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.map_err(|_| ErrorCode::U128CannotConvert)?;
|
||||
.map_err(|_| error!(ErrorCode::U128CannotConvert))?;
|
||||
|
||||
// SRM drop.
|
||||
{
|
||||
|
@ -606,9 +606,9 @@ pub struct Distribute<'info> {
|
|||
has_one = treasury,
|
||||
has_one = stake,
|
||||
)]
|
||||
officer: Account<'info, Officer>,
|
||||
officer: Box<Account<'info, Officer>>,
|
||||
#[account(mut)]
|
||||
treasury: Account<'info, TokenAccount>,
|
||||
treasury: Box<Account<'info, TokenAccount>>,
|
||||
#[account(mut)]
|
||||
stake: Account<'info, TokenAccount>,
|
||||
#[account(mut)]
|
||||
|
@ -900,7 +900,7 @@ pub struct OfficerDidCreate {
|
|||
|
||||
// Error.
|
||||
|
||||
#[error]
|
||||
#[error_code]
|
||||
pub enum ErrorCode {
|
||||
#[msg("Distribution does not add to 100")]
|
||||
InvalidDistribution,
|
||||
|
@ -918,14 +918,14 @@ pub enum ErrorCode {
|
|||
|
||||
fn is_distribution_valid(d: &Distribution) -> Result<()> {
|
||||
if d.burn + d.stake + d.treasury != 100 {
|
||||
return Err(ErrorCode::InvalidDistribution.into());
|
||||
return err!(ErrorCode::InvalidDistribution);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_distribution_ready(accounts: &Distribute) -> Result<()> {
|
||||
if accounts.srm_vault.amount < 1_000_000 {
|
||||
return Err(ErrorCode::InsufficientDistributionAmount.into());
|
||||
return err!(ErrorCode::InsufficientDistributionAmount);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -934,7 +934,7 @@ fn is_distribution_ready(accounts: &Distribute) -> Result<()> {
|
|||
fn is_not_trading(ixs: &UncheckedAccount) -> Result<()> {
|
||||
let data = ixs.try_borrow_data()?;
|
||||
match tx_instructions::load_instruction_at(1, &data) {
|
||||
Ok(_) => Err(ErrorCode::TooManyInstructions.into()),
|
||||
Ok(_) => err!(ErrorCode::TooManyInstructions),
|
||||
Err(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
@ -943,7 +943,7 @@ fn is_stake_reward_ready(accounts: &DropStakeReward) -> Result<()> {
|
|||
// Min drop is 15,0000 SRM.
|
||||
let min_reward: u64 = 15_000_000_000;
|
||||
if accounts.stake.amount < min_reward {
|
||||
return Err(ErrorCode::InsufficientStakeReward.into());
|
||||
return err!(ErrorCode::InsufficientStakeReward);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -9,4 +9,3 @@ chat = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
|
|||
test = "yarn run mocha -t 1000000 tests/"
|
||||
|
||||
[features]
|
||||
safety_checks = false
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "chat",
|
||||
"version": "0.21.0",
|
||||
"version": "0.22.0",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/project-serum/anchor#readme",
|
||||
"bugs": {
|
||||
|
|
|
@ -107,7 +107,7 @@ pub struct Message {
|
|||
pub data: [u8; 280],
|
||||
}
|
||||
|
||||
#[error]
|
||||
#[error_code]
|
||||
pub enum ErrorCode {
|
||||
Unknown,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "composite",
|
||||
"version": "0.21.0",
|
||||
"version": "0.22.0",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/project-serum/anchor#readme",
|
||||
"bugs": {
|
||||
|
|
|
@ -8,7 +8,7 @@ declare_id!("EHthziFziNoac9LBGxEaVN47Y3uUiRoXvqAiR6oes4iU");
|
|||
#[program]
|
||||
mod composite {
|
||||
use super::*;
|
||||
pub fn initialize(_ctx: Context<Initialize>) -> ProgramResult {
|
||||
pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ mod composite {
|
|||
ctx: Context<CompositeUpdate>,
|
||||
dummy_a: u64,
|
||||
dummy_b: u64,
|
||||
) -> ProgramResult {
|
||||
) -> Result<()> {
|
||||
let a = &mut ctx.accounts.foo.dummy_a;
|
||||
let b = &mut ctx.accounts.bar.dummy_b;
|
||||
|
||||
|
|
|
@ -13,4 +13,3 @@ wallet = "~/.config/solana/id.json"
|
|||
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
|
||||
|
||||
[features]
|
||||
safety_checks = false
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "declare-id",
|
||||
"version": "0.21.0",
|
||||
"version": "0.22.0",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/project-serum/anchor#readme",
|
||||
"bugs": {
|
||||
|
|
|
@ -3,12 +3,11 @@ use anchor_lang::prelude::*;
|
|||
// Intentionally different program id than the one defined in Anchor.toml.
|
||||
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
||||
|
||||
|
||||
#[program]
|
||||
mod declare_id {
|
||||
use super::*;
|
||||
|
||||
pub fn initialize(_ctx: Context<Initialize>) -> ProgramResult {
|
||||
pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,4 +9,3 @@ errors = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
|
|||
test = "yarn run mocha -t 1000000 tests/"
|
||||
|
||||
[features]
|
||||
safety_checks = false
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "errors",
|
||||
"version": "0.21.0",
|
||||
"version": "0.22.0",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/project-serum/anchor#readme",
|
||||
"bugs": {
|
||||
|
|
|
@ -10,15 +10,32 @@ mod errors {
|
|||
use super::*;
|
||||
|
||||
pub fn hello(_ctx: Context<Hello>) -> Result<()> {
|
||||
Err(MyError::Hello.into())
|
||||
err!(MyError::Hello)
|
||||
}
|
||||
|
||||
pub fn hello_no_msg(_ctx: Context<Hello>) -> Result<()> {
|
||||
Err(MyError::HelloNoMsg.into())
|
||||
err!(MyError::HelloNoMsg)
|
||||
}
|
||||
|
||||
pub fn hello_next(_ctx: Context<Hello>) -> Result<()> {
|
||||
Err(MyError::HelloNext.into())
|
||||
err!(MyError::HelloNext)
|
||||
}
|
||||
|
||||
pub fn test_require(_ctx: Context<Hello>) -> Result<()> {
|
||||
require!(false, MyError::Hello);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn test_err(_ctx: Context<Hello>) -> Result<()> {
|
||||
err!(MyError::Hello)
|
||||
}
|
||||
|
||||
pub fn test_program_error(_ctx: Context<Hello>) -> Result<()> {
|
||||
Err(ProgramError::InvalidAccountData.into())
|
||||
}
|
||||
|
||||
pub fn test_program_error_with_source(_ctx: Context<Hello>) -> Result<()> {
|
||||
Err(Error::from(ProgramError::InvalidAccountData).with_source(source!()))
|
||||
}
|
||||
|
||||
pub fn mut_error(_ctx: Context<MutError>) -> Result<()> {
|
||||
|
@ -82,7 +99,7 @@ pub struct AccountNotInitializedError<'info> {
|
|||
not_initialized_account: Account<'info, AnyAccount>,
|
||||
}
|
||||
|
||||
#[error]
|
||||
#[error_code]
|
||||
pub enum MyError {
|
||||
#[msg("This is an error message clients will automatically display")]
|
||||
Hello,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue