fix(harness): CollateralPosition PDA on both deposit paths + flow coverage (SOL-375/379/380) - #57
Merged
Merged
Conversation
valthon
force-pushed
the
fix/SOL-375-379-380-opp-collateral
branch
2 times, most recently
from
August 13, 2026 04:23
fea0d97 to
1d24df4
Compare
Contributor
Author
valthon
force-pushed
the
fix/SOL-375-379-380-opp-collateral
branch
from
August 15, 2026 00:13
1d24df4 to
1dacdaa
Compare
Contributor
Author
14/14 e2e flows ✅Cross-repo E2E integration run with all three branches pinned to Directly exercising this branch's surfaces:
Run: https://github.com/Wire-Network/wire-platform-build-system/actions/runs/31853124095 |
valthon
force-pushed
the
fix/SOL-375-379-380-opp-collateral
branch
2 times, most recently
from
August 19, 2026 14:35
f1b1022 to
0cce106
Compare
Contributor
Author
jglanz
requested changes
Aug 20, 2026
jglanz
left a comment
Contributor
There was a problem hiding this comment.
Split out the port lock changes into another PR, and just validate the timeout changes and should be good to go
solana-test-validator's RUST_LOG now defaults to an ADDITIVE filter — agave's own `solana=info,agave=info` plus the program-log target — so on-chain msg!() output reaches <ledger>/validator.log WITHOUT silencing agave's startup diagnostics. A bare program-log directive REPLACES agave's default and hides exactly the panic/bind lines validatorLogTail() exists to surface. A daemon's resolved env is threaded through DaemonConfig into the emitted start.sh, so a validator relaunched from that script produces the same logs the harness run would. The pre-commit hook falls through to a direct pnpm run when mise is present but cannot execute — notably a fresh clone whose mise config has not been trusted yet — rather than hard-blocking every commit. The mise-pinned Node is still used whenever mise works; it is just no longer a hard dependency on local configuration state. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The SOL outpost program moved operator collateral from a bounded global ledger to per-(operator, token_code) CollateralPosition PDAs, and its dispatch handlers settle SPL collateral out of a per-token collateral_vault. The harness now models both. Both deposit paths in SolanaCollateralTool derive and carry the CollateralPosition PDA, and the batch-operator-termination flow snapshots the position's rent so a full-drain close is asserted. The per-token little-endian seed encoder that three call sites had each re-spelled is consolidated into one tokenCodeToLittleEndianBuffer in slugUtils, and every consumer points at it. The outpost bootstrap pre-creates reserve_aggregate's associated token account for each registered SPL collateral mint, via a new idempotent SolanaFundingTool.ensureAssociatedTokenAccount. An OPERATOR_ACTION(SLASH) of SPL collateral settles into that ATA, the program never creates ATAs, and a dropped seizure carries no return attestation to re-drive it — so the destination must exist before the slash lands (SOL-380). A real outpost deploy owes the same one-time step when it registers an SPL collateral token. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VqAoaLdCBqgV2vXgDCGhgY
valthon
force-pushed
the
fix/SOL-375-379-380-opp-collateral
branch
from
August 20, 2026 15:36
0cce106 to
398d0f9
Compare
jglanz
approved these changes
Aug 20, 2026
Findings from a four-reviewer pass (STYLE.md/eslint, manifest rules,
duplication, plus two independent second opinions), each verified
against the source before being applied.
start.sh no longer freezes build-host state. DaemonConfig carried
`SolanaValidatorProcess.resolveEnv()`, which reads the BUILD host's
RUST_LOG, and the renderer emitted the frozen answer as an
unconditional export. Both branches were wrong: a build host WITH
RUST_LOG set stripped the program-log target from the published script
entirely — the exact defect this branch exists to fix — and a build
host without it pinned a value the operator could not override.
DaemonConfig now carries the unconditional `DefaultEnv`, and
environmentLines renders `[ -n "${NAME:-}" ] || export NAME=<value>`
so the run-time environment still wins. The guarded form is used
rather than `${NAME:-…}` because bash re-parses quotes inside an
expansion word, so a value containing a single quote would break the
script; the guard keeps shellQuote's fully-inert single-quoted form.
This restores the renderer's own documented invariant — build-time
conditionals render as SHELL, never frozen.
The ensureAssociatedTokenAccount test asserted nothing. It stubbed
`sendTransaction` while sendAndPoll submits via `sendRawTransaction`,
so its write-count flag could never flip and the ATA-absent branch —
the one SOL-380's argument rests on — had no coverage at all. The stub
now implements the methods the send path actually calls and counts raw
submissions; both branches are asserted.
Seeds and PDA derivation stop forking. `reserve_aggregate` was
re-spelled in SolanaCollateralTool while the same change consumed
`SolanaOutpostBootstrapper.PdaSeed.ReserveAggregate` two files over;
CollateralPosition and CollateralVault join that registry and the tool
derives all three from it. The `findProgramAddressSync` plumbing was
triplicated across two files behind three private one-liners — now one
`SolanaOutpostProgramTool.derivePda`.
tokenCodeToLittleEndianBuffer is renamed slugNameToLittleEndianBuffer
and widened to `number | bigint`. Five of its twelve call sites encode
a RESERVE code, so the old name misdescribed them, and the deleted
predecessor already used the slugName stem. The widening removes the
`BigInt(...)` wrapper the bigint-only signature forced on callers that
hold the `number` SlugName.from returns.
Also: the jest ceiling is raised to the loaded-host worst case, which
is what a green suite actually needs — an undershot ceiling kills a
test inside withFileLock and the teardown then throws
`ENOENT … wire-cluster-ports.lock.lock`, one defect presenting as two.
A dead Anchor Program load built solely for a failure-message
interpolation is gone, along with the divergent program-id source it
introduced. mintMockSplToUser and ensureAssociatedTokenAccount share
an instruction-returning helper, so the former keeps batching ATA
creation with its mint in one transaction. The renderer walks
daemon.env once, via the existing isNotEmpty predicate. Tests were
added for the DaemonConfig env wiring, both start.sh branches, and the
number carrier; the validator tests drop process.env mutation for
resolveEnv's parameter. Every touched file now passes prettier.
pnpm build, pnpm lint, and pnpm test all green: 220/220 suites,
1726/1726 tests.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
jglanz
approved these changes
Aug 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Important
Part of a three-repo set — all must merge together:
CollateralPositionPDA, asset-identity enforcement)The harness deposit paths must pass the
CollateralPositionPDA the program nowrequires; without wire-solana#409 these calls have nowhere to send it, and without this
PR every harness collateral deposit fails against the new program.
Note
The port-lock changes were split out to #76 per review. This PR no longer touches
BindConfigProviderorfsUtils. It does still carry the jesttestTimeoutbump —see "The timeout lives here" below for why that half came back. #76 now depends on this
PR, not the other way round.
Harness side of the SOL-375/379/380 collateral work. Three commits.
Track collateral position PDAs in SOL flows
SolanaCollateralToolderives and passes the per-(operator, token_code)CollateralPositionPDA on both deposit paths.TerminationScenariosnapshots the position's rent, which is refunded on thefull-drain close — so the SOL remit assertion is exact rather than off by the rent.
consolidated into one helper in
slugUtils.reserve_aggregate's ATA for every registered SPLcollateral mint (new idempotent
SolanaFundingTool.ensureAssociatedTokenAccount). AnSPL
SLASHsettles into that ATA; the program never creates ATAs and a dropped seizurehas no return attestation to re-drive it, so the destination must exist first (SOL-380).
A real outpost deploy owes the same one-time step when it registers an SPL collateral
token — the sole remaining production follow-up.
Log validator output; unblock commits on untrusted mise config
solana-test-validator'sRUST_LOGdefaults to an additive filter (agave's ownsolana=info,agave=infoplus the program-log target), so on-chainmsg!()reachesvalidator.logWITHOUT silencing agave's startup diagnostics — a bare program-logdirective would replace agave's default and hide the panic/bind lines
validatorLogTail()surfaces..husky/pre-commitfalls through to a directpnpmrun when mise is present but cannotexecute (a fresh clone whose mise config has not been trusted yet).
OMC style, lint, rules AND dedupe review
A four-reviewer pass (STYLE.md/eslint, manifest rules, duplication, plus two independent
second opinions); every finding was verified against the source before being applied.
start.shno longer freezes build-host state.DaemonConfigcarriedSolanaValidatorProcess.resolveEnv(), which reads the build host'sRUST_LOG, and therenderer emitted that frozen answer as an unconditional
export. Both branches werewrong: a build host with
RUST_LOGset stripped the program-log target from thepublished script entirely — the very defect the commit above exists to fix — and one
without it pinned a value the operator could not override.
DaemonConfignow carries theunconditional
DefaultEnv, and the renderer emits[ -n "${NAME:-}" ] || export NAME=<value>so the run-time environment still wins. (Theguarded form rather than
${NAME:-…}: bash re-parses quotes inside an expansion word, soa value containing
'would break the script.) This restores the renderer's owndocumented invariant — build-time conditionals render as SHELL, never frozen.
The
ensureAssociatedTokenAccounttest asserted nothing. It stubbedsendTransactionwhile
sendAndPollsubmits viasendRawTransaction, so its write-count flag could neverflip — and the ATA-absent branch, the one SOL-380's argument rests on, had no coverage
at all. The stub now implements the methods the send path actually calls; both branches
are asserted.
Seeds and PDA derivation stop forking.
reserve_aggregatewas re-spelled inSolanaCollateralToolwhile the same change consumedSolanaOutpostBootstrapper.PdaSeed.ReserveAggregatetwo files over.CollateralPositionand
CollateralVaultjoin that registry and the tool derives all three from it. ThefindProgramAddressSyncplumbing was triplicated behind three private one-liners acrosstwo files — now one
SolanaOutpostProgramTool.derivePda.tokenCodeToLittleEndianBuffer→slugNameToLittleEndianBuffer, widened tonumber | bigint. Five of its twelve call sites encode a reserve code, so the oldname misdescribed them, and the deleted predecessor already used the
slugNamestem. Thewidening drops the
BigInt(...)wrapper the bigint-only signature forced on callersholding the
numberthatSlugName.fromreturns.Also: a dead Anchor
Programload built solely to interpolate a failure message is gone,along with the divergent program-id source it introduced;
mintMockSplToUserandensureAssociatedTokenAccountshare an instruction-returning helper so the former keepsbatching ATA creation with its mint in one transaction; the renderer walks
daemon.envonce via the existingisNotEmptypredicate; tests added for theDaemonConfigenv wiring, bothstart.shbranches, and thenumbercarrier; thevalidator tests drop
process.envmutation forresolveEnv's parameter; every touchedfile passes prettier.
The timeout lives here
The jest ceiling (30s → 120s, root + cluster-tool) briefly moved to #76 and has come back,
because it is what a green suite actually needs and it is not part of the port-lock
change the reviewer asked to split out.
An earlier revision of this description claimed the
ENOENT … wire-cluster-ports.lock.lockfailures were a second defect requiring #76. That was wrong. They are a consequence
of the undershot ceiling: a test killed at 30s dies inside
withFileLock, jest tears thesuite down, the fixture removes its temp registry dir while
proper-lockfile's refreshtimer still holds the lock, and
onCompromisedthrows. One defect, two symptoms.Measured on the same tree:
Exceeded timeout of 30000 ms)Verification
Rebased on latest
origin/master(78285a80); each commit builds independently.pnpm build✅pnpm lint✅ (no suppressions added; every touched file prettier-clean)pnpm test✅ — 220/220 suites, 1726/1726 tests, all 8 projectsThe cross-repo verification run must override all three repos (
BRANCH_WIRE_SYSIO,BRANCH_WIRE_SOLANA,BRANCH_WIRE_TOOLS_TS=fix/SOL-375-379-380-opp-collateral) — theprogram, the relay manifest, and the harness deposit paths move together.
Prior full-platform e2e evidence (pre-split, same harness surfaces):
14/14 flows green.
🤖 Generated with Claude Code
https://claude.ai/code/session_01VqAoaLdCBqgV2vXgDCGhgY