Skip to content

feat(sdk): add solana.FindAddress on sol_try_find_program_address - #28

Draft
paulbalaji wants to merge 6 commits into
CrocSwap:mainfrom
paulbalaji:syscall-try-find-program-address
Draft

paulbalaji wants to merge 6 commits into
CrocSwap:mainfrom
paulbalaji:syscall-try-find-program-address

Conversation

@paulbalaji

@paulbalaji paulbalaji commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Programs that need a canonical bump loop over CreateAddress, one syscall per bump, and pda.Seeds.FindAddress (#16, merged in #25) does the same. Every try parses the seed group again on top of the runtime's 1,500 CU, and the loop has its own setup. The runtime already has sol_try_find_program_address, which walks the bumps itself.

Solution

solana.FindAddress(c, seeds, program, output) uint64 makes one sol_try_find_program_address syscall (fogo-v0 by name, solana-v3 hash 0x48504a38).

  • seeds is the same encoded group CreateAddress takes, without the bump, so at most 15 seeds.
  • output is 33 bytes: the address, then the bump.
  • Returns 0, the runtime's 1 when no bump works (output untouched), or 2007.
  • Malformed groups, more than 15 seeds and wrong lengths return 2007 before the syscall. Otherwise the runtime aborts on more than 16 seeds or a seed over 32 bytes, and with exactly 16 it charges for all 255 bumps and finds nothing.

The runtime charges 1,500 CU per bump tried, the same as one CreateAddress, and tries 255 down to 1, as find_program_address does. Native contexts run the same search through DeriveAddress.

pda.Seeds.FindAddress now makes one solana.FindAddress call. Same bumps and statuses: 3001 with no room for the bump, 3004 when no bump works, a missing native callback propagates.

Benchmarks

TestFindAddressSBF, whole transaction against main 939b215, same on both profiles:

seeds tries FindAddress CreateAddress loop
one byte 1 1,853 2,238
Hyperlane process_authority 4 6,433 7,181
15 × 32 bytes 4 6,633 7,861

Each extra bump costs exactly 1,500 CU with FindAddress and 1,625 with the loop for the Hyperlane seeds. 373 to 1,228 CU saved per search over 27 seed sets. Before #25 the gap looked bigger (up to 5,233 CU) because the test loop's one-time copy of the group was a byte loop; #22 fixed that, not this PR.

examples/find-address, which uses pda.Seeds.FindAddress: 630 to 960 CU less per search (bump 255: 2,931 to 2,263; bump 250: 10,796 to 9,833).

In the Hyperlane Go programs, measured before #25 on hyperlane-integration-3, switching their PDA search from the loop to FindAddress cut total CU by 0.7% to 2.2% per program (584/584 tests pass).

Tests

  • TestFindAddressSBF on fogo-v0 and solana-v3: 27 seed sets, each address and bump equal to fixture.FindProgramAddress and to a CreateAddress loop in the same program; four malformed groups return 2007.
  • Native: search order and the bump seed encoding, no-bump result 1 with output unchanged, callback status passthrough, 15 seeds of 32 bytes, every length error, missing callback (2009). sdk/pda's 401 solana-pubkey vectors and status tests pass unchanged.
  • examples/find-address passes gosvm test --svm on both profiles.
  • TestCorpusMatchesPreCheckCompiler removes only the new runtime lines and matches every recorded hash.
  • SDK-2 example snapshots (solana/api.go, sdk/pda/pda.go) refreshed for all six examples, so existing SDK-2 projects need re-scaffolding, as with any snapshot change.
  • make test: same failures as main 939b215 on this machine (stdlib provenance/features tests with Go 1.23.2, TestFullSwapFacts under /tmp).
  • verify_rebuild.py + check_rebuild_reference.py pass locally: the reference ELFs are unchanged.

Linked work

Closes #26, together with #27, #29, #30 and #31. If this merges first, #26 should stay open until the rest land.

Rebased onto main after #25. Used by hyperlane-xyz/hyperlane-monorepo#9852 through hyperlane-integration-4.

Programs that need a canonical bump loop over CreateAddress, paying the
seed parsing and staging copy on every bump tried. FindAddress takes the
same seed group without the bump and makes one
sol_try_find_program_address syscall (fogo-v0 by name, solana-v3 hash
0x48504a38), writing the address and bump into a 33-byte output. The
runtime charges 1,500 CU per bump, the same as CreateAddress. Malformed
groups, more than 15 seeds and wrong lengths return 2007 before the
syscall, which would otherwise abort or charge for all 255 bumps.
Native contexts run the same search through DeriveAddress.
pda.Seeds.FindAddress looped over CreateAddress, paying the seed parsing
and setup again for every bump. It now makes one solana.FindAddress call
(sol_try_find_program_address), which tries the same bumps, 255 down to
1, at 1,500 CU each. Statuses are unchanged: 3001 with no room for the
bump, 3004 when no bump works, and a missing native callback propagates.
examples/find-address costs 630 to 960 CU less per search. The SDK-2
example snapshots of sdk/pda/pda.go are refreshed.
@paulbalaji
paulbalaji force-pushed the syscall-try-find-program-address branch from 4f9b430 to 26053c0 Compare October 19, 2026 17:19
paulbalaji added a commit to hyperlane-xyz/hyperlane-monorepo that referenced this pull request Oct 19, 2026
hyperlane-integration-4 (3ddeff0) is upstream main 939b215, which merged
our earlier PRs #9 to #24 in CrocSwap/goSVM#25, plus the open draft PRs:
whole-word runtime loads, stores and key comparisons (CrocSwap/goSVM#27),
solana.FindAddress on sol_try_find_program_address, also behind
pda.Seeds.FindAddress (CrocSwap/goSVM#28), word-at-a-time == on byte
arrays (CrocSwap/goSVM#29), the exact MinimumBalanceFor fast path at
thresholds 2.0 and 1.0 (CrocSwap/goSVM#30), and word compares in front of
hand-written byte compare loops (CrocSwap/goSVM#31). The SDK snapshot is
copied from the new CLI.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Byte-at-a-time runtime and codegen add 0.3k to 4k CU to small instructions

1 participant