Skip to content

Latest commit

 

History

History
321 lines (200 loc) · 14.1 KB

0.31.0.md

File metadata and controls

321 lines (200 loc) · 14.1 KB
title description
Release Notes 0.31.0
Anchor - Release Notes 0.31.0

This release includes various stack memory improvements, ability to customize discriminators, and a brand new account type (LazyAccount).


How to upgrade

  1. Update anchor-cli:

    avm install 0.31.0
  2. Update Anchor crate(s) to 0.31.0.

  3. Update TS package(s) to 0.31.0.

Recommended Solana Version

The recommended Solana version is 2.0.8. This is a special upgrade because some of the Solana binaries have been renamed to Agave, see Agave transition.

You can install the newer tooling by running:

sh -c "$(curl -sSfL https://release.anza.xyz/v2.0.8/install)"

This change is handled automatically if you specify the toolchain.solana_version, see Automatic Agave transition.

IDL

Safety comment checks

In v0.30.1, you'd run into panics in Rust Analyzer if you enabled all features because the idl-build feature expected an environment variable to be set. This was always set if you used anchor build, but since Rust Analyzer did not know about this environment variable, it would panic:

custom attribute panicked
message: Safety checks failed: Failed to get program path

It no longer panics if the environment variable is missing.

address constraint with non-const expressions

There was a regression in v0.30 that made using non-const expressions with the address constraint fail:

#[derive(Accounts)]
pub struct MyIx {
    #[account(address = my_account.authority())]
    pub authority: Signer<'info>,
    pub my_account: Account<'info, MyAccount>,
}

This is now fixed and no longer results in build errors.

See coral-xyz/anchor#3216 for more information.

Program accounts with full paths

The following did not work in v0.30, but it works now:

#[derive(Accounts)]
pub struct FullPath<'info> {
    pub external_program: Program<'info, external::program::External>,
}

IdlBuilder

Building the IDL via the build_idl function made it impossible to extend its functionality e.g. add new parameters without a breaking change. To solve this problem, there is a new way to build IDLs programatically:

let idl = IdlBuilder::new().program_path(path).skip_lint(true).build()?;

See IdlBuilder for more information.

CLI

Automatic Agave transition

As mentioned in Recommended Solana version section, some of the Solana binaries are renamed to Agave. solana-install is deprecated in 1.18.19 and logs a deprecation message when used (this results in parsing failure in older Anchor versions):

⚠️  solana-install is deprecated and will be discontinued when v1.18 is no longer supported. Please switch to Agave: https://github.com/anza-xyz/agave/wiki/Agave-Transition

In this release, if you specify solana_version field with a version greater than 1.18.19, it automatically installs and switches to agave-install:

[toolchain]
solana_version = "2.0.8"

solana-program warning

Adding solana-program as a dependency of Anchor programs should be avoided to decrease the possibility of version conflicts between Solana v1 and v2 crates.

You'll see the following warning on builds if you have solana-program in your dependency list:

WARNING: Adding `solana-program` as a separate dependency might cause conflicts.
To solve, remove the `solana-program` dependency and use the exported crate from `anchor-lang`.
`use solana_program` becomes `use anchor_lang::solana_program`.
Program name: `my-program`

Pass cargo args to IDL build

Both anchor build and anchor idl build commands pass the cargo arguments to the underyling IDL build command. For example:

anchor build -- --features my-feature

now builds the IDL with my-feature enabled.

--no-idl flag for tests

If you make a change to your program, but the API of the program stays the same, building the IDL is pointless and takes additional time.

Similar to --no-idl flag on builds, now you can use:

anchor test --no-idl

clean

anchor clean now also removes the generated .anchor directory.

For those of you that don't know, this command is similar to cargo clean, but it keeps the program keypairs.

Shell completions

You can now generate shell completions, see Generating Shell Completions.

Lang

Stack memory improvements

This is going to be a longer section, TL;DR is stack usage is improved massively when using init constraints, and stack warnings on builds are now reliable when using Solana v2 (platform-tools v1.42). Keep reading if you're interested in learning more, otherwise you can skip to Custom discriminators.

The main place where Anchor programs are likely to hit stack violation errors is a generated function called try_accounts. This is where the instruction is deserialized and constraints are run. Although having everything at the same place is convenient for using constraints, this also makes it very easy to use the fixed amount of stack space (4096 bytes) SVM allocates just by increasing the number of accounts the instruction has. You might be familiar with it from the mangled build logs:

Error: Function _ZN71_$LT$pr..Test$u20$as$u20$anchor_lang..Accounts$LT$pr..TestBumps$GT$$GT$12try_accounts17h5572074d55b9e638E Stack offset of 4112 exceeded max offset of 4096 by 16 bytes, please minimize large stack variables.

The problem was made worse with the following external developments:

  1. Rust/LLVM changes shipped with Solana v1.18 made Anchor's try_accounts function use even more stack space, resulting in much lower threshold to hit stack-related problems.
  2. The stack logs were unreliable, meaning you wouldn't always get warnings during builds if you went over the stack limit.
  3. The feature "Account data direct mapping", which is enabled by default when using local-validator, made the programs run into undefined behavior when a function used more stack space than it had available.

All of this combined meant that your programs used more stack space (1), but sometimes the errors didn't get caught during the compile time (2) or even the runtime (3).

Undefined behavior is particularly challenging to debug because things may appear to work as expected during tests, but a different input to the same test might overwrite an account's data with unintended bytes. There are endless scenarios that could go wrong here, and some examples include #2955, #3113, and #3196.

There isn't much we can do for problems 1 and 3, but the problem 2 is resolved with Solana v2 (platform-tools v1.42), meaning you'll reliably get warnings about all stack problems.

Circling back to Anchor, the biggest offender to the stack usage was identified to be the init constraint. Each init constraint expands to a lengthy code inside try_accounts, resulting in excessive stack usage when multiple init constraints are used. This problem was fixed by moving the init code inside a closure and immediately calling it in order to create and use a different stack frame (#2939).

Related to this topic, there is SIMD-0166, a proposal to introduce dynamic stack frames.

In short, you'll have fewer problems related to stack memory when using Anchor v0.31 and Solana v2. If you're still running into problems, please create an issue in coral-xyz/anchor.

Custom discriminators

Before this release, there were several problems regarding Anchor discriminators:

  • Due to the transaction size limits enforced by the Solana runtime (1232 bytes), 8 bytes can be too high for some use cases
  • The Discriminator trait had a fixed size type field ([u8; 8]), which meant we wouldn't be able to implement it for non-Anchor programs (e.g. in declare_program!)
  • Discriminators were not customizable
  • Changing the name of the data type the discriminator was derived from resulted in a different discriminator

This release solves all of the above problems by introducing support for custom discriminators. The default 8-byte discriminators have not changed, but you can override them via the discriminator argument implemented in various Anchor attribute macros:

  • #[account(discriminator = 1)]
  • #[event(discriminator = [1, 2])]
  • #[instruction(discriminator = MY_CONST)]

All constant expressions are supported.

See example.

It's important that the discriminator is always unique for the type you're implementing it for. While the discriminator can be at any length (including zero), the IDL generation does not currently allow empty discriminators for safety and convenience reasons. However, the Discriminator trait definition still allows empty discriminators because some non-Anchor programs, e.g. the SPL Token program, don't have account discriminators. In that case, safety checks should never depend on the discriminator.

Additionally, the IDL generation step also checks whether you have ambigious discriminators i.e. discriminators that can be used for multiple types. However, you should still consider future possibilities, especially when working with 1-byte discriminators. For example, having an account with discriminator [1] means you can't have any other discriminators that start with 1, e.g. [1, 2 , 3, 4], as it would not be unique.

This change also enables using non-Anchor programs with both Rust (via declare_program!) and in the TS client.

LazyAccount

LazyAccount is an experimental account type that can be used to replace Account when you're running into performance-related problems.

We'll skip the details to keep the release notes as short as possible, but if you're interested, check out its documentation to see if it fits your needs.

Note: You need to enable lazy-account feature of anchor-lang to be able to use LazyAccount.

cfg attribute on instructions

You can now use cfg attributes on instructions:

#[program]
mod my_program {
    #[cfg(feature = "my-feature")]
    pub fn my_feature(ctx: Context<MyFeature>) -> Result<()> {}
}

Unnamed and unit structs with InitSpace

Unnamed and unit struct support was added in v0.30, but deriving InitSpace did not work with these structs. This release adds support for both of them:

#[derive(InitSpace)]
pub struct Unnamed(pub u64, #[max_len(64)] pub Vec<u8>);

#[derive(InitSpace)]
pub struct Unit;

SPL

New Token 2022 instructions

The following Token 2022 instructions were added:

  • burn_checked
  • mint_to_checked
  • approve_checked
  • withdraw_withheld_tokens_from_accounts

TypeScript

Simpler Program construction

TypeScript's automatic inference of JSON files made it difficult to use the Program constructor without additional casting:

import * as idl from './idl/my_program.json'
import type { MyProgram } from './types/my_program'

const program = new Program(idl as unknown as MyProgram, provider)

Casting to unknown or any was necessary when TypeScript automatically inferred the type of the JSON file.

In this release, the program constructor no longer infers the IDL type from the given idl argument. This makes it easier to specify the actual IDL type:

const program = new Program<MyProgram>(idl, provider)

Error improvements

Account resolution error now includes the accounts that weren't able to get resolved:

Reached maximum depth for account resolution. Unresolved accounts: `pda`, `anotherPda`

Similarly, unsupported view method error now includes the possible causes:

Error: Method does not support views. The instruction should return a value, and its accounts must be read-only

Type improvements

It's common to use //@ts-ignore to get the payer keypair in tests:

// @ts-ignore
const payer = program.provider.wallet.payer

To fix this problem, the Provider interface now includes an optional wallet: Wallet field, and similarly, the Wallet interface now includes optional payer: Keypair field.

You can now do:

const payer = program.provider.wallet!.payer!

We're using ! because we know these fields are going to be defined in our testing environment.

Confirmation improvements

Provider.sendAndConfirm now takes in an optional blockhash and uses that for better transaction confirmation:

await program.provider.sendAndConfirm(tx, signers, { blockhash: ... });

Rust Client

tokio support

There was a problem that made it impossible to use tokio with anchor-client. This problem is now fixed, and you can use tokio (with async feature). See tokio example.

Mock client

It's now possible to pass in a mock RPC client for testing by enabling the mock feature:

anchor-client = { version = "0.31.0", features = ["mock"] }

See the full list of notable changes in the CHANGELOG.