Skip to content

fix: Solana batch ops logic in ConfigureTokensForTransfers#2051

Open
chris-de-leon-cll wants to merge 11 commits into
mainfrom
fix/solana-accept-tar-staleness
Open

fix: Solana batch ops logic in ConfigureTokensForTransfers#2051
chris-de-leon-cll wants to merge 11 commits into
mainfrom
fix/solana-accept-tar-staleness

Conversation

@chris-de-leon-cll
Copy link
Copy Markdown
Collaborator

@chris-de-leon-cll chris-de-leon-cll commented May 9, 2026

Summary

This PR fixes a bug in the Solana ConfigureTokenForTransfers sequence related to the RegisterTokenAdminRegistry and AcceptTokenAdminRegistry steps. We illustrate the bug using a real-world example. Suppose:

  • For Solana, we want to register an existing mint under the CLL LnR self-service pool
  • For EVM, we want to deploy a fresh token and pool
  • At the end, we want to connect these two pools

Assume the state of the token admin registry (TAR) on Solana looks like this:

Field State
Mint SPL mint pubkey
Administrator 11111111111111111111111111111111
PendingAdministrator Third-party pubkey ( mcms timelock signer PDA, deployer)

With the current code on main, running token expansion for this scenario leads to the following error:

Error: failed to process token configs for transfers: failed to configure token pool on chain
with selector 124615329519749607: failed to register token metadata: pending admin
GcqdKBdgcJNdBeC1TnZvJTaWuRXXg8WotC5qw1BNBSEp does not match timelock signer
GX38bfJaViHyypF7BTykt2tRVZfx5vXBr21Y6JMnyyq4 or deployer FqrKUJWwei7r8BTwXEJRprf16WvgzmUn7QarDqf8keyB

In this case, RegisterTokenAdminRegistry needs a proposal and adds an NewCcipAdminOverridePendingAdministratorInstruction to the batch here.

Then, AcceptTokenAdminRegistry runs next and queries RPC before the proposal was applied on-chain, so it sees the stale pending admin and rejects it here.

To fix this, we need to conditionally handle the output of RegisterTokenAdminRegistry:

  1. If RegisterTokenAdminRegistry requires a proposal and we're overriding the pending admin with timelock, then we should avoid inspecting the current pending admin in the TAR and schedule an Accept instruction in the same batch.
  2. If RegisterTokenAdminRegistry does not require a proposal, then we can keep the existing flow where AcceptTokenAdminRegistry runs as a separate operation and inspects the live onchain state of TAR.
  3. In any other case, the acceptance of the token admin role is deferred to the external user and not performed inside the sequence

What changed

  1. TokenAdminRegistryOut: add ProposalInstructions []solana.Instruction (paired with BatchOps whenever register/transfer wrapped a proposal ix).
  2. BuildAcceptTokenAdminRegistrySolanaInstruction: builds AcceptAdminRole for a given pendingAdministrator pubkey; Accept reuses this after its RPC/policy checks.
  3. ConfigureTokenForTransfers (Solana adapter):
    • If ProposalInstructions and BatchOps present → append(proposal ix(s), built accept ix)BuildMCMSBatchOperation once (no interim Accept that rereads stale TAR for that fork).
    • Else → unchanged path: Accept as a separate ExecuteOperation, BatchOps from register then accept appended.

Integration (“Scenario 6”): two TokenExpansion passes with explicit third-party PendingAdministrator, then cross-family pools; asserts success and final TAR: Mint unchanged, Administrator = timelock signer PDA, PendingAdministrator = zero, customer key Administrator.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes Solana ConfigureTokenForTransfers batching so AcceptTokenAdminRegistry doesn’t re-read stale Token Admin Registry (TAR) state when RegisterTokenAdminRegistry produced MCMS proposal instructions that won’t be applied on-chain until the batch executes.

Changes:

  • Extend TokenAdminRegistryOut to carry the underlying proposal instruction(s) so callers can construct a single ordered MCMS batch containing both “register/override” and “accept”.
  • Add BuildAcceptTokenAdminRegistrySolanaInstruction helper and update AcceptTokenAdminRegistry to use it.
  • Update the Solana ConfigureTokenForTransfersSequence to conditionally build a combined MCMS batch, and add an integration test scenario covering the real-world failure mode.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
integration-tests/deployment/token_expansion_scenarios_test.go Adds Scenario 6 integration test reproducing third-party TAR pending-admin + cross-family token expansion flow.
chains/solana/deployment/v1_6_0/sequences/tokens.go Updates configure-for-transfers sequence to combine proposal+accept into one MCMS batch when needed.
chains/solana/deployment/v1_6_0/operations/router/router.go Adds proposal-instruction plumbing + accept-instruction builder to support combined batching.

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

Comment thread integration-tests/deployment/token_expansion_scenarios_test.go Outdated
Comment thread chains/solana/deployment/v1_6_0/sequences/tokens.go
Comment thread chains/solana/deployment/v1_6_0/sequences/tokens.go Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 3 out of 3 changed files in this pull request and generated 2 comments.

Comment thread chains/solana/deployment/v1_6_0/sequences/tokens.go Outdated
Comment thread integration-tests/deployment/token_expansion_scenarios_test.go
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 3 out of 3 changed files in this pull request and generated 3 comments.

Comment thread chains/solana/deployment/v1_6_0/sequences/tokens.go Outdated
Comment thread chains/solana/deployment/v1_6_0/operations/router/router.go
Comment thread chains/solana/deployment/v1_6_0/operations/router/router.go
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 3 out of 3 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

chains/solana/deployment/v1_6_0/operations/router/router.go:612

  • pendingAdmin is guaranteed to be non-zero by the time this condition is evaluated (it is set to input.Admin in the pendingAdmin.IsZero() branch above). This makes the pendingAdmin.IsZero() check here redundant and the adjacent comment misleading (it suggests a zero pending admin is possible at this point). Consider simplifying the condition (and updating the comment) to reduce confusion about the accept/proposal decision.
		// pending admin unset = we're proposing and accepting in the same batch, so timelock must be the signer
		// pending admin set = we need a proposal if the admin is not the deployer
		if pendingAdmin.IsZero() || pendingAdmin != chain.DeployerKey.PublicKey() {
			batches, err := utils.BuildMCMSBatchOperation(
				chain.Selector,
				[]solana.Instruction{ixn},

Comment thread chains/solana/deployment/v1_6_0/sequences/tokens.go
Comment thread integration-tests/deployment/token_expansion_scenarios_test.go
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 3 out of 3 changed files in this pull request and generated 1 comment.

Comment thread integration-tests/deployment/token_expansion_scenarios_test.go
@chris-de-leon-cll chris-de-leon-cll marked this pull request as ready for review May 11, 2026 16:16
@chris-de-leon-cll chris-de-leon-cll requested review from a team as code owners May 11, 2026 16:16
deployerPK := chain.DeployerKey.PublicKey()
routerPK := solana.PublicKeyFromBytes(routerAddr)

// If the proposed token admin is timelock or the deployer, then we can run Register + Accept
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.

Can it happen that maybe you need to config the token pool first (for example by running set_pool) and then propose a new administrator with everything already configured?

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.

SetPool should be called after this during the sequence. I guess the only scenario where we couldn't call SetPool after admin registration is if we were registering to an address CLL didn't control? So that would be the manual registration flow and the customer can set it afterwards?

@chris-de-leon-cll chris-de-leon-cll added this pull request to the merge queue May 12, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks May 12, 2026
@chris-de-leon-cll chris-de-leon-cll added this pull request to the merge queue May 12, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks May 12, 2026
@chris-de-leon-cll chris-de-leon-cll added this pull request to the merge queue May 12, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks May 12, 2026
@chris-de-leon-cll chris-de-leon-cll added this pull request to the merge queue May 12, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks May 12, 2026
@chris-de-leon-cll chris-de-leon-cll added this pull request to the merge queue May 12, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks May 12, 2026
@chris-de-leon-cll chris-de-leon-cll added this pull request to the merge queue May 12, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks May 12, 2026
@github-actions
Copy link
Copy Markdown

Metric fix/solana-accept-tar-staleness main
Coverage 70.1% 69.8%

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