Skip to content

feat: Solana 1.6 pool support in ConfigureTokenPool - #2218

Merged
chris-de-leon-cll merged 6 commits into
mainfrom
solana-configure-token-pool
Aug 4, 2026
Merged

feat: Solana 1.6 pool support in ConfigureTokenPool#2218
chris-de-leon-cll merged 6 commits into
mainfrom
solana-configure-token-pool

Conversation

@futureproof1

@futureproof1 futureproof1 commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

What

Makes ConfigureTokenPool's rateLimitAdmin field work for Solana 1.6 BurnMint and LockRelease token pools, and adds integration coverage proving the unsupported fields reject loudly and the tokenTransferFeeConfig path works for Solana.

Follows #2211, which did the equivalent EVM work.

SolanaAdapter joins the existing chain-agnostic TokenPoolAdminAdapter seam by type assertion — no new interface. Because a Solana pool address is a program ID shared across many token mints, SetTokenPoolAdminsSequenceInput gains TokenPoolRef and TokenRef, which the changeset already resolved and previously discarded. Both adapters read the pool address from TokenPoolRef.AddressPoolAddress was redundant and dropped in commit 5 (see below). The EVM adapter changes are trivial: input.PoolAddressinput.TokenPoolRef.Address.

Commits

  1. fix: correct idempotency and error handling in Solana rate limit admin ops — two defects in UpdateRateLimitAdmin{BurnMint,LockRelease}, which are already live in the Solana pool deploy path:
    • Swallowed read error. if err == nil { compare } made a transient RPC or decode failure indistinguishable from "account absent", and both fell through to writing — degrading the no-op guarantee to "always write". Now only rpc.ErrNotFound falls through; every other error is returned.
    • Bogus comparison in the fallback. if authority == input.NewOwner { return nil } compared the program upgrade authority against the desired rate limit admin. When the deployer key was both — normal in tests and single-operator deployments — the op reported success having done nothing.
  2. feat: support rateLimitAdmin on Solana 1.6 token pools in ConfigureTokenPool — widens the sequence input, passes the already-resolved refs at the call site, and implements SetTokenPoolAdmins() on SolanaAdapter. feeAdmin is rejected (Solana pools have no fee admin concept); a nil rateLimitAdmin is a no-op.
  3. test: cover Solana rejection paths and fee config in ConfigureTokenPoolfeeAdmin and finalityConfig rejection, plus the tokenTransferFeeConfig path, which routes Solana pools to the FeeQuoter because their version is below 2.0.0. That path was believed to work but nothing verified it; it does, and the test reads the config back on-chain through the same resolution path the changeset uses.
  4. fix: address review findings on the Solana rate limit admin path — see below.
  5. refactor: drop redundant PoolAddress from SetTokenPoolAdminsSequenceInputPoolAddress always carried the same value as TokenPoolRef.Address. Removed. EVM adapter switched from input.PoolAddress to input.TokenPoolRef.Address.

Two things reviewers should know

Solana callers must pass the pool config PDA as TokenPoolRef, not the pool program ID. The generic ResolveTokenPoolRef does an exact-match datastore lookup by address before the family-specific resolver runs, and a bare Solana pool program ID is exactly what the datastore registers — so the lookup short-circuits and the Solana PDA-normalizing resolver never runs, and token derivation then fails with "token derivation is only possible if a pool PDA is provided". This is inherent rather than a workaround: a program ID is shared across mints and so cannot identify a token. The requirement is now documented on PoolConfigUpdate.TokenPoolRef, where it previously lived only in a test comment.

Widening SetTokenPoolAdminsSequenceInput changes its report hash. Any durable or resumable report store will no longer match previously-recorded SetTokenPoolAdmins reports and will re-execute them. That is safe — the operation reads on-chain state and no-ops before writing — but it is worth knowing.

Review fixes folded into commit 4

  • The same swallowed-error pattern fixed in commit 1 still existed six lines below it: any GetAuthority* failure, not just "account absent", fell back to the program upgrade authority, so a transient read failure could submit SetRateLimitAdmin signed by the wrong authority. Now reuses the pool config read the op already performed — which also removes a redundant RPC round-trip per pool — and keeps the upgrade-authority fallback for the account-absent case only. That fallback is load-bearing: under MCMS the initialize op only queues a proposal, so the pool config account genuinely does not exist yet when this op runs.
  • A Solana idempotency assertion that verified nothing. The re-apply reused the same operations bundle with byte-identical input, so ExecuteSequence's report memoization returned the first apply's cached report without running the handler — the read-compare no-op path was never exercised. Now refreshes the reporter first, matching the idiom every pre-existing EVM test in that file already uses.
  • Dropped no-op ,omitempty tags from the two struct-typed input fields (encoding/json never omits struct values), wrapped dropped address-parse errors with %w, and asserted the third fee-config value (MinFeeUSDCents) actually landed on-chain.

Deliberately not changed

UpdateRateLimitAdmin{BurnMint,LockRelease} keep their signatures — the input type is shared with the transfer-ownership and accept-ownership ops, and these are exported vars in a public module, so a new input type would be source-breaking downstream. That is why NewOwner carries the desired rate limit admin, which reads oddly but is intended. The ops also keep decoding pool state as test_token_pool.State, which has the same wire layout.

Testing

  • TestConfigureTokenPool — 12 subtests pass, including every pre-existing EVM test
  • TestTokensAndTokenPools — passes; this is the regression harness for the operation changes, and it deploys Solana BurnMint and LockRelease pools through MCMS
  • go build and go vet clean in chains/solana, chains/solana/deployment, deployment, integration-tests, and chains/evm

Follow-ups, not blockers

  • The sequence's cheap validation branches (feeAdmin rejection, nil-admin no-op, unknown selector) all run before the chain lookup and are trivially unit-testable without a Solana container.
  • TestConfigureTokenPool_FeeConfig_Solana and TestConfigureTokenPool_UnsupportedFields_Solana each stand up their own Solana container; they are read-mostly and could share one environment.

- stop treating any GetAuthority* read failure as "pool not initialized";
  reuse the pool config read the op already performed, keeping the
  upgrade-authority fallback for the account-absent case only
- document that Solana callers must pass the pool config PDA as
  TokenPoolRef, not the pool program ID
- drop no-op ,omitempty tags from struct-typed input fields
- wrap dropped address-parse errors
@github-actions

Copy link
Copy Markdown

👋 futureproof1, thanks for creating this pull request!

To help reviewers, please consider creating future PRs as drafts first. This allows you to self-review and make any final changes before notifying the team.

Once you're ready, you can mark it as "Ready for review" to request feedback. Thanks!

@chris-de-leon-cll chris-de-leon-cll left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good - just had one comment about simplifying the sequence input, so once that's resolved we should be good to merge!

Comment on lines -574 to -580
// if we had to assume the authority, then the pool isn't initialized yet and therefore
// there won't be an authority set on-chain yet, so we can skip the update since the initializer
// will be able to set the correct authority during initialization
if authority == input.NewOwner {
b.Logger.Info("New owner is the same as the current owner for burn mint token pool with token mint:", input.TokenMint.String())
return sequences.OnChainOutput{}, nil
}

@chris-de-leon-cll chris-de-leon-cll Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[no action needed]

Nice catch here! Looks like this original code was buggy and can be safely removed. It seems like this check was asking: "Is the person who controls the program the same person we want to make the rate limit admin?" If yes, then skip. Don't set anything.

That's incorrect. The operation should ALWAYS set the rate limit admin. Whether the upgrade authority happens to be the same person or a different person is irrelevant.

// Nil fields are left unchanged on-chain.
type SetTokenPoolAdminsSequenceInput struct {
// Selector is the chain selector for the chain on which the pool lives.
Selector uint64 `json:"selector" yaml:"selector"`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah looks like PoolAddress is redundant with TokenPoolRef.Address in SetTokenPoolAdminsSequenceInput, so we can safely remove it

Rationale:

  • The only production call site (configure_token_pool.go:185) sets PoolAddress: fullPoolRef.Address — the same value that TokenPoolRef.Address already carries after resolution.
  • EVM's SetTokenPoolAdmins (chains/evm/deployment/v1_0_0/adapters/pool_adapter.go:210) reads input.PoolAddress; switching to input.TokenPoolRef.Address is a one-line change.
  • Solana never reads input.PoolAddress at all — it uses input.TokenPoolRef.Type and input.TokenRef.Address.

Changes required:

File Change
deployment/tokens/product.go Remove PoolAddress from SetTokenPoolAdminsSequenceInput
chains/evm/deployment/v1_0_0/adapters/pool_adapter.go Line 210-213: input.PoolAddressinput.TokenPoolRef.Address
deployment/tokens/configure_token_pool.go Line 187: remove PoolAddress: line

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PoolAddress is gone and both adapters now read input.TokenPoolRef.Address.

One other thing — Solana did read input.PoolAddress, at four pats in chains/solana/deployment/v1_6_0/sequences/tokens.go: line :640 parsed it into the pool program ID passed to the operation, and line :614,line :642 and line :655. So the removal touched that file too, otherwise it wouldn't compile.

…nput

PoolAddress always carried the same value as TokenPoolRef.Address — the
single call site set both from fullPoolRef — so the two could drift if a
direct caller populated them inconsistently. Both adapters now read the
pool address from TokenPoolRef.Address.

Addresses review feedback on #2218.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Extends the ConfigureTokenPool changeset to support Solana v1.6 BurnMint/LockRelease pools for rateLimitAdmin updates, while explicitly rejecting unsupported admin fields, and adds Solana integration coverage for both admin updates and the legacy fee-config (FeeQuoter) path.

Changes:

  • Widened SetTokenPoolAdminsSequenceInput to carry resolved TokenPoolRef + TokenRef, and updated ConfigureTokenPool (and the EVM adapter) to use TokenPoolRef.Address instead of the removed PoolAddress.
  • Implemented SetTokenPoolAdmins() for Solana v1.6 and fixed Solana rate-limit-admin operations to properly distinguish rpc.ErrNotFound from other read errors and to perform correct idempotency checks.
  • Added integration tests covering Solana admin updates, unsupported-field rejection, and fee-config application/readback for Solana pools (FeeQuoter path).

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
integration-tests/deployment/configure_token_pool_test.go Adds Solana integration helpers/tests for rateLimitAdmin updates, unsupported field rejection, and fee config readback.
deployment/tokens/product.go Updates admin sequence input to include resolved TokenPoolRef and TokenRef.
deployment/tokens/configure_token_pool.go Documents Solana TokenPoolRef requirements and passes resolved refs into SetTokenPoolAdmins.
chains/solana/deployment/v1_6_0/sequences/tokens.go Adds Solana v1.6 SetTokenPoolAdmins() implementation (rateLimitAdmin support; feeAdmin rejection).
chains/solana/deployment/v1_6_0/operations/token_pools/lockrelease.go Fixes error handling/idempotency logic in Solana LnR UpdateRateLimitAdmin.
chains/solana/deployment/v1_6_0/operations/token_pools/burnmint.go Fixes error handling/idempotency logic in Solana BnM UpdateRateLimitAdmin.
chains/evm/deployment/v1_0_0/adapters/pool_adapter.go Switches EVM admin sequence to read the pool address from input.TokenPoolRef.Address.
Suppressed comments (1)

integration-tests/deployment/configure_token_pool_test.go:851

  • The inline comment "helper from Step 6" appears to refer to PR/review steps rather than something in the codebase, which makes it confusing and likely to go stale. Suggest removing it.
	env, bnm, lnr := setupSolanaPoolsForConfigure(t) // helper from Step 6

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread integration-tests/deployment/configure_token_pool_test.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Metric solana-configure-token-pool main
Coverage 70.0% 69.7%

@chris-de-leon-cll
chris-de-leon-cll added this pull request to the merge queue Aug 4, 2026
Merged via the queue into main with commit 8925b12 Aug 4, 2026
57 checks passed
@chris-de-leon-cll
chris-de-leon-cll deleted the solana-configure-token-pool branch August 4, 2026 21:48
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.

4 participants