Skip to content

perf(svm): eliminate redundant Anchor event-authority PDA searches #1571

Description

@Reinis-FRP

Problem and evidence

Spoke uses Anchor 0.31.1 with event CPI and #[event_cpi] on the V5 adapter accounts. Its generated code searches for the event-authority PDA twice per event-producing adapter invocation:

  1. The outer generated accounts constraint uses seeds = [b"__event_authority"], bump.
  2. The generated event-dispatch handler checks the signer and calls find_program_address again.

The Spoke event-authority bump is 253, so each search takes three attempts. Eliminating both searches targets approximately 9,000 CU per deposit/fill execution. PDA-work estimates use Solana's documented 1,500 CU per derivation attempt. Actual deltas must be measured on the same runtime.

In the analyzed benchmark receipts, the inner Spoke event self-CPI consumed 5,119 CU versus 514 CU for Gateway. These are measured call costs, not a direct patched-program delta; the other Spoke search occurs in outer account validation. This overhead also exists in legacy Spoke and is not unique to V5.

Gateway's Anchor 1.1.2 uses the constant EVENT_AUTHORITY_AND_BUMP.0 at both validation sites. Inspect the resolved anchor-syn sources, particularly src/parser/accounts/event_cpi.rs and src/codegen/program/handlers.rs, for both versions when implementing.

Proposed scope and constraints

Evaluate a narrowly scoped, maintainable backport versus a separately justified Anchor upgrade. Determine how to remove both generated searches before selecting the implementation. Do not assume changing the adapter account annotation alone removes the inner dispatcher search.

Preserve the event authority's canonical identity, signer requirement, self-CPI behavior, EVENT_IX_TAG, event discriminators/payloads, public instruction ABI, and account/state layouts. Do not replace authenticated CPI events with log-only emit!, accept arbitrary signed accounts, or edit only a local Cargo registry cache.

A framework upgrade can affect all instructions, IDLs, and generated clients; if chosen, explicitly document and validate that larger scope. Gateway needs no change for this issue.

Acceptance criteria

  • Demonstrate removal of both targeted runtime searches and report outer execution plus inner event-call CU separately.
  • Confirm event bytes and CPI-event decoding/indexer behavior are unchanged for deposit/fill and other affected event-producing instructions.
  • Reject an unsigned canonical event authority and a signed wrong authority; retain authenticated event-dispatch coverage.
  • Run Rust tests, the applicable SVM suite, yarn test-svm-gateway, and guarded SBF builds. Review generated IDL/client and account-layout diffs.
  • Benchmark all fixed fixtures twice with recorded toolchain/dependency versions. Separate direct optimization savings from incidental framework/compiler changes.
  • Update benchmark documentation and the reviewed baseline only after reproducing the result.

Benchmark context and reproduction

This is a potential optimization, not a confirmed patched-binary saving. The reproducible harness is in contracts PR #1568, benchmark commit e80949f92c0a8cd1fcf17191691bd0bcaa10b479. If the harness has not merged when starting work, use that branch/commit as the measurement reference and refresh the implementation base.

The checked-in baseline measures V5 Spoke source 81a2211c27d9107a0835280d3437c05624275a20, Gateway e2b91eb0454136773728f941b33163346e039aa4, and legacy Spoke 7445f72de17900544605c7e6706c5fb3b3784738. V5 execution medians are 73,530 CU for deposit, 84,660 for external fill, and 77,369 for in-place fill followed by balance requirement and full recipient transfer. Buffer preparation is measured separately.

Run from a contracts checkout containing the harness, with installed Yarn dependencies and the documented toolchain:

SVM_SPOKE_ANCHOR="$HOME/.avm/bin/anchor-0.31.1" \
SVM_GATEWAY_ANCHOR="$HOME/.avm/bin/anchor-1.1.2" \
SVM_GATEWAY_CHECKOUT=/path/to/clean/pinned/solana-v5 \
SVM_CU_OUTPUT=target/cu-before \
yarn bench-svm-cu

Repeat after the change into target/cu-after. Compare every fixed fixture, not only aggregate medians; preserve source revisions, compiler/runtime metadata, binary/IDL hashes, and raw receipts. The runner rebuilds programs. Use fresh outputs to check repeatability before deliberately updating the baseline.

The reference used Agave 4.1.2, cargo-build-sbf 4.1.0, platform-tools 1.44/1.52/1.54 for legacy/Spoke/Gateway, and Node 24.14.1. These are consumed-CU measurements using Spoke's test feature, existing ATAs, and the validator's bundled token programs, not a production CU budget. Five fixtures expose PDA-bump variation but are not a worst-case bound.


Sent from Reinis Martinsons's Codex CLI Agent using gpt-6-astra 🤖

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions