Skip to content

cluster-tool: drive the folded liqsol_core OPP outpost + real yield flush - #25

Draft
valthon wants to merge 4 commits into
masterfrom
feat/opp-liqsol-integrated-support
Draft

cluster-tool: drive the folded liqsol_core OPP outpost + real yield flush#25
valthon wants to merge 4 commits into
masterfrom
feat/opp-liqsol-integrated-support

Conversation

@valthon

@valthon valthon commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Teaches the OPP harness to drive the folded liqsol_core integrated outpost — deployed upgradeable, every admin op gated by a global_config PDA — and reworks flow-yield-distribution to exercise the program's real staking-yield path. The integrated outpost is the harness's only Solana OPP mode; the epoch warp the yield flow needs is a per-cluster config option a flow opts into, not a shared env var, so the e2e gate's "no per-flow env" contract holds with zero workflow plumbing.

Changes (2 commits)

1. cluster-tool: drive folded liqsol_core OPP outpost — the harness capability:

  • Upgradeable deploy. SolanaValidatorProcess gains an optional upgradeAuthority; when set, the program loads via --upgradeable-program (so a ProgramData account exists). SolanaValidatorProcessSteps deploys liqsol_core with the per-cluster SOL deployer as that authority — which is also the global_config.admin.
  • global_config gating. SolanaOutpostBootstrapper runs initialize_global_config once, then passes admin + global_config (and adminAta on SPL reserve creation) on every OPP admin instruction. OppSolProgram holds the supporting constants (global_config seed, BPF upgradeable-loader id, per-cluster deployer keypair, epoch-warp validator args).
  • ClusterConfig.solanaEpochWarp — a new per-cluster option (resolved through ClusterBuildOptionsClusterConfigProvider, exposed as a CLI flag, default off). When set, the validator launches warped past Solana epoch 3. Off by default: the warp advances the Solana clock past the depot's sysio.authex 10-minute nonce window, so only a flow with no cross-chain SOL deposit may enable it.

2. flow-yield-distribution: real staking-yield flush — the flow drives the program's genuine yield pipeline: SolanaYieldEmitterTool seeds a staker via the dev-only dev_seed_staker_yield, then cranks flush_staking_yield so liqsol_core itself packs the StakingReward into the outbound buffer (retiring the prior bespoke add_attestation hand-injection). The scenario opts its own cluster into the epoch warp via its ClusterBuildOptions defaults (solanaEpochWarp: true). Because the program derives its own reward ref, the verify matches the depot's unmapped row by the staker's native SOL address (not a fixed ref) and asserts the credited WIRE amount equals the emitted reward.

⚠️ Build requirement

flow-yield-distribution calls the dev-only dev_seed_staker_yield, so the deployed program must be the development build (anchor build -- --features development). This is wired into the e2e gate by wire-solana's BUILD.bazel; every other flow runs on either build.

Verification

Rebased onto current master (post wire-sysio #502 merge — the zod-schema / external-cluster-config era) and re-validated on the rebased tip:

  • e2e gate 13/13 — run 30040425498: all 13 flow-* scenarios passed (0 failed), including yield-distribution, overriding wire-tools-ts to this PR's branch and wire-solana to its companion (#385); wire-sysio/master carries the merged #502, every other repo at its master default.
  • Clean tsc -b build + full eslint . under master's current lint laws.
  • cluster-tool unit suites green, with coverage for the solanaEpochWarp flag + config resolution, the --upgradeable-program argv branch, the pure solanaWarpArgs (warp on/off), resolveUpgradeAuthority (keypair creation + idempotency), and OppSolProgram (deployer-path helper + the epoch-3 warp arithmetic). The live-validator paths (emitSolanaYield, initialize_global_config) are exercised by the e2e gate above.

Companion PRs

  • wire-sysio (#502, merged) — reads inbound envelopes through the IDL decoder (decode_account_data) + retirement of nodeop's dead add_attestation binding, so the folded IDL is consumed verbatim.
  • wire-solana (#385) — the folded outpost, the custody_mint/custody_decimals Reserve fields, the dev_seed_staker_yield helper, and the --features development E2E build.

🤖 Generated with Claude Code

https://claude.ai/code/session_015CbgvM8EhrPL1VjX8RhNze

@valthon
valthon force-pushed the feat/opp-liqsol-integrated-support branch from e84708b to 8701bc7 Compare July 10, 2026 03:08
@valthon
valthon force-pushed the feat/opp-liqsol-integrated-support branch 4 times, most recently from 0038098 to 328dee2 Compare July 21, 2026 22:35
@valthon
valthon marked this pull request as ready for review July 22, 2026 00:35
@valthon
valthon force-pushed the feat/opp-liqsol-integrated-support branch 2 times, most recently from 0059f7e to 3d821e0 Compare July 23, 2026 19:40
valthon added 2 commits July 23, 2026 16:00
Teach the OPP harness to deploy and drive the folded liqsol_core Solana
outpost — the integrated outpost is the harness's only Solana OPP mode.

- Upgradeable deploy: SolanaValidatorProcess gains an optional
  upgradeAuthority; when set the program loads via --upgradeable-program
  so a ProgramData account exists. SolanaValidatorProcessSteps
  materializes a per-cluster deployer keypair as that authority (which is
  also the global_config.admin) before launch.
- global_config gating: SolanaOutpostBootstrapper runs
  initialize_global_config once, then passes admin + global_config (and
  adminAta on SPL reserve creation) on every OPP admin instruction.
  OppSolProgram holds the supporting constants: seeds, the BPF
  upgradeable-loader id, the deployer-keypair path, and the warp args.
- ClusterConfig.solanaEpochWarp: a new per-cluster option (resolved
  through ClusterBuildOptions -> ClusterConfigProvider, exposed as a CLI
  flag, default off). When set the validator launches warped past Solana
  epoch 3 so a flush_staking_yield flow can run; off by default because
  the warp trips the depot's cross-chain deposit nonce window.

Unit tests cover the config round-trip, the CLI flag, the
--upgradeable-program argv branch, solanaWarpArgs, resolveUpgradeAuthority
(creation + idempotency), and OppSolProgram (path helper + warp math).
Drive liqsol_core's genuine staking-yield pipeline instead of injecting a
synthetic attestation. SolanaYieldEmitterTool seeds a staker's on-chain
yield state via the dev-only dev_seed_staker_yield, then cranks
flush_staking_yield so the program itself packs the StakingReward into the
outbound buffer — retiring the bespoke add_attestation hand-injection and
its hand-rolled Borsh encoding.

Because the program derives its own reward ref, the verify matches the
depot's unmapped row by the staker's native SOL address (not a fixed
external_epoch_ref) and asserts the credited WIRE amount equals the
emitted reward. The now-dead SlugName / chain-code / epoch-ref constants
are removed.

Requires the --features development liqsol_core build (dev_seed_staker_yield
is compiled only under that feature), wired into the e2e gate by
wire-solana's BUILD.bazel.
@valthon
valthon force-pushed the feat/opp-liqsol-integrated-support branch from 3d821e0 to 5453fc4 Compare July 23, 2026 20:02
@valthon
valthon requested a review from jglanz July 23, 2026 22:24
…sol-integrated support

Dissolve OppSolProgram into canonical homes: the epoch warp becomes a
first-class SolanaValidatorProcess option (EpochWarpSlotsPerEpoch /
EpochWarpSlot constants; buildArgs emits the pair), the deployer-keypair
identity consolidates on SolanaFundingTool (deployerKeypairFile +
createDeployerKeypair single owner), the liqsol yield-pipeline PDA seeds join
SolanaOutpostBootstrapper.PdaSeed, and the BPF upgradeable-loader id lives on
the bootstrapper namespace as a typed PublicKey.

Split the SOL-side STAKING_REWARD emission into two Report-validated Steps —
planSolanaSeedYield (one dev_seed_staker_yield tx) then planSolanaFlushYield
(one flush_staking_yield tx) — via single-tx tool helpers devSeedStakerYield /
flushStakingYield; drop the dead wireAccount/shareBps inputs from the SOL path.

Correct the epoch-gate attribution (the Clock.epoch >= 3 gate is
dev_seed_staker_yield's MIN_SEED_EPOCH, not the flush) and replace the
incorrect authex-nonce rationale: the createlink nonce is harness Date.now()
validated against the depot's own chain time, so the Solana warp cannot trip
it — the warp stays opt-in because it puts the Solana clock ~80 minutes ahead
of real time, a non-production condition no other flow needs. Document
--solana-epoch-warp in both README create-option tables and the CLAUDE.md CLI
sentence.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K4FBvbwEeTj7ByeJzjehbQ
@valthon

valthon commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Checked out the full wire-platform w/ repo and cleaned up what I could.

The solana warp has to stay a setup config because the stock solana-test-validator (Agave) only supports genesis-time cli arguments for time warps. There is no equivalent to anvil's evm_increaseTime, so we can't do it as a Step without replacing the default solana-test-validator with something custom.

Green 13/13 e2e tests

…ereum,Solana}Schema

ClusterConfig gains dedicated per-chain child schemas — ClusterConfigEthereumSchema
(empty today) and ClusterConfigSolanaSchema — as direct, schema-defaulted children,
and the epoch-warp opt-in moves from the top-level solanaEpochWarp boolean to
solana.epochWarp, with ClusterBuildOptions.solana (SolanaOptions) carrying it
through the option chain (the --solana-epoch-warp flag is unchanged via the
nested shape leaf). flow-yield-distribution opts in via defaults.solana.epochWarp.

Also renames SolanaOutpostBootstrapper.oppAdminAccounts -> getAdminAccounts and
OppAdminAccounts -> AdminAccounts (the opp prefix was redundant inside the OPP
outpost bootstrapper; accessor verb per naming standards).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jglanz
jglanz requested a review from a team July 28, 2026 21:03

@jglanz jglanz 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.

David, I had claude take a run with full context, style, lint and rules. Comments are applied, your arguments were retained but renamed.

FYI - ALL FLOWS ARE INCAPABLE OF RUNNING AGAINST THE #next BRANCH
THIS ISSUE IS BLOCKING BOTH LOCAL AND e2e-tests WORKFLOW

Both test environments must work for an approval.


Specifically the same system that will run the production build, will also run the flow build, so instructions stating --features development are not acceptable.

// Per-cluster opt-in (`ClusterConfig.solana.epochWarp`, set only by
// `flow-yield-distribution`'s scenario `defaults`) — see the config
// field's doc for why every other flow keeps the real-time clock.
epochWarp: ctx.config.solana.epochWarp

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.

BLOCKER: wire-cluster-tool run relaunches through this step (ClusterManager.launchrunStart), re-passing --warp-slot 12300 against a ledger already at/past it — agave exits 1 with warp slot (12300) cannot be less than the working bank slot (12300) (verified on 4.0.3), so a warped cluster can never be restarted. Gate the warp arg pair on the ledger dir not existing yet, matching --slots-per-epoch's genesis-only semantics.

if (this.config.clusterDataPath != null) {
mkdirs(this.config.clusterDataPath)
const persistedFile = Path.join(this.config.clusterDataPath, "sol-deployer-keypair.json")
const persistedFile = SolanaFundingTool.deployerKeypairFile(this.config.clusterDataPath)

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.

Violation (duplication + invariant): with planStart now materializing the deployer via SolanaFundingTool.createDeployerKeypair, this duplicate persist re-implements it, and the generate-fallback above can mint a different identity than the launched upgrade authority — silently breaking the admin == upgrade authority invariant ensureGlobalConfig depends on. Delegate to createDeployerKeypair (or assert the file exists) and drop the fallback.

}
}
return StakingRewardMsg.toBinary(reward)
export async function devSeedStakerYield(

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.

Violation (tools-return-orchestration-units + mandatory tests): the rewritten tool exports two bare side-effecting write functions — a harness tool returns Step factories (plan*/run* inside a SolanaYieldEmitterTool namespace matching the filename), with the flow composing them; and the rewrite ships ZERO unit tests for either function ("unit tests are mandatory for every created or modified symbol — no exceptions"). Either promote these to tool-level plan*/run* factories with tests, or fold the bodies into the flow's existing named runners.

a non-production condition no other flow needs, so no other flow enables it.

Requires the `--features development` `liqsol_core` build (wired into the e2e
gate by wire-solana's `BUILD.bazel`), since a plain `anchor build` omits

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.

the same system that will run the production build, will also run the flow build, so instructions stating --features development are not acceptable.

Merge gate — and it blocks EVERY flow, not just this one: a 7-flow suite run against wire-solana next failed uniformly at deploy-solana: Account \authorityAta` not provided— this branch'sSolanaOutpostBootstrappertargets thenext-integ interface (admin/globalConfig/adminAta, initialize_global_config), and the SOL outpost deploy is in every flow's bootstrap. nextalso lacksdev_seed_staker_yield+ thedevelopment-feature BUILD.bazel wiring **(THIS IS NOT ALOUD)**. Nothing here can go green until wire-solana#385 (next-integ) lands on next`; sequence the merges accordingly.

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.

2 participants