From 7ccdb9df53c4ff99b241d87b8c4b24eee4133970 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 01:24:40 +0000 Subject: [PATCH 1/4] perps: fix the v2 build break in set_funding_rate `perps: let the pool authority retune the funding rate` landed on main after the v2 port and was written against v1: `Context` rather than `&mut Context`, `Signer<'info>`, `Box>`, and `has_one = authority`. The crate does not compile, so `anchor build` fails for the whole project and its 28 tests never run. Nothing caught it because `perpetual-futures` is not a root workspace member, so neither `cargo clippy` nor `cargo fmt --check` has ever seen the crate, and the Anchor workflow only builds projects a push actually touches. The port is the usual set: `&mut Context`, the `'info` lifetime dropped, `Box>` to match every sibling handler, and `has_one` replaced by `address = pool.authority` on the signer. Two more things the crate needed, for the same reason nobody had looked at it: `#[instruction(side: Side)]` warns as an unused variable under v2's `#[derive(Accounts)]` expansion, so it takes the leading underscore the other examples use, and main's new lending and perpetual-futures test code was never formatted. `cargo check` and `cargo clippy -- -D warnings` are both clean on the crate now. The LiteSVM tests still need `cargo build-sbf` to produce the `.so` they embed, which CI does and this change does not affect. --- .../anchor/programs/lending/tests/test_reserve.rs | 11 +++++++++-- .../src/instructions/open_position.rs | 7 +++++-- .../src/instructions/set_funding_rate.rs | 10 +++++----- .../anchor/programs/perpetual-futures/src/lib.rs | 2 +- .../perpetual-futures/tests/test_perpetual_futures.rs | 5 ++++- 5 files changed, 24 insertions(+), 11 deletions(-) diff --git a/finance/lending/anchor/programs/lending/tests/test_reserve.rs b/finance/lending/anchor/programs/lending/tests/test_reserve.rs index 396be7c80..c1cd2af92 100644 --- a/finance/lending/anchor/programs/lending/tests/test_reserve.rs +++ b/finance/lending/anchor/programs/lending/tests/test_reserve.rs @@ -98,8 +98,15 @@ fn slots_per_year_scales_the_per_slot_rate() { env.supply(&borrower, &collateral, 1_000_000_000); let obligation = env.initialize_obligation(&borrower); env.post_collateral(&borrower, obligation, &collateral, 1_000_000_000); - env.try_borrow(&borrower, obligation, &[&collateral], &[], reserve, 500_000_000) - .unwrap(); + env.try_borrow( + &borrower, + obligation, + &[&collateral], + &[], + reserve, + 500_000_000, + ) + .unwrap(); } let elapsed = SLOTS_PER_YEAR / 100; diff --git a/finance/perpetual-futures/anchor/programs/perpetual-futures/src/instructions/open_position.rs b/finance/perpetual-futures/anchor/programs/perpetual-futures/src/instructions/open_position.rs index 4c6d543bf..0a1d96507 100644 --- a/finance/perpetual-futures/anchor/programs/perpetual-futures/src/instructions/open_position.rs +++ b/finance/perpetual-futures/anchor/programs/perpetual-futures/src/instructions/open_position.rs @@ -126,7 +126,10 @@ pub fn handle_open_position( } #[derive(Accounts)] -#[instruction(side: Side)] +// The leading underscore is for rustc: `#[derive(Accounts)]` expands +// `_side` into a path that never reads it, so the plain name warns as +// unused. The `seeds` expression below is the real use. +#[instruction(_side: Side)] pub struct OpenPositionAccountConstraints { #[account(mut)] pub owner: Signer, @@ -142,7 +145,7 @@ pub struct OpenPositionAccountConstraints { init, payer = owner, space = Position::DISCRIMINATOR.len() + Position::INIT_SPACE, - seeds = [POSITION_SEED, pool.address().as_ref(), owner.address().as_ref(), side.as_seed()], + seeds = [POSITION_SEED, pool.address().as_ref(), owner.address().as_ref(), _side.as_seed()], bump, )] pub position: Box>, diff --git a/finance/perpetual-futures/anchor/programs/perpetual-futures/src/instructions/set_funding_rate.rs b/finance/perpetual-futures/anchor/programs/perpetual-futures/src/instructions/set_funding_rate.rs index 03a98fb05..80a3fc1cf 100644 --- a/finance/perpetual-futures/anchor/programs/perpetual-futures/src/instructions/set_funding_rate.rs +++ b/finance/perpetual-futures/anchor/programs/perpetual-futures/src/instructions/set_funding_rate.rs @@ -14,7 +14,7 @@ use crate::state::Pool; /// charged at the rate that was in force for them rather than repriced by the /// new one. pub fn handle_set_funding_rate( - context: Context, + context: &mut Context, funding_rate_per_slot: u64, ) -> Result<()> { let pool = &mut context.accounts.pool; @@ -24,14 +24,14 @@ pub fn handle_set_funding_rate( } #[derive(Accounts)] -pub struct SetFundingRateAccountConstraints<'info> { - pub authority: Signer<'info>, +pub struct SetFundingRateAccountConstraints { + #[account(address = pool.authority)] + pub authority: Signer, #[account( mut, seeds = [POOL_SEED, pool.collateral_mint.as_ref(), pool.oracle_feed.as_ref()], bump = pool.bump, - has_one = authority, )] - pub pool: Box>, + pub pool: Box>, } diff --git a/finance/perpetual-futures/anchor/programs/perpetual-futures/src/lib.rs b/finance/perpetual-futures/anchor/programs/perpetual-futures/src/lib.rs index 8d263a055..5dbaad5da 100644 --- a/finance/perpetual-futures/anchor/programs/perpetual-futures/src/lib.rs +++ b/finance/perpetual-futures/anchor/programs/perpetual-futures/src/lib.rs @@ -86,7 +86,7 @@ pub mod perpetual_futures { /// Pool authority retunes the per-slot funding rate, accruing at the old /// rate first. pub fn set_funding_rate( - context: Context, + context: &mut Context, funding_rate_per_slot: u64, ) -> Result<()> { instructions::handle_set_funding_rate(context, funding_rate_per_slot) diff --git a/finance/perpetual-futures/anchor/programs/perpetual-futures/tests/test_perpetual_futures.rs b/finance/perpetual-futures/anchor/programs/perpetual-futures/tests/test_perpetual_futures.rs index 6d7bf79c3..4aeb986f0 100644 --- a/finance/perpetual-futures/anchor/programs/perpetual-futures/tests/test_perpetual_futures.rs +++ b/finance/perpetual-futures/anchor/programs/perpetual-futures/tests/test_perpetual_futures.rs @@ -957,7 +957,10 @@ fn test_set_funding_rate_settles_at_the_old_rate_first() { let flat = funding_for(false); let retuned = funding_for(true); - assert!(flat > 0, "the flat run must pay some funding to compare against"); + assert!( + flat > 0, + "the flat run must pay some funding to compare against" + ); // Half the elapsed slots at 1x and half at 2x is 1.5x the flat run. Had the // handler skipped its accrual, the new rate would have applied to every From 5fbd062884aff5792430d6a20d3f52dc8cab70d8 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 16:11:06 +0000 Subject: [PATCH 2/4] ci: build Anchor from source, since the pre-release has no prebuilt binary The Anchor job died in its install step: Error: No prebuilt binary found for version `2.0.0-rc.1` (HTTP 404). Try `avm install 2.0.0-rc.1 --from-source`. No project was built, and the summary step then failed to read a failed_projects.txt that nothing had written. Earlier runs passed on a warm toolchain cache, which skipped the install entirely and hid this; any run that misses the cache hits it. Taking avm's own suggestion. The source build is slow, and the existing cache keyed on the version is what keeps it off the critical path after the first run. --- .github/workflows/anchor.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/anchor.yml b/.github/workflows/anchor.yml index 24c1ab449..6f89b811f 100644 --- a/.github/workflows/anchor.yml +++ b/.github/workflows/anchor.yml @@ -202,8 +202,12 @@ jobs: current="$(anchor --version 2>/dev/null | head -n1 | awk '{print $2}' || true)" if [ "$current" != "2.0.0-rc.1" ]; then cargo install --git https://github.com/solana-foundation/anchor avm --force - # 2.0.0-rc.1 is a pre-release, so avm needs it named explicitly. - avm install 2.0.0-rc.1 + # 2.0.0-rc.1 is a pre-release, so avm needs it named explicitly, and + # --from-source because there is no prebuilt binary published for it: + # a plain `avm install` 404s and takes the whole job down before any + # project is built. The cache above is what keeps the source build + # off the critical path on later runs. + avm install 2.0.0-rc.1 --from-source avm use 2.0.0-rc.1 fi echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" From 19eb399556dcfd44805c9f40cf7282102418197a Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 16:13:13 +0000 Subject: [PATCH 3/4] ci: reformat the lever IDL so Biome passes The TypeScript workflow runs `biome check ./`, which formats JSON as well as TypeScript. `anchor build` regenerated `basics/cross-program-invocation/anchor/idls/lever.json` with one array element per line, and Biome puts those on a single line, so the check has failed on every commit since the IDL was last regenerated. `main` is red on this today. Formatting only; the IDL's contents are unchanged, so `declare_program!` and the LiteSVM tests that read it see exactly what they saw before. --- .../anchor/idls/lever.json | 33 ++----------------- 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/basics/cross-program-invocation/anchor/idls/lever.json b/basics/cross-program-invocation/anchor/idls/lever.json index a8ace89ce..3ec862d79 100644 --- a/basics/cross-program-invocation/anchor/idls/lever.json +++ b/basics/cross-program-invocation/anchor/idls/lever.json @@ -9,16 +9,7 @@ "instructions": [ { "name": "initialize", - "discriminator": [ - 175, - 175, - 109, - 31, - 13, - 152, - 155, - 237 - ], + "discriminator": [175, 175, 109, 31, 13, 152, 155, 237], "accounts": [ { "name": "power", @@ -39,16 +30,7 @@ }, { "name": "switch_power", - "discriminator": [ - 226, - 238, - 56, - 172, - 191, - 45, - 122, - 87 - ], + "discriminator": [226, 238, 56, 172, 191, 45, 122, 87], "accounts": [ { "name": "power", @@ -66,16 +48,7 @@ "accounts": [ { "name": "PowerStatus", - "discriminator": [ - 145, - 147, - 198, - 35, - 253, - 101, - 231, - 26 - ] + "discriminator": [145, 147, 198, 35, 253, 101, 231, 26] } ], "types": [ From 95b839b3d6cbf0a80e591cf5e3b9efd61d2c5524 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 16:45:48 +0000 Subject: [PATCH 4/4] ci: install the Anchor CLI from crates.io, not through avm The previous commit's `avm install --from-source` was the wrong fix. avm builds the CLI from the `v2.0.0-rc.1` git tag, which is not the same code as the `anchor-lang` 2.0.0-rc.1 published on crates.io that every program here compiles against. The CLI and the library disagreed about the IDL, and `anchor build` failed across roughly half the projects for reasons that had nothing to do with the projects: error[E0405]: cannot find trait `IdlBuild` in crate `anchor_lang` Error: missing field `variants` at line 1 column 15428 Both come from `anchor idl build`: the CLI emits a shim referencing a trait the published crate does not have, and writes IDL JSON its own parser then rejects. crates.io publishes `anchor-cli` 2.0.0-rc.1 alongside the library, so installing it there keeps the two on one release. avm is a version manager and this workflow pins a single version, so it was not buying anything. `--locked` builds the CLI against its own published lockfile so the result does not drift with the registry. The cache key changes with the install method, so a run cannot resurrect a binary built the old way. --- .github/workflows/anchor.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/anchor.yml b/.github/workflows/anchor.yml index 6f89b811f..5b209e88b 100644 --- a/.github/workflows/anchor.yml +++ b/.github/workflows/anchor.yml @@ -190,28 +190,28 @@ jobs: with: path: | ~/.cargo/bin/anchor - ~/.cargo/bin/avm - ~/.avm # Pinned to match the anchor-lang/anchor-spl crate version the programs depend on. - key: anchor-toolchain-${{ runner.os }}-2.0.0-rc.1 + key: anchor-cli-crates-io-${{ runner.os }}-2.0.0-rc.1 - name: Install Anchor 2.0.0-rc.1 run: | - export PATH="$HOME/.cargo/bin:$HOME/.avm/bin:$PATH" + export PATH="$HOME/.cargo/bin:$PATH" # `anchor --version` can emit more than one line; read the first line only so # a multi-line value never reaches downstream parsing. current="$(anchor --version 2>/dev/null | head -n1 | awk '{print $2}' || true)" if [ "$current" != "2.0.0-rc.1" ]; then - cargo install --git https://github.com/solana-foundation/anchor avm --force - # 2.0.0-rc.1 is a pre-release, so avm needs it named explicitly, and - # --from-source because there is no prebuilt binary published for it: - # a plain `avm install` 404s and takes the whole job down before any - # project is built. The cache above is what keeps the source build - # off the critical path on later runs. - avm install 2.0.0-rc.1 --from-source - avm use 2.0.0-rc.1 + # Install the CLI from crates.io rather than through avm. avm has no + # prebuilt binary for this pre-release (a plain `avm install` 404s), and + # its --from-source path builds the CLI from the git tag, which is not + # the same code as the published anchor-lang 2.0.0-rc.1 the programs + # compile against: `anchor idl build` then emits a shim referencing + # `anchor_lang::IdlBuild`, which that crate does not have, and writes + # IDL JSON the parser rejects with "missing field `variants`". + # Installing the CLI from crates.io keeps it and the library on one + # published release. --locked builds it against its own published + # lockfile so the result does not drift with the registry. + cargo install anchor-cli --version 2.0.0-rc.1 --locked --force fi echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" - echo "$HOME/.avm/bin" >> "$GITHUB_PATH" - name: Install Surfpool run: curl -sL https://run.surfpool.run/ | bash - name: Display Versions