Hack: make ts/ into the repo root, so yarn can work
This commit is contained in:
parent
01106c1bb1
commit
4ef5a52ab8
|
@ -1,21 +1,3 @@
|
|||
[submodule "examples/swap/deps/serum-dex"]
|
||||
path = tests/swap/deps/serum-dex
|
||||
url = https://github.com/project-serum/serum-dex
|
||||
[submodule "examples/cfo/deps/serum-dex"]
|
||||
path = tests/cfo/deps/serum-dex
|
||||
url = https://github.com/project-serum/serum-dex
|
||||
[submodule "examples/cfo/deps/swap"]
|
||||
path = tests/cfo/deps/swap
|
||||
url = https://github.com/project-serum/swap.git
|
||||
branch = armani/cfo
|
||||
[submodule "examples/cfo/deps/stake"]
|
||||
path = tests/cfo/deps/stake
|
||||
url = https://github.com/project-serum/stake.git
|
||||
branch = armani/cfo
|
||||
[submodule "examples/permissioned-markets/deps/serum-dex"]
|
||||
path = tests/permissioned-markets/deps/serum-dex
|
||||
url = https://github.com/project-serum/serum-dex
|
||||
[submodule "tests/auction-house"]
|
||||
path = tests/auction-house
|
||||
url = https://github.com/armaniferrante/auction-house
|
||||
branch = armani/pda
|
||||
|
|
608
CHANGELOG.md
608
CHANGELOG.md
|
@ -1,608 +0,0 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
**Note:** Version 0 of Semantic Versioning is handled differently from version 1 and above.
|
||||
The minor version will be incremented upon a breaking change and the patch version will be incremented for features.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.24.2] - 2022-04-13
|
||||
|
||||
### Fixes
|
||||
|
||||
* lang: Fix `returns` being serialized as `null` instead of `undefined` in IDL ([#1782](https://github.com/project-serum/anchor/pull/1782)).
|
||||
|
||||
## [0.24.1] - 2022-04-12
|
||||
|
||||
### Fixes
|
||||
|
||||
* lang: Fix `anchor build` failing if `Test.toml` included a relative path that didn't exist yet because it's created by `anchor build` ([#1772](https://github.com/project-serum/anchor/pull/1772)).
|
||||
* cli: Update js/ts template to use new `AnchorProvider` class ([#1770](https://github.com/project-serum/anchor/pull/1770)).
|
||||
|
||||
## [0.24.0] - 2022-04-12
|
||||
|
||||
### Features
|
||||
|
||||
* lang: Add support for multiple test suites with separate local validators ([#1681](https://github.com/project-serum/anchor/pull/1681)).
|
||||
* lang: Add return values to CPI client ([#1598](https://github.com/project-serum/anchor/pull/1598)).
|
||||
* ts: Add view functions ([#1695](https://github.com/project-serum/anchor/pull/1695)).
|
||||
* avm: New `avm update` command to update the Anchor CLI to the latest version ([#1670](https://github.com/project-serum/anchor/pull/1670)).
|
||||
* cli: Update js/ts templates to use new `program.methods` syntax ([#1732](https://github.com/project-serum/anchor/pull/1732)).
|
||||
* cli: Workspaces created with `anchor init` now come with the `prettier` formatter and scripts included ([#1741](https://github.com/project-serum/anchor/pull/1741)).
|
||||
* ts: Add `pubkeys` function to methods builder to get all instruction account addresses ([#1733](https://github.com/project-serum/anchor/pull/1733)).
|
||||
* ts: Export `LangErrorCode` and `LangErrorMessage` from `error.ts` ([#1756](https://github.com/project-serum/anchor/pull/1756)).
|
||||
|
||||
### Fixes
|
||||
|
||||
* avm: `avm install` no longer downloads the version if already installed in the machine ([#1670](https://github.com/project-serum/anchor/pull/1670)).
|
||||
* cli: make `anchor test` fail when used with `--skip-deploy` option and without `--skip-local-validator` option but there already is a running validator ([#1675](https://github.com/project-serum/anchor/pull/1675)).
|
||||
* lang: Return proper error instead of panicking if account length is smaller than discriminator in functions of `(Account)Loader` ([#1678](https://github.com/project-serum/anchor/pull/1678)).
|
||||
* cli: Add `@types/bn.js` to `devDependencies` in cli template ([#1712](https://github.com/project-serum/anchor/pull/1712)).
|
||||
* ts: Event listener no longer crashes on Program Upgrade or any other unexpected log ([#1757](https://github.com/project-serum/anchor/pull/1757)).
|
||||
|
||||
### Breaking
|
||||
|
||||
* avm: `avm install` switches to the newly installed version after installation finishes ([#1670](https://github.com/project-serum/anchor/pull/1670)).
|
||||
* spl: Re-export the `spl_token` crate ([#1665](https://github.com/project-serum/anchor/pull/1665)).
|
||||
* lang, cli, spl: Update solana toolchain to v1.9.13 ([#1653](https://github.com/project-serum/anchor/pull/1653) and [#1751](https://github.com/project-serum/anchor/pull/1751)).
|
||||
* lang: `Program` type now deserializes `programdata_address` only on demand ([#1723](https://github.com/project-serum/anchor/pull/1723)).
|
||||
* ts: Make `Provider` an interface and adjust its signatures and add `AnchorProvider` implementor class ([#1707](https://github.com/project-serum/anchor/pull/1707)).
|
||||
* spl: Change "to" to "from" in `token::burn` ([#1080](https://github.com/project-serum/anchor/pull/1080)).
|
||||
|
||||
## [0.23.0] - 2022-03-20
|
||||
|
||||
### Features
|
||||
|
||||
* cli: Add `anchor clean` command that's the same as `cargo clean` but preserves keypairs inside `target/deploy` ([#1470](https://github.com/project-serum/anchor/issues/1470)).
|
||||
* cli: Running `anchor init` now initializes a new git repository for the workspace. This can be disabled with the `--no-git` flag ([#1605](https://github.com/project-serum/anchor/pull/1605)).
|
||||
* cli: Add support for `anchor idl fetch` to work outside anchor workspace ([#1509](https://github.com/project-serum/anchor/pull/1509)).
|
||||
* cli: [[test.validator.clone]] also clones the program data account of programs owned by the bpf upgradeable loader ([#1481](https://github.com/project-serum/anchor/issues/1481)).
|
||||
* lang: Add new `AccountSysvarMismatch` error code and test cases for sysvars ([#1535](https://github.com/project-serum/anchor/pull/1535)).
|
||||
* lang: Replace `std::io::Cursor` with a custom `Write` impl that uses the Solana mem syscalls ([#1589](https://github.com/project-serum/anchor/pull/1589)).
|
||||
* lang: Add `require_neq`, `require_keys_neq`, `require_gt`, and `require_gte` comparison macros ([#1622](https://github.com/project-serum/anchor/pull/1622)).
|
||||
* lang: Handle arrays with const as size in instruction data ([#1623](https://github.com/project-serum/anchor/issues/1623).
|
||||
* spl: Add support for revoke instruction ([#1493](https://github.com/project-serum/anchor/pull/1493)).
|
||||
* ts: Add provider parameter to `Spl.token` factory method ([#1597](https://github.com/project-serum/anchor/pull/1597)).
|
||||
|
||||
### Fixes
|
||||
|
||||
* ts: Fix the loss of strict typing using the `methods` namespace on builder functions ([#1539](https://github.com/project-serum/anchor/pull/1539)).
|
||||
* spl: Update `spl/governance` to use new errors ([#1582](https://github.com/project-serum/anchor/pull/1582)).
|
||||
* client: Fix `Cluster`'s `FromStr` implementation ([#1362](https://github.com/project-serum/anchor/pull/1362)).
|
||||
* lang: Implement `Key` for `Pubkey` again, so `associated_token::*` constraints can use pubkey targets again ([#1601](https://github.com/project-serum/anchor/pull/1601)).
|
||||
* lang: Adjust error code so `#[error_code]` works with just importing `anchor_lang::error_code` ([#1610](https://github.com/project-serum/anchor/pull/1610)).
|
||||
* ts: Fix `spl-token` coder account parsing ([#1604](https://github.com/project-serum/anchor/pull/1604)).
|
||||
* cli: Fix `npm install` fallback if `yarn` install doesn't work ([#1643](https://github.com/project-serum/anchor/pull/1643)).
|
||||
* lang: Fix bug where `owner = <target>` would not compile because of missing type annotation ([#1648](https://github.com/project-serum/anchor/pull/1648)).
|
||||
* ts: Adjust `send` and `simulate` functions in `provider.ts`, so they use the return value of `Wallet.signTransaction`([#1527](https://github.com/project-serum/anchor/pull/1527)).
|
||||
|
||||
### Breaking
|
||||
|
||||
* ts: Mark `transaction`, `instruction`, `simulate` and `rpc` program namespaces as deprecated in favor of `methods` ([#1539](https://github.com/project-serum/anchor/pull/1539)).
|
||||
* ts: No longer allow manual setting of globally resolvable program public keys in `methods#accounts()`. ([#1548][https://github.com/project-serum/anchor/pull/1548])
|
||||
* lang/ts: Events are now emitted using the `sol_log_data` syscall ([#1608](https://github.com/project-serum/anchor/pull/1608)).
|
||||
* lang: Remove space calculation using `#[derive(Default)]` ([#1519](https://github.com/project-serum/anchor/pull/1519)).
|
||||
* lang: Add support for logging expected and actual values and pubkeys. Add `require_eq` and `require_keys_eq` macros. Add default error code to `require` macro ([#1572](https://github.com/project-serum/anchor/pull/1572)).
|
||||
* lang: Add `system_program` CPI wrapper functions. Make `system_program` module public instead of re-exporting `system_program::System`([#1629](https://github.com/project-serum/anchor/pull/1629)).
|
||||
* cli: `avm use` no long prompts [y/n] if an install is needed first - it just tells the user to `avm install` ([#1565](https://github.com/project-serum/anchor/pull/1565))
|
||||
* ts: Add `AnchorError` with program stack and also a program stack for non-`AnchorError` errors ([#1640](https://github.com/project-serum/anchor/pull/1640)). `AnchorError` is not returned for `processed` tx that have `skipPreflight` set to `true` (it falls back to `ProgramError` or the raw solana library error).
|
||||
|
||||
## [0.22.1] - 2022-02-28
|
||||
|
||||
### Fixes
|
||||
|
||||
* cli: Fix rust template ([#1488](https://github.com/project-serum/anchor/pull/1488)).
|
||||
* lang: Handle array sizes with variable sizes in events and array size casting in IDL parsing ([#1485](https://github.com/project-serum/anchor/pull/1485))
|
||||
|
||||
|
||||
## [0.22.0] - 2022-02-20
|
||||
|
||||
### Features
|
||||
|
||||
* 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
|
||||
|
||||
* ts: Allow nullable types for `Option<T>` mapped types ([#1428](https://github.com/project-serum/anchor/pull/1428)).
|
||||
|
||||
### Breaking
|
||||
|
||||
* lang: Enforce that the payer for an init-ed account be marked `mut` ([#1271](https://github.com/project-serum/anchor/pull/1271)).
|
||||
* 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).
|
||||
* change all `solana_program::program::invoke()` to `solana_program::program::invoke().map_err(Into::into)` and `solana_program::program::invoke_signed()` to `solana_program::program::invoke_signed().map_err(Into::into)`
|
||||
|
||||
## [0.21.0] - 2022-02-07
|
||||
|
||||
### Fixes
|
||||
|
||||
* ts: Fix the root type declaration of the `Wallet` / `NodeWallet` class ([#1363](https://github.com/project-serum/anchor/pull/1363)).
|
||||
* ts: Improve type mapping of Account fields into Typescript with additional support for `Option<T>` and `Vec<String>` types ([#1393](https://github.com/project-serum/anchor/pull/1393)).
|
||||
|
||||
### Features
|
||||
|
||||
* lang: Add `seeds::program` constraint for specifying which program_id to use when deriving PDAs ([#1197](https://github.com/project-serum/anchor/pull/1197)).
|
||||
* lang: `Context` now has a new `bumps: BTree<String, u8>` argument, mapping account name to bump seed "found" by the accounts context. This allows one to access bump seeds without having to pass them in from the client or recalculate them in the handler ([#1367](https://github.com/project-serum/anchor/pull/1367)).
|
||||
* lang, ts: Automatically infer PDA addresses ([#1331](https://github.com/project-serum/anchor/pull/1331)).
|
||||
* ts: Remove error logging in the event parser when log websocket encounters a program error ([#1313](https://github.com/project-serum/anchor/pull/1313)).
|
||||
* ts: Add new `methods` namespace to the program client, introducing a more ergonomic builder API ([#1324](https://github.com/project-serum/anchor/pull/1324)).
|
||||
* ts: Add registry utility for fetching the latest verified build ([#1371](https://github.com/project-serum/anchor/pull/1371)).
|
||||
* cli: Expose the solana-test-validator --account flag in Anchor.toml via [[test.validator.account]] ([#1366](https://github.com/project-serum/anchor/pull/1366)).
|
||||
* cli: Add avm, a tool for managing anchor-cli versions ([#1385](https://github.com/project-serum/anchor/pull/1385)).
|
||||
|
||||
### Breaking
|
||||
|
||||
* lang: Put `init_if_needed` behind a feature flag to decrease wrong usage ([#1258](https://github.com/project-serum/anchor/pull/1258)).
|
||||
* lang: rename `loader_account` module to `account_loader` module ([#1279](https://github.com/project-serum/anchor/pull/1279))
|
||||
* lang: The `Accounts` trait's `try_accounts` method now has an additional `bumps: &mut BTreeMap<String, u8>` argument, which accumulates bump seeds ([#1367](https://github.com/project-serum/anchor/pull/1367)).
|
||||
* lang: Providing `bump = <target>` targets with `init` will now error. On `init` only, it is required to use `bump` without a target and access the seed inside function handlers via `ctx.bumps.get("<pda-account-name")`. For subsequent seeds constraints (without init), it is recommended to store the bump on your account and use it as a `bump = <target>` target to minimize compute units used ([#1380](https://github.com/project-serum/anchor/pull/1380)).
|
||||
* ts: `Coder` is now an interface and the existing class has been renamed to `BorshCoder`. This change allows the generation of Anchor clients for non anchor programs ([#1259](https://github.com/project-serum/anchor/pull/1259/files)).
|
||||
* cli: [[test.clone]] key in Anchor.toml is renamed to [[test.validator.clone]] ([#1366](https://github.com/project-serum/anchor/pull/1366)).
|
||||
|
||||
|
||||
## [0.20.1] - 2022-01-09
|
||||
|
||||
### Fixes
|
||||
|
||||
* lang: Improved error msgs when required programs are missing when using the `init` constraint([#1257](https://github.com/project-serum/anchor/pull/1257))
|
||||
|
||||
### Features
|
||||
|
||||
* lang: Allow repr overrides for zero copy accounts ([#1273](https://github.com/project-serum/anchor/pull/1273)).
|
||||
|
||||
## [0.20.0] - 2022-01-06
|
||||
|
||||
### Fixes
|
||||
|
||||
* lang: `init_if_needed` now checks rent exemption when init is not needed ([#1250](https://github.com/project-serum/anchor/pull/1250)).
|
||||
* lang: Add missing owner check when `associated_token::authority` is used ([#1240](https://github.com/project-serum/anchor/pull/1240)).
|
||||
* ts: Add type declarations for conditional `workspace` and `Wallet` exports ([#1137](https://github.com/project-serum/anchor/pull/1137)).
|
||||
* ts: Change commitment message `recent` to `processed` and `max` to `finalized` ([#1128](https://github.com/project-serum/anchor/pull/1128))
|
||||
* ts: fix `translateAddress` which currently leads to failing browser code. Now uses `PublicKey` constructor instead of prototype chain constructor name checking which doesn't work in the presence of code minifying/mangling([#1138](https://github.com/project-serum/anchor/pull/1138))
|
||||
* lang: add missing check that verifies that account is ATA when using `init_if_needed` and init is not needed([#1221](https://github.com/project-serum/anchor/pull/1221))
|
||||
|
||||
### Features
|
||||
|
||||
* lang: Add `programdata_address: Option<Pubkey>` field to `Program` account. Will be populated if account is a program owned by the upgradable bpf loader ([#1125](https://github.com/project-serum/anchor/pull/1125))
|
||||
* lang,ts,ci,cli,docs: update solana toolchain to version 1.8.5([#1133](https://github.com/project-serum/anchor/pull/1133)).
|
||||
* lang: Account wrappers for non-Anchor programs no longer have to implement the `serialize` function because it has a default impl now. Similarly, they no longer have to implement `try_deserialize` which now delegates to `try_deserialize_unchecked` by default([#1156](https://github.com/project-serum/anchor/pull/1156)).
|
||||
* lang: Add `set_inner` method to `Account<'a, T>` to enable easy updates ([#1177](https://github.com/project-serum/anchor/pull/1177)).
|
||||
* lang: Handle arrays with const as length ([#968](https://github.com/project-serum/anchor/pull/968)).
|
||||
* ts: Add optional commitment argument to `fetch` and `fetchMultiple` ([#1171](https://github.com/project-serum/anchor/pull/1171)).
|
||||
* lang: Implement `AsRef<T>` for `Account<'a, T>`([#1173](https://github.com/project-serum/anchor/pull/1173))
|
||||
* cli: Add `anchor expand` command which wraps around `cargo expand` ([#1160](https://github.com/project-serum/anchor/pull/1160))
|
||||
|
||||
### Breaking
|
||||
|
||||
* client: Client::new and Client::new_with_options now accept `Rc<dyn Signer>` instead of `Keypair` ([#975](https://github.com/project-serum/anchor/pull/975)).
|
||||
* lang, ts: Change error enum name and message for 'wrong program ownership' account validation ([#1154](https://github.com/project-serum/anchor/pull/1154)).
|
||||
* lang: Change from `#[repr(packed)]` to `#[repr(C)]` for zero copy accounts ([#1106](https://github.com/project-serum/anchor/pull/1106)).
|
||||
* lang: Account types can now be found either in the `prelude` module or the `accounts` module but not longer directly under the root.
|
||||
Deprecated account types are no longer imported by the prelude ([#1208](https://github.com/project-serum/anchor/pull/1208)).
|
||||
|
||||
## [0.19.0] - 2021-12-08
|
||||
|
||||
### Fixes
|
||||
|
||||
* lang: Add `deprecated` attribute to `ProgramAccount` ([#1014](https://github.com/project-serum/anchor/pull/1014)).
|
||||
* cli: Add version number from programs `Cargo.toml` into extracted IDL ([#1061](https://github.com/project-serum/anchor/pull/1061)).
|
||||
* lang: Add `deprecated` attribute to `Loader`([#1078](https://github.com/project-serum/anchor/pull/1078)).
|
||||
* lang: the `init_if_needed` attribute now checks that given attributes (e.g. space, owner, token::authority etc.) are validated even when init is not needed ([#1096](https://github.com/project-serum/anchor/pull/1096)).
|
||||
|
||||
### Features
|
||||
|
||||
* lang: Add `ErrorCode::AccountNotInitialized` error to separate the situation when the account has the wrong owner from when it does not exist (#[1024](https://github.com/project-serum/anchor/pull/1024)).
|
||||
* lang: Called instructions now log their name by default. This can be turned off with the `no-log-ix-name` flag ([#1057](https://github.com/project-serum/anchor/pull/1057)).
|
||||
* lang: `ProgramData` and `UpgradableLoaderState` can now be passed into `Account` as generics. see [UpgradeableLoaderState](https://docs.rs/solana-program/latest/solana_program/bpf_loader_upgradeable/enum.UpgradeableLoaderState.html). `UpgradableLoaderState` can also be matched on to get `ProgramData`, but when `ProgramData` is used instead, anchor does the serialization and checking that it is actually program data for you ([#1095](https://github.com/project-serum/anchor/pull/1095)).
|
||||
* ts: Add better error msgs in the ts client if something wrong (i.e. not a pubkey or a string) is passed in as an account in an instruction accounts object ([#1098](https://github.com/project-serum/anchor/pull/1098)).
|
||||
* ts: Add inputs `postInstructions` and `preInstructions` as a replacement for (the now deprecated) `instructions` ([#1007](https://github.com/project-serum/anchor/pull/1007)).
|
||||
* ts: Add `getAccountInfo` helper method to account namespace/client ([#1084](https://github.com/project-serum/anchor/pull/1084)).
|
||||
|
||||
### Breaking
|
||||
|
||||
* lang, ts: Error codes have been mapped to new numbers to allow for more errors per namespace ([#1096](https://github.com/project-serum/anchor/pull/1096)).
|
||||
|
||||
## [0.18.2] - 2021-11-14
|
||||
|
||||
* cli: Replace global JavaScript dependency installs with local.
|
||||
|
||||
### Features
|
||||
|
||||
* lang: Add `SystemAccount<'info>` account type for generic wallet addresses or accounts owned by the system program ([#954](https://github.com/project-serum/anchor/pull/954))
|
||||
|
||||
### Fixes
|
||||
|
||||
* cli: fix dns in NODE_OPTIONS ([#928](https://github.com/project-serum/anchor/pull/928)).
|
||||
* cli: output TypeScript IDL in `idl parse` subcommand ([#941](https://github.com/project-serum/anchor/pull/941)).
|
||||
* cli: Add fields `os` and `cpu` to npm package `@project-serum/anchor-cli` ([#976](https://github.com/project-serum/anchor/pull/976)).
|
||||
* cli: Allow specify output directory for TypeScript IDL ([#940](https://github.com/project-serum/anchor/pull/940)).
|
||||
|
||||
### Breaking
|
||||
|
||||
* spl: Move permissioned markets into dex repository ([#962](https://github.com/project-serum/anchor/pull/962)).
|
||||
|
||||
## [0.18.0] - 2021-10-24
|
||||
|
||||
### Features
|
||||
|
||||
* cli: Add support for configuration options for `solana-test-validator` in Anchor.toml ([#834](https://github.com/project-serum/anchor/pull/834)).
|
||||
* cli: `target/types` directory now created on build to store a TypeScript types file for each program's IDL ([#795](https://github.com/project-serum/anchor/pull/795)).
|
||||
* ts: `Program<T>` can now be typed with an IDL type ([#795](https://github.com/project-serum/anchor/pull/795)).
|
||||
* lang: Add `mint::freeze_authority` keyword for mint initialization within `#[derive(Accounts)]` ([#835](https://github.com/project-serum/anchor/pull/835)).
|
||||
* lang: Add `AccountLoader` type for `zero_copy` accounts with support for CPI ([#792](https://github.com/project-serum/anchor/pull/792)).
|
||||
* lang: Add `#[account(init_if_needed)]` keyword for allowing one to invoke the same instruction even if the account was created already ([#906](https://github.com/project-serum/anchor/pull/906)).
|
||||
* lang: Add custom errors support for raw constraints ([#905](https://github.com/project-serum/anchor/pull/905)).
|
||||
* lang, cli, spl: Update solana toolchain to v1.8.0 ([#886](https://github.com/project-serum/anchor/pull/886)).
|
||||
* lang: Add custom errors support for `signer`, `mut`, `has_one`, `owner`, raw constraints and `address` ([#905](https://github.com/project-serum/anchor/pull/905), [#913](https://github.com/project-serum/anchor/pull/913)).
|
||||
|
||||
### Breaking
|
||||
|
||||
* lang: Accounts marked with the `#[account(signer)]` constraint now enforce signer when the `"cpi"` feature is enabled ([#849](https://github.com/project-serum/anchor/pull/849)).
|
||||
|
||||
## [0.17.0] - 2021-10-03
|
||||
|
||||
### Features
|
||||
|
||||
* cli: Add `localnet` command for starting a local `solana-test-validator` with the workspace deployed ([#820](https://github.com/project-serum/anchor/pull/820)).
|
||||
|
||||
### Breaking
|
||||
|
||||
* `CpiContext` accounts must now be used with the accounts struct generated in the `crate::cpi::accounts::*` module. These structs correspond to the accounts context for each instruction, except that each field is of type `AccountInfo` ([#824](https://github.com/project-serum/anchor/pull/824)).
|
||||
|
||||
## [0.16.2] - 2021-09-27
|
||||
|
||||
### Features
|
||||
|
||||
* lang: Add `--detach` flag to `anchor test` ([#770](https://github.com/project-serum/anchor/pull/770)).
|
||||
* lang: Add `associated_token` keyword for initializing associated token accounts within `#[derive(Accounts)]` ([#790](https://github.com/project-serum/anchor/pull/790)).
|
||||
* cli: Allow passing through cargo flags for build command ([#719](https://github.com/project-serum/anchor/pull/719)).
|
||||
* cli: Allow passing through cargo flags for test, verify, and publish commands ([#804](https://github.com/project-serum/anchor/pull/804)).
|
||||
|
||||
### Fixes
|
||||
|
||||
* lang: Generated `AccountMeta`s for Rust clients now properly set the `isSigner` field ([#762](https://github.com/project-serum/anchor/pull/762)).
|
||||
|
||||
## [0.16.1] - 2021-09-17
|
||||
|
||||
### Fixes
|
||||
|
||||
* lang: `Signer` type now sets isSigner to true in the IDL ([#750](https://github.com/project-serum/anchor/pull/750)).
|
||||
|
||||
## [0.16.0] - 2021-09-16
|
||||
|
||||
### Features
|
||||
|
||||
* lang: `Program` type introduced for executable accounts ([#705](https://github.com/project-serum/anchor/pull/705)).
|
||||
* lang: `Signer` type introduced for signing accounts where data is not used ([#705](https://github.com/project-serum/anchor/pull/705)).
|
||||
* lang: `UncheckedAccount` type introduced as a preferred alias for `AccountInfo` ([#745](https://github.com/project-serum/anchor/pull/745)).
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* lang: `#[account(owner = <pubkey>)]` now requires a `Pubkey` instead of an account ([#691](https://github.com/project-serum/anchor/pull/691)).
|
||||
|
||||
## [0.15.0] - 2021-09-07
|
||||
|
||||
### Features
|
||||
|
||||
* lang: Add new `Account` type to replace `ProgramAccount` and `CpiAccount`, both of which are deprecated ([#686](https://github.com/project-serum/anchor/pull/686)).
|
||||
* lang: `Box` can be used with `Account` types to reduce stack usage ([#686](https://github.com/project-serum/anchor/pull/686)).
|
||||
* lang: Add `Owner` trait, which is automatically implemented by all `#[account]` structs ([#686](https://github.com/project-serum/anchor/pull/686)).
|
||||
* lang: Check that ProgramAccount writable before mut borrow (`anchor-debug` only) ([#681](https://github.com/project-serum/anchor/pull/681)).
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* lang: All programs must now define their program id in source via `declare_id!` ([#686](https://github.com/project-serum/anchor/pull/686)).
|
||||
|
||||
## [0.14.0] - 2021-09-02
|
||||
|
||||
### Features
|
||||
|
||||
* lang: Ignore `Unnamed` structs instead of panic ([#605](https://github.com/project-serum/anchor/pull/605)).
|
||||
* lang: Add constraints for initializing mint accounts as pdas, `#[account(init, seeds = [...], mint::decimals = <expr>, mint::authority = <expr>)]` ([#562](https://github.com/project-serum/anchor/pull/562)).
|
||||
* lang: Add `AsRef<AccountInfo>` for `AccountInfo` wrappers ([#652](https://github.com/project-serum/anchor/pull/652)).
|
||||
* lang: Optimize `trait Key` by removing `AccountInfo` cloning ([#652](https://github.com/project-serum/anchor/pull/652)).
|
||||
* cli, client, lang: Update solana toolchain to v1.7.11 ([#653](https://github.com/project-serum/anchor/pull/653)).
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* lang: Change `#[account(init, seeds = [...], token = <expr>, authority = <expr>)]` to `#[account(init, token::mint = <expr> token::authority = <expr>)]` ([#562](https://github.com/project-serum/anchor/pull/562)).
|
||||
* lang: `#[associated]` and `#[account(associated = <target>, with = <target>)]` are both removed ([#612](https://github.com/project-serum/anchor/pull/612)).
|
||||
* cli: Removed `anchor launch` command ([#634](https://github.com/project-serum/anchor/pull/634)).
|
||||
* lang: `#[account(init)]` now creates the account inside the same instruction to be consistent with initializing PDAs. To maintain the old behavior of `init`, replace it with `#[account(zero)]` ([#641](https://github.com/project-serum/anchor/pull/641)).
|
||||
* lang: `bump` must be provided when using the `seeds` constraint. This has been added as an extra safety constraint to ensure that whenever a PDA is initialized via a constraint the bump used is the one created by `Pubkey::find_program_address` ([#641](https://github.com/project-serum/anchor/pull/641)).
|
||||
* lang: `try_from_init` has been removed from `Loader`, `ProgramAccount`, and `CpiAccount` and replaced with `try_from_unchecked` ([#641](https://github.com/project-serum/anchor/pull/641)).
|
||||
* lang: Remove `AccountsInit` trait ([#641](https://github.com/project-serum/anchor/pull/641)).
|
||||
* lang: `try_from` methods for `ProgramAccount`, `Loader`, and `ProgramState` now take in an additional `program_id: &Pubkey` parameter ([#660](https://github.com/project-serum/anchor/pull/660)).
|
||||
|
||||
## [0.13.2] - 2021-08-11
|
||||
|
||||
### Fixes
|
||||
|
||||
* cli: Fix `anchor init` command "Workspace not found" regression ([#598](https://github.com/project-serum/anchor/pull/598)).
|
||||
|
||||
## [0.13.1] - 2021-08-10
|
||||
|
||||
### Features
|
||||
|
||||
* cli: Programs embedded into genesis during tests will produce program logs ([#594](https://github.com/project-serum/anchor/pull/594)).
|
||||
|
||||
### Fixes
|
||||
|
||||
* cli: Allows Cargo.lock to exist in workspace subdirectories when publishing ([#593](https://github.com/project-serum/anchor/pull/593)).
|
||||
|
||||
## [0.13.0] - 2021-08-08
|
||||
|
||||
### Features
|
||||
|
||||
* cli: Adds a `[registry]` section in the Anchor toml ([#570](https://github.com/project-serum/anchor/pull/570)).
|
||||
* cli: Adds the `anchor login <api-token>` command ([#570](https://github.com/project-serum/anchor/pull/570)).
|
||||
* cli: Adds the `anchor publish <package>` command ([#570](https://github.com/project-serum/anchor/pull/570)).
|
||||
* cli: Adds a root level `anchor_version` field to the Anchor.toml for specifying the anchor docker image to use for verifiable builds ([#570](https://github.com/project-serum/anchor/pull/570)).
|
||||
* cli: Adds a root level `solana_version` field to the Anchor.toml for specifying the solana toolchain to use for verifiable builds ([#570](https://github.com/project-serum/anchor/pull/570)).
|
||||
* lang: Dynamically fetch rent sysvar for when using `init` ([#587](https://github.com/project-serum/anchor/pull/587)).
|
||||
|
||||
### Breaking
|
||||
|
||||
* cli: `[clusters.<network>]` Anchor.toml section has been renamed to `[programs.<network>]` ([#570](https://github.com/project-serum/anchor/pull/570)).
|
||||
* cli: `[workspace]` member and exclude arrays must now be filepaths relative to the workpsace root ([#570](https://github.com/project-serum/anchor/pull/570)).
|
||||
|
||||
## [0.12.0] - 2021-08-03
|
||||
|
||||
### Features
|
||||
|
||||
* cli: Add keys `members` / `exclude` in config `programs` section ([#546](https://github.com/project-serum/anchor/pull/546)).
|
||||
* cli: Allow program address configuration for test command through `clusters.localnet` ([#554](https://github.com/project-serum/anchor/pull/554)).
|
||||
* lang: IDLs are now parsed from the entire crate ([#517](https://github.com/project-serum/anchor/pull/517)).
|
||||
* spl: Dex permissioned markets proxy ([#519](https://github.com/project-serum/anchor/pull/519), [#543](https://github.com/project-serum/anchor/pull/543)).
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* ts: Use `hex` by default for decoding Instruction ([#547](https://github.com/project-serum/anchor/pull/547)).
|
||||
* lang: `CpiAccount::reload` mutates the existing struct instead of returning a new one ([#526](https://github.com/project-serum/anchor/pull/526)).
|
||||
* cli: Anchor.toml now requires an explicit `[scripts]` test command ([#550](https://github.com/project-serum/anchor/pull/550)).
|
||||
|
||||
## [0.11.1] - 2021-07-09
|
||||
|
||||
### Features
|
||||
|
||||
* lang: Adds `require` macro for specifying assertions that return error codes on failure ([#483](https://github.com/project-serum/anchor/pull/483)).
|
||||
* lang: Allow one to specify arbitrary programs as the owner when creating PDA ([#483](https://github.com/project-serum/anchor/pull/483)).
|
||||
* lang: A new `bump` keyword is added to the accounts constraints, which is used to add an optional bump seed to the end of a `seeds` array. When used in conjunction with *both* `init` and `seeds`, then the program executes `find_program_address` to assert that the given bump is the canonical bump ([#483](https://github.com/project-serum/anchor/pull/483)).
|
||||
|
||||
### Fixes
|
||||
|
||||
* lang: Preserve all instruction data for fallback functions ([#483](https://github.com/project-serum/anchor/pull/483)).
|
||||
* ts: Event listener not firing when creating associated accounts ([#356](https://github.com/project-serum/anchor/issues/356)).
|
||||
|
||||
## [0.11.0] - 2021-07-03
|
||||
|
||||
### Features
|
||||
|
||||
* lang: Add fallback functions ([#457](https://github.com/project-serum/anchor/pull/457)).
|
||||
* lang: Add feature flag for using the old state account discriminator. This is a temporary flag for those with programs built prior to v0.7.0 but want to use the latest Anchor version. Expect this to be removed in a future version ([#446](https://github.com/project-serum/anchor/pull/446)).
|
||||
* lang: Add generic support to Accounts ([#496](https://github.com/project-serum/anchor/pull/496)).
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* cli: Remove `.spec` suffix on TypeScript tests files ([#441](https://github.com/project-serum/anchor/pull/441)).
|
||||
* lang: Remove `belongs_to` constraint ([#459](https://github.com/project-serum/anchor/pull/459)).
|
||||
|
||||
## [0.10.0] - 2021-06-27
|
||||
|
||||
### Features
|
||||
|
||||
* lang: Add `#[account(address = <expr>)]` constraint for asserting the address of an account ([#400](https://github.com/project-serum/anchor/pull/400)).
|
||||
* lang: Add `#[account(init, token = <mint-target>, authority = <token-owner-target>...)]` constraint for initializing SPL token accounts as program derived addresses for the program. Can be used when initialized via `seeds` or `associated` ([#400](https://github.com/project-serum/anchor/pull/400)).
|
||||
* lang: Add `associated_seeds!` macro for generating signer seeds for CPIs signed by an `#[account(associated = <target>)]` account ([#400](https://github.com/project-serum/anchor/pull/400)).
|
||||
* cli: Add `[scripts]` section to the Anchor.toml for specifying workspace scripts that can be run via `anchor run <script>` ([#400](https://github.com/project-serum/anchor/pull/400)).
|
||||
* cli: `[clusters.<network>]` table entries can now also use `{ address = <base58-str>, idl = <filepath-str> }` to specify workspace programs ([#400](https://github.com/project-serum/anchor/pull/400)).
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* cli: Remove `--yarn` flag in favor of using `npx` ([#432](https://github.com/project-serum/anchor/pull/432)).
|
||||
|
||||
## [0.9.0] - 2021-06-15
|
||||
|
||||
### Features
|
||||
|
||||
* lang: Instruction data is now available to accounts constraints ([#386](https://github.com/project-serum/anchor/pull/386)).
|
||||
* lang: Initialize program derived addresses with accounts constraints ([#386](https://github.com/project-serum/anchor/pull/386)).
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* lang: Event field names in IDLs are now mixed case. ([#379](https://github.com/project-serum/anchor/pull/379)).
|
||||
* lang: Accounts trait now accepts an additional `&[u8]` parameter ([#386](https://github.com/project-serum/anchor/pull/386)).
|
||||
|
||||
## [0.8.0] - 2021-06-10
|
||||
|
||||
### Features
|
||||
|
||||
* cli: Add `--program-name` option for build command to build a single program at a time ([#362](https://github.com/project-serum/anchor/pull/362)).
|
||||
* cli, client: Parse custom cluster urls from str ([#369](https://github.com/project-serum/anchor/pull/369)).
|
||||
* cli, client, lang: Update solana toolchain to v1.7.1 ([#368](https://github.com/project-serum/anchor/pull/369)).
|
||||
* ts: Instruction decoding and formatting ([#372](https://github.com/project-serum/anchor/pull/372)).
|
||||
* lang: Add `#[account(close = <destination>)]` constraint for closing accounts and sending the rent exemption lamports to a specified destination account ([#371](https://github.com/project-serum/anchor/pull/371)).
|
||||
|
||||
### Fixes
|
||||
|
||||
* lang: Allows one to use `remaining_accounts` with `CpiContext` by implementing the `ToAccountMetas` trait on `CpiContext` ([#351](https://github.com/project-serum/anchor/pull/351/files)).
|
||||
|
||||
### Breaking
|
||||
|
||||
* lang, ts: Framework defined error codes are introduced, reserving error codes 0-300 for Anchor, and 300 and up for user defined error codes ([#354](https://github.com/project-serum/anchor/pull/354)).
|
||||
|
||||
## [0.7.0] - 2021-05-31
|
||||
|
||||
### Features
|
||||
|
||||
* cli: Add global options for override Anchor.toml values ([#313](https://github.com/project-serum/anchor/pull/313)).
|
||||
* spl: Add `SetAuthority` instruction ([#307](https://github.com/project-serum/anchor/pull/307/files)).
|
||||
* spl: Add init and close open orders instructions ([#245](https://github.com/project-serum/anchor/pull/245)).
|
||||
* lang: `constraint = <expression>` added as a replacement for (the now deprecated) string literal constraints ([#341](https://github.com/project-serum/anchor/pull/341)).
|
||||
* lang: Span information is now preserved, providing informative compiler error messages ([#341](https://github.com/project-serum/anchor/pull/341)).
|
||||
* ts: Address metadata is now optional for `anchor.workspace` clients ([#310](https://github.com/project-serum/anchor/pull/310)).
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* ts: Retrieving deserialized accounts from the `<program>.account.<my-account>` and `<program>.state` namespaces now require explicitly invoking the `fetch` API. For example, `program.account.myAccount(<adddress>)` and `program.state()` is now `program.account.myAccount.fetch(<address>)` and `program.state.fetch()` ([#322](https://github.com/project-serum/anchor/pull/322)).
|
||||
* lang: `#[account(associated)]` now requires `init` to be provided to create an associated account. If not provided, then the address will be assumed to exist, and a constraint will be added to ensure the correctness of the address ([#318](https://github.com/project-serum/anchor/pull/318)).
|
||||
* lang, ts: Change account discriminator pre-image of the `#[state]` account discriminator to be namespaced by "state:" ([#320](https://github.com/project-serum/anchor/pull/320)).
|
||||
* lang, ts: Change domain delimiters for the pre-image of the instruciton sighash to be a single colon `:` to be consistent with accounts ([#321](https://github.com/project-serum/anchor/pull/321)).
|
||||
* lang: Associated constraints no longer automatically implement `mut` ([#341](https://github.com/project-serum/anchor/pull/341)).
|
||||
* lang: Associated `space` constraints must now be literal integers instead of literal strings ([#341](https://github.com/project-serum/anchor/pull/341)).
|
||||
|
||||
## [0.6.0] - 2021-05-23
|
||||
|
||||
### Features
|
||||
|
||||
* ts: Add `program.simulate` namespace ([#266](https://github.com/project-serum/anchor/pull/266)).
|
||||
* ts: Introduce `Address` type, allowing one to use Base 58 encoded strings in public APIs ([#304](https://github.com/project-serum/anchor/pull/304)).
|
||||
* ts: Replace deprecated `web3.Account` with `web3.Signer` in public APIs ([#296](https://github.com/project-serum/anchor/pull/296)).
|
||||
* ts: Generated `anchor.workspace` clients can now be customized per network with `[cluster.<slug>]` in the Anchor.toml ([#308](https://github.com/project-serum/anchor/pull/308)).
|
||||
* cli: Add yarn flag to test command ([#267](https://github.com/project-serum/anchor/pull/267)).
|
||||
* cli: Add `--skip-build` flag to test command ([301](https://github.com/project-serum/anchor/pull/301)).
|
||||
* cli: Add `anchor shell` command to spawn a node shell populated with an Anchor.toml based environment ([#303](https://github.com/project-serum/anchor/pull/303)).
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* cli: The Anchor.toml's `wallet` and `cluster` settings must now be under the `[provider]` table ([#305](https://github.com/project-serum/anchor/pull/305)).
|
||||
* ts: Event coder `decode` API changed to decode strings directly instead of buffers ([#292](https://github.com/project-serum/anchor/pull/292)).
|
||||
* ts: Event coder `encode` API removed ([#292](https://github.com/project-serum/anchor/pull/292)).
|
||||
|
||||
## [0.5.0] - 2021-05-07
|
||||
|
||||
### Features
|
||||
|
||||
* client: Adds support for state instructions ([#248](https://github.com/project-serum/anchor/pull/248)).
|
||||
* lang: Add `anchor-debug` feature flag for logging ([#253](https://github.com/project-serum/anchor/pull/253)).
|
||||
* ts: Add support for u16 ([#255](https://github.com/project-serum/anchor/pull/255)).
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* client: Renames `RequestBuilder::new` to `RequestBuilder::from` ([#248](https://github.com/project-serum/anchor/pull/248)).
|
||||
* lang: Renames the generated `instruction::state::Ctor` struct to `instruction::state::New` ([#248](https://github.com/project-serum/anchor/pull/248)).
|
||||
|
||||
## [0.4.5] - 2021-04-29
|
||||
|
||||
* spl: Add serum DEX CPI client ([#224](https://github.com/project-serum/anchor/pull/224)).
|
||||
|
||||
## [0.4.4] - 2021-04-18
|
||||
|
||||
### Features
|
||||
|
||||
* lang: Allows one to specify multiple `with` targets when creating associated acconts ([#197](https://github.com/project-serum/anchor/pull/197)).
|
||||
* lang, ts: Add array support ([#202](https://github.com/project-serum/anchor/pull/202)).
|
||||
* lang: Zero copy deserialization for accounts ([#202](https://github.com/project-serum/anchor/pull/202), [#206](https://github.com/project-serum/anchor/pull/206)).
|
||||
* lang, spl, cli, client: Upgrade solana toolchain to 1.6.6 ([#210](https://github.com/project-serum/anchor/pull/210)).
|
||||
|
||||
## [0.4.3] - 2021-04-13
|
||||
|
||||
### Features
|
||||
|
||||
* lang: CPI clients for program state instructions ([#43](https://github.com/project-serum/anchor/pull/43)).
|
||||
* lang: Add `#[account(owner = <program>)]` constraint ([#178](https://github.com/project-serum/anchor/pull/178)).
|
||||
* lang, cli, ts: Add `#[account(associated = <target>)]` and `#[associated]` attributes for creating associated program accounts within programs. The TypeScript package can fetch these accounts with a new `<program>.account.<account-name>.associated` (and `associatedAddress`) method ([#186](https://github.com/project-serum/anchor/pull/186)).
|
||||
|
||||
### Fixes
|
||||
|
||||
* lang: Unused `#[account]`s are now parsed into the IDL correctly ([#177](https://github.com/project-serum/anchor/pull/177)).
|
||||
|
||||
## [0.4.2] - 2021-04-10
|
||||
|
||||
### Features
|
||||
|
||||
* cli: Fund Anchor.toml configured wallet when testing ([#164](https://github.com/project-serum/anchor/pull/164)).
|
||||
* spl: Add initialize_account instruction for spl tokens ([#166](https://github.com/project-serum/anchor/pull/166)).
|
||||
|
||||
## [0.4.1] - 2021-04-06
|
||||
|
||||
* cli: Version verifiable docker builder ([#145](https://github.com/project-serum/anchor/pull/145)).
|
||||
|
||||
## [0.4.0] - 2021-04-04
|
||||
|
||||
### Features
|
||||
|
||||
* cli: Specify test files to run ([#118](https://github.com/project-serum/anchor/pull/118)).
|
||||
* lang: Allow overriding the `#[state]` account's size ([#121](https://github.com/project-serum/anchor/pull/121)).
|
||||
* lang, client, ts: Add event emission and subscriptions ([#89](https://github.com/project-serum/anchor/pull/89)).
|
||||
* lang/account: Allow namespacing account discriminators ([#128](https://github.com/project-serum/anchor/pull/128)).
|
||||
* cli: TypeScript migrations ([#132](https://github.com/project-serum/anchor/pull/132)).
|
||||
* lang: Add `#[account(executable)]` attribute ([#140](https://github.com/project-serum/anchor/pull/140)).
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* client: Replace url str with `Cluster` struct when constructing clients ([#89](https://github.com/project-serum/anchor/pull/89)).
|
||||
* lang: Changes the account discriminator of `IdlAccount` to be namespaced by `"internal"` ([#128](https://github.com/project-serum/anchor/pull/128)).
|
||||
* lang, spl, cli: Upgrade solana toolchain to 1.6.3, a major version upgrade even though only the minor version is incremented. This allows for the removal of `-#![feature(proc_macro_hygiene)]`. ([#139](https://github.com/project-serum/anchor/pull/139)).
|
||||
|
||||
## [0.3.0] - 2021-03-12
|
||||
|
||||
### Features
|
||||
|
||||
* ts: Allow preloading instructions for state rpc transactions ([cf9c84](https://github.com/project-serum/anchor/commit/cf9c847e4144989b5bc1936149d171e90204777b)).
|
||||
* ts: Export sighash coder function ([734c75](https://github.com/project-serum/anchor/commit/734c751882f43beec7ea3f0f4d988b502e3f24e4)).
|
||||
* cli: Specify programs to embed into local validator genesis via Anchor.toml while testing ([b3803a](https://github.com/project-serum/anchor/commit/b3803aec03fbbae1a794c9aa6a789e6cb58fda99)).
|
||||
* cli: Allow skipping the creation of a local validator when testing against localnet ([#93](https://github.com/project-serum/anchor/pull/93)).
|
||||
* cli: Adds support for tests with Typescript ([#94](https://github.com/project-serum/anchor/pull/94)).
|
||||
* cli: Deterministic and verifiable builds ([#100](https://github.com/project-serum/anchor/pull/100)).
|
||||
* cli, lang: Add write buffers for IDL upgrades ([#107](https://github.com/project-serum/anchor/pull/107)).
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
* lang: Removes `IdlInstruction::Clear` ([#107](https://github.com/project-serum/anchor/pull/107)).
|
||||
|
||||
### Fixes
|
||||
|
||||
* cli: Propagates mocha test exit status on error ([79b791](https://github.com/project-serum/anchor/commit/79b791ffa85ffae5b6163fa853562aa568650f21)).
|
||||
|
||||
## [0.2.1] - 2021-02-11
|
||||
|
||||
### Features
|
||||
|
||||
* cli: Embed workspace programs into local validator genesis when testing ([733ec3](https://github.com/project-serum/anchor/commit/733ec300b0308e7d007873b0975585d836333fd4)).
|
||||
* cli: Stream program logs to `.anchor/program-logs` directory when testing ([ce5ca7](https://github.com/project-serum/anchor/commit/ce5ca7ddab6e0fd579deddcd02094b3f798bbe6a)).
|
||||
* spl: Add shared memory api [(d92cb1)](https://github.com/project-serum/anchor/commit/d92cb1516b78696d1257e41d0c5ac6821716300e).
|
||||
* lang/attribute/access-control: Allow specifying multiple modifier functions ([845df6](https://github.com/project-serum/anchor/commit/845df6d1960bb544fa0f2e3331ec79cc804edeb6)).
|
||||
* lang/syn: Allow state structs that don't have a ctor or impl block (just trait implementations) ([a78000](https://github.com/project-serum/anchor/commit/a7800026833d64579e5b19c90d724ecc20d2a455)).
|
||||
* ts: Add instruction method to state namespace ([627c27](https://github.com/project-serum/anchor/commit/627c275e9cdf3dafafcf44473ba8146cc7979d44)).
|
||||
* lang/syn, ts: Add support for u128 and i128 ([#83](https://github.com/project-serum/anchor/pull/83)).
|
||||
|
||||
## [0.2.0] - 2021-02-08
|
||||
|
||||
### Features
|
||||
|
||||
* lang: Adds the ability to create and use CPI program interfaces ([#66](https://github.com/project-serum/anchor/pull/66/files?file-filters%5B%5D=)).
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* lang, client, ts: Migrate from rust enum based method dispatch to a variant of sighash ([#64](https://github.com/project-serum/anchor/pull/64)).
|
||||
|
||||
## [0.1.0] - 2021-01-31
|
||||
|
||||
Initial release.
|
||||
|
||||
### Includes
|
||||
|
||||
* lang: `anchor-lang` crate providing a Rust eDSL for Solana.
|
||||
* lang/attribute/access-control: Internal attribute macro for function modifiers.
|
||||
* lang/attribute/account: Internal attribute macro for defining Anchor accounts.
|
||||
* lang/attribute/error: Internal attribute macro for defining Anchor program errors.
|
||||
* lang/attribute/program: Internal attribute macro for defining an Anchor program.
|
||||
* lang/attribute/state: Internal attribute macro for defining an Anchor program state struct.
|
||||
* lang/derive/accounts: Internal derive macro for defining deserialized account structs.
|
||||
* lang/syn: Internal crate for parsing the Anchor eDSL, generating code, and an IDL.
|
||||
* spl: `anchor-spl` crate providing CPI clients for Anchor programs.
|
||||
* client: `anchor-client` crate providing Rust clients for Anchor programs.
|
||||
* ts: `@project-serum/anchor` package for generating TypeScript clients.
|
||||
* cli: Command line interface for managing Anchor programs.
|
|
@ -1,3 +0,0 @@
|
|||
# Code of Conduct
|
||||
|
||||
The Anchor repository follows the Rust [Code of Conduct](https://www.rust-lang.org/conduct.html).
|
|
@ -1,41 +0,0 @@
|
|||
# Contributing to Anchor
|
||||
|
||||
Thank you for your interest in contributing to Anchor! All contributions are welcome no
|
||||
matter how big or small. This includes (but is not limited to) filing issues,
|
||||
adding documentation, fixing bugs, creating examples, and implementing features.
|
||||
|
||||
## Finding issues to work on
|
||||
|
||||
If you're looking to get started,
|
||||
check out [good first issues](https://github.com/project-serum/anchor/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
|
||||
or issues where [help is wanted](https://github.com/project-serum/anchor/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22).
|
||||
For simple documentation changes or typos, feel free to just open a pull request.
|
||||
|
||||
If you're considering larger changes or self motivated features, please file an issue
|
||||
and engage with the maintainers in [Discord](https://discord.gg/sxy4zxBckh).
|
||||
|
||||
## Choosing an issue
|
||||
|
||||
If you'd like to contribute, please claim an issue by commenting, forking, and
|
||||
opening a pull request, even if empty. This allows the maintainers to track who
|
||||
is working on what issue as to not overlap work.
|
||||
|
||||
## Issue Guidelines
|
||||
|
||||
Please follow these guidelines:
|
||||
|
||||
Before coding:
|
||||
- choose a branch name that describes the issue you're working on
|
||||
- enable [commit signing](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits)
|
||||
|
||||
While coding:
|
||||
- Submit a draft PR asap
|
||||
- Only change code directly relevant to your PR. Sometimes you might find some code that could really need some refactoring. However, if it's not relevant to your PR, do not touch it. File an issue instead. This allows the reviewer to focus on a single problem at a time.
|
||||
- If you write comments, do not exceed 80 chars per line. This allows contributors who work with multiple open windows to still read the comments without horizontally scrolling.
|
||||
- Write adversarial tests. For example, if you're adding a new account type, do not only write tests where the instruction succeeds. Also write tests that test whether the instruction fails, if a check inside the new type is violated.
|
||||
|
||||
After coding:
|
||||
- If you've moved code around, build the docs with `cargo doc --open` and adjust broken links
|
||||
- Adjust the cli templates if necessary
|
||||
- If you made a change to anchor's periphery (avm or cli), make a PR to the `anchor-book` repo if necessary
|
||||
- If you've added a new folder to the `tests` directory, add it to the [CI](./.github/workflows/tests.yaml).
|
File diff suppressed because it is too large
Load Diff
21
Cargo.toml
21
Cargo.toml
|
@ -1,21 +0,0 @@
|
|||
[profile.release]
|
||||
lto = true
|
||||
|
||||
[profile.release.package.anchor-cli]
|
||||
codegen-units = 1
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"cli",
|
||||
"client",
|
||||
"lang",
|
||||
"lang/attribute/*",
|
||||
"lang/derive/*",
|
||||
"lang/syn",
|
||||
"spl",
|
||||
]
|
||||
exclude = [
|
||||
"tests/swap/deps/serum-dex",
|
||||
"tests/cfo/deps/serum-dex",
|
||||
"tests/permissioned-markets/deps/serum-dex",
|
||||
]
|
202
LICENSE
202
LICENSE
|
@ -1,202 +0,0 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2020 Serum Foundation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
38
Makefile
38
Makefile
|
@ -1,38 +0,0 @@
|
|||
.PHONY: build-cli
|
||||
build-cli:
|
||||
cargo build -p anchor-cli --release
|
||||
cp target/release/anchor cli/npm-package/anchor
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
find . -type d -name .anchor -print0 | xargs -0 rm -rf
|
||||
find . -type d -name node_modules -print0 | xargs -0 rm -rf
|
||||
find . -type d -name target -print0 | xargs -0 rm -rf
|
||||
|
||||
.PHONY: publish
|
||||
publish:
|
||||
cd lang/syn/ && cargo publish && cd ../../
|
||||
sleep 25
|
||||
cd lang/derive/accounts/ && cargo publish && cd ../../../
|
||||
sleep 25
|
||||
cd lang/attribute/access-control/ && cargo publish && cd ../../../
|
||||
sleep 25
|
||||
cd lang/attribute/account/ && cargo publish && cd ../../../
|
||||
sleep 25
|
||||
cd lang/attribute/constant/ && cargo publish && cd ../../../
|
||||
sleep 25
|
||||
cd lang/attribute/error/ && cargo publish && cd ../../../
|
||||
sleep 25
|
||||
cd lang/attribute/interface/ && cargo publish && cd ../../../
|
||||
sleep 25
|
||||
cd lang/attribute/program/ && cargo publish && cd ../../..
|
||||
sleep 25
|
||||
cd lang/attribute/state/ && cargo publish && cd ../../../
|
||||
sleep 25
|
||||
cd lang/attribute/event/ && cargo publish && cd ../../../
|
||||
sleep 25
|
||||
cd lang/ && cargo publish && cd ../
|
||||
sleep 25
|
||||
cd spl/ && cargo publish && cd ../
|
||||
sleep 25
|
||||
cd client/ && cargo publish && cd ../
|
119
README.md
119
README.md
|
@ -1,119 +1,10 @@
|
|||
<div align="center">
|
||||
<img height="170x" src="https://media.discordapp.net/attachments/813444514949103658/890278520553603092/export.png?width=746&height=746" />
|
||||
# @project-serum/anchor
|
||||
|
||||
<h1>Anchor</h1>
|
||||
[![npm](https://img.shields.io/npm/v/@project-serum/anchor.svg?color=blue)](https://www.npmjs.com/package/@project-serum/anchor)
|
||||
[![Docs](https://img.shields.io/badge/docs-typedoc-blue)](https://project-serum.github.io/anchor/ts/index.html)
|
||||
|
||||
<p>
|
||||
<strong>Solana Sealevel Framework</strong>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a href="https://github.com/project-serum/anchor/actions"><img alt="Build Status" src="https://github.com/project-serum/anchor/actions/workflows/tests.yaml/badge.svg" /></a>
|
||||
<a href="https://project-serum.github.io/anchor/"><img alt="Tutorials" src="https://img.shields.io/badge/docs-tutorials-blueviolet" /></a>
|
||||
<a href="https://discord.gg/PDeRXyVURd"><img alt="Discord Chat" src="https://img.shields.io/discord/889577356681945098?color=blueviolet" /></a>
|
||||
<a href="https://opensource.org/licenses/Apache-2.0"><img alt="License" src="https://img.shields.io/github/license/project-serum/anchor?color=blueviolet" /></a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
Anchor is a framework for Solana's [Sealevel](https://medium.com/solana-labs/sealevel-parallel-processing-thousands-of-smart-contracts-d814b378192) runtime providing several convenient developer tools for writing smart contracts.
|
||||
|
||||
- Rust eDSL for writing Solana programs
|
||||
- [IDL](https://en.wikipedia.org/wiki/Interface_description_language) specification
|
||||
- TypeScript package for generating clients from IDL
|
||||
- CLI and workspace management for developing complete applications
|
||||
|
||||
If you're familiar with developing in Ethereum's [Solidity](https://docs.soliditylang.org/en/v0.7.4/), [Truffle](https://www.trufflesuite.com/), [web3.js](https://github.com/ethereum/web3.js), then the experience will be familiar. Although the DSL syntax and semantics are targeted at Solana, the high level flow of writing RPC request handlers, emitting an IDL, and generating clients from IDL is the same.
|
||||
|
||||
## Getting Started
|
||||
|
||||
For a quickstart guide and in depth tutorials, see the [anchor book](https://book.anchor-lang.com) and the older [documentation](https://project-serum.github.io/anchor/getting-started/introduction.html) that is being phased out.
|
||||
To jump straight to examples, go [here](https://github.com/project-serum/anchor/tree/master/examples). For the latest Rust and TypeScript API documentation, see [docs.rs](https://docs.rs/anchor-lang) and the [typedoc](https://project-serum.github.io/anchor/ts/index.html).
|
||||
|
||||
## Packages
|
||||
|
||||
| Package | Description | Version | Docs |
|
||||
| :-- | :-- | :--| :-- |
|
||||
| `anchor-lang` | Rust primitives for writing programs on Solana | [![Crates.io](https://img.shields.io/crates/v/anchor-lang?color=blue)](https://crates.io/crates/anchor-lang) | [![Docs.rs](https://docs.rs/anchor-lang/badge.svg)](https://docs.rs/anchor-lang) |
|
||||
| `anchor-spl` | CPI clients for SPL programs on Solana | ![crates](https://img.shields.io/crates/v/anchor-spl?color=blue) | [![Docs.rs](https://docs.rs/anchor-spl/badge.svg)](https://docs.rs/anchor-spl) |
|
||||
| `anchor-client` | Rust client for Anchor programs | ![crates](https://img.shields.io/crates/v/anchor-client?color=blue) | [![Docs.rs](https://docs.rs/anchor-client/badge.svg)](https://docs.rs/anchor-client) |
|
||||
| `@project-serum/anchor` | TypeScript client for Anchor programs | [![npm](https://img.shields.io/npm/v/@project-serum/anchor.svg?color=blue)](https://www.npmjs.com/package/@project-serum/anchor) | [![Docs](https://img.shields.io/badge/docs-typedoc-blue)](https://project-serum.github.io/anchor/ts/index.html) |
|
||||
| `@project-serum/anchor-cli` | CLI to support building and managing an Anchor workspace | [![npm](https://img.shields.io/npm/v/@project-serum/anchor-cli.svg?color=blue)](https://www.npmjs.com/package/@project-serum/anchor-cli) | [![Docs](https://img.shields.io/badge/docs-typedoc-blue)](https://project-serum.github.io/anchor/cli/commands.html) |
|
||||
TypeScript client for Anchor programs.
|
||||
|
||||
## Note
|
||||
|
||||
* **Anchor is in active development, so all APIs are subject to change.**
|
||||
* **This code is unaudited. Use at your own risk.**
|
||||
|
||||
## Examples
|
||||
|
||||
Here's a counter program, where only the designated `authority`
|
||||
can increment the count.
|
||||
|
||||
```rust
|
||||
use anchor_lang::prelude::*;
|
||||
|
||||
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
||||
|
||||
#[program]
|
||||
mod counter {
|
||||
use super::*;
|
||||
|
||||
pub fn initialize(ctx: Context<Initialize>, start: u64) -> Result<()> {
|
||||
let counter = &mut ctx.accounts.counter;
|
||||
counter.authority = *ctx.accounts.authority.key;
|
||||
counter.count = start;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn increment(ctx: Context<Increment>) -> Result<()> {
|
||||
let counter = &mut ctx.accounts.counter;
|
||||
counter.count += 1;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct Initialize<'info> {
|
||||
#[account(init, payer = authority, space = 48)]
|
||||
pub counter: Account<'info, Counter>,
|
||||
pub authority: Signer<'info>,
|
||||
pub system_program: Program<'info, System>,
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct Increment<'info> {
|
||||
#[account(mut, has_one = authority)]
|
||||
pub counter: Account<'info, Counter>,
|
||||
pub authority: Signer<'info>,
|
||||
}
|
||||
|
||||
#[account]
|
||||
pub struct Counter {
|
||||
pub authority: Pubkey,
|
||||
pub count: u64,
|
||||
}
|
||||
```
|
||||
|
||||
For more, see the [examples](https://github.com/project-serum/anchor/tree/master/examples)
|
||||
and [tests](https://github.com/project-serum/anchor/tree/master/tests) directories.
|
||||
|
||||
## License
|
||||
|
||||
Anchor is licensed under [Apache 2.0](./LICENSE).
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in Anchor by you, as defined in the Apache-2.0 license, shall be
|
||||
licensed as above, without any additional terms or conditions.
|
||||
|
||||
## Contribution
|
||||
|
||||
Thank you for your interest in contributing to Anchor!
|
||||
Please see the [CONTRIBUTING.md](./CONTRIBUTING.md) to learn how.
|
||||
|
||||
### Thanks ❤️
|
||||
|
||||
<div align="center">
|
||||
<a href="https://github.com/project-serum/anchor/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=project-serum/anchor" width="100%" />
|
||||
</a>
|
||||
</div>
|
||||
* `@project-serum/anchor` depends on node.js native modules. Therefore, webpack 5 will not work with current version. You will either need to rollback to webpack 4, or use a polyfill for each missing dependency.
|
File diff suppressed because it is too large
Load Diff
|
@ -1,28 +0,0 @@
|
|||
[package]
|
||||
name = "avm"
|
||||
version = "0.24.2"
|
||||
rust-version = "1.56"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "avm"
|
||||
path = "src/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "anchor"
|
||||
path = "src/anchor/main.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.0.13", features = [ "derive" ]}
|
||||
cfg-if = "1.0.0"
|
||||
anyhow = "1.0.32"
|
||||
dirs = "1.0.5"
|
||||
semver = "1.0.4"
|
||||
serde = { version = "1.0.136", features = [ "derive" ]}
|
||||
serde_json = "1.0.78"
|
||||
thiserror = "1.0.30"
|
||||
once_cell = { version = "1.8.0" }
|
||||
reqwest = { version = "0.11.9", features = ['blocking', 'json'] }
|
||||
tempfile = "3.3.0"
|
||||
|
||||
[workspace]
|
|
@ -1,24 +0,0 @@
|
|||
use std::{env, fs, process::Command};
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let args = env::args().skip(1).collect::<Vec<String>>();
|
||||
|
||||
let version = avm::current_version()
|
||||
.map_err(|_e| anyhow::anyhow!("Anchor version not set. Please run `avm use latest`."))?;
|
||||
|
||||
let binary_path = avm::version_binary_path(&version);
|
||||
if fs::metadata(&binary_path).is_err() {
|
||||
anyhow::bail!(
|
||||
"anchor-cli {} not installed. Please run `avm use {}`.",
|
||||
version,
|
||||
version
|
||||
);
|
||||
}
|
||||
Command::new(binary_path)
|
||||
.args(args)
|
||||
.spawn()?
|
||||
.wait_with_output()
|
||||
.expect("Failed to run anchor-cli");
|
||||
|
||||
Ok(())
|
||||
}
|
316
avm/src/lib.rs
316
avm/src/lib.rs
|
@ -1,316 +0,0 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use once_cell::sync::Lazy;
|
||||
use reqwest::header::USER_AGENT;
|
||||
use semver::Version;
|
||||
use serde::{de, Deserialize};
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Stdio;
|
||||
|
||||
/// Storage directory for AVM, ~/.avm
|
||||
pub static AVM_HOME: Lazy<PathBuf> = Lazy::new(|| {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(test)] {
|
||||
let dir = tempfile::tempdir().expect("Could not create temporary directory");
|
||||
dir.path().join(".avm")
|
||||
} else {
|
||||
let mut user_home = dirs::home_dir().expect("Could not find home directory");
|
||||
user_home.push(".avm");
|
||||
user_home
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/// Path to the current version file ~/.avm/.version
|
||||
pub fn current_version_file_path() -> PathBuf {
|
||||
let mut current_version_file_path = AVM_HOME.to_path_buf();
|
||||
current_version_file_path.push(".version");
|
||||
current_version_file_path
|
||||
}
|
||||
|
||||
/// Read the current version from the version file
|
||||
pub fn current_version() -> Result<Version> {
|
||||
let v = fs::read_to_string(current_version_file_path().as_path())
|
||||
.map_err(|e| anyhow!("Could not read version file: {}", e))?;
|
||||
Version::parse(v.trim_end_matches('\n').to_string().as_str())
|
||||
.map_err(|e| anyhow!("Could not parse version file: {}", e))
|
||||
}
|
||||
|
||||
/// Path to the binary for the given version
|
||||
pub fn version_binary_path(version: &Version) -> PathBuf {
|
||||
let mut version_path = AVM_HOME.join("bin");
|
||||
version_path.push(format!("anchor-{}", version));
|
||||
version_path
|
||||
}
|
||||
|
||||
/// Update the current version to a new version
|
||||
pub fn use_version(version: &Version) -> Result<()> {
|
||||
let installed_versions = read_installed_versions();
|
||||
// Make sure the requested version is installed
|
||||
if !installed_versions.contains(version) {
|
||||
if let Ok(current) = current_version() {
|
||||
println!(
|
||||
"Version {} is not installed, staying on version {}.",
|
||||
version, current
|
||||
);
|
||||
} else {
|
||||
println!("Version {} is not installed, no current version.", version);
|
||||
}
|
||||
|
||||
return Err(anyhow!(
|
||||
"You need to run 'avm install {}' to install it before using it.",
|
||||
version
|
||||
));
|
||||
}
|
||||
|
||||
let mut current_version_file = fs::File::create(current_version_file_path().as_path())?;
|
||||
current_version_file.write_all(version.to_string().as_bytes())?;
|
||||
println!("Now using anchor version {}.", current_version()?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Update to the latest version
|
||||
pub fn update() -> Result<()> {
|
||||
// Find last stable version
|
||||
let version = &get_latest_version();
|
||||
|
||||
install_version(version, false)
|
||||
}
|
||||
|
||||
/// Install a version of anchor-cli
|
||||
pub fn install_version(version: &Version, force: bool) -> Result<()> {
|
||||
// If version is already installed we ignore the request.
|
||||
let installed_versions = read_installed_versions();
|
||||
if installed_versions.contains(version) && !force {
|
||||
println!("Version {} is already installed", version);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let exit = std::process::Command::new("cargo")
|
||||
.args(&[
|
||||
"install",
|
||||
"--git",
|
||||
"https://github.com/project-serum/anchor",
|
||||
"--tag",
|
||||
&format!("v{}", &version),
|
||||
"anchor-cli",
|
||||
"--locked",
|
||||
"--root",
|
||||
AVM_HOME.to_str().unwrap(),
|
||||
])
|
||||
.stdout(Stdio::inherit())
|
||||
.stderr(Stdio::inherit())
|
||||
.output()
|
||||
.map_err(|e| {
|
||||
anyhow::format_err!("Cargo install for {} failed: {}", version, e.to_string())
|
||||
})?;
|
||||
if !exit.status.success() {
|
||||
return Err(anyhow!(
|
||||
"Failed to install {}, is it a valid version?",
|
||||
version
|
||||
));
|
||||
}
|
||||
fs::rename(
|
||||
&AVM_HOME.join("bin").join("anchor"),
|
||||
&AVM_HOME.join("bin").join(format!("anchor-{}", version)),
|
||||
)?;
|
||||
// If .version file is empty or not parseable, write the newly installed version to it
|
||||
if current_version().is_err() {
|
||||
let mut current_version_file = fs::File::create(current_version_file_path().as_path())?;
|
||||
current_version_file.write_all(version.to_string().as_bytes())?;
|
||||
}
|
||||
|
||||
use_version(version)
|
||||
}
|
||||
|
||||
/// Remove an installed version of anchor-cli
|
||||
pub fn uninstall_version(version: &Version) -> Result<()> {
|
||||
let version_path = AVM_HOME.join("bin").join(format!("anchor-{}", version));
|
||||
if !version_path.exists() {
|
||||
return Err(anyhow!("anchor-cli {} is not installed", version));
|
||||
}
|
||||
if version == ¤t_version().unwrap() {
|
||||
return Err(anyhow!("anchor-cli {} is currently in use", version));
|
||||
}
|
||||
fs::remove_file(version_path.as_path())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ensure the users home directory is setup with the paths required by AVM.
|
||||
pub fn ensure_paths() {
|
||||
let home_dir = AVM_HOME.to_path_buf();
|
||||
if !home_dir.as_path().exists() {
|
||||
fs::create_dir_all(home_dir.clone()).expect("Could not create .avm directory");
|
||||
}
|
||||
let bin_dir = home_dir.join("bin");
|
||||
if !bin_dir.as_path().exists() {
|
||||
fs::create_dir_all(bin_dir).expect("Could not create .avm/bin directory");
|
||||
}
|
||||
if !current_version_file_path().exists() {
|
||||
fs::File::create(current_version_file_path()).expect("Could not create .version file");
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve a list of installable versions of anchor-cli using the GitHub API and tags on the Anchor
|
||||
/// repository.
|
||||
pub fn fetch_versions() -> Vec<semver::Version> {
|
||||
#[derive(Deserialize)]
|
||||
struct Release {
|
||||
#[serde(rename = "name", deserialize_with = "version_deserializer")]
|
||||
version: semver::Version,
|
||||
}
|
||||
|
||||
fn version_deserializer<'de, D>(deserializer: D) -> Result<semver::Version, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
let s: &str = de::Deserialize::deserialize(deserializer)?;
|
||||
Version::parse(s.trim_start_matches('v')).map_err(de::Error::custom)
|
||||
}
|
||||
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let versions: Vec<Release> = client
|
||||
.get("https://api.github.com/repos/project-serum/anchor/tags")
|
||||
.header(USER_AGENT, "avm https://github.com/project-serum/anchor")
|
||||
.send()
|
||||
.unwrap()
|
||||
.json()
|
||||
.unwrap();
|
||||
versions.into_iter().map(|r| r.version).collect()
|
||||
}
|
||||
|
||||
/// Print available versions and flags indicating installed, current and latest
|
||||
pub fn list_versions() -> Result<()> {
|
||||
let installed_versions = read_installed_versions();
|
||||
|
||||
let mut available_versions = fetch_versions();
|
||||
// Reverse version list so latest versions are printed last
|
||||
available_versions.reverse();
|
||||
|
||||
available_versions.iter().enumerate().for_each(|(i, v)| {
|
||||
print!("{}", v);
|
||||
let mut flags = vec![];
|
||||
if i == available_versions.len() - 1 {
|
||||
flags.push("latest");
|
||||
}
|
||||
if installed_versions.contains(v) {
|
||||
flags.push("installed");
|
||||
}
|
||||
if current_version().is_ok() && current_version().unwrap() == v.clone() {
|
||||
flags.push("current");
|
||||
}
|
||||
if flags.is_empty() {
|
||||
println!();
|
||||
} else {
|
||||
println!("\t({})", flags.join(", "));
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_latest_version() -> semver::Version {
|
||||
let available_versions = fetch_versions();
|
||||
available_versions.first().unwrap().clone()
|
||||
}
|
||||
|
||||
/// Read the installed anchor-cli versions by reading the binaries in the AVM_HOME/bin directory.
|
||||
pub fn read_installed_versions() -> Vec<semver::Version> {
|
||||
let home_dir = AVM_HOME.to_path_buf();
|
||||
let mut versions = vec![];
|
||||
for file in fs::read_dir(&home_dir.join("bin")).unwrap() {
|
||||
let file_name = file.unwrap().file_name();
|
||||
// Match only things that look like anchor-*
|
||||
if file_name.to_str().unwrap().starts_with("anchor-") {
|
||||
let version = file_name
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.trim_start_matches("anchor-")
|
||||
.parse::<semver::Version>()
|
||||
.unwrap();
|
||||
versions.push(version);
|
||||
}
|
||||
}
|
||||
|
||||
versions
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::*;
|
||||
use semver::Version;
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
|
||||
#[test]
|
||||
fn test_ensure_paths() {
|
||||
ensure_paths();
|
||||
assert!(AVM_HOME.exists());
|
||||
let bin_dir = AVM_HOME.join("bin");
|
||||
assert!(bin_dir.exists());
|
||||
let current_version_file = AVM_HOME.join(".version");
|
||||
assert!(current_version_file.exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_current_version_file_path() {
|
||||
ensure_paths();
|
||||
assert!(current_version_file_path().exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_version_binary_path() {
|
||||
assert!(
|
||||
version_binary_path(&Version::parse("0.18.2").unwrap())
|
||||
== AVM_HOME.join("bin/anchor-0.18.2")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_current_version() {
|
||||
ensure_paths();
|
||||
let mut current_version_file =
|
||||
fs::File::create(current_version_file_path().as_path()).unwrap();
|
||||
current_version_file.write_all("0.18.2".as_bytes()).unwrap();
|
||||
// Sync the file to disk before the read in current_version() to
|
||||
// mitigate the read not seeing the written version bytes.
|
||||
current_version_file.sync_all().unwrap();
|
||||
assert!(current_version().unwrap() == Version::parse("0.18.2").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "anchor-cli 0.18.1 is not installed")]
|
||||
fn test_uninstall_non_installed_version() {
|
||||
uninstall_version(&Version::parse("0.18.1").unwrap()).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "anchor-cli 0.18.2 is currently in use")]
|
||||
fn test_uninstalled_in_use_version() {
|
||||
ensure_paths();
|
||||
let version = Version::parse("0.18.2").unwrap();
|
||||
let mut current_version_file =
|
||||
fs::File::create(current_version_file_path().as_path()).unwrap();
|
||||
current_version_file.write_all("0.18.2".as_bytes()).unwrap();
|
||||
// Sync the file to disk before the read in current_version() to
|
||||
// mitigate the read not seeing the written version bytes.
|
||||
current_version_file.sync_all().unwrap();
|
||||
// Create a fake binary for anchor-0.18.2 in the bin directory
|
||||
fs::File::create(version_binary_path(&version)).unwrap();
|
||||
uninstall_version(&version).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_installed_versions() {
|
||||
ensure_paths();
|
||||
let version = Version::parse("0.18.2").unwrap();
|
||||
// Create a fake binary for anchor-0.18.2 in the bin directory
|
||||
fs::File::create(version_binary_path(&version)).unwrap();
|
||||
let expected = vec![version];
|
||||
assert!(read_installed_versions() == expected);
|
||||
// Should ignore this file because its not anchor- prefixed
|
||||
fs::File::create(AVM_HOME.join("bin").join("garbage").as_path()).unwrap();
|
||||
assert!(read_installed_versions() == expected);
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
use anyhow::{Error, Result};
|
||||
use clap::{Parser, Subcommand};
|
||||
use semver::Version;
|
||||
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
#[derive(Parser)]
|
||||
#[clap(name = "avm", about = "Anchor version manager", version)]
|
||||
pub struct Cli {
|
||||
#[clap(subcommand)]
|
||||
command: Commands,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum Commands {
|
||||
#[clap(about = "Use a specific version of Anchor")]
|
||||
Use {
|
||||
#[clap(parse(try_from_str = parse_version))]
|
||||
version: Version,
|
||||
},
|
||||
#[clap(about = "Install a version of Anchor")]
|
||||
Install {
|
||||
#[clap(parse(try_from_str = parse_version))]
|
||||
version: Version,
|
||||
#[clap(long)]
|
||||
/// Flag to force installation even if the version
|
||||
/// is already installed
|
||||
force: bool,
|
||||
},
|
||||
#[clap(about = "Uninstall a version of Anchor")]
|
||||
Uninstall {
|
||||
#[clap(parse(try_from_str = parse_version))]
|
||||
version: Version,
|
||||
},
|
||||
#[clap(about = "List available versions of Anchor")]
|
||||
List {},
|
||||
#[clap(about = "Update to the latest Anchor version")]
|
||||
Update {},
|
||||
}
|
||||
|
||||
// If `latest` is passed use the latest available version.
|
||||
fn parse_version(version: &str) -> Result<Version, Error> {
|
||||
if version == "latest" {
|
||||
Ok(avm::get_latest_version())
|
||||
} else {
|
||||
Version::parse(version).map_err(|e| anyhow::anyhow!(e))
|
||||
}
|
||||
}
|
||||
pub fn entry(opts: Cli) -> Result<()> {
|
||||
match opts.command {
|
||||
Commands::Use { version } => avm::use_version(&version),
|
||||
Commands::Install { version, force } => avm::install_version(&version, force),
|
||||
Commands::Uninstall { version } => avm::uninstall_version(&version),
|
||||
Commands::List {} => avm::list_versions(),
|
||||
Commands::Update {} => avm::update(),
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Make sure the user's home directory is setup with the paths required by AVM.
|
||||
avm::ensure_paths();
|
||||
|
||||
let opt = Cli::parse();
|
||||
entry(opt)
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
[package]
|
||||
name = "anchor-cli"
|
||||
version = "0.24.2"
|
||||
authors = ["armaniferrante <armaniferrante@gmail.com>"]
|
||||
rust-version = "1.56"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "anchor"
|
||||
path = "src/bin/main.rs"
|
||||
|
||||
[features]
|
||||
dev = []
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.0.13", features = ["derive"] }
|
||||
anyhow = "1.0.32"
|
||||
syn = { version = "1.0.60", features = ["full", "extra-traits"] }
|
||||
anchor-lang = { path = "../lang" }
|
||||
anchor-client = { path = "../client" }
|
||||
anchor-syn = { path = "../lang/syn", features = ["idl", "init-if-needed"] }
|
||||
serde_json = "1.0"
|
||||
shellexpand = "2.1.0"
|
||||
toml = "0.5.8"
|
||||
semver = "1.0.4"
|
||||
serde = { version = "1.0.122", features = ["derive"] }
|
||||
solana-sdk = "~1.9.13"
|
||||
solana-program = "~1.9.13"
|
||||
solana-client = "~1.9.13"
|
||||
solana-cli-config = "~1.9.13"
|
||||
solana-faucet = "~1.9.13"
|
||||
dirs = "3.0"
|
||||
heck = "0.3.1"
|
||||
flate2 = "1.0.19"
|
||||
rand = "0.7.3"
|
||||
tar = "0.4.35"
|
||||
reqwest = { version = "0.11.4", features = ["multipart", "blocking"] }
|
||||
tokio = "1.0"
|
||||
pathdiff = "0.2.0"
|
||||
cargo_toml = "0.9.2"
|
||||
walkdir = "2.3.2"
|
||||
chrono = "0.4.19"
|
||||
portpicker = "0.1.1"
|
|
@ -1,97 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
const fs = require("fs");
|
||||
const { spawn, spawnSync } = require("child_process");
|
||||
const path = require("path");
|
||||
const { arch, platform } = require("os");
|
||||
const { version } = require("./package.json");
|
||||
|
||||
const PACKAGE_VERSION = `anchor-cli ${version}`;
|
||||
const PACKAGE_ANCHOR_PATH = path.join(__dirname, "anchor");
|
||||
|
||||
function getBinaryVersion(location) {
|
||||
const result = spawnSync(location, ["--version"]);
|
||||
const error =
|
||||
(result.error && result.error.toString()) ||
|
||||
(result.stderr.length > 0 && result.stderr.toString().trim()) ||
|
||||
null;
|
||||
return [error, result.stdout && result.stdout.toString().trim()];
|
||||
}
|
||||
|
||||
function runAnchor(location) {
|
||||
const args = process.argv.slice(2);
|
||||
const anchor = spawn(location, args, { stdio: "inherit" });
|
||||
anchor.on("exit", (code, signal) => {
|
||||
process.on("exit", () => {
|
||||
if (signal) {
|
||||
process.kill(process.pid, signal);
|
||||
} else {
|
||||
process.exit(code);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
process.on("SIGINT", function () {
|
||||
anchor.kill("SIGINT");
|
||||
anchor.kill("SIGTERM");
|
||||
});
|
||||
}
|
||||
|
||||
function tryPackageAnchor() {
|
||||
if (arch() !== "x64" || platform() !== "linux") {
|
||||
console.error(`Only x86_64 / Linux distributed in NPM package right now.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const [error, binaryVersion] = getBinaryVersion(PACKAGE_ANCHOR_PATH);
|
||||
if (error !== null) {
|
||||
console.error(`Failed to get version of local binary: ${error}`);
|
||||
return false;
|
||||
}
|
||||
if (binaryVersion !== PACKAGE_VERSION) {
|
||||
console.error(
|
||||
`Package binary version is not correct. Expected "${PACKAGE_VERSION}", found "${binaryVersion}".`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
runAnchor(PACKAGE_ANCHOR_PATH);
|
||||
return true;
|
||||
}
|
||||
|
||||
function trySystemAnchor() {
|
||||
console.error("Trying globally installed anchor.");
|
||||
|
||||
const absolutePath = process.env.PATH.split(":")
|
||||
.filter((dir) => dir !== path.dirname(process.argv[1]))
|
||||
.find((dir) => {
|
||||
try {
|
||||
fs.accessSync(`${dir}/anchor`, fs.constants.X_OK);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!absolutePath) {
|
||||
console.error(`Could not find globally installed anchor, install with cargo.`);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
const absoluteBinaryPath = `${absolutePath}/anchor`;
|
||||
|
||||
const [error, binaryVersion] = getBinaryVersion(absoluteBinaryPath);
|
||||
if (error !== null) {
|
||||
console.error(`Failed to get version of global binary: ${error}`);
|
||||
return;
|
||||
}
|
||||
if (binaryVersion !== PACKAGE_VERSION) {
|
||||
console.error(
|
||||
`Globally installed anchor version is not correct. Expected "${PACKAGE_VERSION}", found "${binaryVersion}".`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
runAnchor(absoluteBinaryPath);
|
||||
}
|
||||
|
||||
tryPackageAnchor() || trySystemAnchor();
|
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"name": "@project-serum/anchor-cli",
|
||||
"version": "0.24.2",
|
||||
"description": "Anchor CLI tool",
|
||||
"homepage": "https://github.com/project-serum/anchor#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/project-serum/anchor/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/project-serum/anchor.git"
|
||||
},
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"bin": {
|
||||
"anchor": "./anchor.js"
|
||||
},
|
||||
"scripts": {
|
||||
"prepack": "[ \"$(uname -op)\" != \"x86_64 GNU/Linux\" ] && (echo Can be packed only on x86_64 GNU/Linux && exit 1) || ([ \"$(./anchor --version)\" != \"anchor-cli $(jq -r .version package.json)\" ] && (echo Check anchor binary version && exit 2) || exit 0)"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
use anchor_cli::Opts;
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
anchor_cli::entry(Opts::parse())
|
||||
}
|
1116
cli/src/config.rs
1116
cli/src/config.rs
File diff suppressed because it is too large
Load Diff
3102
cli/src/lib.rs
3102
cli/src/lib.rs
File diff suppressed because it is too large
Load Diff
|
@ -1,36 +0,0 @@
|
|||
#[macro_export]
|
||||
macro_rules! home_path {
|
||||
($my_struct:ident, $path:literal) => {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct $my_struct(String);
|
||||
|
||||
impl Default for $my_struct {
|
||||
fn default() -> Self {
|
||||
match dirs::home_dir() {
|
||||
None => {
|
||||
println!("$HOME doesn't exist. This probably won't do what you want.");
|
||||
$my_struct(".".to_string())
|
||||
}
|
||||
Some(mut path) => {
|
||||
path.push($path);
|
||||
$my_struct(path.as_path().display().to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for $my_struct {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for $my_struct {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self(s.to_string()))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,376 +0,0 @@
|
|||
use crate::config::ProgramWorkspace;
|
||||
use crate::VERSION;
|
||||
use anchor_syn::idl::Idl;
|
||||
use anyhow::Result;
|
||||
use heck::{CamelCase, MixedCase, SnakeCase};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
|
||||
pub fn default_program_id() -> Pubkey {
|
||||
"Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
|
||||
.parse()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn virtual_manifest() -> &'static str {
|
||||
r#"[workspace]
|
||||
members = [
|
||||
"programs/*"
|
||||
]
|
||||
"#
|
||||
}
|
||||
|
||||
pub fn credentials(token: &str) -> String {
|
||||
format!(
|
||||
r#"[registry]
|
||||
token = "{}"
|
||||
"#,
|
||||
token
|
||||
)
|
||||
}
|
||||
|
||||
pub fn idl_ts(idl: &Idl) -> Result<String> {
|
||||
let mut idl = idl.clone();
|
||||
for acc in idl.accounts.iter_mut() {
|
||||
acc.name = acc.name.to_mixed_case();
|
||||
}
|
||||
let idl_json = serde_json::to_string_pretty(&idl)?;
|
||||
Ok(format!(
|
||||
r#"export type {} = {};
|
||||
|
||||
export const IDL: {} = {};
|
||||
"#,
|
||||
idl.name.to_camel_case(),
|
||||
idl_json,
|
||||
idl.name.to_camel_case(),
|
||||
idl_json
|
||||
))
|
||||
}
|
||||
|
||||
pub fn cargo_toml(name: &str) -> String {
|
||||
format!(
|
||||
r#"[package]
|
||||
name = "{0}"
|
||||
version = "0.1.0"
|
||||
description = "Created with Anchor"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
name = "{1}"
|
||||
|
||||
[features]
|
||||
no-entrypoint = []
|
||||
no-idl = []
|
||||
no-log-ix-name = []
|
||||
cpi = ["no-entrypoint"]
|
||||
default = []
|
||||
|
||||
[profile.release]
|
||||
overflow-checks = true
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = "{2}"
|
||||
"#,
|
||||
name,
|
||||
name.to_snake_case(),
|
||||
VERSION,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn deploy_js_script_host(cluster_url: &str, script_path: &str) -> String {
|
||||
format!(
|
||||
r#"
|
||||
const anchor = require('@project-serum/anchor');
|
||||
|
||||
// Deploy script defined by the user.
|
||||
const userScript = require("{0}");
|
||||
|
||||
async function main() {{
|
||||
const url = "{1}";
|
||||
const preflightCommitment = 'recent';
|
||||
const connection = new anchor.web3.Connection(url, preflightCommitment);
|
||||
const wallet = anchor.Wallet.local();
|
||||
|
||||
const provider = new anchor.AnchorProvider(connection, wallet, {{
|
||||
preflightCommitment,
|
||||
commitment: 'recent',
|
||||
}});
|
||||
|
||||
// Run the user's deploy script.
|
||||
userScript(provider);
|
||||
}}
|
||||
main();
|
||||
"#,
|
||||
script_path, cluster_url,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn deploy_ts_script_host(cluster_url: &str, script_path: &str) -> String {
|
||||
format!(
|
||||
r#"import * as anchor from '@project-serum/anchor';
|
||||
|
||||
// Deploy script defined by the user.
|
||||
const userScript = require("{0}");
|
||||
|
||||
async function main() {{
|
||||
const url = "{1}";
|
||||
const preflightCommitment = 'recent';
|
||||
const connection = new anchor.web3.Connection(url, preflightCommitment);
|
||||
const wallet = anchor.Wallet.local();
|
||||
|
||||
const provider = new anchor.AnchorProvider(connection, wallet, {{
|
||||
preflightCommitment,
|
||||
commitment: 'recent',
|
||||
}});
|
||||
|
||||
// Run the user's deploy script.
|
||||
userScript(provider);
|
||||
}}
|
||||
main();
|
||||
"#,
|
||||
script_path, cluster_url,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn deploy_script() -> &'static str {
|
||||
r#"// Migrations are an early feature. Currently, they're nothing more than this
|
||||
// single deploy script that's invoked from the CLI, injecting a provider
|
||||
// configured from the workspace's Anchor.toml.
|
||||
|
||||
const anchor = require("@project-serum/anchor");
|
||||
|
||||
module.exports = async function (provider) {
|
||||
// Configure client to use the provider.
|
||||
anchor.setProvider(provider);
|
||||
|
||||
// Add your deploy script here.
|
||||
};
|
||||
"#
|
||||
}
|
||||
|
||||
pub fn ts_deploy_script() -> &'static str {
|
||||
r#"// Migrations are an early feature. Currently, they're nothing more than this
|
||||
// single deploy script that's invoked from the CLI, injecting a provider
|
||||
// configured from the workspace's Anchor.toml.
|
||||
|
||||
const anchor = require("@project-serum/anchor");
|
||||
|
||||
module.exports = async function (provider) {
|
||||
// Configure client to use the provider.
|
||||
anchor.setProvider(provider);
|
||||
|
||||
// Add your deploy script here.
|
||||
};
|
||||
"#
|
||||
}
|
||||
|
||||
pub fn xargo_toml() -> &'static str {
|
||||
r#"[target.bpfel-unknown-unknown.dependencies.std]
|
||||
features = []
|
||||
"#
|
||||
}
|
||||
|
||||
pub fn lib_rs(name: &str) -> String {
|
||||
format!(
|
||||
r#"use anchor_lang::prelude::*;
|
||||
|
||||
declare_id!("{}");
|
||||
|
||||
#[program]
|
||||
pub mod {} {{
|
||||
use super::*;
|
||||
|
||||
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {{
|
||||
Ok(())
|
||||
}}
|
||||
}}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct Initialize {{}}
|
||||
"#,
|
||||
default_program_id(),
|
||||
name.to_snake_case(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn mocha(name: &str) -> String {
|
||||
format!(
|
||||
r#"const anchor = require("@project-serum/anchor");
|
||||
|
||||
describe("{}", () => {{
|
||||
// Configure the client to use the local cluster.
|
||||
anchor.setProvider(anchor.AnchorProvider.env());
|
||||
|
||||
it("Is initialized!", async () => {{
|
||||
// Add your test here.
|
||||
const program = anchor.workspace.{};
|
||||
const tx = await program.methods.initialize().rpc();
|
||||
console.log("Your transaction signature", tx);
|
||||
}});
|
||||
}});
|
||||
"#,
|
||||
name,
|
||||
name.to_camel_case(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn package_json() -> String {
|
||||
format!(
|
||||
r#"{{
|
||||
"scripts": {{
|
||||
"lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
|
||||
"lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
|
||||
}},
|
||||
"dependencies": {{
|
||||
"@project-serum/anchor": "^{0}"
|
||||
}},
|
||||
"devDependencies": {{
|
||||
"chai": "^4.3.4",
|
||||
"mocha": "^9.0.3",
|
||||
"prettier": "^2.6.2"
|
||||
}}
|
||||
}}
|
||||
"#,
|
||||
VERSION
|
||||
)
|
||||
}
|
||||
|
||||
pub fn ts_package_json() -> String {
|
||||
format!(
|
||||
r#"{{
|
||||
"scripts": {{
|
||||
"lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
|
||||
"lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
|
||||
}},
|
||||
"dependencies": {{
|
||||
"@project-serum/anchor": "^{0}"
|
||||
}},
|
||||
"devDependencies": {{
|
||||
"chai": "^4.3.4",
|
||||
"mocha": "^9.0.3",
|
||||
"ts-mocha": "^8.0.0",
|
||||
"@types/bn.js": "^5.1.0",
|
||||
"@types/chai": "^4.3.0",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"typescript": "^4.3.5",
|
||||
"prettier": "^2.6.2"
|
||||
}}
|
||||
}}
|
||||
"#,
|
||||
VERSION
|
||||
)
|
||||
}
|
||||
|
||||
pub fn ts_mocha(name: &str) -> String {
|
||||
format!(
|
||||
r#"import * as anchor from "@project-serum/anchor";
|
||||
import {{ Program }} from "@project-serum/anchor";
|
||||
import {{ {} }} from "../target/types/{}";
|
||||
|
||||
describe("{}", () => {{
|
||||
// Configure the client to use the local cluster.
|
||||
anchor.setProvider(anchor.AnchorProvider.env());
|
||||
|
||||
const program = anchor.workspace.{} as Program<{}>;
|
||||
|
||||
it("Is initialized!", async () => {{
|
||||
// Add your test here.
|
||||
const tx = await program.methods.initialize().rpc();
|
||||
console.log("Your transaction signature", tx);
|
||||
}});
|
||||
}});
|
||||
"#,
|
||||
name.to_camel_case(),
|
||||
name.to_snake_case(),
|
||||
name,
|
||||
name.to_camel_case(),
|
||||
name.to_camel_case(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn ts_config() -> &'static str {
|
||||
r#"{
|
||||
"compilerOptions": {
|
||||
"types": ["mocha", "chai"],
|
||||
"typeRoots": ["./node_modules/@types"],
|
||||
"lib": ["es2015"],
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
||||
"#
|
||||
}
|
||||
|
||||
pub fn git_ignore() -> &'static str {
|
||||
r#"
|
||||
.anchor
|
||||
.DS_Store
|
||||
target
|
||||
**/*.rs.bk
|
||||
node_modules
|
||||
test-ledger
|
||||
"#
|
||||
}
|
||||
|
||||
pub fn prettier_ignore() -> &'static str {
|
||||
r#"
|
||||
.anchor
|
||||
.DS_Store
|
||||
target
|
||||
node_modules
|
||||
dist
|
||||
build
|
||||
test-ledger
|
||||
"#
|
||||
}
|
||||
|
||||
pub fn node_shell(
|
||||
cluster_url: &str,
|
||||
wallet_path: &str,
|
||||
programs: Vec<ProgramWorkspace>,
|
||||
) -> Result<String> {
|
||||
let mut eval_string = format!(
|
||||
r#"
|
||||
const anchor = require('@project-serum/anchor');
|
||||
const web3 = anchor.web3;
|
||||
const PublicKey = anchor.web3.PublicKey;
|
||||
const Keypair = anchor.web3.Keypair;
|
||||
|
||||
const __wallet = new anchor.Wallet(
|
||||
Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(
|
||||
require('fs').readFileSync(
|
||||
"{}",
|
||||
{{
|
||||
encoding: "utf-8",
|
||||
}},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
const __connection = new web3.Connection("{}", "processed");
|
||||
const provider = new anchor.AnchorProvider(__connection, __wallet, {{
|
||||
commitment: "processed",
|
||||
preflightcommitment: "processed",
|
||||
}});
|
||||
anchor.setProvider(provider);
|
||||
"#,
|
||||
wallet_path, cluster_url,
|
||||
);
|
||||
|
||||
for program in programs {
|
||||
eval_string.push_str(&format!(
|
||||
r#"
|
||||
anchor.workspace.{} = new anchor.Program({}, new PublicKey("{}"), provider);
|
||||
"#,
|
||||
program.name.to_camel_case(),
|
||||
serde_json::to_string(&program.idl)?,
|
||||
program.program_id
|
||||
));
|
||||
}
|
||||
|
||||
Ok(eval_string)
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
[package]
|
||||
name = "anchor-client"
|
||||
version = "0.24.2"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
rust-version = "1.56"
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
description = "Rust client for Anchor programs"
|
||||
|
||||
[features]
|
||||
debug = []
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = { path = "../lang", version = "0.24.2" }
|
||||
anyhow = "1.0.32"
|
||||
regex = "1.4.5"
|
||||
serde = { version = "1.0.122", features = ["derive"] }
|
||||
solana-client = "~1.9.13"
|
||||
solana-sdk = "~1.9.13"
|
||||
solana-account-decoder = "~1.9.13"
|
||||
thiserror = "1.0.20"
|
||||
url = "2.2.2"
|
|
@ -1,20 +0,0 @@
|
|||
[package]
|
||||
name = "example"
|
||||
version = "0.1.0"
|
||||
authors = ["Armani Ferrante <armaniferrante@gmail.com>"]
|
||||
rust-version = "1.56"
|
||||
edition = "2021"
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
anchor-client = { path = "../", features = ["debug"] }
|
||||
basic-2 = { path = "../../examples/tutorial/basic-2/programs/basic-2", features = ["no-entrypoint"] }
|
||||
basic-4 = { path = "../../examples/tutorial/basic-4/programs/basic-4", features = ["no-entrypoint"] }
|
||||
composite = { path = "../../tests/composite/programs/composite", features = ["no-entrypoint"] }
|
||||
events = { path = "../../tests/events/programs/events", features = ["no-entrypoint"] }
|
||||
shellexpand = "2.1.0"
|
||||
anyhow = "1.0.32"
|
||||
rand = "0.7.3"
|
||||
clap = { version = "3.0.0-rc.0", features = ["derive"] }
|
||||
solana-sdk = "~1.9.13"
|
|
@ -1,66 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# A script to run the example as an integration test. It starts up a localnet
|
||||
# and executes the current directory's rust binary.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# ./run.sh
|
||||
#
|
||||
# Run this script from within the `example/` directory in which it is located.
|
||||
# The anchor cli must be installed.
|
||||
#
|
||||
# cargo install --git https://github.com/project-serum/anchor anchor-cli --locked
|
||||
#
|
||||
################################################################################
|
||||
|
||||
set -euox pipefail
|
||||
|
||||
main() {
|
||||
#
|
||||
# Build programs.
|
||||
#
|
||||
local composite_pid="EHthziFziNoac9LBGxEaVN47Y3uUiRoXvqAiR6oes4iU"
|
||||
local basic_2_pid="Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
|
||||
local basic_4_pid="CwrqeMj2U8tFr1Rhkgwc84tpAsqbt9pTt2a4taoTADPr"
|
||||
local events_pid="2dhGsWUzy5YKUsjZdLHLmkNpUDAXkNa9MYWsPc4Ziqzy"
|
||||
|
||||
#
|
||||
# Bootup validator.
|
||||
#
|
||||
solana-test-validator -r \
|
||||
--bpf-program $composite_pid ../../tests/composite/target/deploy/composite.so \
|
||||
--bpf-program $basic_2_pid ../../examples/tutorial/basic-2/target/deploy/basic_2.so \
|
||||
--bpf-program $basic_4_pid ../../examples/tutorial/basic-4/target/deploy/basic_4.so \
|
||||
--bpf-program $events_pid ../../tests/events/target/deploy/events.so \
|
||||
> test-validator.log &
|
||||
sleep 5
|
||||
|
||||
#
|
||||
# Run Test.
|
||||
#
|
||||
cargo run -- --composite-pid $composite_pid --basic-2-pid $basic_2_pid --basic-4-pid $basic_4_pid --events-pid $events_pid
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
pkill -P $$ || true
|
||||
wait || true
|
||||
}
|
||||
|
||||
trap_add() {
|
||||
trap_add_cmd=$1; shift || fatal "${FUNCNAME} usage error"
|
||||
for trap_add_name in "$@"; do
|
||||
trap -- "$(
|
||||
extract_trap_cmd() { printf '%s\n' "${3:-}"; }
|
||||
eval "extract_trap_cmd $(trap -p "${trap_add_name}")"
|
||||
printf '%s\n' "${trap_add_cmd}"
|
||||
)" "${trap_add_name}" \
|
||||
|| fatal "unable to add to trap ${trap_add_name}"
|
||||
done
|
||||
}
|
||||
|
||||
declare -f -t trap_add
|
||||
trap_add 'cleanup' EXIT
|
||||
main
|
|
@ -1,229 +0,0 @@
|
|||
use anchor_client::solana_sdk::commitment_config::CommitmentConfig;
|
||||
use anchor_client::solana_sdk::pubkey::Pubkey;
|
||||
use anchor_client::solana_sdk::signature::read_keypair_file;
|
||||
use anchor_client::solana_sdk::signature::{Keypair, Signer};
|
||||
use anchor_client::solana_sdk::system_instruction;
|
||||
use anchor_client::{Client, Cluster, EventContext};
|
||||
use anyhow::Result;
|
||||
use solana_sdk::system_program;
|
||||
// The `accounts` and `instructions` modules are generated by the framework.
|
||||
use basic_2::accounts as basic_2_accounts;
|
||||
use basic_2::instruction as basic_2_instruction;
|
||||
use basic_2::Counter;
|
||||
use events::instruction as events_instruction;
|
||||
use events::MyEvent;
|
||||
// The `accounts` and `instructions` modules are generated by the framework.
|
||||
use basic_4::accounts as basic_4_accounts;
|
||||
use basic_4::basic_4::Counter as CounterState;
|
||||
use basic_4::instruction as basic_4_instruction;
|
||||
use clap::Parser;
|
||||
// The `accounts` and `instructions` modules are generated by the framework.
|
||||
use composite::accounts::{Bar, CompositeUpdate, Foo, Initialize};
|
||||
use composite::instruction as composite_instruction;
|
||||
use composite::{DummyA, DummyB};
|
||||
use rand::rngs::OsRng;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct Opts {
|
||||
#[clap(long)]
|
||||
composite_pid: Pubkey,
|
||||
#[clap(long)]
|
||||
basic_2_pid: Pubkey,
|
||||
#[clap(long)]
|
||||
basic_4_pid: Pubkey,
|
||||
#[clap(long)]
|
||||
events_pid: Pubkey,
|
||||
}
|
||||
|
||||
// This example assumes a local validator is running with the programs
|
||||
// deployed at the addresses given by the CLI args.
|
||||
fn main() -> Result<()> {
|
||||
println!("Starting test...");
|
||||
let opts = Opts::parse();
|
||||
|
||||
// Wallet and cluster params.
|
||||
let payer = read_keypair_file(&*shellexpand::tilde("~/.config/solana/id.json"))
|
||||
.expect("Example requires a keypair file");
|
||||
let url = Cluster::Custom(
|
||||
"http://localhost:8899".to_string(),
|
||||
"ws://127.0.0.1:8900".to_string(),
|
||||
);
|
||||
|
||||
// Client.
|
||||
let client = Client::new_with_options(url, Rc::new(payer), CommitmentConfig::processed());
|
||||
|
||||
// Run tests.
|
||||
composite(&client, opts.composite_pid)?;
|
||||
basic_2(&client, opts.basic_2_pid)?;
|
||||
basic_4(&client, opts.basic_4_pid)?;
|
||||
events(&client, opts.events_pid)?;
|
||||
|
||||
// Success.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Runs a client for examples/tutorial/composite.
|
||||
//
|
||||
// Make sure to run a localnet with the program deploy to run this example.
|
||||
fn composite(client: &Client, pid: Pubkey) -> Result<()> {
|
||||
// Program client.
|
||||
let program = client.program(pid);
|
||||
|
||||
// `Initialize` parameters.
|
||||
let dummy_a = Keypair::generate(&mut OsRng);
|
||||
let dummy_b = Keypair::generate(&mut OsRng);
|
||||
|
||||
// Build and send a transaction.
|
||||
program
|
||||
.request()
|
||||
.instruction(system_instruction::create_account(
|
||||
&program.payer(),
|
||||
&dummy_a.pubkey(),
|
||||
program.rpc().get_minimum_balance_for_rent_exemption(500)?,
|
||||
500,
|
||||
&program.id(),
|
||||
))
|
||||
.instruction(system_instruction::create_account(
|
||||
&program.payer(),
|
||||
&dummy_b.pubkey(),
|
||||
program.rpc().get_minimum_balance_for_rent_exemption(500)?,
|
||||
500,
|
||||
&program.id(),
|
||||
))
|
||||
.signer(&dummy_a)
|
||||
.signer(&dummy_b)
|
||||
.accounts(Initialize {
|
||||
dummy_a: dummy_a.pubkey(),
|
||||
dummy_b: dummy_b.pubkey(),
|
||||
})
|
||||
.args(composite_instruction::Initialize)
|
||||
.send()?;
|
||||
|
||||
// Assert the transaction worked.
|
||||
let dummy_a_account: DummyA = program.account(dummy_a.pubkey())?;
|
||||
let dummy_b_account: DummyB = program.account(dummy_b.pubkey())?;
|
||||
assert_eq!(dummy_a_account.data, 0);
|
||||
assert_eq!(dummy_b_account.data, 0);
|
||||
|
||||
// Build and send another transaction, using composite account parameters.
|
||||
program
|
||||
.request()
|
||||
.accounts(CompositeUpdate {
|
||||
foo: Foo {
|
||||
dummy_a: dummy_a.pubkey(),
|
||||
},
|
||||
bar: Bar {
|
||||
dummy_b: dummy_b.pubkey(),
|
||||
},
|
||||
})
|
||||
.args(composite_instruction::CompositeUpdate {
|
||||
dummy_a: 1234,
|
||||
dummy_b: 4321,
|
||||
})
|
||||
.send()?;
|
||||
|
||||
// Assert the transaction worked.
|
||||
let dummy_a_account: DummyA = program.account(dummy_a.pubkey())?;
|
||||
let dummy_b_account: DummyB = program.account(dummy_b.pubkey())?;
|
||||
assert_eq!(dummy_a_account.data, 1234);
|
||||
assert_eq!(dummy_b_account.data, 4321);
|
||||
|
||||
println!("Composite success!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Runs a client for examples/tutorial/basic-2.
|
||||
//
|
||||
// Make sure to run a localnet with the program deploy to run this example.
|
||||
fn basic_2(client: &Client, pid: Pubkey) -> Result<()> {
|
||||
let program = client.program(pid);
|
||||
|
||||
// `Create` parameters.
|
||||
let counter = Keypair::generate(&mut OsRng);
|
||||
let authority = program.payer();
|
||||
|
||||
// Build and send a transaction.
|
||||
program
|
||||
.request()
|
||||
.signer(&counter)
|
||||
.accounts(basic_2_accounts::Create {
|
||||
counter: counter.pubkey(),
|
||||
user: authority,
|
||||
system_program: system_program::ID,
|
||||
})
|
||||
.args(basic_2_instruction::Create { authority })
|
||||
.send()?;
|
||||
|
||||
let counter_account: Counter = program.account(counter.pubkey())?;
|
||||
|
||||
assert_eq!(counter_account.authority, authority);
|
||||
assert_eq!(counter_account.count, 0);
|
||||
|
||||
println!("Basic 2 success!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn events(client: &Client, pid: Pubkey) -> Result<()> {
|
||||
let program = client.program(pid);
|
||||
|
||||
let (sender, receiver) = std::sync::mpsc::channel();
|
||||
let handle = program.on(move |_ctx: &EventContext, event: MyEvent| {
|
||||
sender.send(event).unwrap();
|
||||
})?;
|
||||
|
||||
std::thread::sleep(Duration::from_millis(1000));
|
||||
|
||||
program
|
||||
.request()
|
||||
.args(events_instruction::Initialize {})
|
||||
.send()?;
|
||||
|
||||
let event = receiver.recv().unwrap();
|
||||
assert_eq!(event.data, 5);
|
||||
assert_eq!(event.label, "hello".to_string());
|
||||
|
||||
// TODO: remove once https://github.com/solana-labs/solana/issues/16102
|
||||
// is addressed. Until then, drop the subscription handle in another
|
||||
// thread so that we deadlock in the other thread as to not block
|
||||
// this thread.
|
||||
std::thread::spawn(move || {
|
||||
drop(handle);
|
||||
});
|
||||
|
||||
println!("Events success!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn basic_4(client: &Client, pid: Pubkey) -> Result<()> {
|
||||
let program = client.program(pid);
|
||||
let authority = program.payer();
|
||||
|
||||
// Invoke the state's `new` constructor.
|
||||
program
|
||||
.state_request()
|
||||
.accounts(basic_4_accounts::Auth { authority })
|
||||
.new(basic_4_instruction::state::New)
|
||||
.send()?;
|
||||
let counter_account: CounterState = program.state()?;
|
||||
assert_eq!(counter_account.authority, authority);
|
||||
assert_eq!(counter_account.count, 0);
|
||||
|
||||
// Call a state method.
|
||||
program
|
||||
.state_request()
|
||||
.accounts(basic_4_accounts::Auth { authority })
|
||||
.args(basic_4_instruction::state::Increment)
|
||||
.send()?;
|
||||
let counter_account: CounterState = program.state()?;
|
||||
assert_eq!(counter_account.authority, authority);
|
||||
assert_eq!(counter_account.count, 1);
|
||||
|
||||
println!("Basic 4 success!");
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,169 +0,0 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum Cluster {
|
||||
Testnet,
|
||||
Mainnet,
|
||||
Devnet,
|
||||
Localnet,
|
||||
Debug,
|
||||
Custom(String, String),
|
||||
}
|
||||
|
||||
impl Default for Cluster {
|
||||
fn default() -> Self {
|
||||
Cluster::Localnet
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Cluster {
|
||||
type Err = anyhow::Error;
|
||||
fn from_str(s: &str) -> Result<Cluster> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"t" | "testnet" => Ok(Cluster::Testnet),
|
||||
"m" | "mainnet" => Ok(Cluster::Mainnet),
|
||||
"d" | "devnet" => Ok(Cluster::Devnet),
|
||||
"l" | "localnet" => Ok(Cluster::Localnet),
|
||||
"g" | "debug" => Ok(Cluster::Debug),
|
||||
_ if s.starts_with("http") => {
|
||||
let http_url = s;
|
||||
|
||||
// Taken from:
|
||||
// https://github.com/solana-labs/solana/blob/aea8f0df1610248d29d8ca3bc0d60e9fabc99e31/web3.js/src/util/url.ts
|
||||
|
||||
let mut ws_url = Url::parse(http_url)?;
|
||||
if let Some(port) = ws_url.port() {
|
||||
ws_url.set_port(Some(port + 1))
|
||||
.map_err(|_| anyhow!("Unable to set port"))?;
|
||||
}
|
||||
if ws_url.scheme() == "https" {
|
||||
ws_url.set_scheme("wss")
|
||||
.map_err(|_| anyhow!("Unable to set scheme"))?;
|
||||
} else {
|
||||
ws_url.set_scheme("ws")
|
||||
.map_err(|_| anyhow!("Unable to set scheme"))?;
|
||||
}
|
||||
|
||||
|
||||
Ok(Cluster::Custom(http_url.to_string(), ws_url.to_string()))
|
||||
}
|
||||
_ => Err(anyhow::Error::msg(
|
||||
"Cluster must be one of [localnet, testnet, mainnet, devnet] or be an http or https url\n",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Cluster {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let clust_str = match self {
|
||||
Cluster::Testnet => "testnet",
|
||||
Cluster::Mainnet => "mainnet",
|
||||
Cluster::Devnet => "devnet",
|
||||
Cluster::Localnet => "localnet",
|
||||
Cluster::Debug => "debug",
|
||||
Cluster::Custom(url, _ws_url) => url,
|
||||
};
|
||||
write!(f, "{}", clust_str)
|
||||
}
|
||||
}
|
||||
|
||||
impl Cluster {
|
||||
pub fn url(&self) -> &str {
|
||||
match self {
|
||||
Cluster::Devnet => "https://api.devnet.solana.com",
|
||||
Cluster::Testnet => "https://api.testnet.solana.com",
|
||||
Cluster::Mainnet => "https://api.mainnet-beta.solana.com",
|
||||
Cluster::Localnet => "http://127.0.0.1:8899",
|
||||
Cluster::Debug => "http://34.90.18.145:8899",
|
||||
Cluster::Custom(url, _ws_url) => url,
|
||||
}
|
||||
}
|
||||
pub fn ws_url(&self) -> &str {
|
||||
match self {
|
||||
Cluster::Devnet => "wss://api.devnet.solana.com",
|
||||
Cluster::Testnet => "wss://api.testnet.solana.com",
|
||||
Cluster::Mainnet => "wss://api.mainnet-beta.solana.com",
|
||||
Cluster::Localnet => "ws://127.0.0.1:9000",
|
||||
Cluster::Debug => "ws://34.90.18.145:9000",
|
||||
Cluster::Custom(_url, ws_url) => ws_url,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn test_cluster(name: &str, cluster: Cluster) {
|
||||
assert_eq!(Cluster::from_str(name).unwrap(), cluster);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cluster_parse() {
|
||||
test_cluster("testnet", Cluster::Testnet);
|
||||
test_cluster("mainnet", Cluster::Mainnet);
|
||||
test_cluster("devnet", Cluster::Devnet);
|
||||
test_cluster("localnet", Cluster::Localnet);
|
||||
test_cluster("debug", Cluster::Debug);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_cluster_bad_parse() {
|
||||
let bad_url = "httq://my_custom_url.test.net";
|
||||
Cluster::from_str(bad_url).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_http_port() {
|
||||
let url = "http://my-url.com:7000/";
|
||||
let cluster = Cluster::from_str(url).unwrap();
|
||||
assert_eq!(
|
||||
Cluster::Custom(url.to_string(), "ws://my-url.com:7001/".to_string()),
|
||||
cluster
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_http_no_port() {
|
||||
let url = "http://my-url.com/";
|
||||
let cluster = Cluster::from_str(url).unwrap();
|
||||
assert_eq!(
|
||||
Cluster::Custom(url.to_string(), "ws://my-url.com/".to_string()),
|
||||
cluster
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_https_port() {
|
||||
let url = "https://my-url.com:7000/";
|
||||
let cluster = Cluster::from_str(url).unwrap();
|
||||
assert_eq!(
|
||||
Cluster::Custom(url.to_string(), "wss://my-url.com:7001/".to_string()),
|
||||
cluster
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_https_no_port() {
|
||||
let url = "https://my-url.com/";
|
||||
let cluster = Cluster::from_str(url).unwrap();
|
||||
assert_eq!(
|
||||
Cluster::Custom(url.to_string(), "wss://my-url.com/".to_string()),
|
||||
cluster
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_upper_case() {
|
||||
let url = "http://my-url.com/FooBar";
|
||||
let cluster = Cluster::from_str(url).unwrap();
|
||||
assert_eq!(
|
||||
Cluster::Custom(url.to_string(), "ws://my-url.com/FooBar".to_string()),
|
||||
cluster
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,591 +0,0 @@
|
|||
//! `anchor_client` provides an RPC client to send transactions and fetch
|
||||
//! deserialized accounts from Solana programs written in `anchor_lang`.
|
||||
|
||||
use anchor_lang::solana_program::instruction::{AccountMeta, Instruction};
|
||||
use anchor_lang::solana_program::program_error::ProgramError;
|
||||
use anchor_lang::solana_program::pubkey::Pubkey;
|
||||
use anchor_lang::solana_program::system_program;
|
||||
use anchor_lang::{AccountDeserialize, Discriminator, InstructionData, ToAccountMetas};
|
||||
use regex::Regex;
|
||||
use solana_account_decoder::UiAccountEncoding;
|
||||
use solana_client::client_error::ClientError as SolanaClientError;
|
||||
use solana_client::pubsub_client::{PubsubClient, PubsubClientError, PubsubClientSubscription};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_client::rpc_config::{
|
||||
RpcAccountInfoConfig, RpcProgramAccountsConfig, RpcTransactionLogsConfig,
|
||||
RpcTransactionLogsFilter,
|
||||
};
|
||||
use solana_client::rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType};
|
||||
use solana_client::rpc_response::{Response as RpcResponse, RpcLogsResponse};
|
||||
use solana_sdk::account::Account;
|
||||
use solana_sdk::bs58;
|
||||
use solana_sdk::commitment_config::CommitmentConfig;
|
||||
use solana_sdk::signature::{Signature, Signer};
|
||||
use solana_sdk::transaction::Transaction;
|
||||
use std::convert::Into;
|
||||
use std::iter::Map;
|
||||
use std::rc::Rc;
|
||||
use std::vec::IntoIter;
|
||||
use thiserror::Error;
|
||||
|
||||
pub use anchor_lang;
|
||||
pub use cluster::Cluster;
|
||||
pub use solana_client;
|
||||
pub use solana_sdk;
|
||||
|
||||
mod cluster;
|
||||
|
||||
const PROGRAM_LOG: &str = "Program log: ";
|
||||
const PROGRAM_DATA: &str = "Program data: ";
|
||||
|
||||
/// EventHandle unsubscribes from a program event stream on drop.
|
||||
pub type EventHandle = PubsubClientSubscription<RpcResponse<RpcLogsResponse>>;
|
||||
|
||||
/// Client defines the base configuration for building RPC clients to
|
||||
/// communicate with Anchor programs running on a Solana cluster. It's
|
||||
/// primary use is to build a `Program` client via the `program` method.
|
||||
pub struct Client {
|
||||
cfg: Config,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn new(cluster: Cluster, payer: Rc<dyn Signer>) -> Self {
|
||||
Self {
|
||||
cfg: Config {
|
||||
cluster,
|
||||
payer,
|
||||
options: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_options(
|
||||
cluster: Cluster,
|
||||
payer: Rc<dyn Signer>,
|
||||
options: CommitmentConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
cfg: Config {
|
||||
cluster,
|
||||
payer,
|
||||
options: Some(options),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn program(&self, program_id: Pubkey) -> Program {
|
||||
Program {
|
||||
program_id,
|
||||
cfg: Config {
|
||||
cluster: self.cfg.cluster.clone(),
|
||||
options: self.cfg.options,
|
||||
payer: self.cfg.payer.clone(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Internal configuration for a client.
|
||||
#[derive(Debug)]
|
||||
struct Config {
|
||||
cluster: Cluster,
|
||||
payer: Rc<dyn Signer>,
|
||||
options: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
/// Program is the primary client handle to be used to build and send requests.
|
||||
#[derive(Debug)]
|
||||
pub struct Program {
|
||||
program_id: Pubkey,
|
||||
cfg: Config,
|
||||
}
|
||||
|
||||
impl Program {
|
||||
pub fn payer(&self) -> Pubkey {
|
||||
self.cfg.payer.pubkey()
|
||||
}
|
||||
|
||||
/// Returns a request builder.
|
||||
pub fn request(&self) -> RequestBuilder {
|
||||
RequestBuilder::from(
|
||||
self.program_id,
|
||||
self.cfg.cluster.url(),
|
||||
self.cfg.payer.clone(),
|
||||
self.cfg.options,
|
||||
RequestNamespace::Global,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a request builder for program state.
|
||||
pub fn state_request(&self) -> RequestBuilder {
|
||||
RequestBuilder::from(
|
||||
self.program_id,
|
||||
self.cfg.cluster.url(),
|
||||
self.cfg.payer.clone(),
|
||||
self.cfg.options,
|
||||
RequestNamespace::State { new: false },
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the account at the given address.
|
||||
pub fn account<T: AccountDeserialize>(&self, address: Pubkey) -> Result<T, ClientError> {
|
||||
let rpc_client = RpcClient::new_with_commitment(
|
||||
self.cfg.cluster.url().to_string(),
|
||||
self.cfg.options.unwrap_or_default(),
|
||||
);
|
||||
let account = rpc_client
|
||||
.get_account_with_commitment(&address, CommitmentConfig::processed())?
|
||||
.value
|
||||
.ok_or(ClientError::AccountNotFound)?;
|
||||
let mut data: &[u8] = &account.data;
|
||||
T::try_deserialize(&mut data).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Returns all program accounts of the given type matching the given filters
|
||||
pub fn accounts<T: AccountDeserialize + Discriminator>(
|
||||
&self,
|
||||
filters: Vec<RpcFilterType>,
|
||||
) -> Result<Vec<(Pubkey, T)>, ClientError> {
|
||||
self.accounts_lazy(filters)?.collect()
|
||||
}
|
||||
|
||||
/// Returns all program accounts of the given type matching the given filters as an iterator
|
||||
/// Deserialization is executed lazily
|
||||
pub fn accounts_lazy<T: AccountDeserialize + Discriminator>(
|
||||
&self,
|
||||
filters: Vec<RpcFilterType>,
|
||||
) -> Result<ProgramAccountsIterator<T>, ClientError> {
|
||||
let account_type_filter = RpcFilterType::Memcmp(Memcmp {
|
||||
offset: 0,
|
||||
bytes: MemcmpEncodedBytes::Base58(bs58::encode(T::discriminator()).into_string()),
|
||||
encoding: None,
|
||||
});
|
||||
let config = RpcProgramAccountsConfig {
|
||||
filters: Some([vec![account_type_filter], filters].concat()),
|
||||
account_config: RpcAccountInfoConfig {
|
||||
encoding: Some(UiAccountEncoding::Base64),
|
||||
data_slice: None,
|
||||
commitment: None,
|
||||
},
|
||||
with_context: None,
|
||||
};
|
||||
Ok(ProgramAccountsIterator {
|
||||
inner: self
|
||||
.rpc()
|
||||
.get_program_accounts_with_config(&self.id(), config)?
|
||||
.into_iter()
|
||||
.map(|(key, account)| {
|
||||
Ok((key, T::try_deserialize(&mut (&account.data as &[u8]))?))
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn state<T: AccountDeserialize>(&self) -> Result<T, ClientError> {
|
||||
self.account(anchor_lang::__private::state::address(&self.program_id))
|
||||
}
|
||||
|
||||
pub fn rpc(&self) -> RpcClient {
|
||||
RpcClient::new_with_commitment(
|
||||
self.cfg.cluster.url().to_string(),
|
||||
self.cfg.options.unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn id(&self) -> Pubkey {
|
||||
self.program_id
|
||||
}
|
||||
|
||||
pub fn on<T: anchor_lang::Event + anchor_lang::AnchorDeserialize>(
|
||||
&self,
|
||||
f: impl Fn(&EventContext, T) + Send + 'static,
|
||||
) -> Result<EventHandle, ClientError> {
|
||||
let addresses = vec![self.program_id.to_string()];
|
||||
let filter = RpcTransactionLogsFilter::Mentions(addresses);
|
||||
let ws_url = self.cfg.cluster.ws_url().to_string();
|
||||
let cfg = RpcTransactionLogsConfig {
|
||||
commitment: self.cfg.options,
|
||||
};
|
||||
let self_program_str = self.program_id.to_string();
|
||||
let (client, receiver) = PubsubClient::logs_subscribe(&ws_url, filter, cfg)?;
|
||||
std::thread::spawn(move || {
|
||||
loop {
|
||||
match receiver.recv() {
|
||||
Ok(logs) => {
|
||||
let ctx = EventContext {
|
||||
signature: logs.value.signature.parse().unwrap(),
|
||||
slot: logs.context.slot,
|
||||
};
|
||||
let mut logs = &logs.value.logs[..];
|
||||
if !logs.is_empty() {
|
||||
if let Ok(mut execution) = Execution::new(&mut logs) {
|
||||
for l in logs {
|
||||
// Parse the log.
|
||||
let (event, new_program, did_pop) = {
|
||||
if self_program_str == execution.program() {
|
||||
handle_program_log(&self_program_str, l).unwrap_or_else(
|
||||
|e| {
|
||||
println!("Unable to parse log: {}", e);
|
||||
std::process::exit(1);
|
||||
},
|
||||
)
|
||||
} else {
|
||||
let (program, did_pop) =
|
||||
handle_system_log(&self_program_str, l);
|
||||
(None, program, did_pop)
|
||||
}
|
||||
};
|
||||
// Emit the event.
|
||||
if let Some(e) = event {
|
||||
f(&ctx, e);
|
||||
}
|
||||
// Switch program context on CPI.
|
||||
if let Some(new_program) = new_program {
|
||||
execution.push(new_program);
|
||||
}
|
||||
// Program returned.
|
||||
if did_pop {
|
||||
execution.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_err) => {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Ok(client)
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator with items of type (Pubkey, T). Used to lazily deserialize account structs.
|
||||
/// Wrapper type hides the inner type from usages so the implementation can be changed.
|
||||
pub struct ProgramAccountsIterator<T> {
|
||||
inner: Map<IntoIter<(Pubkey, Account)>, AccountConverterFunction<T>>,
|
||||
}
|
||||
|
||||
/// Function type that accepts solana accounts and returns deserialized anchor accounts
|
||||
type AccountConverterFunction<T> = fn((Pubkey, Account)) -> Result<(Pubkey, T), ClientError>;
|
||||
|
||||
impl<T> Iterator for ProgramAccountsIterator<T> {
|
||||
type Item = Result<(Pubkey, T), ClientError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next()
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_program_log<T: anchor_lang::Event + anchor_lang::AnchorDeserialize>(
|
||||
self_program_str: &str,
|
||||
l: &str,
|
||||
) -> Result<(Option<T>, Option<String>, bool), ClientError> {
|
||||
// Log emitted from the current program.
|
||||
if let Some(log) = l
|
||||
.strip_prefix(PROGRAM_LOG)
|
||||
.or_else(|| l.strip_prefix(PROGRAM_DATA))
|
||||
{
|
||||
let borsh_bytes = match anchor_lang::__private::base64::decode(&log) {
|
||||
Ok(borsh_bytes) => borsh_bytes,
|
||||
_ => {
|
||||
#[cfg(feature = "debug")]
|
||||
println!("Could not base64 decode log: {}", log);
|
||||
return Ok((None, None, false));
|
||||
}
|
||||
};
|
||||
|
||||
let mut slice: &[u8] = &borsh_bytes[..];
|
||||
let disc: [u8; 8] = {
|
||||
let mut disc = [0; 8];
|
||||
disc.copy_from_slice(&borsh_bytes[..8]);
|
||||
slice = &slice[8..];
|
||||
disc
|
||||
};
|
||||
let mut event = None;
|
||||
if disc == T::discriminator() {
|
||||
let e: T = anchor_lang::AnchorDeserialize::deserialize(&mut slice)
|
||||
.map_err(|e| ClientError::LogParseError(e.to_string()))?;
|
||||
event = Some(e);
|
||||
}
|
||||
Ok((event, None, false))
|
||||
}
|
||||
// System log.
|
||||
else {
|
||||
let (program, did_pop) = handle_system_log(self_program_str, l);
|
||||
Ok((None, program, did_pop))
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_system_log(this_program_str: &str, log: &str) -> (Option<String>, bool) {
|
||||
if log.starts_with(&format!("Program {} log:", this_program_str)) {
|
||||
(Some(this_program_str.to_string()), false)
|
||||
} else if log.contains("invoke") {
|
||||
(Some("cpi".to_string()), false) // Any string will do.
|
||||
} else {
|
||||
let re = Regex::new(r"^Program (.*) success*$").unwrap();
|
||||
if re.is_match(log) {
|
||||
(None, true)
|
||||
} else {
|
||||
(None, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Execution {
|
||||
stack: Vec<String>,
|
||||
}
|
||||
|
||||
impl Execution {
|
||||
pub fn new(logs: &mut &[String]) -> Result<Self, ClientError> {
|
||||
let l = &logs[0];
|
||||
*logs = &logs[1..];
|
||||
|
||||
let re = Regex::new(r"^Program (.*) invoke.*$").unwrap();
|
||||
let c = re
|
||||
.captures(l)
|
||||
.ok_or_else(|| ClientError::LogParseError(l.to_string()))?;
|
||||
let program = c
|
||||
.get(1)
|
||||
.ok_or_else(|| ClientError::LogParseError(l.to_string()))?
|
||||
.as_str()
|
||||
.to_string();
|
||||
Ok(Self {
|
||||
stack: vec![program],
|
||||
})
|
||||
}
|
||||
|
||||
pub fn program(&self) -> String {
|
||||
assert!(!self.stack.is_empty());
|
||||
self.stack[self.stack.len() - 1].clone()
|
||||
}
|
||||
|
||||
pub fn push(&mut self, new_program: String) {
|
||||
self.stack.push(new_program);
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) {
|
||||
assert!(!self.stack.is_empty());
|
||||
self.stack.pop().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EventContext {
|
||||
pub signature: Signature,
|
||||
pub slot: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
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),
|
||||
#[error("{0}")]
|
||||
SolanaClientPubsubError(#[from] PubsubClientError),
|
||||
#[error("Unable to parse log: {0}")]
|
||||
LogParseError(String),
|
||||
}
|
||||
|
||||
/// `RequestBuilder` provides a builder interface to create and send
|
||||
/// transactions to a cluster.
|
||||
pub struct RequestBuilder<'a> {
|
||||
cluster: String,
|
||||
program_id: Pubkey,
|
||||
accounts: Vec<AccountMeta>,
|
||||
options: CommitmentConfig,
|
||||
instructions: Vec<Instruction>,
|
||||
payer: Rc<dyn Signer>,
|
||||
// Serialized instruction data for the target RPC.
|
||||
instruction_data: Option<Vec<u8>>,
|
||||
signers: Vec<&'a dyn Signer>,
|
||||
// True if the user is sending a state instruction.
|
||||
namespace: RequestNamespace,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum RequestNamespace {
|
||||
Global,
|
||||
State {
|
||||
// True if the request is to the state's new ctor.
|
||||
new: bool,
|
||||
},
|
||||
Interface,
|
||||
}
|
||||
|
||||
impl<'a> RequestBuilder<'a> {
|
||||
pub fn from(
|
||||
program_id: Pubkey,
|
||||
cluster: &str,
|
||||
payer: Rc<dyn Signer>,
|
||||
options: Option<CommitmentConfig>,
|
||||
namespace: RequestNamespace,
|
||||
) -> Self {
|
||||
Self {
|
||||
program_id,
|
||||
payer,
|
||||
cluster: cluster.to_string(),
|
||||
accounts: Vec::new(),
|
||||
options: options.unwrap_or_default(),
|
||||
instructions: Vec::new(),
|
||||
instruction_data: None,
|
||||
signers: Vec::new(),
|
||||
namespace,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn payer(mut self, payer: Rc<dyn Signer>) -> Self {
|
||||
self.payer = payer;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn cluster(mut self, url: &str) -> Self {
|
||||
self.cluster = url.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn instruction(mut self, ix: Instruction) -> Self {
|
||||
self.instructions.push(ix);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn program(mut self, program_id: Pubkey) -> Self {
|
||||
self.program_id = program_id;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn accounts(mut self, accounts: impl ToAccountMetas) -> Self {
|
||||
let mut metas = accounts.to_account_metas(None);
|
||||
self.accounts.append(&mut metas);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn options(mut self, options: CommitmentConfig) -> Self {
|
||||
self.options = options;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn args(mut self, args: impl InstructionData) -> Self {
|
||||
self.instruction_data = Some(args.data());
|
||||
self
|
||||
}
|
||||
|
||||
/// Invokes the `#[state]`'s `new` constructor.
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
#[must_use]
|
||||
pub fn new(mut self, args: impl InstructionData) -> Self {
|
||||
assert!(self.namespace == RequestNamespace::State { new: false });
|
||||
self.namespace = RequestNamespace::State { new: true };
|
||||
self.instruction_data = Some(args.data());
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn signer(mut self, signer: &'a dyn Signer) -> Self {
|
||||
self.signers.push(signer);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn instructions(&self) -> Result<Vec<Instruction>, ClientError> {
|
||||
let mut accounts = match self.namespace {
|
||||
RequestNamespace::State { new } => match new {
|
||||
false => vec![AccountMeta::new(
|
||||
anchor_lang::__private::state::address(&self.program_id),
|
||||
false,
|
||||
)],
|
||||
true => vec![
|
||||
AccountMeta::new_readonly(self.payer.pubkey(), true),
|
||||
AccountMeta::new(
|
||||
anchor_lang::__private::state::address(&self.program_id),
|
||||
false,
|
||||
),
|
||||
AccountMeta::new_readonly(
|
||||
Pubkey::find_program_address(&[], &self.program_id).0,
|
||||
false,
|
||||
),
|
||||
AccountMeta::new_readonly(system_program::ID, false),
|
||||
AccountMeta::new_readonly(self.program_id, false),
|
||||
],
|
||||
},
|
||||
_ => Vec::new(),
|
||||
};
|
||||
accounts.extend_from_slice(&self.accounts);
|
||||
|
||||
let mut instructions = self.instructions.clone();
|
||||
if let Some(ix_data) = &self.instruction_data {
|
||||
instructions.push(Instruction {
|
||||
program_id: self.program_id,
|
||||
data: ix_data.clone(),
|
||||
accounts,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(instructions)
|
||||
}
|
||||
|
||||
pub fn send(self) -> Result<Signature, ClientError> {
|
||||
let instructions = self.instructions()?;
|
||||
|
||||
let mut signers = self.signers;
|
||||
signers.push(&*self.payer);
|
||||
|
||||
let rpc_client = RpcClient::new_with_commitment(self.cluster, self.options);
|
||||
|
||||
let tx = {
|
||||
let latest_hash = rpc_client.get_latest_blockhash()?;
|
||||
Transaction::new_signed_with_payer(
|
||||
&instructions,
|
||||
Some(&self.payer.pubkey()),
|
||||
&signers,
|
||||
latest_hash,
|
||||
)
|
||||
};
|
||||
|
||||
rpc_client
|
||||
.send_and_confirm_transaction(&tx)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn new_execution() {
|
||||
let mut logs: &[String] =
|
||||
&["Program 7Y8VDzehoewALqJfyxZYMgYCnMTCDhWuGfJKUvjYWATw invoke [1]".to_string()];
|
||||
let exe = Execution::new(&mut logs).unwrap();
|
||||
assert_eq!(
|
||||
exe.stack[0],
|
||||
"7Y8VDzehoewALqJfyxZYMgYCnMTCDhWuGfJKUvjYWATw".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn handle_system_log_pop() {
|
||||
let log = "Program 7Y8VDzehoewALqJfyxZYMgYCnMTCDhWuGfJKUvjYWATw success";
|
||||
let (program, did_pop) = handle_system_log("asdf", log);
|
||||
assert_eq!(program, None);
|
||||
assert!(did_pop);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn handle_system_log_no_pop() {
|
||||
let log = "Program 7swsTUiQ6KUK4uFYquQKg4epFRsBnvbrTf2fZQCa2sTJ qwer";
|
||||
let (program, did_pop) = handle_system_log("asdf", log);
|
||||
assert_eq!(program, None);
|
||||
assert!(!did_pop);
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
WORKDIR=$(PWD)
|
||||
#
|
||||
# Extract anchor version from the Cargo.toml.
|
||||
#
|
||||
ANCHOR_CLI=v$(shell awk -F ' = ' '$$1 ~ /version/ { gsub(/[\"]/, "", $$2); printf("%s",$$2) }' ../cli/Cargo.toml)
|
||||
#
|
||||
# Solana toolchain.
|
||||
#
|
||||
SOLANA_CLI=v1.9.13
|
||||
#
|
||||
# Build version should match the Anchor cli version.
|
||||
#
|
||||
IMG_ORG ?= projectserum
|
||||
IMG_VER ?= $(ANCHOR_CLI)
|
||||
|
||||
.PHONY: build build-push build-shell publish
|
||||
|
||||
default:
|
||||
|
||||
build: build/Dockerfile
|
||||
@docker build \
|
||||
--build-arg ANCHOR_CLI=$(ANCHOR_CLI) \
|
||||
--build-arg SOLANA_CLI=$(SOLANA_CLI) \
|
||||
$@ -t $(IMG_ORG)/$@:$(IMG_VER)
|
||||
|
||||
build-push:
|
||||
@docker push $(IMG_ORG)/build:$(IMG_VER)
|
||||
|
||||
build-shell:
|
||||
@docker run -ti --rm --net=host \
|
||||
-v $(WORKDIR)/..:/workdir \
|
||||
$(IMG_ORG)/build:$(IMG_VER) bash
|
||||
|
||||
publish: build build-push
|
|
@ -1,49 +0,0 @@
|
|||
#
|
||||
# Docker image to generate deterministic, verifiable builds of Anchor programs.
|
||||
# This must be run *after* a given ANCHOR_CLI version is published and a git tag
|
||||
# is released on GitHub.
|
||||
#
|
||||
|
||||
FROM ubuntu:18.04
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ARG SOLANA_CLI
|
||||
ARG ANCHOR_CLI
|
||||
|
||||
ENV NODE_VERSION="v17.0.1"
|
||||
ENV HOME="/root"
|
||||
ENV PATH="${HOME}/.cargo/bin:${PATH}"
|
||||
ENV PATH="${HOME}/.local/share/solana/install/active_release/bin:${PATH}"
|
||||
ENV PATH="${HOME}/.nvm/versions/node/${NODE_VERSION}/bin:${PATH}"
|
||||
|
||||
# Install base utilities.
|
||||
RUN mkdir -p /workdir && mkdir -p /tmp && \
|
||||
apt-get update -qq && apt-get upgrade -qq && apt-get install -qq \
|
||||
build-essential git curl wget jq pkg-config python3-pip \
|
||||
libssl-dev libudev-dev
|
||||
|
||||
# Install rust.
|
||||
RUN curl "https://sh.rustup.rs" -sfo rustup.sh && \
|
||||
sh rustup.sh -y && \
|
||||
rustup component add rustfmt clippy
|
||||
|
||||
# Install node / npm / yarn.
|
||||
RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
|
||||
ENV NVM_DIR="${HOME}/.nvm"
|
||||
RUN . $NVM_DIR/nvm.sh && \
|
||||
nvm install ${NODE_VERSION} && \
|
||||
nvm use ${NODE_VERSION} && \
|
||||
nvm alias default node && \
|
||||
npm install -g yarn
|
||||
|
||||
# Install Solana tools.
|
||||
RUN sh -c "$(curl -sSfL https://release.solana.com/${SOLANA_CLI}/install)"
|
||||
|
||||
# Install anchor.
|
||||
RUN cargo install --git https://github.com/project-serum/anchor --tag ${ANCHOR_CLI} anchor-cli --locked
|
||||
|
||||
# Build a dummy program to bootstrap the BPF SDK (doing this speeds up builds).
|
||||
RUN mkdir -p /tmp && cd tmp && anchor init dummy && cd dummy && anchor build
|
||||
|
||||
WORKDIR /workdir
|
|
@ -1,12 +0,0 @@
|
|||
pids
|
||||
logs
|
||||
node_modules
|
||||
npm-debug.log
|
||||
coverage/
|
||||
run
|
||||
dist
|
||||
.DS_Store
|
||||
.nyc_output
|
||||
.basement
|
||||
config.local.js
|
||||
basement_dist
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"name": "anchor",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"authors": {
|
||||
"name": "",
|
||||
"email": ""
|
||||
},
|
||||
"repository": "/anchor",
|
||||
"scripts": {
|
||||
"deploy": "gh-pages -d src/.vuepress/dist",
|
||||
"dev": "vuepress dev src",
|
||||
"build": "vuepress build src"
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@xiaopanda/vuepress-plugin-code-copy": "^1.0.3",
|
||||
"gh-pages": "^3.1.0",
|
||||
"vuepress": "^1.5.3",
|
||||
"vuepress-plugin-dehydrate": "^1.1.5",
|
||||
"vuepress-theme-default-prefers-color-scheme": "^2.0.0"
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
<template>
|
||||
<p class="demo">
|
||||
{{ msg }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
msg: 'Hello this is <Foo-Bar>'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,3 +0,0 @@
|
|||
<template>
|
||||
<p class="demo">This is another component</p>
|
||||
</template>
|
|
@ -1,15 +0,0 @@
|
|||
<template>
|
||||
<p class="demo">
|
||||
{{ msg }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
msg: 'Hello this is <demo-component>'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,104 +0,0 @@
|
|||
const { description } = require("../../package");
|
||||
|
||||
module.exports = {
|
||||
base: "/anchor/",
|
||||
/**
|
||||
* Ref:https://v1.vuepress.vuejs.org/config/#title
|
||||
*/
|
||||
title: "⚓ Anchor",
|
||||
/**
|
||||
* Ref:https://v1.vuepress.vuejs.org/config/#description
|
||||
*/
|
||||
description: description,
|
||||
|
||||
/**
|
||||
* Extra tags to be injected to the page HTML `<head>`
|
||||
*
|
||||
* ref:https://v1.vuepress.vuejs.org/config/#head
|
||||
*/
|
||||
head: [
|
||||
["link", { rel: "icon", href: "data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>⚓ </text></svg>"}],
|
||||
["meta", { name: "theme-color", content: "#3eaf7c" }],
|
||||
["meta", { name: "apple-mobile-web-app-capable", content: "yes" }],
|
||||
[
|
||||
"meta",
|
||||
{ name: "apple-mobile-web-app-status-bar-style", content: "black" },
|
||||
],
|
||||
],
|
||||
|
||||
theme: "default-prefers-color-scheme",
|
||||
|
||||
/**
|
||||
* Theme configuration, here is the default theme configuration for VuePress.
|
||||
*
|
||||
* ref:https://v1.vuepress.vuejs.org/theme/default-theme-config.html
|
||||
*/
|
||||
themeConfig: {
|
||||
repo: "",
|
||||
editLinks: false,
|
||||
docsDir: "",
|
||||
editLinkText: "",
|
||||
lastUpdated: false,
|
||||
sidebarDepth: 2,
|
||||
sidebar: [
|
||||
{
|
||||
collapsable: false,
|
||||
title: "Getting Started",
|
||||
children: [
|
||||
"/getting-started/introduction",
|
||||
"/getting-started/installation",
|
||||
],
|
||||
},
|
||||
{
|
||||
collapsable: false,
|
||||
title: "Teams",
|
||||
children: [
|
||||
"/getting-started/projects",
|
||||
],
|
||||
},
|
||||
{
|
||||
collapsable: false,
|
||||
title: "Tutorials",
|
||||
children: [
|
||||
"/tutorials/tutorial-0",
|
||||
"/tutorials/tutorial-1",
|
||||
"/tutorials/tutorial-2",
|
||||
"/tutorials/tutorial-3",
|
||||
"/tutorials/tutorial-4",
|
||||
],
|
||||
},
|
||||
{
|
||||
collapsable: false,
|
||||
title: "CLI",
|
||||
children: [
|
||||
"/cli/commands",
|
||||
],
|
||||
},
|
||||
{
|
||||
collapsable: false,
|
||||
title: "Source Verification",
|
||||
children: [
|
||||
"/getting-started/verification",
|
||||
"/getting-started/publishing",
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
nav: [
|
||||
{ text: "The Anchor Book", link: "https://book.anchor-lang.com" },
|
||||
{ text: "Rust", link: "https://docs.rs/anchor-lang/latest/anchor_lang/" },
|
||||
{ text: "TypeScript", link: "https://project-serum.github.io/anchor/ts/index.html" },
|
||||
{ text: "GitHub", link: "https://github.com/project-serum/anchor" }
|
||||
],
|
||||
},
|
||||
|
||||
/**
|
||||
* Apply plugins,ref:https://v1.vuepress.vuejs.org/zh/plugin/
|
||||
*/
|
||||
plugins: [
|
||||
"dehydrate",
|
||||
"@vuepress/plugin-back-to-top",
|
||||
"@vuepress/plugin-medium-zoom",
|
||||
"@xiaopanda/vuepress-plugin-code-copy",
|
||||
],
|
||||
};
|
|
@ -1,15 +0,0 @@
|
|||
/**
|
||||
* Client app enhancement file.
|
||||
*
|
||||
* https://v1.vuepress.vuejs.org/guide/basic-config.html#app-level-enhancements
|
||||
*/
|
||||
|
||||
export default ({
|
||||
Vue, // the version of Vue being used in the VuePress app
|
||||
options, // the options for the root Vue instance
|
||||
router, // the router instance for the app
|
||||
siteData, // site metadata
|
||||
}) => {
|
||||
// ...apply enhancements for the site.
|
||||
router.addRoutes([{ path: "/", redirect: "/getting-started/introduction" }]);
|
||||
};
|
|
@ -1,8 +0,0 @@
|
|||
/**
|
||||
* Custom Styles here.
|
||||
*
|
||||
* ref:https://v1.vuepress.vuejs.org/config/#index-styl
|
||||
*/
|
||||
|
||||
.home .hero img
|
||||
max-width 450px!important
|
|
@ -1,10 +0,0 @@
|
|||
/**
|
||||
* Custom palette here.
|
||||
*
|
||||
* ref:https://v1.vuepress.vuejs.org/zh/config/#palette-styl
|
||||
*/
|
||||
|
||||
$accentColor = #3eaf7c
|
||||
$textColor = #2c3e50
|
||||
$borderColor = #eaecef
|
||||
$codeBgColor = #282c34
|
|
@ -1,245 +0,0 @@
|
|||
# Commands
|
||||
|
||||
A CLI is provided to support building and managing an Anchor workspace.
|
||||
For a comprehensive list of commands and options, run `anchor -h` on any
|
||||
of the following subcommands.
|
||||
|
||||
```
|
||||
anchor-cli
|
||||
|
||||
USAGE:
|
||||
anchor <SUBCOMMAND>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
SUBCOMMANDS:
|
||||
build Builds the workspace
|
||||
cluster Cluster commands
|
||||
deploy Deploys each program in the workspace
|
||||
expand Expands the macros of a program or the workspace
|
||||
help Prints this message or the help of the given subcommand(s)
|
||||
idl Commands for interacting with interface definitions
|
||||
init Initializes a workspace
|
||||
migrate Runs the deploy migration script
|
||||
new Creates a new program
|
||||
test Runs integration tests against a localnetwork
|
||||
upgrade Upgrades a single program. The configured wallet must be the upgrade authority
|
||||
verify Verifies the on-chain bytecode matches the locally compiled artifact. Run this
|
||||
command inside a program subdirectory, i.e., in the dir containing the program's
|
||||
Cargo.toml
|
||||
```
|
||||
|
||||
|
||||
## Build
|
||||
|
||||
```
|
||||
anchor build
|
||||
```
|
||||
|
||||
Builds programs in the workspace targeting Solana's BPF runtime and emitting IDLs in the `target/idl` directory.
|
||||
|
||||
```
|
||||
anchor build --verifiable
|
||||
```
|
||||
|
||||
Runs the build inside a docker image so that the output binary is deterministic (assuming a Cargo.lock file is used). This command must be run from within a single crate subdirectory within the workspace. For example, `programs/<my-program>/`.
|
||||
|
||||
## Cluster
|
||||
|
||||
### Cluster list
|
||||
|
||||
```
|
||||
anchor cluster list
|
||||
```
|
||||
|
||||
This lists cluster endpoints:
|
||||
|
||||
```
|
||||
Cluster Endpoints:
|
||||
|
||||
* Mainnet - https://solana-api.projectserum.com
|
||||
* Mainnet - https://api.mainnet-beta.solana.com
|
||||
* Devnet - https://api.devnet.solana.com
|
||||
* Testnet - https://api.testnet.solana.com
|
||||
```
|
||||
|
||||
## Deploy
|
||||
|
||||
```
|
||||
anchor deploy
|
||||
```
|
||||
|
||||
Deploys all programs in the workspace to the configured cluster.
|
||||
|
||||
::: tip Note
|
||||
This is different from the `solana program deploy` command, because everytime it's run
|
||||
it will generate a *new* program address.
|
||||
:::
|
||||
|
||||
## Expand
|
||||
|
||||
```
|
||||
anchor expand
|
||||
```
|
||||
|
||||
If run inside a program folder, expands the macros of the program.
|
||||
|
||||
If run in the workspace but outside a program folder, expands the macros of the workspace.
|
||||
|
||||
If run with the `--program-name` option, expand only the given program.
|
||||
|
||||
## Idl
|
||||
|
||||
The `idl` subcommand provides commands for interacting with interface definition files.
|
||||
It's recommended to use these commands to store an IDL on chain, at a deterministic
|
||||
address, as a function of nothing but the the program's ID. This
|
||||
allows us to generate clients for a program using nothing but the program ID.
|
||||
|
||||
### Idl Init
|
||||
|
||||
```
|
||||
anchor idl init -f <target/idl/program.json> <program-id>
|
||||
```
|
||||
|
||||
Creates an idl account, writing the given `<target/idl/program.json>` file into a program owned account. By default, the size of the account is double the size of the IDL,
|
||||
allowing room for growth in case the idl needs to be upgraded in the future.
|
||||
|
||||
### Idl Fetch
|
||||
|
||||
```
|
||||
anchor idl fetch -o <out-file.json> <program-id>
|
||||
```
|
||||
|
||||
Fetches an IDL from the configured blockchain. For example, make sure
|
||||
your `Anchor.toml` is pointing to the `mainnet` cluster and run
|
||||
|
||||
```
|
||||
anchor idl fetch GrAkKfEpTKQuVHG2Y97Y2FF4i7y7Q5AHLK94JBy7Y5yv
|
||||
```
|
||||
|
||||
### Idl Authority
|
||||
|
||||
```
|
||||
anchor idl authority <program-id>
|
||||
```
|
||||
|
||||
Outputs the IDL account's authority. This is the wallet that has the ability to
|
||||
update the IDL.
|
||||
|
||||
### Idl Erase Authority
|
||||
|
||||
```
|
||||
anchor idl erase-authority -p <program-id>
|
||||
```
|
||||
|
||||
Erases the IDL account's authority so that upgrades can no longer occur. The
|
||||
configured wallet must be the current authority.
|
||||
|
||||
### Idl Upgrade
|
||||
|
||||
```
|
||||
anchor idl upgrade <program-id> -f <target/idl/program.json>
|
||||
```
|
||||
|
||||
Upgrades the IDL file on chain to the new `target/idl/program.json` idl.
|
||||
The configured wallet must be the current authority.
|
||||
|
||||
```
|
||||
anchor idl set-authority -n <new-authority> -p <program-id>
|
||||
```
|
||||
|
||||
Sets a new authority on the IDL account. Both the `new-authority` and `program-id`
|
||||
must be encoded in base 58.
|
||||
|
||||
## Init
|
||||
|
||||
```
|
||||
anchor init
|
||||
```
|
||||
|
||||
Initializes a project workspace with the following structure.
|
||||
|
||||
* `Anchor.toml`: Anchor configuration file.
|
||||
* `Cargo.toml`: Rust workspace configuration file.
|
||||
* `package.json`: JavaScript dependencies file.
|
||||
* `programs/`: Directory for Solana program crates.
|
||||
* `app/`: Directory for your application frontend.
|
||||
* `tests/`: Directory for JavaScript integration tests.
|
||||
* `migrations/deploy.js`: Deploy script.
|
||||
|
||||
## Migrate
|
||||
|
||||
```
|
||||
anchor migrate
|
||||
```
|
||||
|
||||
Runs the deploy script located at `migrations/deploy.js`, injecting a provider configured
|
||||
from the workspace's `Anchor.toml`. For example,
|
||||
|
||||
```javascript
|
||||
// File: migrations/deploys.js
|
||||
|
||||
const anchor = require("@project-serum/anchor");
|
||||
|
||||
module.exports = async function (provider) {
|
||||
anchor.setProvider(provider);
|
||||
|
||||
// Add your deploy script here.
|
||||
}
|
||||
```
|
||||
|
||||
Migrations are a new feature
|
||||
and only support this simple deploy script at the moment.
|
||||
|
||||
## New
|
||||
|
||||
```
|
||||
anchor new <program-name>
|
||||
```
|
||||
|
||||
Creates a new program in the workspace's `programs/` directory initialized with boilerplate.
|
||||
|
||||
## Test
|
||||
|
||||
```
|
||||
anchor test
|
||||
```
|
||||
|
||||
Run an integration test suit against the configured cluster, deploying new versions
|
||||
of all workspace programs before running them.
|
||||
|
||||
If the configured network is a localnet, then automatically starts the localnetwork and runs
|
||||
the test.
|
||||
|
||||
::: tip Note
|
||||
Be sure to shutdown any other local validators, otherwise `anchor test` will fail to run.
|
||||
|
||||
If you'd prefer to run the program against your local validator use `anchor test --skip-local-validator`.
|
||||
:::
|
||||
|
||||
When running tests we stream program logs to `.anchor/program-logs/<address>.<program-name>.log`
|
||||
|
||||
::: tip Note
|
||||
The Anchor workflow [recommends](https://www.parity.io/paritys-checklist-for-secure-smart-contract-development/)
|
||||
to test your program using integration tests in a language other
|
||||
than Rust to make sure that bugs related to syntax misunderstandings
|
||||
are coverable with tests and not just replicated in tests.
|
||||
:::
|
||||
|
||||
## Upgrade
|
||||
|
||||
```
|
||||
anchor upgrade <target/deploy/program.so> --program-id <program-id>
|
||||
```
|
||||
|
||||
Uses Solana's upgradeable BPF loader to upgrade the on chain program code.
|
||||
|
||||
## Verify
|
||||
|
||||
```
|
||||
anchor verify <program-id>
|
||||
```
|
||||
|
||||
Verifies the on-chain bytecode matches the locally compiled artifact.
|
|
@ -1,74 +0,0 @@
|
|||
# Installing Dependencies
|
||||
|
||||
To get started, make sure to setup all the prerequisite tools on your local machine
|
||||
(an installer has not yet been developed).
|
||||
|
||||
## Install Rust
|
||||
|
||||
For an introduction to Rust, see the excellent Rust [book](https://doc.rust-lang.org/book/).
|
||||
|
||||
```bash
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
source $HOME/.cargo/env
|
||||
rustup component add rustfmt
|
||||
```
|
||||
|
||||
## Install Solana
|
||||
|
||||
See the solana [docs](https://docs.solana.com/cli/install-solana-cli-tools) for installation instructions. On macOS and Linux,
|
||||
|
||||
```bash
|
||||
sh -c "$(curl -sSfL https://release.solana.com/v1.9.1/install)"
|
||||
```
|
||||
|
||||
## Install Yarn
|
||||
|
||||
[Yarn](https://yarnpkg.com/) is recommended for JavaScript package management.
|
||||
|
||||
```bash
|
||||
npm install -g yarn
|
||||
```
|
||||
|
||||
## Install Anchor
|
||||
|
||||
### Install using pre-build binary on x86_64 Linux
|
||||
|
||||
Anchor binaries are available via an NPM package [`@project-serum/anchor-cli`](https://www.npmjs.com/package/@project-serum/anchor-cli). Only x86_64 Linux is supported currently, you must build from source for other OS'.
|
||||
|
||||
```bash
|
||||
npm i -g @project-serum/anchor-cli
|
||||
```
|
||||
|
||||
### Build from source for other operating systems
|
||||
|
||||
For now, we can use Cargo to install the CLI.
|
||||
|
||||
```bash
|
||||
cargo install --git https://github.com/project-serum/anchor --tag v0.24.2 anchor-cli --locked
|
||||
```
|
||||
|
||||
On Linux systems you may need to install additional dependencies if `cargo install` fails. On Ubuntu,
|
||||
|
||||
```bash
|
||||
sudo apt-get update && sudo apt-get upgrade && sudo apt-get install -y pkg-config build-essential libudev-dev
|
||||
```
|
||||
|
||||
Now verify the CLI is installed properly.
|
||||
|
||||
```bash
|
||||
anchor --version
|
||||
```
|
||||
|
||||
## Start a Project
|
||||
|
||||
To initialize a new project, simply run:
|
||||
|
||||
```bash
|
||||
anchor init <new-project-name>
|
||||
```
|
||||
|
||||
## Minimum version requirements
|
||||
|
||||
| Build tool | Version |
|
||||
|:------------|:---------------|
|
||||
| Node.js | v11.0.0 |
|
|
@ -1,18 +0,0 @@
|
|||
# Introduction
|
||||
|
||||
<div style="border: 2px solid red; text-align: center; padding: 10px 10px 10px 10px; box-sizing: border-box"> This documentation is being sunset in favor of <a href="https://book.anchor-lang.com" rel="noopener noreferrer" target="_blank">The Anchor Book</a>. At this point in time, either documentation may contain information that the other does not.</div>
|
||||
|
||||
Anchor is a framework for Solana's [Sealevel](https://medium.com/solana-labs/sealevel-parallel-processing-thousands-of-smart-contracts-d814b378192) runtime providing several convenient developer tools.
|
||||
|
||||
- Rust crates and eDSL for writing Solana programs
|
||||
- [IDL](https://en.wikipedia.org/wiki/Interface_description_language) specification
|
||||
- TypeScript package for generating clients from IDL
|
||||
- CLI and workspace management for developing complete applications
|
||||
|
||||
If you're familiar with developing in Ethereum's [Solidity](https://docs.soliditylang.org/en/v0.7.4/), [Truffle](https://www.trufflesuite.com/), [web3.js](https://github.com/ethereum/web3.js) or Parity's [Ink!](https://github.com/paritytech/ink), then the experience will be familiar. Although the DSL syntax and semantics are targeted at Solana, the high level flow of writing RPC request handlers, emitting an IDL, and generating clients from IDL is the same.
|
||||
|
||||
Here, we'll walk through several tutorials demonstrating how to use Anchor. To skip the tutorials and jump straight to examples, go [here](https://github.com/project-serum/anchor/blob/master/examples). For an introduction to Solana, see the [docs](https://docs.solana.com/developing/programming-model/overview).
|
||||
|
||||
::: tip NOTE
|
||||
Anchor is in active development, so all APIs are subject to change. If you are one of the early developers to try it out and have feedback, please reach out by [filing an issue](https://github.com/project-serum/anchor/issues/new). This documentation is a work in progress and is expected to change dramatically as features continue to be built out. If you have any problems, consult the [source](https://github.com/project-serum/anchor) or feel free to ask questions on the [Discord](https://discord.gg/JgVgQ82erk).
|
||||
:::
|
|
@ -1,24 +0,0 @@
|
|||
# Projects
|
||||
|
||||
Open a pull request to add your project to the [list](https://github.com/project-serum/anchor/blob/master/docs/src/getting-started/projects.md).
|
||||
|
||||
* [Serum](https://github.com/project-serum)
|
||||
* [Synthetify](https://github.com/Synthetify)
|
||||
* [SolFarm](https://solfarm.io/)
|
||||
* [Zeta Markets](https://zeta.markets/)
|
||||
* [Saber](https://saber.so)
|
||||
* [01](https://01protocol.com/)
|
||||
* [Parrot Finance](https://parrot.fi/)
|
||||
* [Marinade Finance](https://marinade.finance/)
|
||||
* [Aldrin](https://dex.aldrin.com/)
|
||||
* [Cyclos](https://cyclos.io/)
|
||||
* [Solend](https://solend.fi)
|
||||
* [Drift](https://www.drift.trade/)
|
||||
* [Fabric](https://stake.fsynth.io/)
|
||||
* [Jet Protocol](https://jetprotocol.io/)
|
||||
* [Quarry](https://quarry.so/)
|
||||
* [PsyOptions](https://psyoptions.io/)
|
||||
* [sosol](https://sosol.app/)
|
||||
* [Arrow Protocol](https://arrowprotocol.com/)
|
||||
* [Hubble Protocol](https://hubbleprotocol.io/)
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
# Publishing Source
|
||||
|
||||
The Anchor Program Registry at [anchor.projectserum.com](https://anchor.projectserum.com)
|
||||
hosts a catalog of verified programs on Solana both written with and without Anchor. It is recommended
|
||||
that authors of smart contracts publish their source to promote best
|
||||
practices for security and transparency.
|
||||
|
||||
::: tip note
|
||||
The Anchor Program Registry is currently in alpha testing. For access to publishing
|
||||
please ask on [Discord](https://discord.gg/rg5ZZPmmTm).
|
||||
:::
|
||||
|
||||
## Getting Started
|
||||
|
||||
The process for publishing is mostly identical to `crates.io`.
|
||||
|
||||
* Signup for an account [here](https://anchor.projectserum.com/signup).
|
||||
* Confirm your email by clicking the link sent to your address.
|
||||
* Navigate to your Username -> Account Settings on the top navbar.
|
||||
* Click "New Token" in the **API Access** section.
|
||||
* Run `anchor login <token>` at the command line.
|
||||
|
||||
And you're ready to interact with the registry.
|
||||
|
||||
## Configuring a Build
|
||||
|
||||
Whether your program is written in Anchor or not, all source being published must
|
||||
have an `Anchor.toml` to define the build.
|
||||
|
||||
An example `Anchor.toml` config looks as follows,
|
||||
|
||||
```toml
|
||||
anchor_version = "0.24.2"
|
||||
|
||||
[workspace]
|
||||
members = ["programs/multisig"]
|
||||
|
||||
[provider]
|
||||
cluster = "mainnet"
|
||||
wallet = "~/.config/solana/id.json"
|
||||
|
||||
[programs.mainnet]
|
||||
multisig = "A9HAbnCwoD6f2NkZobKFf6buJoN9gUVVvX5PoUnDHS6u"
|
||||
|
||||
[programs.localnet]
|
||||
multisig = "A9HAbnCwoD6f2NkZobKFf6buJoN9gUVVvX5PoUnDHS6u"
|
||||
```
|
||||
|
||||
Here there are four sections.
|
||||
|
||||
1. `anchor_version` (optional) - sets the anchor docker image to use. By default, the builder will use the latest version of Anchor.
|
||||
2. `[workspace]` (optional) - sets the paths--relative to the `Anchor.toml`--
|
||||
to all programs in the local
|
||||
workspace, i.e., the path to the `Cargo.toml` manifest associated with each
|
||||
program that can be compiled by the `anchor` CLI. For programs using the
|
||||
standard Anchor workflow, this can be ommitted. For programs not written in Anchor
|
||||
but still want to publish, this should be added.
|
||||
3. `[provider]` - configures the wallet and cluster settings. Here, `mainnet` is used because the registry only supports `mainnet` binary verification at the moment.
|
||||
3. `[programs.mainnet]` - configures each program in the workpace, providing
|
||||
the `address` of the program to verify.
|
||||
|
||||
::: tip
|
||||
When defining program in `[programs.mainnet]`, make sure the name provided
|
||||
matches the **lib** name for your program, which is defined
|
||||
by your program's Cargo.toml.
|
||||
:::
|
||||
|
||||
### Examples
|
||||
|
||||
#### Anchor Program
|
||||
|
||||
An example of a toml file for an Anchor program can be found [here](https://anchor.projectserum.com/build/2).
|
||||
|
||||
#### Non Anchor Program
|
||||
|
||||
An example of a toml file for a non-anchor program can be found [here](https://anchor.projectserum.com/build/1).
|
||||
|
||||
## Publishing
|
||||
|
||||
To publish to the Anchor Program Registry, change directories to the `Anchor.toml`
|
||||
defined root and run
|
||||
|
||||
```bash
|
||||
anchor publish <program-name>
|
||||
```
|
||||
|
||||
where `<program-name>` is as defined in `[programs.mainnet]`, i.e., `multisig`
|
||||
in the example above.
|
|
@ -1,52 +0,0 @@
|
|||
# Verifiable Builds
|
||||
|
||||
Building programs with the Solana CLI may embed machine specfic
|
||||
code into the resulting binary. As a result, building the same program
|
||||
on different machines may produce different executables. To get around this
|
||||
problem, one can build inside a docker image with pinned dependencies to produce
|
||||
a verifiable build.
|
||||
|
||||
Anchor makes this easy by providing CLI commands to build and take care of
|
||||
docker for you. To get started, first make sure you
|
||||
[install](https://docs.docker.com/get-docker/) docker on your local machine.
|
||||
|
||||
## Building
|
||||
|
||||
To produce a verifiable build, run
|
||||
|
||||
```bash
|
||||
anchor build --verifiable
|
||||
```
|
||||
|
||||
## Verifying
|
||||
|
||||
To verify a build against a program deployed on mainnet, run
|
||||
|
||||
```bash
|
||||
anchor verify -p <lib-name> <program-id>
|
||||
```
|
||||
|
||||
where the `<lib-name>` is defined by your program's Cargo.toml.
|
||||
|
||||
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.24.2` one can run
|
||||
|
||||
```
|
||||
docker pull projectserum/build:v0.24.2
|
||||
```
|
||||
|
||||
## Removing an Image
|
||||
In the event you run a verifiable build from the CLI and exit prematurely,
|
||||
it's possible the docker image may still be building in the background.
|
||||
|
||||
To remove, run
|
||||
|
||||
```
|
||||
docker rm -f anchor-program
|
||||
```
|
||||
|
||||
where `anchor-program` is the name of the image created by default from within
|
||||
the Anchor CLI.
|
|
@ -1,23 +0,0 @@
|
|||
---
|
||||
home: true
|
||||
heroText: Anchor
|
||||
tagline: A framework for building Solana programs
|
||||
actionText: Get Started →
|
||||
actionLink: /getting-started/introduction
|
||||
features:
|
||||
- title: Security
|
||||
details: Anchor eliminates many footguns of raw Solana programs by default and allows you to add more security checks without disrupting your business logic.
|
||||
- title: Code Generation
|
||||
details: (De)Serialization, cross-program invocations, account initialization, and more.
|
||||
- title: IDL & Client Generation
|
||||
details: Anchor generates an IDL based on your program and automatically creates a typescript client with it.
|
||||
- title: Verifiability
|
||||
details: Anchor programs can be built verifiably, so users know that the on-chain program matches the code base.
|
||||
- title: Workspace Management
|
||||
details: The CLI helps you manage workspaces with multiple programs, e2e tests, and more.
|
||||
- title: Compatibility
|
||||
details: Anchor programs can interact with all non-anchor programs on Solana.
|
||||
|
||||
footer: Apache License 2.0
|
||||
---
|
||||
<div style="border: 2px solid red; text-align: center; padding: 10px 10px 10px 10px; box-sizing: border-box"> This documentation is being sunset in favor of <a href="https://book.anchor-lang.com" rel="noopener noreferrer" target="_blank">The Anchor Book</a>. At this point in time, either documentation may contain information that the other does not.</div>
|
|
@ -1,189 +0,0 @@
|
|||
# A Minimal Example
|
||||
|
||||
Here, we introduce Anchor's core syntax elements and project workflow. This tutorial assumes all
|
||||
[prerequisites](../getting-started/installation.md) are installed.
|
||||
|
||||
## Clone the Repo
|
||||
|
||||
To get started, clone the repo.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/project-serum/anchor
|
||||
```
|
||||
|
||||
Next, checkout the tagged branch of the same version of the anchor cli you have installed.
|
||||
|
||||
```bash
|
||||
git checkout tags/<version>
|
||||
```
|
||||
|
||||
Change directories to the [example](https://github.com/project-serum/anchor/tree/master/examples/tutorial/basic-0).
|
||||
|
||||
```bash
|
||||
cd anchor/examples/tutorial/basic-0
|
||||
```
|
||||
|
||||
And install any additional JavaScript dependencies:
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
## Starting a Localnet
|
||||
|
||||
In a separate terminal, start a local network. If you're running solana
|
||||
for the first time, generate a wallet.
|
||||
|
||||
```
|
||||
solana-keygen new
|
||||
```
|
||||
|
||||
Then run
|
||||
|
||||
```
|
||||
solana-test-validator
|
||||
```
|
||||
|
||||
Then, shut it down.
|
||||
|
||||
The test validator will be used when testing Anchor programs. Make sure to turn off the validator before you begin testing Anchor programs.
|
||||
|
||||
::: details
|
||||
As you'll see later, starting a localnet manually like this is not necessary when testing with Anchor,
|
||||
but is done for educational purposes in this tutorial.
|
||||
:::
|
||||
|
||||
## Defining a Program
|
||||
|
||||
We define the minimum viable program as follows.
|
||||
|
||||
<<< @/../examples/tutorial/basic-0/programs/basic-0/src/lib.rs
|
||||
|
||||
* `#[program]` First, notice that a program is defined with the `#[program]` attribute, where each
|
||||
inner method defines an RPC request handler, or, in Solana parlance, an "instruction"
|
||||
handler. These handlers are the entrypoints to your program that clients may invoke, as
|
||||
we will see soon.
|
||||
|
||||
* `Context<Initialize>` The first parameter of _every_ RPC handler is the `Context` struct, which is a simple
|
||||
container for the currently executing `program_id` generic over
|
||||
`Accounts`--here, the `Initialize` struct.
|
||||
|
||||
* `#[derive(Accounts)]` The `Accounts` derive macro marks a struct containing all the accounts that must be
|
||||
specified for a given instruction. To understand Accounts on Solana, see the
|
||||
[docs](https://docs.solana.com/developing/programming-model/accounts).
|
||||
In subsequent tutorials, we'll demonstrate how an `Accounts` struct can be used to
|
||||
specify constraints on accounts given to your program. Since this example doesn't touch any
|
||||
accounts, we skip this (important) detail.
|
||||
|
||||
## Building and Emitting an IDL
|
||||
|
||||
After creating a program, you can use the `anchor` CLI to build and emit an IDL, from which clients
|
||||
can be generated.
|
||||
|
||||
```bash
|
||||
anchor build
|
||||
```
|
||||
|
||||
::: details
|
||||
The `build` command is a convenience combining two steps.
|
||||
|
||||
1) `cargo build-bpf`
|
||||
2) `anchor idl parse -f program/src/lib.rs -o target/idl/basic_0.json`.
|
||||
:::
|
||||
|
||||
Once run, you should see your build artifacts, as usual, in your `target/` directory. Additionally,
|
||||
a `target/idl/basic_0.json` file is created. Inspecting its contents you should see
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"name": "basic_0",
|
||||
"instructions": [
|
||||
{
|
||||
"name": "initialize",
|
||||
"accounts": [],
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
From this file a client can be generated. Note that this file is created by parsing the `src/lib.rs`
|
||||
file in your program's crate.
|
||||
|
||||
::: tip
|
||||
If you've developed on Ethereum, the IDL is analogous to the `abi.json`.
|
||||
:::
|
||||
|
||||
## Deploying
|
||||
|
||||
Once built, we can deploy the program by running
|
||||
|
||||
```bash
|
||||
anchor deploy
|
||||
```
|
||||
|
||||
Take note of the program's deployed address. We'll use it next.
|
||||
|
||||
## Generating a Client
|
||||
|
||||
Now that we've built a program, deployed it to a local cluster, and generated an IDL,
|
||||
we can use the IDL to generate a client to speak to our on-chain program. For example,
|
||||
see [client.js](https://github.com/project-serum/anchor/tree/master/examples/tutorial/basic-0/client.js).
|
||||
|
||||
<<< @/../examples/tutorial/basic-0/client.js#main
|
||||
|
||||
Notice how we dynamically created the `initialize` method under
|
||||
the `rpc` namespace.
|
||||
|
||||
Now, make sure to plugin your program's address into `<YOUR-PROGRAM-ID>` (a mild
|
||||
annoyance that we'll address next). In order to run the client, you'll also need the path
|
||||
to your wallet's keypair you generated when you ran `solana-keygen new`; you can find it
|
||||
by running
|
||||
|
||||
```bash
|
||||
solana config get keypair
|
||||
```
|
||||
|
||||
Once you've got it, run the client with the environment variable `ANCHOR_WALLET` set to
|
||||
that path, e.g.
|
||||
|
||||
```bash
|
||||
ANCHOR_WALLET=<YOUR-KEYPAIR-PATH> node client.js
|
||||
```
|
||||
|
||||
You just successfully created a client and executed a transaction on your localnet.
|
||||
|
||||
## Workspaces
|
||||
|
||||
So far we've seen the basics of how to create, deploy, and make RPCs to a program, but
|
||||
deploying a program, copy and pasting the address, and explicitly reading
|
||||
an IDL is all a bit tedious, and can easily get out of hand the more tests and the more
|
||||
programs you have. For this reason, we introduce the concept of a workspace.
|
||||
|
||||
Inspecting [tests/basic-0.js](https://github.com/project-serum/anchor/tree/master/examples/tutorial/basic-0/tests/basic-0.js), we see the above example can be reduced to
|
||||
|
||||
<<< @/../examples/tutorial/basic-0/tests/basic-0.js#code
|
||||
|
||||
The `workspace` namespace provides access to all programs in the local project and is
|
||||
automatically updated to reflect the latest deployment, making it easy to change
|
||||
your program, update your JavaScript, and run your tests in a fast feedback loop.
|
||||
|
||||
::: tip NOTE
|
||||
For now, the workspace feature is only available when running the `anchor test` command,
|
||||
which will automatically `build`, `deploy`, and `test` all programs against a localnet
|
||||
in one command.
|
||||
:::
|
||||
|
||||
Finally, we can run the test. Don't forget to kill the local validator started earlier.
|
||||
We won't need to start one manually for any future tutorials.
|
||||
|
||||
```bash
|
||||
anchor test
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
We've introduced the basic syntax of writing programs in Anchor along with a productive
|
||||
workflow for building and testing. However, programs aren't all that interesting without
|
||||
interacting with persistent state. We'll cover that next.
|
|
@ -1,106 +0,0 @@
|
|||
# Arguments and Accounts
|
||||
|
||||
This tutorial covers the basics of creating and mutating accounts using Anchor.
|
||||
It's recommended to read [Tutorial 0](./tutorial-0.md) first, as this tutorial will
|
||||
build on top of it.
|
||||
|
||||
## Clone the Repo
|
||||
|
||||
To get started, clone the repo.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/project-serum/anchor
|
||||
```
|
||||
|
||||
Change directories to the [example](https://github.com/project-serum/anchor/tree/master/examples/tutorial/basic-1).
|
||||
|
||||
```bash
|
||||
cd anchor/examples/tutorial/basic-1
|
||||
```
|
||||
|
||||
And install any additional JavaScript dependencies:
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
## Defining a Program
|
||||
|
||||
We define our program as follows
|
||||
|
||||
<<< @/../examples/tutorial/basic-1/programs/basic-1/src/lib.rs
|
||||
|
||||
Some new syntax elements are introduced here.
|
||||
|
||||
### `initialize` instruction
|
||||
|
||||
First, let's start with the initialize instruction. Notice the `data` argument passed into the program. This argument and any other valid
|
||||
Rust types can be passed to the instruction to define inputs to the program.
|
||||
|
||||
Additionally,
|
||||
notice how we take a mutable reference to `my_account` and assign the `data` to it. This leads us to
|
||||
the `Initialize` struct, deriving `Accounts`. There are two things to notice about `Initialize`.
|
||||
|
||||
1. The `my_account` field is of type `Account<'info, MyAccount>` and the deserialized data structure is `MyAccount`.
|
||||
2. The `my_account` field is marked with the `init` attribute. This will create a new
|
||||
account owned by the current program, zero initialized. When using `init`, one must also provide
|
||||
`payer`, which will fund the account creation, `space`, which defines how large the account should be,
|
||||
and the `system_program`, which is required by the runtime for creating the account.
|
||||
|
||||
::: details
|
||||
All accounts created with Anchor are laid out as follows: `8-byte-discriminator || borsh
|
||||
serialized data`. The 8-byte-discriminator is created from the first 8 bytes of the
|
||||
`Sha256` hash of the account's type--using the example above, `sha256("account:MyAccount")[..8]`.
|
||||
The `account:` is a fixed prefix.
|
||||
|
||||
Importantly, this allows a program to know for certain an account is indeed of a given type.
|
||||
Without it, a program would be vulnerable to account injection attacks, where a malicious user
|
||||
specifies an account of an unexpected type, causing the program to do unexpected things.
|
||||
|
||||
On account creation, this 8-byte discriminator doesn't exist, since the account storage is
|
||||
zeroed. The first time an Anchor program mutates an account, this discriminator is prepended
|
||||
to the account storage array and all subsequent accesses to the account (not decorated with
|
||||
`#[account(init)]`) will check for this discriminator.
|
||||
:::
|
||||
|
||||
### `update` instruction
|
||||
|
||||
Similarly, the `Update` accounts struct is marked with the `#[account(mut)]` attribute.
|
||||
Marking an account as `mut` persists any changes made upon exiting the program.
|
||||
|
||||
Here we've covered the basics of how to interact with accounts. In a later tutorial,
|
||||
we'll delve more deeply into deriving `Accounts`, but for now, just know
|
||||
you must mark an account `init` when using it for the first time and `mut`
|
||||
for persisting changes.
|
||||
|
||||
## Creating and Initializing Accounts
|
||||
|
||||
We can interact with the program as follows.
|
||||
|
||||
<<< @/../examples/tutorial/basic-1/tests/basic-1.js#code-simplified
|
||||
|
||||
The last element passed into the method is common amongst all dynamically generated
|
||||
methods on the `rpc` namespace, containing several options for a transaction. Here,
|
||||
we specify the `accounts` field, an object of all the addresses the transaction
|
||||
needs to touch, and the `signers` array of all `Signer` objects needed to sign the
|
||||
transaction. Because `myAccount` is being created, the Solana runtime requires it
|
||||
to sign the transaction.
|
||||
|
||||
::: details
|
||||
If you've developed on Solana before, you might notice two things 1) the ordering of the accounts doesn't
|
||||
matter and 2) the `isWritable` and `isSigner`
|
||||
options are not specified on the account anywhere. In both cases, the framework takes care
|
||||
of these details for you, by reading the IDL.
|
||||
:::
|
||||
|
||||
As before, we can run the example tests.
|
||||
|
||||
```
|
||||
anchor test
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
We've covered all the basics of developing applications using Anchor. However, we've
|
||||
left out one important aspect to ensure the security of our programs--validating input
|
||||
and access control. We'll cover that next.
|
|
@ -1,77 +0,0 @@
|
|||
# Account Constraints and Access Control
|
||||
|
||||
This tutorial covers how to specify constraints and access control on accounts, a problem
|
||||
somewhat unique to the parallel nature of Solana.
|
||||
|
||||
On Solana, a transaction must specify all accounts required for execution. And because an untrusted client specifies those accounts, a program must responsibly validate all such accounts are what the client claims they are--in addition to any instruction specific access control the program needs to do.
|
||||
|
||||
For example, you could imagine easily writing a faulty token program that forgets to check if the **signer** of a transaction claiming to be the **owner** of a Token `Account` actually matches the **owner** on that account. Furthermore, imagine what might happen if the program expects a `Mint` account but a malicious user gives a token `Account`.
|
||||
|
||||
To address these problems, Anchor provides several types, traits, and macros. It's easiest to understand by seeing how they're used in an example, but a couple include
|
||||
|
||||
- [Accounts](https://docs.rs/anchor-lang/latest/anchor_lang/derive.Accounts.html): derive macro implementing the `Accounts` [trait](https://docs.rs/anchor-lang/latest/anchor_lang/trait.Accounts.html), allowing a struct to transform
|
||||
from the untrusted `&[AccountInfo]` slice given to a Solana program into a validated struct
|
||||
of deserialized account types.
|
||||
- [#[account]](https://docs.rs/anchor-lang/latest/anchor_lang/attr.account.html): attribute macro implementing [AccountSerialize](https://docs.rs/anchor-lang/latest/anchor_lang/trait.AccountSerialize.html) and [AccountDeserialize](https://docs.rs/anchor-lang/latest/anchor_lang/trait.AnchorDeserialize.html), automatically prepending a unique 8 byte discriminator to the account array. The discriminator is defined by the first 8 bytes of the `Sha256` hash of the account's Rust identifier--i.e., the struct type name--and ensures no account can be substituted for another.
|
||||
- [Account](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/account/struct.Account.html): a wrapper type for a deserialized account implementing `AccountDeserialize`. Using this type within an `Accounts` struct will ensure the account is **owned** by the address defined by `declare_id!` where the inner account was defined.
|
||||
|
||||
With the above, we can define preconditions for any instruction handler expecting a certain set of
|
||||
accounts, allowing us to more easily reason about the security of our programs.
|
||||
|
||||
## Clone the Repo
|
||||
|
||||
To get started, clone the repo.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/project-serum/anchor
|
||||
```
|
||||
|
||||
Change directories to the [example](https://github.com/project-serum/anchor/tree/master/examples/tutorial/basic-2).
|
||||
|
||||
```bash
|
||||
cd anchor/examples/tutorial/basic-2
|
||||
```
|
||||
|
||||
And install any additional JavaScript dependencies:
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
## Defining a Program
|
||||
|
||||
Here we have a simple **Counter** program, where anyone can create a counter, but only the assigned
|
||||
**authority** can increment it.
|
||||
|
||||
<<< @/../examples/tutorial/basic-2/programs/basic-2/src/lib.rs
|
||||
|
||||
If you've gone through the previous tutorials the `create` instruction should be straightforward.
|
||||
Let's focus on the `increment` instruction, specifically the `Increment` struct deriving
|
||||
`Accounts`.
|
||||
|
||||
```rust
|
||||
#[derive(Accounts)]
|
||||
pub struct Increment<'info> {
|
||||
#[account(mut, has_one = authority)]
|
||||
pub counter: Account<'info, Counter>,
|
||||
pub authority: Signer<'info>,
|
||||
}
|
||||
```
|
||||
|
||||
Here, a couple `#[account(..)]` attributes are used.
|
||||
|
||||
- `mut`: tells the program to persist all changes to the account.
|
||||
- `has_one`: enforces the constraint that `Increment.counter.authority == Increment.authority.key`.
|
||||
|
||||
Another new concept here is the `Signer` type. This enforces the constraint that the `authority`
|
||||
account **signed** the transaction. However, anchor doesn't fetch the data on that account.
|
||||
|
||||
If any of these constraints do not hold, then the `increment` instruction will never be executed.
|
||||
This allows us to completely separate account validation from our program's business logic, allowing us
|
||||
to reason about each concern more easily. For more, see the full [list](https://docs.rs/anchor-lang/latest/anchor_lang/derive.Accounts.html) of account constraints.
|
||||
|
||||
## Next Steps
|
||||
|
||||
We've covered the basics for writing a single program using Anchor on Solana. But the power of
|
||||
blockchains come not from a single program, but from combining multiple _composable_ programs
|
||||
(buzzword...check). Next, we'll see how to call one program from another.
|
|
@ -1,84 +0,0 @@
|
|||
# Cross Program Invocations (CPI)
|
||||
|
||||
This tutorial covers how to call one program from another, a process known as
|
||||
*cross-program-invocation* (CPI).
|
||||
|
||||
## Clone the Repo
|
||||
|
||||
To get started, clone the repo.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/project-serum/anchor
|
||||
```
|
||||
|
||||
Change directories to the [example](https://github.com/project-serum/anchor/tree/master/examples/tutorial/basic-3).
|
||||
|
||||
```bash
|
||||
cd anchor/examples/tutorial/basic-3
|
||||
```
|
||||
|
||||
And install any additional JavaScript dependencies:
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
## Defining a Puppet Program
|
||||
|
||||
We start with the program that will be called by another program, the puppet.
|
||||
|
||||
<<< @/../examples/tutorial/basic-3/programs/puppet/src/lib.rs
|
||||
|
||||
If you've followed along the other tutorials, this should be straight forward. We have
|
||||
a program with two instructions, `initialize`, which does nothing other than the
|
||||
initialization of the account (remember, the program *transparently* prepends a unique 8
|
||||
byte discriminator the first time an account is used), and `set_data`, which takes a previously
|
||||
initialized account, and sets its data field.
|
||||
|
||||
Now, suppose we wanted to call `set_data` from another program.
|
||||
|
||||
## Defining a Puppet Master Program
|
||||
|
||||
We define a new `puppet-master` crate, which successfully executes the Puppet program's `set_data`
|
||||
instruction via CPI.
|
||||
|
||||
<<< @/../examples/tutorial/basic-3/programs/puppet-master/src/lib.rs#core
|
||||
|
||||
Things to notice
|
||||
|
||||
* We create a `CpiContext` object with the target instruction's accounts and program,
|
||||
here `SetData` and `puppet_program`.
|
||||
* To invoke an instruction on another program, just use the `cpi` module on the crate, here, `puppet::cpi::set_data`.
|
||||
* Our `Accounts` struct contains the puppet account we are calling into via CPI. Accounts used for CPI are not specifically denoted
|
||||
as such with the `CpiAccount` label since v0.15. Accounts used for CPI are not fundamentally different from `Program` or `Signer`
|
||||
accounts except for their role and ownership in the specific context in which they are used.
|
||||
|
||||
::: tip
|
||||
When using another Anchor program for CPI, make sure to specify the `cpi` feature in your `Cargo.toml`.
|
||||
If you look at the `Cargo.toml` for this example, you'll see
|
||||
`puppet = { path = "../puppet", features = ["cpi"] }`.
|
||||
:::
|
||||
|
||||
## Signer Seeds
|
||||
|
||||
Often it's useful for a program to sign instructions. For example, if a program controls a token
|
||||
account and wants to send tokens to another account, it must sign. In Solana, this is done by specifying
|
||||
"signer seeds" on CPI. To do this using the example above, simply change
|
||||
`CpiContext::new(cpi_accounts, cpi_program)` to
|
||||
`CpiContext::new_with_signer(cpi_accounts, cpi_program, signer_seeds)`.
|
||||
|
||||
For more background on signing with program derived addresses, see the official Solana [documentation](https://docs.solana.com/developing/programming-model/calling-between-programs#program-signed-accounts).
|
||||
|
||||
## Return values
|
||||
|
||||
Solana currently has no way to return values from CPI, alas. However, you can approximate this
|
||||
by having the callee write return values to an account and the caller read that account to
|
||||
retrieve the return value. In future work, Anchor should do this transparently.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Now that you can have your programs call other programs, you should be able to access all the work being done by other developers in your own applications!
|
||||
|
||||
## Next Steps
|
||||
|
||||
We just covered Cross Program Invocation and showed how anchor can handle talking to multiple different programs in the solana ecosystem. In the next step, we will teach you how to handle errors and in Anchor.
|
|
@ -1,56 +0,0 @@
|
|||
# Errors
|
||||
|
||||
If you've ever programmed on a blockchain, you've probably been frustrated by
|
||||
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
|
||||
|
||||
For example,
|
||||
|
||||
```rust
|
||||
use anchor_lang::prelude::*;
|
||||
|
||||
#[program]
|
||||
mod errors {
|
||||
use super::*;
|
||||
pub fn hello(_ctx: Context<Hello>) -> Result<()> {
|
||||
Err(error!(ErrorCode::Hello))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct Hello {}
|
||||
|
||||
#[error_code]
|
||||
pub enum ErrorCode {
|
||||
#[msg("This is an error message clients will automatically display")]
|
||||
Hello,
|
||||
}
|
||||
```
|
||||
|
||||
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 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.
|
||||
|
||||
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
|
||||
|
||||
When using the client, we get the error message.
|
||||
|
||||
```javascript
|
||||
try {
|
||||
const tx = await program.rpc.hello();
|
||||
assert.ok(false);
|
||||
} catch (err) {
|
||||
const errMsg = "This is an error message clients will automatically display";
|
||||
assert.equal(err.toString(), errMsg);
|
||||
}
|
||||
```
|
||||
|
||||
It's that easy. :)
|
||||
|
||||
To run the full example, go [here](https://github.com/project-serum/anchor/tree/master/examples/tutorial/basic-4).
|
|
@ -1,9 +0,0 @@
|
|||
[provider]
|
||||
cluster = "localnet"
|
||||
wallet = "~/.config/solana/id.json"
|
||||
|
||||
[programs.localnet]
|
||||
basic_0 = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
|
||||
|
||||
[scripts]
|
||||
test = "yarn run mocha -t 1000000 tests/"
|
|
@ -1,4 +0,0 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"programs/*"
|
||||
]
|
|
@ -1,29 +0,0 @@
|
|||
// client.js is used to introduce the reader to generating clients from IDLs.
|
||||
// It is not expected users directly test with this example. For a more
|
||||
// ergonomic example, see `tests/basic-0.js` in this workspace.
|
||||
|
||||
const anchor = require("@project-serum/anchor");
|
||||
|
||||
// Configure the local cluster.
|
||||
anchor.setProvider(anchor.AnchorProvider.local());
|
||||
|
||||
async function main() {
|
||||
// #region main
|
||||
// Read the generated IDL.
|
||||
const idl = JSON.parse(
|
||||
require("fs").readFileSync("./target/idl/basic_0.json", "utf8")
|
||||
);
|
||||
|
||||
// Address of the deployed program.
|
||||
const programId = new anchor.web3.PublicKey("<YOUR-PROGRAM-ID>");
|
||||
|
||||
// Generate the program client from IDL.
|
||||
const program = new anchor.Program(idl, programId);
|
||||
|
||||
// Execute the RPC.
|
||||
await program.rpc.initialize();
|
||||
// #endregion main
|
||||
}
|
||||
|
||||
console.log("Running client.");
|
||||
main().then(() => console.log("Success"));
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"name": "basic-0",
|
||||
"version": "0.24.2",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/project-serum/anchor#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/project-serum/anchor/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/project-serum/anchor.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=11"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "anchor test --skip-lint"
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
[package]
|
||||
name = "basic-0"
|
||||
version = "0.1.0"
|
||||
description = "Created with Anchor"
|
||||
rust-version = "1.56"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
name = "basic_0"
|
||||
|
||||
[features]
|
||||
no-entrypoint = []
|
||||
cpi = ["no-entrypoint"]
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = { path = "../../../../../lang" }
|
|
@ -1,2 +0,0 @@
|
|||
[target.bpfel-unknown-unknown.dependencies.std]
|
||||
features = []
|
|
@ -1,14 +0,0 @@
|
|||
use anchor_lang::prelude::*;
|
||||
|
||||
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
||||
|
||||
#[program]
|
||||
mod basic_0 {
|
||||
use super::*;
|
||||
pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct Initialize {}
|
|
@ -1,16 +0,0 @@
|
|||
const anchor = require("@project-serum/anchor");
|
||||
|
||||
describe("basic-0", () => {
|
||||
// Configure the client to use the local cluster.
|
||||
anchor.setProvider(anchor.AnchorProvider.local());
|
||||
|
||||
it("Uses the workspace to invoke the initialize instruction", async () => {
|
||||
// #region code
|
||||
// Read the deployed program from the workspace.
|
||||
const program = anchor.workspace.Basic0;
|
||||
|
||||
// Execute the RPC.
|
||||
await program.rpc.initialize();
|
||||
// #endregion code
|
||||
});
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
[provider]
|
||||
cluster = "localnet"
|
||||
wallet = "~/.config/solana/id.json"
|
||||
|
||||
[programs.localnet]
|
||||
basic_1 = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
|
||||
|
||||
[scripts]
|
||||
test = "yarn run mocha -t 1000000 tests/"
|
|
@ -1,4 +0,0 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"programs/*"
|
||||
]
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"name": "basic-1",
|
||||
"version": "0.24.2",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/project-serum/anchor#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/project-serum/anchor/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/project-serum/anchor.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=11"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "anchor test --skip-lint"
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
[package]
|
||||
name = "basic-1"
|
||||
version = "0.1.0"
|
||||
description = "Created with Anchor"
|
||||
rust-version = "1.56"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
name = "basic_1"
|
||||
|
||||
[features]
|
||||
no-entrypoint = []
|
||||
cpi = ["no-entrypoint"]
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = { path = "../../../../../lang" }
|
|
@ -1,2 +0,0 @@
|
|||
[target.bpfel-unknown-unknown.dependencies.std]
|
||||
features = []
|
|
@ -1,40 +0,0 @@
|
|||
use anchor_lang::prelude::*;
|
||||
|
||||
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
||||
|
||||
#[program]
|
||||
mod basic_1 {
|
||||
use super::*;
|
||||
|
||||
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) -> Result<()> {
|
||||
let my_account = &mut ctx.accounts.my_account;
|
||||
my_account.data = data;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct Initialize<'info> {
|
||||
#[account(init, payer = user, space = 8 + 8)]
|
||||
pub my_account: Account<'info, MyAccount>,
|
||||
#[account(mut)]
|
||||
pub user: Signer<'info>,
|
||||
pub system_program: Program<'info, System>,
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct Update<'info> {
|
||||
#[account(mut)]
|
||||
pub my_account: Account<'info, MyAccount>,
|
||||
}
|
||||
|
||||
#[account]
|
||||
pub struct MyAccount {
|
||||
pub data: u64,
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
const assert = require("assert");
|
||||
const anchor = require("@project-serum/anchor");
|
||||
const { SystemProgram } = anchor.web3;
|
||||
|
||||
describe("basic-1", () => {
|
||||
// Use a local provider.
|
||||
const provider = anchor.AnchorProvider.local();
|
||||
|
||||
// Configure the client to use the local cluster.
|
||||
anchor.setProvider(provider);
|
||||
|
||||
it("Creates and initializes an account in a single atomic transaction (simplified)", async () => {
|
||||
// #region code-simplified
|
||||
// The program to execute.
|
||||
const program = anchor.workspace.Basic1;
|
||||
|
||||
// The Account to create.
|
||||
const myAccount = anchor.web3.Keypair.generate();
|
||||
|
||||
// Create the new account and initialize it with the program.
|
||||
// #region code-simplified
|
||||
await program.rpc.initialize(new anchor.BN(1234), {
|
||||
accounts: {
|
||||
myAccount: myAccount.publicKey,
|
||||
user: provider.wallet.publicKey,
|
||||
systemProgram: SystemProgram.programId,
|
||||
},
|
||||
signers: [myAccount],
|
||||
});
|
||||
// #endregion code-simplified
|
||||
|
||||
// Fetch the newly created account from the cluster.
|
||||
const account = await program.account.myAccount.fetch(myAccount.publicKey);
|
||||
|
||||
// Check it's state was initialized.
|
||||
assert.ok(account.data.eq(new anchor.BN(1234)));
|
||||
|
||||
// Store the account for the next test.
|
||||
_myAccount = myAccount;
|
||||
});
|
||||
|
||||
it("Updates a previously created account", async () => {
|
||||
const myAccount = _myAccount;
|
||||
|
||||
// #region update-test
|
||||
|
||||
// The program to execute.
|
||||
const program = anchor.workspace.Basic1;
|
||||
|
||||
// Invoke the update rpc.
|
||||
await program.rpc.update(new anchor.BN(4321), {
|
||||
accounts: {
|
||||
myAccount: myAccount.publicKey,
|
||||
},
|
||||
});
|
||||
|
||||
// Fetch the newly updated account.
|
||||
const account = await program.account.myAccount.fetch(myAccount.publicKey);
|
||||
|
||||
// Check it's state was mutated.
|
||||
assert.ok(account.data.eq(new anchor.BN(4321)));
|
||||
|
||||
// #endregion update-test
|
||||
});
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
[provider]
|
||||
cluster = "localnet"
|
||||
wallet = "~/.config/solana/id.json"
|
||||
|
||||
[programs.localnet]
|
||||
basic_2 = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
|
||||
|
||||
[scripts]
|
||||
test = "yarn run mocha -t 1000000 tests/"
|
|
@ -1,4 +0,0 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"programs/*"
|
||||
]
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"name": "basic-2",
|
||||
"version": "0.24.2",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/project-serum/anchor#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/project-serum/anchor/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/project-serum/anchor.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=11"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "anchor test --skip-lint"
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
[package]
|
||||
name = "basic-2"
|
||||
version = "0.1.0"
|
||||
description = "Created with Anchor"
|
||||
rust-version = "1.56"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
name = "basic_2"
|
||||
|
||||
[features]
|
||||
no-entrypoint = []
|
||||
cpi = ["no-entrypoint"]
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = { path = "../../../../../lang" }
|
|
@ -1,2 +0,0 @@
|
|||
[target.bpfel-unknown-unknown.dependencies.std]
|
||||
features = []
|
|
@ -1,43 +0,0 @@
|
|||
use anchor_lang::prelude::*;
|
||||
|
||||
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
||||
|
||||
#[program]
|
||||
mod basic_2 {
|
||||
use super::*;
|
||||
|
||||
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>) -> Result<()> {
|
||||
let counter = &mut ctx.accounts.counter;
|
||||
counter.count += 1;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct Create<'info> {
|
||||
#[account(init, payer = user, space = 8 + 40)]
|
||||
pub counter: Account<'info, Counter>,
|
||||
#[account(mut)]
|
||||
pub user: Signer<'info>,
|
||||
pub system_program: Program<'info, System>,
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct Increment<'info> {
|
||||
#[account(mut, has_one = authority)]
|
||||
pub counter: Account<'info, Counter>,
|
||||
pub authority: Signer<'info>,
|
||||
}
|
||||
|
||||
#[account]
|
||||
pub struct Counter {
|
||||
pub authority: Pubkey,
|
||||
pub count: u64,
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
const assert = require("assert");
|
||||
const anchor = require("@project-serum/anchor");
|
||||
const { SystemProgram } = anchor.web3;
|
||||
|
||||
describe("basic-2", () => {
|
||||
const provider = anchor.AnchorProvider.local();
|
||||
|
||||
// Configure the client to use the local cluster.
|
||||
anchor.setProvider(provider);
|
||||
|
||||
// Counter for the tests.
|
||||
const counter = anchor.web3.Keypair.generate();
|
||||
|
||||
// Program for the tests.
|
||||
const program = anchor.workspace.Basic2;
|
||||
|
||||
it("Creates a counter", async () => {
|
||||
await program.rpc.create(provider.wallet.publicKey, {
|
||||
accounts: {
|
||||
counter: counter.publicKey,
|
||||
user: provider.wallet.publicKey,
|
||||
systemProgram: SystemProgram.programId,
|
||||
},
|
||||
signers: [counter],
|
||||
});
|
||||
|
||||
let counterAccount = await program.account.counter.fetch(counter.publicKey);
|
||||
|
||||
assert.ok(counterAccount.authority.equals(provider.wallet.publicKey));
|
||||
assert.ok(counterAccount.count.toNumber() === 0);
|
||||
});
|
||||
|
||||
it("Updates a counter", async () => {
|
||||
await program.rpc.increment({
|
||||
accounts: {
|
||||
counter: counter.publicKey,
|
||||
authority: provider.wallet.publicKey,
|
||||
},
|
||||
});
|
||||
|
||||
const counterAccount = await program.account.counter.fetch(
|
||||
counter.publicKey
|
||||
);
|
||||
|
||||
assert.ok(counterAccount.authority.equals(provider.wallet.publicKey));
|
||||
assert.ok(counterAccount.count.toNumber() == 1);
|
||||
});
|
||||
});
|
|
@ -1,10 +0,0 @@
|
|||
[provider]
|
||||
cluster = "localnet"
|
||||
wallet = "~/.config/solana/id.json"
|
||||
|
||||
[programs.localnet]
|
||||
puppet = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
|
||||
puppet_master = "HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L"
|
||||
|
||||
[scripts]
|
||||
test = "yarn run mocha -t 1000000 tests/"
|
|
@ -1,4 +0,0 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"programs/*"
|
||||
]
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"name": "basic-3",
|
||||
"version": "0.24.2",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/project-serum/anchor#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/project-serum/anchor/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/project-serum/anchor.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=11"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "anchor test --skip-lint"
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
[package]
|
||||
name = "puppet-master"
|
||||
version = "0.1.0"
|
||||
description = "Created with Anchor"
|
||||
rust-version = "1.56"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
name = "puppet_master"
|
||||
|
||||
[features]
|
||||
no-entrypoint = []
|
||||
cpi = ["no-entrypoint"]
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = { path = "../../../../../lang" }
|
||||
puppet = { path = "../puppet", features = ["cpi"] }
|
|
@ -1,2 +0,0 @@
|
|||
[target.bpfel-unknown-unknown.dependencies.std]
|
||||
features = []
|
|
@ -1,28 +0,0 @@
|
|||
// #region core
|
||||
use anchor_lang::prelude::*;
|
||||
use puppet::cpi::accounts::SetData;
|
||||
use puppet::program::Puppet;
|
||||
use puppet::{self, Data};
|
||||
|
||||
declare_id!("HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L");
|
||||
|
||||
#[program]
|
||||
mod puppet_master {
|
||||
use super::*;
|
||||
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(),
|
||||
};
|
||||
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
|
||||
puppet::cpi::set_data(cpi_ctx, data)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct PullStrings<'info> {
|
||||
#[account(mut)]
|
||||
pub puppet: Account<'info, Data>,
|
||||
pub puppet_program: Program<'info, Puppet>,
|
||||
}
|
||||
// #endregion core
|
|
@ -1,17 +0,0 @@
|
|||
[package]
|
||||
name = "puppet"
|
||||
version = "0.1.0"
|
||||
description = "Created with Anchor"
|
||||
rust-version = "1.56"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
name = "puppet"
|
||||
|
||||
[features]
|
||||
no-entrypoint = []
|
||||
cpi = ["no-entrypoint"]
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = { path = "../../../../../lang" }
|
|
@ -1,2 +0,0 @@
|
|||
[target.bpfel-unknown-unknown.dependencies.std]
|
||||
features = []
|
|
@ -1,37 +0,0 @@
|
|||
use anchor_lang::prelude::*;
|
||||
|
||||
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
||||
|
||||
#[program]
|
||||
pub mod puppet {
|
||||
use super::*;
|
||||
pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_data(ctx: Context<SetData>, data: u64) -> Result<()> {
|
||||
let puppet = &mut ctx.accounts.puppet;
|
||||
puppet.data = data;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct Initialize<'info> {
|
||||
#[account(init, payer = user, space = 8 + 8)]
|
||||
pub puppet: Account<'info, Data>,
|
||||
#[account(mut)]
|
||||
pub user: Signer<'info>,
|
||||
pub system_program: Program<'info, System>,
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct SetData<'info> {
|
||||
#[account(mut)]
|
||||
pub puppet: Account<'info, Data>,
|
||||
}
|
||||
|
||||
#[account]
|
||||
pub struct Data {
|
||||
pub data: u64,
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
const assert = require("assert");
|
||||
const anchor = require("@project-serum/anchor");
|
||||
const { SystemProgram } = anchor.web3;
|
||||
|
||||
describe("basic-3", () => {
|
||||
const provider = anchor.AnchorProvider.local();
|
||||
|
||||
// Configure the client to use the local cluster.
|
||||
anchor.setProvider(provider);
|
||||
|
||||
it("Performs CPI from puppet master to puppet", async () => {
|
||||
const puppetMaster = anchor.workspace.PuppetMaster;
|
||||
const puppet = anchor.workspace.Puppet;
|
||||
|
||||
// Initialize a new puppet account.
|
||||
const newPuppetAccount = anchor.web3.Keypair.generate();
|
||||
const tx = await puppet.rpc.initialize({
|
||||
accounts: {
|
||||
puppet: newPuppetAccount.publicKey,
|
||||
user: provider.wallet.publicKey,
|
||||
systemProgram: SystemProgram.programId,
|
||||
},
|
||||
signers: [newPuppetAccount],
|
||||
});
|
||||
|
||||
// Invoke the puppet master to perform a CPI to the puppet.
|
||||
await puppetMaster.rpc.pullStrings(new anchor.BN(111), {
|
||||
accounts: {
|
||||
puppet: newPuppetAccount.publicKey,
|
||||
puppetProgram: puppet.programId,
|
||||
},
|
||||
});
|
||||
|
||||
// Check the state updated.
|
||||
puppetAccount = await puppet.account.data.fetch(newPuppetAccount.publicKey);
|
||||
assert.ok(puppetAccount.data.eq(new anchor.BN(111)));
|
||||
});
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
[provider]
|
||||
cluster = "localnet"
|
||||
wallet = "~/.config/solana/id.json"
|
||||
|
||||
[programs.localnet]
|
||||
basic_4 = "CwrqeMj2U8tFr1Rhkgwc84tpAsqbt9pTt2a4taoTADPr"
|
||||
|
||||
[scripts]
|
||||
test = "yarn run mocha -t 1000000 tests/"
|
|
@ -1,4 +0,0 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"programs/*"
|
||||
]
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"name": "basic-4",
|
||||
"version": "0.24.2",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/project-serum/anchor#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/project-serum/anchor/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/project-serum/anchor.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=11"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "anchor test --skip-lint"
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
[package]
|
||||
name = "basic-4"
|
||||
version = "0.1.0"
|
||||
description = "Created with Anchor"
|
||||
rust-version = "1.56"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
name = "basic_4"
|
||||
|
||||
[features]
|
||||
no-entrypoint = []
|
||||
cpi = ["no-entrypoint"]
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = { path = "../../../../../lang" }
|
|
@ -1,2 +0,0 @@
|
|||
[target.bpfel-unknown-unknown.dependencies.std]
|
||||
features = []
|
|
@ -1,44 +0,0 @@
|
|||
// #region code
|
||||
use anchor_lang::prelude::*;
|
||||
|
||||
declare_id!("CwrqeMj2U8tFr1Rhkgwc84tpAsqbt9pTt2a4taoTADPr");
|
||||
|
||||
#[program]
|
||||
pub mod basic_4 {
|
||||
use super::*;
|
||||
|
||||
#[state]
|
||||
pub struct Counter {
|
||||
pub authority: Pubkey,
|
||||
pub count: u64,
|
||||
}
|
||||
|
||||
impl Counter {
|
||||
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>) -> anchor_lang::Result<()> {
|
||||
if &self.authority != ctx.accounts.authority.key {
|
||||
return Err(error!(ErrorCode::Unauthorized));
|
||||
}
|
||||
self.count += 1;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct Auth<'info> {
|
||||
authority: Signer<'info>,
|
||||
}
|
||||
// #endregion code
|
||||
|
||||
#[error_code]
|
||||
pub enum ErrorCode {
|
||||
#[msg("You are not authorized to perform this action.")]
|
||||
Unauthorized,
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
const assert = require("assert");
|
||||
const anchor = require("@project-serum/anchor");
|
||||
|
||||
describe("basic-4", () => {
|
||||
const provider = anchor.AnchorProvider.local();
|
||||
|
||||
// Configure the client to use the local cluster.
|
||||
anchor.setProvider(provider);
|
||||
|
||||
const program = anchor.workspace.Basic4;
|
||||
|
||||
it("Is runs the constructor", async () => {
|
||||
// #region ctor
|
||||
// Initialize the program's state struct.
|
||||
await program.state.rpc.new({
|
||||
accounts: {
|
||||
authority: provider.wallet.publicKey,
|
||||
},
|
||||
});
|
||||
// #endregion ctor
|
||||
|
||||
// Fetch the state struct from the network.
|
||||
// #region accessor
|
||||
const state = await program.state.fetch();
|
||||
// #endregion accessor
|
||||
|
||||
assert.ok(state.count.eq(new anchor.BN(0)));
|
||||
});
|
||||
|
||||
it("Executes a method on the program", async () => {
|
||||
// #region instruction
|
||||
await program.state.rpc.increment({
|
||||
accounts: {
|
||||
authority: provider.wallet.publicKey,
|
||||
},
|
||||
});
|
||||
// #endregion instruction
|
||||
const state = await program.state.fetch();
|
||||
assert.ok(state.count.eq(new anchor.BN(1)));
|
||||
});
|
||||
});
|
|
@ -1,22 +0,0 @@
|
|||
{
|
||||
"name": "anchor-examples",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w",
|
||||
"lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check"
|
||||
},
|
||||
"workspaces": [
|
||||
"basic-0",
|
||||
"basic-1",
|
||||
"basic-2",
|
||||
"basic-3",
|
||||
"basic-4"
|
||||
],
|
||||
"dependencies": {
|
||||
"@project-serum/anchor": "file:../../ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "^9.1.3",
|
||||
"prettier": "^2.5.1"
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +0,0 @@
|
|||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
[package]
|
||||
name = "anchor-lang"
|
||||
version = "0.24.2"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/project-serum/anchor"
|
||||
rust-version = "1.56"
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
description = "Solana Sealevel eDSL"
|
||||
|
||||
[features]
|
||||
init-if-needed = ["anchor-derive-accounts/init-if-needed"]
|
||||
derive = []
|
||||
default = []
|
||||
anchor-debug = [
|
||||
"anchor-attribute-access-control/anchor-debug",
|
||||
"anchor-attribute-account/anchor-debug",
|
||||
"anchor-attribute-constant/anchor-debug",
|
||||
"anchor-attribute-error/anchor-debug",
|
||||
"anchor-attribute-event/anchor-debug",
|
||||
"anchor-attribute-interface/anchor-debug",
|
||||
"anchor-attribute-program/anchor-debug",
|
||||
"anchor-attribute-program/anchor-debug",
|
||||
"anchor-attribute-state/anchor-debug",
|
||||
"anchor-derive-accounts/anchor-debug"
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
anchor-attribute-access-control = { path = "./attribute/access-control", version = "0.24.2" }
|
||||
anchor-attribute-account = { path = "./attribute/account", version = "0.24.2" }
|
||||
anchor-attribute-constant = { path = "./attribute/constant", version = "0.24.2" }
|
||||
anchor-attribute-error = { path = "./attribute/error", version = "0.24.2" }
|
||||
anchor-attribute-program = { path = "./attribute/program", version = "0.24.2" }
|
||||
anchor-attribute-state = { path = "./attribute/state", version = "0.24.2" }
|
||||
anchor-attribute-interface = { path = "./attribute/interface", version = "0.24.2" }
|
||||
anchor-attribute-event = { path = "./attribute/event", version = "0.24.2" }
|
||||
anchor-derive-accounts = { path = "./derive/accounts", version = "0.24.2" }
|
||||
arrayref = "0.3.6"
|
||||
base64 = "0.13.0"
|
||||
borsh = "0.9"
|
||||
bytemuck = "1.4.0"
|
||||
solana-program = "~1.9.13"
|
||||
thiserror = "1.0.20"
|
||||
bincode = "1.3.3"
|
|
@ -1,23 +0,0 @@
|
|||
[package]
|
||||
name = "anchor-attribute-access-control"
|
||||
version = "0.24.2"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/project-serum/anchor"
|
||||
license = "Apache-2.0"
|
||||
description = "Anchor attribute macro for instruction access control"
|
||||
rust-version = "1.56"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[features]
|
||||
anchor-debug = ["anchor-syn/anchor-debug"]
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = { version = "1.0.60", features = ["full"] }
|
||||
anyhow = "1.0.32"
|
||||
anchor-syn = { path = "../../syn", version = "0.24.2" }
|
||||
regex = "1.0"
|
|
@ -1,81 +0,0 @@
|
|||
extern crate proc_macro;
|
||||
|
||||
use quote::quote;
|
||||
use syn::parse_macro_input;
|
||||
|
||||
/// Executes the given access control method before running the decorated
|
||||
/// instruction handler. Any method in scope of the attribute can be invoked
|
||||
/// with any arguments from the associated instruction handler.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// use anchor_lang::prelude::*;
|
||||
///
|
||||
/// #[program]
|
||||
/// mod errors {
|
||||
/// use super::*;
|
||||
///
|
||||
/// #[access_control(Create::accounts(&ctx, bump_seed))]
|
||||
/// pub fn create(ctx: Context<Create>, bump_seed: u8) -> Result<()> {
|
||||
/// let my_account = &mut ctx.accounts.my_account;
|
||||
/// my_account.bump_seed = bump_seed;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Accounts)]
|
||||
/// pub struct Create {
|
||||
/// #[account(init)]
|
||||
/// my_account: Account<'info, MyAccount>,
|
||||
/// }
|
||||
///
|
||||
/// impl Create {
|
||||
/// pub fn accounts(ctx: &Context<Create>, bump_seed: u8) -> Result<()> {
|
||||
/// let seeds = &[ctx.accounts.my_account.to_account_info().key.as_ref(), &[bump_seed]];
|
||||
/// Pubkey::create_program_address(seeds, ctx.program_id)
|
||||
/// .map_err(|_| ErrorCode::InvalidNonce)?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This example demonstrates a useful pattern. Not only can you use
|
||||
/// `#[access_control]` to ensure any invariants or preconditions hold prior to
|
||||
/// executing an instruction, but also it can be used to finish any validation
|
||||
/// on the `Accounts` struct, particularly when instruction arguments are
|
||||
/// needed. Here, we use the given `bump_seed` to verify it creates a valid
|
||||
/// program-derived address.
|
||||
#[proc_macro_attribute]
|
||||
pub fn access_control(
|
||||
args: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let mut args = args.to_string();
|
||||
args.retain(|c| !c.is_whitespace());
|
||||
let access_control: Vec<proc_macro2::TokenStream> = args
|
||||
.split(')')
|
||||
.filter(|ac| !ac.is_empty())
|
||||
.map(|ac| format!("{})", ac)) // Put back on the split char.
|
||||
.map(|ac| format!("{}?;", ac)) // Add `?;` syntax.
|
||||
.map(|ac| ac.parse().unwrap())
|
||||
.collect();
|
||||
|
||||
let item_fn = parse_macro_input!(input as syn::ItemFn);
|
||||
|
||||
let fn_attrs = item_fn.attrs;
|
||||
let fn_vis = item_fn.vis;
|
||||
let fn_sig = item_fn.sig;
|
||||
let fn_block = item_fn.block;
|
||||
|
||||
let fn_stmts = fn_block.stmts;
|
||||
|
||||
proc_macro::TokenStream::from(quote! {
|
||||
#(#fn_attrs)*
|
||||
#fn_vis #fn_sig {
|
||||
|
||||
#(#access_control)*
|
||||
|
||||
#(#fn_stmts)*
|
||||
}
|
||||
})
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
[package]
|
||||
name = "anchor-attribute-account"
|
||||
version = "0.24.2"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/project-serum/anchor"
|
||||
license = "Apache-2.0"
|
||||
description = "Anchor attribute macro for defining an account"
|
||||
rust-version = "1.56"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[features]
|
||||
anchor-debug = ["anchor-syn/anchor-debug"]
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = { version = "1.0.60", features = ["full"] }
|
||||
anyhow = "1.0.32"
|
||||
anchor-syn = { path = "../../syn", version = "0.24.2", features = ["hash"] }
|
||||
rustversion = "1.0.3"
|
||||
bs58 = "0.4.0"
|
|
@ -1,296 +0,0 @@
|
|||
//! Copied from solana/sdk/macro so that Anchor programs don't need to specify
|
||||
//! `solana_program` as an additional crate dependency, but instead can access
|
||||
//! it via `anchor_lang::declare_id`.
|
||||
//!
|
||||
//! Convenience macro to declare a static public key and functions to interact with it
|
||||
//!
|
||||
//! Input: a single literal base58 string representation of a program's id
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
use proc_macro2::{Delimiter, Span, TokenTree};
|
||||
use quote::{quote, ToTokens};
|
||||
use std::convert::TryFrom;
|
||||
use syn::{
|
||||
bracketed,
|
||||
parse::{Parse, ParseStream, Result},
|
||||
punctuated::Punctuated,
|
||||
token::Bracket,
|
||||
Expr, Ident, LitByte, LitStr, Path, Token,
|
||||
};
|
||||
|
||||
fn parse_id(
|
||||
input: ParseStream,
|
||||
pubkey_type: proc_macro2::TokenStream,
|
||||
) -> Result<proc_macro2::TokenStream> {
|
||||
let id = if input.peek(syn::LitStr) {
|
||||
let id_literal: LitStr = input.parse()?;
|
||||
parse_pubkey(&id_literal, &pubkey_type)?
|
||||
} else {
|
||||
let expr: Expr = input.parse()?;
|
||||
quote! { #expr }
|
||||
};
|
||||
|
||||
if !input.is_empty() {
|
||||
let stream: proc_macro2::TokenStream = input.parse()?;
|
||||
return Err(syn::Error::new_spanned(stream, "unexpected token"));
|
||||
}
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn id_to_tokens(
|
||||
id: &proc_macro2::TokenStream,
|
||||
pubkey_type: proc_macro2::TokenStream,
|
||||
tokens: &mut proc_macro2::TokenStream,
|
||||
) {
|
||||
tokens.extend(quote! {
|
||||
/// The static program ID
|
||||
pub static ID: #pubkey_type = #id;
|
||||
|
||||
/// Confirms that a given pubkey is equivalent to the program ID
|
||||
pub fn check_id(id: &#pubkey_type) -> bool {
|
||||
id == &ID
|
||||
}
|
||||
|
||||
/// Returns the program ID
|
||||
pub fn id() -> #pubkey_type {
|
||||
ID
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn test_id() {
|
||||
assert!(check_id(&id()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn deprecated_id_to_tokens(
|
||||
id: &proc_macro2::TokenStream,
|
||||
pubkey_type: proc_macro2::TokenStream,
|
||||
tokens: &mut proc_macro2::TokenStream,
|
||||
) {
|
||||
tokens.extend(quote! {
|
||||
/// The static program ID
|
||||
pub static ID: #pubkey_type = #id;
|
||||
|
||||
/// Confirms that a given pubkey is equivalent to the program ID
|
||||
#[deprecated()]
|
||||
pub fn check_id(id: &#pubkey_type) -> bool {
|
||||
id == &ID
|
||||
}
|
||||
|
||||
/// Returns the program ID
|
||||
#[deprecated()]
|
||||
pub fn id() -> #pubkey_type {
|
||||
ID
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn test_id() {
|
||||
#[allow(deprecated)]
|
||||
assert!(check_id(&id()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub struct Id(proc_macro2::TokenStream);
|
||||
|
||||
impl Parse for Id {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
parse_id(
|
||||
input,
|
||||
quote! { anchor_lang::solana_program::pubkey::Pubkey },
|
||||
)
|
||||
.map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Id {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
id_to_tokens(
|
||||
&self.0,
|
||||
quote! { anchor_lang::solana_program::pubkey::Pubkey },
|
||||
tokens,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct IdDeprecated(proc_macro2::TokenStream);
|
||||
|
||||
impl Parse for IdDeprecated {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
parse_id(
|
||||
input,
|
||||
quote! { anchor_lang::solana_program::pubkey::Pubkey },
|
||||
)
|
||||
.map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for IdDeprecated {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
deprecated_id_to_tokens(
|
||||
&self.0,
|
||||
quote! { anchor_lang::solana_program::pubkey::Pubkey },
|
||||
tokens,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct ProgramSdkId(proc_macro2::TokenStream);
|
||||
impl Parse for ProgramSdkId {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
parse_id(
|
||||
input,
|
||||
quote! { anchor_lang::solana_program::pubkey::Pubkey },
|
||||
)
|
||||
.map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ProgramSdkId {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
id_to_tokens(
|
||||
&self.0,
|
||||
quote! { anchor_lang::solana_program::pubkey::Pubkey },
|
||||
tokens,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct ProgramSdkIdDeprecated(proc_macro2::TokenStream);
|
||||
impl Parse for ProgramSdkIdDeprecated {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
parse_id(
|
||||
input,
|
||||
quote! { anchor_lang::solana_program::pubkey::Pubkey },
|
||||
)
|
||||
.map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ProgramSdkIdDeprecated {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
deprecated_id_to_tokens(
|
||||
&self.0,
|
||||
quote! { anchor_lang::solana_program::pubkey::Pubkey },
|
||||
tokens,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)] // `respan` may be compiled out
|
||||
struct RespanInput {
|
||||
to_respan: Path,
|
||||
respan_using: Span,
|
||||
}
|
||||
|
||||
impl Parse for RespanInput {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let to_respan: Path = input.parse()?;
|
||||
let _comma: Token![,] = input.parse()?;
|
||||
let respan_tree: TokenTree = input.parse()?;
|
||||
match respan_tree {
|
||||
TokenTree::Group(g) if g.delimiter() == Delimiter::None => {
|
||||
let ident: Ident = syn::parse2(g.stream())?;
|
||||
Ok(RespanInput {
|
||||
to_respan,
|
||||
respan_using: ident.span(),
|
||||
})
|
||||
}
|
||||
val => Err(syn::Error::new_spanned(
|
||||
val,
|
||||
"expected None-delimited group",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_pubkey(
|
||||
id_literal: &LitStr,
|
||||
pubkey_type: &proc_macro2::TokenStream,
|
||||
) -> Result<proc_macro2::TokenStream> {
|
||||
let id_vec = bs58::decode(id_literal.value())
|
||||
.into_vec()
|
||||
.map_err(|_| syn::Error::new_spanned(&id_literal, "failed to decode base58 string"))?;
|
||||
let id_array = <[u8; 32]>::try_from(<&[u8]>::clone(&&id_vec[..])).map_err(|_| {
|
||||
syn::Error::new_spanned(
|
||||
&id_literal,
|
||||
format!("pubkey array is not 32 bytes long: len={}", id_vec.len()),
|
||||
)
|
||||
})?;
|
||||
let bytes = id_array.iter().map(|b| LitByte::new(*b, Span::call_site()));
|
||||
Ok(quote! {
|
||||
#pubkey_type::new_from_array(
|
||||
[#(#bytes,)*]
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
struct Pubkeys {
|
||||
method: Ident,
|
||||
num: usize,
|
||||
pubkeys: proc_macro2::TokenStream,
|
||||
}
|
||||
impl Parse for Pubkeys {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let pubkey_type = quote! {
|
||||
anchor_lang::solana_program::pubkey::Pubkey
|
||||
};
|
||||
|
||||
let method = input.parse()?;
|
||||
let _comma: Token![,] = input.parse()?;
|
||||
let (num, pubkeys) = if input.peek(syn::LitStr) {
|
||||
let id_literal: LitStr = input.parse()?;
|
||||
(1, parse_pubkey(&id_literal, &pubkey_type)?)
|
||||
} else if input.peek(Bracket) {
|
||||
let pubkey_strings;
|
||||
bracketed!(pubkey_strings in input);
|
||||
let punctuated: Punctuated<LitStr, Token![,]> =
|
||||
Punctuated::parse_terminated(&pubkey_strings)?;
|
||||
let mut pubkeys: Punctuated<proc_macro2::TokenStream, Token![,]> = Punctuated::new();
|
||||
for string in punctuated.iter() {
|
||||
pubkeys.push(parse_pubkey(string, &pubkey_type)?);
|
||||
}
|
||||
(pubkeys.len(), quote! {#pubkeys})
|
||||
} else {
|
||||
let stream: proc_macro2::TokenStream = input.parse()?;
|
||||
return Err(syn::Error::new_spanned(stream, "unexpected token"));
|
||||
};
|
||||
|
||||
Ok(Pubkeys {
|
||||
method,
|
||||
num,
|
||||
pubkeys,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Pubkeys {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
let Pubkeys {
|
||||
method,
|
||||
num,
|
||||
pubkeys,
|
||||
} = self;
|
||||
|
||||
let pubkey_type = quote! {
|
||||
anchor_lang::solana_program::pubkey::Pubkey
|
||||
};
|
||||
if *num == 1 {
|
||||
tokens.extend(quote! {
|
||||
pub fn #method() -> #pubkey_type {
|
||||
#pubkeys
|
||||
}
|
||||
});
|
||||
} else {
|
||||
tokens.extend(quote! {
|
||||
pub fn #method() -> ::std::vec::Vec<#pubkey_type> {
|
||||
vec![#pubkeys]
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue