Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e283f0b8f2 | ||
|
|
00a7f13c6f |
+56
-9
@@ -99,10 +99,27 @@ sikre tokenens pris eller muliggøre ny funktionalitet.
|
||||
|
||||
### 2.2 Buybacks og burn
|
||||
En af de vigtigste mekanismer i Evelyn er det automatiske tilbagekøb og efterfølgende burn af 'Evelyn IOU' tokens.
|
||||
En fast procentdel af overskuddet anvendes på at købe tokens på markedet – uanset den aktuelle pris – og brænde
|
||||
dem permanent. Dette reducerer det samlede udbud og skaber et kontinuerligt deflationært pres, hvilket typisk er
|
||||
positivt for investorerne. Buyback-strategien sikrer, at investorerne får en god pris for deres tokens, når det
|
||||
udnyttes at ikke alle investorer sælger på samme tid, da de har forskellige forventninger til prisen.
|
||||
En fast procentdel af overskuddet anvendes på at købe tokens på markedet, når
|
||||
Evelyn Price Index er under nul, og brænde dem permanent. Det reducerer udbuddet,
|
||||
når den aktuelle EVE-pris er lavere end Evelyns referencepris.
|
||||
|
||||
Når et input ellers er klar til køb, læser burneren en frisk kanonisk EVE/USDT-pris
|
||||
fra `TickerService` og kalder den tilsvarende `EvelynStatusService.getReferencePrice()`
|
||||
med det aktuelle beslutningstidspunkt. Køb tillades kun ved
|
||||
`aktuel pris < referencepris`; lighed giver intet køb. Der bruges samme eksisterende
|
||||
maksimale prisalder som til burn-værdiansættelsen, uden nye prisindstillinger.
|
||||
Referencekurven beregnes af status-servicen; hverken den sidst gemte historikværdi
|
||||
eller grafens punkter bruges til købsbeslutningen.
|
||||
|
||||
Ved lighed, højere pris eller manglende, ugyldige, for gamle eller utilgængelige
|
||||
prisdata springes alle nye køb over i iterationen, og årsagen logges. Forløbet
|
||||
fortsætter til EVE-saldo og mulig burn efter de selvstændige burn-regler. En fejl i
|
||||
referenceopslaget blokerer derfor ikke burn, når dens egen friske værdiansættelse
|
||||
og øvrige krav er opfyldt. Priserne vurderes igen ved næste ellers berettigede
|
||||
iteration; et prisskip sætter ingen permanent suspension.
|
||||
|
||||
Med referencepris 15 USDT/EVE tillader 14,99 et ellers berettiget køb; 15,00 og
|
||||
15,01 springer købet over. I alle tilfælde fortsætter EVE-burn-vurderingen.
|
||||
|
||||
Produktionsprocessen `evelyn.iou-burner.prod` accepterer følgende inputs i rækkefølge:
|
||||
|
||||
@@ -131,15 +148,45 @@ ingen inputtoken/USDC-pris eller nye Ticker-kilder og historikfiler.
|
||||
Højst ét input forsøges vekslet pr. iteration, med en times pause efter hver
|
||||
afsluttet iteration. Jupiter vælger pool og rute til EVE. USDT har fortsat første
|
||||
prioritet, så en vedvarende spendabel USDT-saldo kan udsætte SyrupUSDC-veksling.
|
||||
Hele den synlige EVE-saldo er derefter berettiget til burn, også ved direkte
|
||||
EVE-indbetalinger. Burn kræver stadig en frisk EVE/USDT-observation gennem det
|
||||
uændrede `TickerService.getLatestPrice()` og bekræftes ved `FINALIZED` før det
|
||||
eneste notifikationsforsøg. Inputgrænserne begrænser ikke EVE-burn.
|
||||
Efter det mulige buyback, også når prisbetingelsen springer købet over,
|
||||
genlæses hele den synlige EVE-saldo, også ved direkte
|
||||
EVE-indbetalinger. Ejeren har valgt et produktionsminimum på **0,1 EVE pr. burn**
|
||||
og en **reserve på 0,01 EVE**. Hub'en angiver begge som `MoneyAmount` med EVE's
|
||||
kanoniske UUID. Minimum skal være positivt, reserve skal være ikke-negativ;
|
||||
en reserve på nul er gyldig. Null-værdier og andre valutaer afvises ved oprettelse.
|
||||
|
||||
Burnbeløbet beregnes som `max(EVE-saldo - EVE-reserve, 0)`, afrundet ned til
|
||||
EVE-mintens præcision. Der brændes kun, når dette faktiske beløb er positivt og
|
||||
mindst minimum. Når tærsklen nås, brændes hele det berettigede beløb over
|
||||
reserven; minimum er en tærskel, og der er intet burn-maksimum. Eventuelle
|
||||
afrundingsrester bliver i wallet'en sammen med reserven. Små buybacks fortsætter,
|
||||
når købspris og inputkrav tillader det, og samler EVE op mellem burns.
|
||||
Under tærsklen sker hverken burn eller
|
||||
burn-notifikation.
|
||||
|
||||
| EVE-saldo | Produktionshandling | EVE tilbage efter succes |
|
||||
| ---: | --- | ---: |
|
||||
| 0,105 | Ingen burn | 0,105 |
|
||||
| 0,11 | Burn 0,1 EVE | 0,01 |
|
||||
| 0,135 | Burn 0,125 EVE | 0,01 |
|
||||
|
||||
Issue #93 bruger minimum 1 EVE og reserve 0,1 EVE som adfærdseksempler:
|
||||
1,05 EVE giver ingen burn, 1,1 EVE giver burn af 1 EVE, og 1,35 EVE giver burn af
|
||||
1,25 EVE. Produktionsværdierne ovenfor blev valgt særskilt den 2026-10-06.
|
||||
|
||||
Burn kræver stadig en frisk EVE/USDT-observation gennem det uændrede
|
||||
`TickerService.getLatestPrice()` og bekræftes ved `FINALIZED` før det eneste
|
||||
notifikationsforsøg. Pending burn og notifikationens EVE-beløb og dollarværdi
|
||||
bruger det faktisk indsendte burnbeløb uden reserven. Et pending burn afklares
|
||||
med sin oprindelige signatur uden ny tærskelvurdering eller genindsendelse.
|
||||
Inputgrænserne begrænser ikke EVE-burn.
|
||||
|
||||
Ved ukendt swap-resultat gemmes den oprindelige signatur, inputvaluta, beløb og
|
||||
validitetsoplysninger i processen. Senere iterationer undersøger samme signatur
|
||||
uden at gensende transaktionen. Bekræftet succes eller fejl ved mindst `CONFIRMED`
|
||||
frigiver nye swaps med frisklæste saldi. Udløb frigiver kun et swap, når den
|
||||
frigiver mulig ny inputvurdering med frisklæste saldi; ethvert nyt køb skal stadig
|
||||
bestå den aktuelle prisbetingelse. Afklaringen er read-only og fortsætter også
|
||||
ved pris lig med eller over referenceprisen. Udløb frigiver kun et swap, når den
|
||||
oprindelige transaktions validitet er udløbet, og frisk finalized historik for
|
||||
signerens adresse med tilstrækkelig lokal dækning beviser, at den ikke blev udført.
|
||||
En begrænset historikside, der ikke dækker hele perioden, er fortsat uafklaret.
|
||||
|
||||
@@ -177,7 +177,9 @@ nenjim.journal.service</code></pre>
|
||||
<code>start()</code>, and recorded only after successful return. An omitted process is therefore neither
|
||||
constructed, registered, started, nor displayed by Process Manager.</p>
|
||||
<p><code>[AUTO_START]</code> is the complete manually ordered process dependency closure. The Hub does not infer,
|
||||
insert, recursively construct, or reorder dependencies; JavaFX runtime must precede selected JavaFX
|
||||
insert, recursively construct, or reorder dependencies. Ticker and Production Evelyn status must precede
|
||||
a selected Production IOU Burner; the Hub injects that status service for reference-price decisions before
|
||||
new buybacks and fails clearly if the predecessor is unavailable. JavaFX runtime must precede selected JavaFX
|
||||
applications. Construction, registration, and startup remain fail-fast without rollback. Earlier successful
|
||||
snapshot entries remain truthful after a later failure, and the online point plus blocking wait are reached
|
||||
only after every configured process succeeds.</p>
|
||||
|
||||
@@ -283,6 +283,11 @@ navngivne `Production`- og `Test`-instanser gennem `EvelynStatusServiceImpl` og
|
||||
`EvelynStatusService`-views i Mission Control. Process Manager viser de nye ID'er fra Hub'ens startup-snapshot.
|
||||
Historik ligger fortsat i `data/evelyn/<instance-name>/status.log` med formatversion 1 og kræver ingen migration.
|
||||
|
||||
Hvis `evelyn.iou-burner.prod` vælges, skal både `assetaz.ticker.default` og
|
||||
`evelyn.status.prod` stå før den. Hub'en injicerer Production status-servicen til
|
||||
referenceprisopslag før nye buybacks; en manglende eller senere status-forgænger
|
||||
giver en tydelig konstruktionsfejl uden implicit oprettelse eller omordning.
|
||||
|
||||
Efter validering konstruerer og starter Hub'en Registry, registrerer det som `nenjim.registry.service` og registrerer
|
||||
startup-milepælen. Derefter konstrueres og registreres hele den nuværende passive graph uden at starte workers eller
|
||||
processer. Først herefter behandles resten af `[AUTO_START]` i den angivne rækkefølge, begyndende med Journal. Hver
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-10-06
|
||||
@@ -0,0 +1,28 @@
|
||||
# Design
|
||||
|
||||
## Context
|
||||
|
||||
See proposal.md for the operational need. The burner already reloads EVE after its independent buyback stage, retains known burn signatures before finalization, and prioritizes pending burns. It can reuse supported-program and mint-supply precision validation already used for input selection. Monetary values and CurrencyType are immutable.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:** Preserve a configured EVE reserve and accumulate small buys/deposits until a representable burn amount qualifies, retaining existing transaction and notification safety.
|
||||
|
||||
**Non-Goals:** Fixed burn batches, maximum burn size, a new configuration framework, notification batching, altered buybacks, persistent reconciliation or live deployment.
|
||||
|
||||
## Decisions
|
||||
|
||||
1. Add `minimumBurnAmount` and `eveReserve` MoneyAmount constructor arguments beside input bounds. Validate non-null amount/currency/UUID, canonical EVE UUID, positive minimum and non-negative reserve before startup, copying into canonical EVE MoneyAmount values. Stale display metadata is accepted by UUID. A separate record or ValueTag adds no needed domain safety.
|
||||
2. Reload EVE after buybacks. Subtract the reserve and skip non-positive available balance, then resolve EVE mint precision using its detected token program. Round DOWN and compare that actual amount to the exact unrounded minimum. Skip non-positive/below-minimum amounts before price lookup/submission. Rounding configuration up or down would alter the owner's reserve/minimum; instead any fractional remainder stays in the wallet.
|
||||
3. Submit all eligible EVE above reserve, using the same amount for price multiplication and PendingBurn. Keep pending-burn priority and awaiting unchanged so later thresholds/balances never rebuild an unresolved burn.
|
||||
4. The owner chose production minimum **0.1 EVE** and reserve **0.01 EVE** on 2026-10-06. Supply both using canonical EVE in the production Hub. Issue #93's 1/0.1 values remain independent constructor acceptance examples, not production defaults.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [EVE mint metadata unavailable] → Postpone burning without notification, using existing warning and interruption behavior; buybacks have already been evaluated independently.
|
||||
- [Configuration precision exceeds mint precision] → Round only the eligible burn amount DOWN; compare to exact minimum and retain at least the exact reserve.
|
||||
- [Unknown transaction outcome] → Preserve original signature and existing process-local suspension/reconciliation behavior. Restart still requires external reconciliation.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Compile the reference sources and update the production construction call together. Verify controlled threshold, precision, accumulation, direct-deposit, validation, pending-state and notification cases offline, synchronize specs and archive. Deployment is a separate task; no private deployment files or live transactions are touched. Rollback requires restoring the prior constructor and Hub call together, after reconciling uncertain transactions.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Proposal
|
||||
|
||||
## Why
|
||||
|
||||
Small Evelyn buybacks currently create tiny burns and excessive proof notifications. Accumulating EVE for larger burns while retaining a wallet reserve supports predictable Evelyn operation without changing its small buybacks.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Add independently configured EVE `MoneyAmount` values for a strictly positive burn minimum and non-negative retained reserve.
|
||||
- Burn the entire balance above reserve, rounded down to mint precision, only when that actual amount reaches the minimum.
|
||||
- Keep buybacks independent and preserve pending transaction reconciliation and successful FINALIZED proof delivery using the actual burned amount.
|
||||
- Configure production with the owner's chosen minimum 0.1 EVE and reserve 0.01 EVE; retain issue #93's 1/0.1 examples as acceptance cases.
|
||||
- **BREAKING**: Reference implementation constructor requires the two burn policy amounts.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
None.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `evelyn-iou-burner-service`: EVE burn policy configuration/validation, precision-aware eligibility above reserve, submitted/notification amounts and production settings.
|
||||
|
||||
## Impact
|
||||
|
||||
Reference burner implementation and its domain JavaDoc, production Hub constructor, Evelyn configuration documentation and directly affected OpenSpec requirements. No new ValueTags, administration API, buyback policy, scheduling, deployment changes or permanent tests. Verification uses controlled offline dependencies without live transactions.
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
# Spec Delta
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Independent EVE burn policy configuration
|
||||
The constructor SHALL accept minimumBurnAmount and eveReserve as EVE-denominated MoneyAmount values identified by canonical EVE UUID regardless of supplied display metadata. Both values, their amounts, currencies and UUIDs SHALL be non-null; minimum SHALL be strictly positive and reserve non-negative. Zero reserve SHALL be valid. Invalid burn configuration SHALL fail before worker startup without changing existing input validation.
|
||||
|
||||
#### Scenario: Invalid burn configuration
|
||||
- **WHEN** either value, amount, currency or UUID is null, a UUID is not EVE, the minimum is zero/negative, or the reserve is negative
|
||||
- **THEN** construction fails before creating a worker or transaction
|
||||
|
||||
#### Scenario: Stale EVE metadata and zero reserve
|
||||
- **WHEN** both values carry the EVE UUID with stale names/symbols and the reserve is zero
|
||||
- **THEN** construction succeeds and uses canonical EVE identity with the configured exact numeric amounts
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Complete visible EVE balance is eligible
|
||||
After the optional swap attempt, the service SHALL detect the fixed EVE mint's supported token program internally and reload the wallet's complete available EVE balance. A missing EVE mint account, unsupported program, absent wallet token account, zero balance, or negative/invalid balance SHALL cause no burn or notification in that iteration. The service SHALL subtract its configured EVE reserve, clamp negative available amounts to zero and round down to validated EVE mint precision using the detected token program. It SHALL submit a new burn only when the actual rounded amount is positive and at least the configured minimum burn amount; equality qualifies. It SHALL burn the entire eligible amount above reserve, without a maximum or fixed batch size, including EVE deposited before the iteration or visible after a failed or absent swap. Below threshold or with unusable mint precision it SHALL retain the EVE and send no burn notification. This threshold SHALL NOT suppress independent buybacks.
|
||||
|
||||
#### Scenario: Existing EVE without buyback
|
||||
- **WHEN** no swap is performed but the wallet contains a positive EVE balance
|
||||
- **THEN** the service evaluates that complete balance under the EVE reserve, precision and burn-minimum rules
|
||||
|
||||
#### Scenario: EVE account is absent or empty
|
||||
- **WHEN** the wallet has no EVE token account or its complete balance is zero
|
||||
- **THEN** the iteration submits no burn and sends no notification
|
||||
|
||||
#### Scenario: Issue example threshold and reserve
|
||||
- **WHEN** minimum is 1 EVE and reserve is 0.1 EVE
|
||||
- **THEN** balances 0, 0.05, 0.1, 1.05 and 1.099999 produce no burn; balance 1.1 burns 1 and balance 1.35 burns 1.25, retaining 0.1 after success
|
||||
|
||||
#### Scenario: Accumulation across small buybacks
|
||||
- **WHEN** individually eligible small buybacks accumulate EVE over multiple iterations
|
||||
- **THEN** each below-threshold iteration still permits its buyback and sends no burn message; the first qualifying iteration burns all eligible EVE and subsequent accumulation starts with the retained reserve
|
||||
|
||||
#### Scenario: Exact configuration exceeds mint precision
|
||||
- **WHEN** six-decimal mint precision rounds available 1.0000009 to 1.000000 while minimum is 1.0000001
|
||||
- **THEN** no burn occurs; no amount is rounded upward and the reserve is never consumed to reach the minimum
|
||||
|
||||
### Requirement: Fresh EVE value is required before submission
|
||||
Immediately before a new burn, the service SHALL obtain the latest canonical EVE/USDT observation and require a positive price, non-null observation timestamp, and age no greater than `maximumPriceAge` milliseconds. USDT SHALL be treated as USD for notification value, calculated as the actual rounded eligible EVE amount above reserve multiplied by that fresh price. If the pair is unsupported or the observation is missing, invalid, or stale, the service SHALL warn, postpone the burn, send no notification, and retry only by re-evaluating state in a later scheduled iteration.
|
||||
|
||||
#### Scenario: Fresh EVE price exists
|
||||
- **WHEN** a positive rounded EVE amount at or above the burn minimum and sufficiently fresh positive EVE/USDT observation are available
|
||||
- **THEN** the service calculates the notification's USD value from that actual eligible burn amount, excluding reserve before submitting the burn
|
||||
|
||||
#### Scenario: EVE price is stale
|
||||
- **WHEN** EVE is present but its observation is older than the configured age limit
|
||||
- **THEN** the service leaves EVE unburned and sends no notification in that iteration
|
||||
|
||||
### Requirement: Single complete-balance burn submission and pending state
|
||||
For a positive rounded EVE amount at or above the minimum with a usable price, the service SHALL call the wallet burn operation exactly once with the fixed EVE mint and entire eligible rounded amount above reserve. It SHALL NOT automatically retry, rebuild, re-sign, or resubmit within or across iterations. Immediately after a non-blank signature is returned, it SHALL retain process-local pending data containing that signature, actual submitted EVE amount excluding reserve, and its calculated notification value or complete notification text before awaiting the signature.
|
||||
|
||||
If burn submission fails without a usable signature and its outcome may be unknown, including an I/O or interruption path whose contract permits post-submission uncertainty, the service SHALL conservatively suspend further burn submissions for the current process. It MAY continue ordinary iteration work that does not submit another burn. Recovery SHALL require external reconciliation followed by a complete process restart, not a Burner service stop/start operation.
|
||||
|
||||
#### Scenario: Burn returns a signature
|
||||
- **WHEN** the wallet submits the entire eligible rounded amount above reserve and returns a non-blank signature
|
||||
- **THEN** pending state is retained before the service begins finalization waiting
|
||||
|
||||
#### Scenario: Submission outcome may be unknown without a signature
|
||||
- **WHEN** burn submission fails or is interrupted without providing a usable signature and may already have reached Solana
|
||||
- **THEN** the service performs no further burn submission until external reconciliation and a complete process restart
|
||||
|
||||
#### Scenario: Pending burn bypasses new threshold evaluation
|
||||
- **WHEN** a retained burn signature remains pending while balance is now below the minimum or at the reserve
|
||||
- **THEN** only the original signature is awaited, its original submitted amount/value are preserved and it is never re-evaluated, rebuilt or resubmitted
|
||||
|
||||
### Requirement: Exact token-capped amount and per-currency reserve
|
||||
The service SHALL select the lesser of positive `balance - minimumReserve` and the input UUID's maximum token amount, then round downward to mint precision. It SHALL skip non-positive results and actual rounded amounts below that UUID's minimum, continuing to later inputs in the same iteration without counting a swap attempt. Equality SHALL qualify. It SHALL never round upward, top up from reserves or exceed either existing bound. Input selection SHALL require no input/USDC Ticker observation. Minima and maxima SHALL constrain buyback inputs only; EVE burns SHALL remain uncapped and governed independently by their own EVE minimum and reserve. Native SOL reserves SHALL use observed balance without estimating future fees.
|
||||
|
||||
#### Scenario: SyrupUSDC maximum is lower than spendable balance
|
||||
- **WHEN** SyrupUSDC balance is 0.5, its reserve is 0.095, its minimum is 0.045 and its maximum is 0.2
|
||||
- **THEN** 0.2 SyrupUSDC is selected without requesting a SyrupUSDC/USDC observation
|
||||
|
||||
#### Scenario: SyrupUSDC spendable balance is lower than the maximum
|
||||
- **WHEN** SyrupUSDC balance is 0.245, its reserve is 0.095, its minimum is 0.045 and its maximum is 0.2
|
||||
- **THEN** 0.150 SyrupUSDC is selected before downward mint-precision rounding
|
||||
|
||||
#### Scenario: Reserve consumes the balance
|
||||
- **WHEN** the current balance is equal to or below that entry's minimum reserve
|
||||
- **THEN** no amount of that currency is intentionally swapped
|
||||
|
||||
#### Scenario: USDT maximum preserves the production bound
|
||||
- **WHEN** USDT balance is 0.35, its reserve is 0.1, its minimum is 0.05 and its maximum is 0.25
|
||||
- **THEN** 0.25 USDT is selected before downward mint-precision rounding
|
||||
|
||||
#### Scenario: Precision rounding never increases the spend
|
||||
- **WHEN** the lesser of spendable balance and maximum is 0.1509999, the mint supports six decimals and its minimum is no greater than 0.150999
|
||||
- **THEN** the service selects 0.150999, and if downward rounding instead produces zero the input is skipped
|
||||
|
||||
#### Scenario: Native transaction fees
|
||||
- **WHEN** native SOL is selected with a configured reserve
|
||||
- **THEN** the service enforces the reserve against the observed balance only and does not estimate fees, even though later fees may reduce the final balance or cause execution to fail
|
||||
|
||||
#### Scenario: USDT below and at its minimum
|
||||
- **WHEN** USDT reserve is 0.1 and minimum is 0.05
|
||||
- **THEN** balances 0.14 and 0.149999 are skipped, while balance 0.15 selects exactly 0.05
|
||||
|
||||
#### Scenario: Tiny first input permits useful later input
|
||||
- **WHEN** USDT balance is 0.100001 with reserve 0.1/minimum 0.05, and SyrupUSDC balance is 0.502048 with reserve 0.095/minimum 0.045/maximum 0.2
|
||||
- **THEN** the USDT remainder is skipped and 0.2 SyrupUSDC is the only attempted swap
|
||||
|
||||
#### Scenario: SyrupUSDC minimum boundary
|
||||
- **WHEN** SyrupUSDC reserve is 0.095 and minimum is 0.045
|
||||
- **THEN** balance 0.14 selects exactly 0.045 while 0.139999 is skipped
|
||||
|
||||
#### Scenario: Actual rounded candidate falls below a fractional minimum
|
||||
- **WHEN** six-decimal input precision rounds candidate 0.0500009 down to 0.050000 and its minimum is 0.0500001
|
||||
- **THEN** the candidate is skipped without rounding up or using reserves
|
||||
|
||||
#### Scenario: Input minima do not suppress direct EVE burn
|
||||
- **WHEN** every configured input is below its minimum but the wallet contains EVE with a usable EVE/USDT price
|
||||
- **THEN** the complete EVE balance is still evaluated and its rounded eligible amount above the EVE reserve is burned if it meets the EVE burn minimum
|
||||
|
||||
### Requirement: Production burner accepts USDT and SyrupUSDC
|
||||
The production Hub SHALL configure USDT first with reserve 0.1, minimum input 0.05 and maximum input 0.25 USDT, and SyrupUSDC second with reserve 0.095, minimum input 0.045 and maximum input 0.2 SyrupUSDC. These SHALL be token-denominated limits without a USD-value guarantee. The production EVE burn minimum SHALL be 0.1 EVE and its retained reserve SHALL be 0.01 EVE, as selected by the owner on 2026-10-06, configured as MoneyAmount values using the canonical EVE UUID. The existing burner wallet, Jupiter route selection, registration, hourly interval, slippage, EVE/USDT freshness, confirmation settings and burn recovery behavior SHALL be preserved without new Ticker sources or history storage.
|
||||
|
||||
#### Scenario: Production inputs and settings
|
||||
- **WHEN** the Hub constructs the production burner
|
||||
- **THEN** it supplies the exact reserves, minima and maxima as MoneyAmount values in USDT-then-SyrupUSDC priority, supplies minimum burn 0.1 EVE/reserve 0.01 EVE and retains the other existing production arguments
|
||||
|
||||
#### Scenario: Production burn threshold
|
||||
- **WHEN** production EVE balance is below 0.11, exactly 0.11, or 0.135 with usable mint precision and fresh price
|
||||
- **THEN** below 0.11 produces no burn, 0.11 burns 0.1 and 0.135 burns 0.125; successful burns retain 0.01 EVE
|
||||
@@ -0,0 +1,15 @@
|
||||
# Tasks
|
||||
|
||||
## 1. EVE burn policy
|
||||
|
||||
- [x] 1.1 Add canonical EVE MoneyAmount minimum/reserve constructor validation and constructor JavaDoc; verify invalid/null/wrong UUID values fail before worker/transaction activity, stale metadata works and zero reserve is valid in controlled offline checks.
|
||||
- [x] 1.2 Replace full-balance submission with reserve-aware downward precision rounding and actual-minimum qualification; update domain JavaDoc and verify issue boundaries, rounding, direct deposits, accumulation, independent buybacks and actual pending/notification amounts with controlled dependencies.
|
||||
|
||||
## 2. Production configuration
|
||||
|
||||
- [x] 2.1 Supply owner's selected minimum 0.1 EVE and reserve 0.01 EVE in the production Hub, update Evelyn docs and verify source arguments plus production 0.11 threshold/0.135 balance examples offline.
|
||||
|
||||
## 3. Integration and completion
|
||||
|
||||
- [x] 3.1 Compile relevant sources, verify pending burn/swap reconciliation and suspension regressions without live transactions, and record results in verification.md; run strict OpenSpec validation and git diff --check.
|
||||
- [x] 3.2 Synchronize directly affected main specification, verify all delta blocks match while unrelated content survives, run strict validation and archive completed artifacts/tasks.
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
# Verification
|
||||
|
||||
Verified on 2026-10-06 with `/usr/local/software/java/jfx-25`.
|
||||
|
||||
## Compilation and controlled acceptance checks
|
||||
|
||||
- `JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew compileJava compileTestJava --offline --no-daemon` → **BUILD SUCCESSFUL in 6s**; normal Detag and both source compilations executed. The initial sandbox attempt could not create Gradle's existing cache lock outside the workspace; the same compilation passed with approved cache access.
|
||||
- Temporary harness sources in `/tmp/evelyn-issue93` compiled with `javac --enable-preview --release 25 -cp 'build/classes/java/main:build/libs/*' -d /tmp/evelyn-issue93`.
|
||||
- `java --enable-preview -cp '/tmp/evelyn-issue93:build/classes/java/main:build/libs/*' Issue93Verification` → **PASS: 235 focused burn-policy assertions**.
|
||||
- Same invocation for the copied `Issue92Verification` → **PASS: 89 buyback-minimum regression assertions**.
|
||||
- Same invocation for the copied `Issue91Verification`, restricted to its burner checks → **PASS: 63 pending-swap/recovery regression assertions**. Unchanged RPC/Jupiter library checks were not rerun.
|
||||
- Production source check confirms canonical EVE minimum **0.1** and reserve **0.01** in the correct constructor order and preserved input bounds, hourly interval, freshness and finalization arguments.
|
||||
|
||||
All wallet, transaction, chain, pricing and notification dependencies are controlled. No real RPC, signing or live financial operation occurs. No permanent automated tests were added. The older temporary harness copies add only a positive EVE burn minimum of 0.000001 and zero reserve to their constructor calls, retaining their original buyback/recovery expectations; original temporary files are preserved.
|
||||
|
||||
The #93 checks cover every issue #93 1/0.1 example and production 0.1/0.01 boundaries, numerical equality across decimal scales, zero reserve, fractional minimum above the rounded candidate, fractional reserve preservation, zero after rounding, both SPL Token and Token-2022 precision, missing/unsupported mint data, supply/mint/program mismatch, invalid decimals, I/O and interruption, missing/negative balance, stale price, repeated small buyback accumulation and subsequent reserve-based accumulation, direct deposits with absent/below-minimum inputs, no burn maximum, failed/unknown buybacks, original pending signature and amount/value across timeout/I/O/interruption, failed finalization without notification, once-only notification after delayed finalization, delivery failure and signature-less burn suspension with continued buybacks. Null/invalid numeric configuration, wrong/unknown UUIDs and stale EVE metadata are checked before worker/transaction activity; CurrencyType itself rejects null UUIDs before burner configuration.
|
||||
|
||||
An initial temporary harness compile failed because its notification-error fixture used Throwable rather than the transport's IOException contract; the fixture was corrected. Its default successful swap output was also corrected from zero to a positive amount, preventing fixture-only result validation failures. The final checks above pass against the unchanged production implementation.
|
||||
|
||||
## Review and workflow
|
||||
|
||||
Parent independent source/design/documentation and harness review found no material findings. Two delta scenario wording fixes were applied to state the actual eligible amount and grammatically separate the new production burn settings. Scope is limited to constructor burn policy, EVE eligibility/actual amount use, JavaDoc, production arguments, Evelyn docs and their specification. Buyback limits, priority, pending recovery, lifecycle, Jupiter, Ticker, ValueTags and private deployment files are preserved.
|
||||
|
||||
Strict active-change validation and `git diff --check` passed before synchronization. Final strict validation of the active change and synchronized main specification passed. Six delta requirement blocks match the main specification exactly; 12 untouched requirements remain byte-for-byte intact. The purpose now describes accumulated, threshold-qualified burns above reserve. Existing long-requirement informational hints remain because modified requirement blocks stay whole.
|
||||
|
||||
An initial temporary synchronization matcher allowed a requirement title to span lines and copied an invalid delta header into the main specification. Strict validation caught this before archive. The matcher was corrected and the main-spec edit rebuilt from the clean starting version plus the six intended blocks, then compared for exact delta matching and untouched-content preservation. Final strict validation passes.
|
||||
|
||||
All four planning artifacts and all five tasks are complete. The completed change is archived at `openspec/changes/archive/2026-10-06-93-add-minimum-eve-burn-and-reserve`. After archival, `openspec list --json` reports no active changes, and `openspec validate --archived --json --report findings` passed for all **31** archived changes with **0 failures**. Final whitespace validation also passes.
|
||||
|
||||
No deployment, push, Gitea write or live integration was performed.
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-10-06
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
# Design
|
||||
|
||||
## Context
|
||||
|
||||
See proposal.md for motivation and the two delta specifications for required behavior. The burner already selects one rounded, reserve/minimum/maximum-qualified input, invokes Jupiter at most once, and then independently reloads EVE for burn evaluation. Its fresh canonical EVE/USDT reader is also used for burn valuation. The Hub constructs processes in the manually configured order and rejects unavailable process dependencies.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:** Insert the purchase condition at the last point before a new Jupiter attempt, use the corresponding live reference API, and retain independent burn and pending-transaction behavior.
|
||||
|
||||
**Non-Goals:** A general price-policy framework, another clock/configuration/freshness option, duplicated curve calculations, stored-history decisions, or changes to transaction execution, Ticker sources, lifecycle and deployment selection.
|
||||
|
||||
## Decisions
|
||||
|
||||
1. Add a required `EvelynStatusService` constructor dependency beside Ticker. Production composition uses the existing `requireProcessDependency` guard for `evelyn.status.prod`. The Hub does not infer or start dependencies; operators must place Ticker and Production status before the burner. Constructing a private reference calculator in the burner would duplicate ownership and risk policy drift.
|
||||
2. After the first eligible rounded input is selected, reuse `findFreshPrice(eveUsdtTradingPair)`, obtain the reference through `getReferencePrice(Instant.now())`, validate its canonical pair and positive amount, and compare with `BigDecimal.compareTo`. This avoids division/index rounding and keeps the check close to submission. Checking before input selection would age the decision during balance/mint work; history/chart reads would use stale sample time.
|
||||
3. A rejected condition returns only from `attemptAtMostOneSwap`, preserving the unconditional later `processCompleteEveBalance`. No other input can bypass a globally rejected EVE price. Missing/invalid reference or runtime lookup failure produces a safe warning; an at/above comparison logs the values and decision instant. No suspension state is set. Burn valuation independently reads its existing fresh price and never depends on reference availability.
|
||||
4. Pending burn and swap paths stay ahead of input selection. Reconciliation is read-only regardless of price, and only a newly eligible purchase after reconciliation invokes the gate. Amount limits, precision, unknown-outcome handling and notifications remain as existing contracts define.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Market movement after observation] → Use the existing maximum price age and the last purchase-selection point; Jupiter execution and slippage rules remain unchanged.
|
||||
- [Reference service failure] → Skip the purchase for this iteration, keep burn processing and re-evaluate next time; log exception type without arbitrary provider text.
|
||||
- [Missing deployment predecessor] → Fail clearly through existing Hub dependency validation; document order without editing private deployment configuration.
|
||||
- [Unknown transaction after restart] → Retain current pending-state/restart contracts; the price gate cannot replace reconciliation.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Compile and verify offline, then review the change. A later separately authorized deployment must have `assetaz.ticker.default` and `evelyn.status.prod` before a selected `evelyn.iou-burner.prod`. This implementation request changes no live configuration, performs no transactions and deploys nothing. Rollback restores the preceding application version and its constructor composition; uncertain transactions must first be reconciled under the existing restart rules.
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
# Proposal
|
||||
|
||||
## Why
|
||||
|
||||
Issue #94 requires Evelyn IOU buybacks to reduce supply specifically while the Evelyn Price Index is below zero. The burner currently buys whenever an input is eligible, so it needs the existing reference-price policy before submitting a new purchase while preserving independent EVE burning.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Permit new buybacks only with a fresh canonical EVE/USDT price strictly below the corresponding Evelyn status service's reference price at the current decision instant.
|
||||
- Skip only the purchase when either price is unusable or the market price is at/above reference; continue existing EVE burn processing and re-evaluate next iteration.
|
||||
- Preserve pending-burn priority, read-only pending-swap reconciliation, at most one new swap, and all existing amount, reserve, precision and transaction rules.
|
||||
- Inject the Production Evelyn status service through the existing Hub composition, documenting its explicit startup dependency and the purchase-only policy.
|
||||
- Update constructor/domain JavaDoc and operating documentation and verify the boundaries and recovery paths offline.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
None.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `evelyn-iou-burner-service`: Gate new purchases on the existing reference-price API while keeping reconciliation and burns independent.
|
||||
- `nenjim-hub-configuration`: Supply the corresponding Production status service to the burner using existing explicit dependency ordering.
|
||||
|
||||
## Impact
|
||||
|
||||
The burner constructor gains an `EvelynStatusService` dependency, supplied by `NenjimHubImpl`. Runtime input configuration, schedule, Ticker sources, history, ValueTags and live deployment configuration remain outside this change. Verification uses controlled offline collaborators without financial transactions or permanent test additions.
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
# Spec Delta
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: At most one confirmed swap attempt per iteration
|
||||
After selecting a positive exact amount and satisfying the current below-reference price condition, the service SHALL invoke the existing Jupiter exact-input operation once with the selected CIS-resolved input mint, fixed EVE output mint, selected amount, and configured slippage. Normal return SHALL be treated as already `CONFIRMED` and SHALL NOT trigger a second confirmation wait. Once invoked, no later input currency SHALL be attempted in that iteration regardless of success or failure, and the service SHALL proceed to reload the complete EVE balance whenever processing was not interrupted.
|
||||
|
||||
A structured definitive on-chain swap failure SHALL be logged without suspending later-process swaps. A timed-out outcome or known-signature confirmation communication error SHALL retain process-local pending swap signature, canonical input, attempted amount and original validity metadata for later read-only reconciliation. Proven pre-submission errors SHALL permit a fresh attempt in a later scheduled iteration. An ambiguous post-submission error without a trustworthy signature SHALL retain manual suspension. Interruption SHALL preserve available pending context and propagate normally. No unknown outcome SHALL cause automatic rebuilding, re-signing or resubmission; pending swaps SHALL still permit existing EVE burn evaluation.
|
||||
|
||||
#### Scenario: First swap fails definitively
|
||||
- **WHEN** the first attempted input returns a definitive on-chain failure
|
||||
- **THEN** no second input is attempted in that iteration, the EVE balance is still reloaded, and a later iteration may attempt a new swap
|
||||
|
||||
#### Scenario: Swap outcome is unknown
|
||||
- **WHEN** the attempted swap times out, throws an I/O failure that permits unknown post-submission status, or is interrupted
|
||||
- **THEN** no equivalent or alternative swap is attempted while its outcome is unresolved; a known signature is checked in later iterations and a signature-less ambiguous submission requires manual reconciliation
|
||||
|
||||
#### Scenario: Swap returns normally
|
||||
- **WHEN** Jupiter returns a successful result whose transaction already reached `CONFIRMED`
|
||||
- **THEN** the burner performs no additional confirmation wait and reloads the wallet instead of deriving its burn amount only from the returned output amount
|
||||
|
||||
### Requirement: Production burner accepts USDT and SyrupUSDC
|
||||
The production Hub SHALL configure USDT first with reserve 0.1, minimum input 0.05 and maximum input 0.25 USDT, and SyrupUSDC second with reserve 0.095, minimum input 0.045 and maximum input 0.2 SyrupUSDC. These SHALL be token-denominated limits without a USD-value guarantee. The production EVE burn minimum SHALL be 0.1 EVE and its retained reserve SHALL be 0.01 EVE, as selected by the owner on 2026-10-06, configured as MoneyAmount values using the canonical EVE UUID. The production Hub SHALL supply its corresponding Production Evelyn status service for current reference-price decisions. The existing burner wallet, Jupiter route selection, registration, hourly interval, slippage, EVE/USDT freshness, confirmation settings and burn recovery behavior SHALL be preserved without new Ticker sources or history storage.
|
||||
|
||||
#### Scenario: Production inputs and settings
|
||||
- **WHEN** the Hub constructs the production burner
|
||||
- **THEN** it supplies the exact reserves, minima and maxima as MoneyAmount values in USDT-then-SyrupUSDC priority, supplies minimum burn 0.1 EVE/reserve 0.01 EVE and the Production status service, and retains the other existing production arguments
|
||||
|
||||
#### Scenario: Production burn threshold
|
||||
- **WHEN** production EVE balance is below 0.11, exactly 0.11, or 0.135 with usable mint precision and fresh price
|
||||
- **THEN** below 0.11 produces no burn, 0.11 burns 0.1 and 0.135 burns 0.125; successful burns retain 0.01 EVE
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: New buybacks require a below-reference market price
|
||||
An otherwise eligible new buyback SHALL require a fresh canonical EVE/USDT observation within the existing maximum price age and a usable canonical EVE/USDT reference price from the injected corresponding Evelyn status service at the current decision instant. Both prices SHALL be positive. Only actual price strictly below reference SHALL permit submission; equality SHALL not qualify. The existing public reference operation SHALL be reused without copying curve calculations or reading stored history.
|
||||
|
||||
#### Scenario: Strict boundary below reference
|
||||
- **WHEN** an input is otherwise eligible and reference price is 15 USDT/EVE
|
||||
- **THEN** market price 14.99 permits the existing buyback, while 15.00 and 15.01 submit no new swap from any input in that iteration
|
||||
|
||||
#### Scenario: Required prices are unusable
|
||||
- **WHEN** market observation is missing, stale, incomplete, unavailable or has an invalid pair/non-positive price, or reference lookup throws, is missing or has an invalid pair/non-positive price
|
||||
- **THEN** no new swap is submitted and a safe diagnostic explains why the purchase was skipped
|
||||
|
||||
#### Scenario: Reference uses the current decision instant
|
||||
- **WHEN** the first usable input is ready for a new swap
|
||||
- **THEN** the corresponding status service is asked for its reference price at the current instant rather than using a sampled history point or the observation timestamp
|
||||
|
||||
### Requirement: Price skips preserve independent burn processing and recovery
|
||||
A rejected buyback price condition SHALL skip purchases only, continue complete EVE-balance processing under the existing independent burn requirements, and SHALL NOT create a suspension. Each subsequent otherwise eligible iteration SHALL re-evaluate prices. Pending-burn priority and read-only pending-swap reconciliation SHALL remain independent of the new-purchase condition; any newly permitted purchase after reconciliation SHALL pass the current price check.
|
||||
|
||||
#### Scenario: Existing EVE burns at or above reference
|
||||
- **WHEN** market price is equal to or above reference and existing EVE satisfies its own reserve, minimum, precision, fresh valuation and transaction requirements
|
||||
- **THEN** no new swap occurs and the existing eligible EVE is burned normally
|
||||
|
||||
#### Scenario: Reference unavailable but burn valuation is usable
|
||||
- **WHEN** reference lookup is unavailable or invalid but existing EVE and its fresh canonical valuation satisfy the burn requirements
|
||||
- **THEN** the purchase is skipped and the independent eligible burn still occurs
|
||||
|
||||
#### Scenario: Next scheduled iteration recovers
|
||||
- **WHEN** one iteration skips a purchase because its prices do not qualify and a later otherwise eligible iteration has usable prices below reference
|
||||
- **THEN** the later iteration can attempt its one new swap without a restart or manual reset
|
||||
|
||||
#### Scenario: Pending swap is resolved above reference
|
||||
- **WHEN** a pending swap resolves read-only at or above reference
|
||||
- **THEN** it is cleared normally, any otherwise eligible new purchase is price-checked and skipped, and existing EVE burn evaluation continues
|
||||
|
||||
#### Scenario: Unresolved swap and pending burn retain priority
|
||||
- **WHEN** a swap remains unresolved or the iteration starts with a pending burn
|
||||
- **THEN** existing transaction-priority rules apply without evaluating a new-purchase price condition or submitting a new swap
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
# Spec Delta
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Selected processes are constructed and started lazily in exact order
|
||||
The Hub SHALL support the modern process IDs `nenjim.registry.service`, `nenjim.journal.service`, `assetaz.ticker.default`, `jupiter-perps-alarm.default`, `evelyn.status.prod`, `evelyn.status.test`, `evelyn.iou-burner.prod`, `evelyn.liquidity.prod`, `nenjim.javafx-runtime.service`, `evelyn.mission-control.default`, and `nenjim.process-manager.default` through implementation-only construction capability that creates no process instance during validation. After passive composition, the Hub SHALL visit every configured entry after Registry in declared order, beginning with Journal. At each entry it SHALL construct only that process and any explicitly composed process-specific passive dependencies or administration view, register the process under the exact configured ID, resolve it through the canonical `NenjimProcess` contract where applicable, call `start()`, and record the ID only after successful return.
|
||||
|
||||
The production IOU burner SHALL receive the already constructed Production Evelyn status service, and SHALL require both Ticker and `evelyn.status.prod` to precede `evelyn.iou-burner.prod`. The configuration SHALL be the complete manually ordered process dependency closure. The Hub SHALL not discover, insert, recursively construct, reorder, or automatically start dependencies. A process omitted from `[AUTO_START]` SHALL not be constructed, registered, started, or recorded, even when its implementation is available. Legacy process prototypes outside the modern Hub graph SHALL not be selectable.
|
||||
|
||||
#### Scenario: Configured process starts successfully
|
||||
- **WHEN** a configured process entry is reached and its dependencies are already available
|
||||
- **THEN** that process is constructed, registered, started, and recorded before the next entry is reached
|
||||
|
||||
#### Scenario: Process is omitted
|
||||
- **WHEN** a known process ID does not appear in `[AUTO_START]`
|
||||
- **THEN** no instance of that process is constructed or registered and its lifecycle is never started or recorded
|
||||
|
||||
#### Scenario: Jupiter Perps Alarm is selected
|
||||
- **WHEN** `jupiter-perps-alarm.default` is explicitly listed after its required predecessors
|
||||
- **THEN** the otherwise inactive modern alarm process is constructed, registered, started and recorded at that exact position
|
||||
|
||||
#### Scenario: Dependency order is invalid
|
||||
- **WHEN** a selected process is reached before a process dependency required by its current constructor or startup behavior
|
||||
- **THEN** construction or startup fails clearly without implicit dependency construction, repair, or reordering
|
||||
|
||||
#### Scenario: JavaFX processes are omitted
|
||||
- **WHEN** a valid headless plan omits `nenjim.javafx-runtime.service`, `evelyn.mission-control.default`, and `nenjim.process-manager.default`
|
||||
- **THEN** none of those objects is constructed or registered and the JavaFX toolkit remains uninitialized by the Hub
|
||||
|
||||
#### Scenario: Renamed status services retain dependency wiring
|
||||
- **WHEN** `evelyn.status.prod` and `evelyn.status.test` are listed after Ticker and before Mission Control
|
||||
- **THEN** the Hub constructs separate Production/Test status services, registers the renamed IDs, and injects the corresponding typed service views into Mission Control in the configured order
|
||||
|
||||
#### Scenario: Deployment still contains an old status ID
|
||||
- **WHEN** a private deployment configuration contains `evelyn.service.prod` or `evelyn.service.test`
|
||||
- **THEN** validation rejects the unknown ID before startup; deployment migration replaces only that ID with `evelyn.status.prod` or `evelyn.status.test`, preserving selection and order
|
||||
|
||||
#### Scenario: Liquidity is explicitly selected
|
||||
- **WHEN** `evelyn.liquidity.prod` is reached in a valid startup plan
|
||||
- **THEN** the Hub loads its runtime configuration, binds the dedicated signer/wallet and Jupiter service to each other, constructs direct Raydium access and registers/starts the liquidity process without inserting a Ticker or status dependency or reordering the plan
|
||||
|
||||
#### Scenario: Production burner has its reference-price dependency
|
||||
- **WHEN** `evelyn.iou-burner.prod` is reached after Ticker and `evelyn.status.prod`
|
||||
- **THEN** the Hub injects the corresponding Production status service for buyback price decisions
|
||||
|
||||
#### Scenario: Production burner precedes its status dependency
|
||||
- **WHEN** the burner is reached without a previously constructed Production status service
|
||||
- **THEN** construction fails clearly without creating or starting the omitted dependency or the burner
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
# Tasks
|
||||
|
||||
## 1. Purchase policy and independent burns
|
||||
|
||||
- [x] 1.1 Require the corresponding Evelyn status service and gate a fully eligible new purchase on fresh canonical actual price strictly below its current reference; verify constructor validation, below/equal/above and unavailable/invalid prices with controlled offline checks.
|
||||
- [x] 1.2 Update constructor/domain JavaDoc and Evelyn operating documentation; verify price skips still burn eligible existing EVE, re-evaluate next iteration and preserve pending burn/swap priority in the same offline checks.
|
||||
|
||||
## 2. Hub composition
|
||||
|
||||
- [x] 2.1 Inject the Production status dependency through the existing Hub guard and document the explicit Ticker/status/burner order; verify production arguments and fail-fast dependency behavior without starting the Hub or editing deployment configuration.
|
||||
|
||||
## 3. Integrated verification
|
||||
|
||||
- [x] 3.1 Compile main and existing test sources through normal Gradle/Detag, execute focused offline purchase/burn/recovery checks and relevant prior burner harnesses, and record their final results without adding permanent tests or running live transactions.
|
||||
- [x] 3.2 Validate the scoped OpenSpec change strictly and check whitespace and changed-file scope; deliver completed planning artifacts and verification evidence for review.
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
# Verification
|
||||
|
||||
Verified on 2026-10-06 with `/usr/local/software/java/jfx-25`.
|
||||
|
||||
## Compilation and controlled acceptance checks
|
||||
|
||||
- `JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew compileJava compileTestJava --offline --no-daemon` → **BUILD SUCCESSFUL in 8s**. Normal Gradle-integrated Detag and both source compilations executed. The initial sandbox attempt could not create the existing Gradle cache lock outside the workspace; the same compilation passed with approved cache access.
|
||||
- Temporary sources in `/tmp/evelyn-issue94` compiled with `javac --enable-preview --release 25 -cp 'build/classes/java/main:build/libs/*' -d /tmp/evelyn-issue94`.
|
||||
- `java --enable-preview -cp '/tmp/evelyn-issue94:build/classes/java/main:build/libs/*' Issue94Verification` → **PASS: 459 focused issue #94 assertions**.
|
||||
- The copied `Issue93Verification` → **PASS: 246 burn-policy regression assertions**.
|
||||
- The copied `Issue92Verification` → **PASS: 115 input-minimum regression assertions**.
|
||||
- The copied `Issue91Verification`, restricted to burner checks → **PASS: 80 pending-swap/recovery regression assertions**. Unchanged Solana/Jupiter library checks were not rerun.
|
||||
- Production composition source check verifies the corresponding Production status dependency and preserved input reserves/minima/maxima, EVE burn minimum/reserve, hourly interval, slippage, freshness and finalization arguments.
|
||||
- An INFO/WARN-enabled controlled run verifies purchase-skip reasons for at/above reference, unavailable/invalid reference and unusable market price, without arbitrary reference/Ticker exception messages.
|
||||
|
||||
The **900** controlled assertions invoke the actual burner iteration with offline wallet, chain, swap, Ticker, reference and notification collaborators. No RPC, signing, live financial operation, application/Hub startup or deployment configuration read occurs. The Hub dependency guard is invoked directly without constructing a Hub. No permanent automated tests were added. Prior harness copies only add an always-below reference stub to constructor calls and update #92's expected canonical EVE/USDT lookup count for eligible purchases; original temporary files remain intact.
|
||||
|
||||
The #94 harness covers 14.99/15.00/15.01 against reference 15, numeric equality across scales, a close below-reference decimal, the current reference decision instant instead of observation/history time, complete independent burns and reserve preservation, missing/unavailable/non-positive/null-amount/wrong-pair market and reference data, stale market observations, fresh independent burn valuation after a purchase-price failure, stale burn valuation postponement, next-iteration recovery from every rejection category, input priority and amount limits, below-minimum input fallback, no-eligible-input/direct-EVE-only paths, manual swap suspension and null status validation. It verifies original-signature read-only reconciliation while price is above reference, unresolved retention, definitive success/failure/proven expiry followed by a newly rejected purchase, later below-reference recovery, and simultaneous pending burn/swap priority with no reference lookup or new transaction. PriceObservation itself rejects null observation timestamp/price before they can reach the burner.
|
||||
|
||||
An initial temporary harness compilation used the wrong fully qualified component-ID type; it was corrected to the repository's existing `com.r35157.nenjim.service.registry.valuetypes.NenjimComponentId`. Final compiled checks pass. Concurrent initial Java invocations reported harmless performance-data lock warnings; the controlled diagnostic run disabled performance data. No production fix was needed for either verification-only issue.
|
||||
|
||||
## Review and workflow
|
||||
|
||||
Parent independent review of the proposal/specs/design, implementation, operating documentation and controlled harness found no actionable findings. The existing malformed burn JavaDoc line was corrected while updating that same list item. Scope is limited to the required reference dependency, new-purchase condition, Hub composition, related documentation and two OpenSpec capability deltas. Transaction execution, reconciliation, burn valuation/policy, ValueTags, scheduling and private deployment files retain their existing behavior.
|
||||
|
||||
Strict validation of the active change and `git diff --check` pass. All four planning artifact types and all five implementation tasks are complete. The active change is ready for review; main specifications have not been synchronized and the change has not been archived.
|
||||
|
||||
No deployment, commit, push, Gitea write or live integration was performed.
|
||||
|
||||
## Follow-up: clearer purchase-skip logs
|
||||
|
||||
The owner's requested logging refinement keeps price eligibility unchanged. INFO price skips now show a direct labelled comparison, for example `Skipping buyback as EVE price is at or above reference (market=15.01 >= reference=15 USDT/EVE)`, followed by the trading pair, decision instant and next action. Prices use plain decimals without redundant trailing zeros. WARN skips retain their reason; unavailable or invalid reference-price diagnostics also include the known market price.
|
||||
|
||||
`JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew compileJava --offline --no-daemon` passed. The existing temporary #94 harness passed its 459 assertions again with INFO/WARN enabled. Its captured output in `/tmp/evelyn-issue94/issue94-logging-refinement.log` confirms the numeric comparison for both equality and higher prices, reference failure details, missing/stale market diagnostics and exclusion of arbitrary exception messages. No new tests were added.
|
||||
|
||||
## Finalization
|
||||
|
||||
On 2026-10-06, the owner requested synchronization and archiving. Both capability deltas were merged into the main specifications for `evelyn-iou-burner-service` and `nenjim-hub-configuration`, preserving unrelated requirements and existing purpose sections. Every delta requirement was compared against the resulting main spec, and strict validation of both main specs and the completed change passed; only informational hints about long requirement descriptions were reported.
|
||||
|
||||
The seven completed change files, including `.openspec.yaml` and all five completed tasks, were archived intact to `openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price`. OpenSpec reports no remaining active changes. This finalization records the subsequent sync/archive after the initial implementation and logging-refinement verification above.
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-10-06
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
# Design
|
||||
|
||||
## Context
|
||||
|
||||
See proposal.md for motivation and the two delta specifications for required behavior. The burner already selects one rounded, reserve/minimum/maximum-qualified input, invokes Jupiter at most once, and then independently reloads EVE for burn evaluation. Its fresh canonical EVE/USDT reader is also used for burn valuation. The Hub constructs processes in the manually configured order and rejects unavailable process dependencies.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:** Insert the purchase condition at the last point before a new Jupiter attempt, use the corresponding live reference API, and retain independent burn and pending-transaction behavior.
|
||||
|
||||
**Non-Goals:** A general price-policy framework, another clock/configuration/freshness option, duplicated curve calculations, stored-history decisions, or changes to transaction execution, Ticker sources, lifecycle and deployment selection.
|
||||
|
||||
## Decisions
|
||||
|
||||
1. Add a required `EvelynStatusService` constructor dependency beside Ticker. Production composition uses the existing `requireProcessDependency` guard for `evelyn.status.prod`. The Hub does not infer or start dependencies; operators must place Ticker and Production status before the burner. Constructing a private reference calculator in the burner would duplicate ownership and risk policy drift.
|
||||
2. After the first eligible rounded input is selected, reuse `findFreshPrice(eveUsdtTradingPair)`, obtain the reference through `getReferencePrice(Instant.now())`, validate its canonical pair and positive amount, and compare with `BigDecimal.compareTo`. This avoids division/index rounding and keeps the check close to submission. Checking before input selection would age the decision during balance/mint work; history/chart reads would use stale sample time.
|
||||
3. A rejected condition returns only from `attemptAtMostOneSwap`, preserving the unconditional later `processCompleteEveBalance`. No other input can bypass a globally rejected EVE price. Missing/invalid reference or runtime lookup failure produces a safe warning; an at/above comparison logs the values and decision instant. No suspension state is set. Burn valuation independently reads its existing fresh price and never depends on reference availability.
|
||||
4. Pending burn and swap paths stay ahead of input selection. Reconciliation is read-only regardless of price, and only a newly eligible purchase after reconciliation invokes the gate. Amount limits, precision, unknown-outcome handling and notifications remain as existing contracts define.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Market movement after observation] → Use the existing maximum price age and the last purchase-selection point; Jupiter execution and slippage rules remain unchanged.
|
||||
- [Reference service failure] → Skip the purchase for this iteration, keep burn processing and re-evaluate next time; log exception type without arbitrary provider text.
|
||||
- [Missing deployment predecessor] → Fail clearly through existing Hub dependency validation; document order without editing private deployment configuration.
|
||||
- [Unknown transaction after restart] → Retain current pending-state/restart contracts; the price gate cannot replace reconciliation.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Compile and verify offline, then review the change. A later separately authorized deployment must have `assetaz.ticker.default` and `evelyn.status.prod` before a selected `evelyn.iou-burner.prod`. This implementation request changes no live configuration, performs no transactions and deploys nothing. Rollback restores the preceding application version and its constructor composition; uncertain transactions must first be reconciled under the existing restart rules.
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
# Proposal
|
||||
|
||||
## Why
|
||||
|
||||
Issue #94 requires Evelyn IOU buybacks to reduce supply specifically while the Evelyn Price Index is below zero. The burner currently buys whenever an input is eligible, so it needs the existing reference-price policy before submitting a new purchase while preserving independent EVE burning.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Permit new buybacks only with a fresh canonical EVE/USDT price strictly below the corresponding Evelyn status service's reference price at the current decision instant.
|
||||
- Skip only the purchase when either price is unusable or the market price is at/above reference; continue existing EVE burn processing and re-evaluate next iteration.
|
||||
- Preserve pending-burn priority, read-only pending-swap reconciliation, at most one new swap, and all existing amount, reserve, precision and transaction rules.
|
||||
- Inject the Production Evelyn status service through the existing Hub composition, documenting its explicit startup dependency and the purchase-only policy.
|
||||
- Update constructor/domain JavaDoc and operating documentation and verify the boundaries and recovery paths offline.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
None.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `evelyn-iou-burner-service`: Gate new purchases on the existing reference-price API while keeping reconciliation and burns independent.
|
||||
- `nenjim-hub-configuration`: Supply the corresponding Production status service to the burner using existing explicit dependency ordering.
|
||||
|
||||
## Impact
|
||||
|
||||
The burner constructor gains an `EvelynStatusService` dependency, supplied by `NenjimHubImpl`. Runtime input configuration, schedule, Ticker sources, history, ValueTags and live deployment configuration remain outside this change. Verification uses controlled offline collaborators without financial transactions or permanent test additions.
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
# Spec Delta
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: At most one confirmed swap attempt per iteration
|
||||
After selecting a positive exact amount and satisfying the current below-reference price condition, the service SHALL invoke the existing Jupiter exact-input operation once with the selected CIS-resolved input mint, fixed EVE output mint, selected amount, and configured slippage. Normal return SHALL be treated as already `CONFIRMED` and SHALL NOT trigger a second confirmation wait. Once invoked, no later input currency SHALL be attempted in that iteration regardless of success or failure, and the service SHALL proceed to reload the complete EVE balance whenever processing was not interrupted.
|
||||
|
||||
A structured definitive on-chain swap failure SHALL be logged without suspending later-process swaps. A timed-out outcome or known-signature confirmation communication error SHALL retain process-local pending swap signature, canonical input, attempted amount and original validity metadata for later read-only reconciliation. Proven pre-submission errors SHALL permit a fresh attempt in a later scheduled iteration. An ambiguous post-submission error without a trustworthy signature SHALL retain manual suspension. Interruption SHALL preserve available pending context and propagate normally. No unknown outcome SHALL cause automatic rebuilding, re-signing or resubmission; pending swaps SHALL still permit existing EVE burn evaluation.
|
||||
|
||||
#### Scenario: First swap fails definitively
|
||||
- **WHEN** the first attempted input returns a definitive on-chain failure
|
||||
- **THEN** no second input is attempted in that iteration, the EVE balance is still reloaded, and a later iteration may attempt a new swap
|
||||
|
||||
#### Scenario: Swap outcome is unknown
|
||||
- **WHEN** the attempted swap times out, throws an I/O failure that permits unknown post-submission status, or is interrupted
|
||||
- **THEN** no equivalent or alternative swap is attempted while its outcome is unresolved; a known signature is checked in later iterations and a signature-less ambiguous submission requires manual reconciliation
|
||||
|
||||
#### Scenario: Swap returns normally
|
||||
- **WHEN** Jupiter returns a successful result whose transaction already reached `CONFIRMED`
|
||||
- **THEN** the burner performs no additional confirmation wait and reloads the wallet instead of deriving its burn amount only from the returned output amount
|
||||
|
||||
### Requirement: Production burner accepts USDT and SyrupUSDC
|
||||
The production Hub SHALL configure USDT first with reserve 0.1, minimum input 0.05 and maximum input 0.25 USDT, and SyrupUSDC second with reserve 0.095, minimum input 0.045 and maximum input 0.2 SyrupUSDC. These SHALL be token-denominated limits without a USD-value guarantee. The production EVE burn minimum SHALL be 0.1 EVE and its retained reserve SHALL be 0.01 EVE, as selected by the owner on 2026-10-06, configured as MoneyAmount values using the canonical EVE UUID. The production Hub SHALL supply its corresponding Production Evelyn status service for current reference-price decisions. The existing burner wallet, Jupiter route selection, registration, hourly interval, slippage, EVE/USDT freshness, confirmation settings and burn recovery behavior SHALL be preserved without new Ticker sources or history storage.
|
||||
|
||||
#### Scenario: Production inputs and settings
|
||||
- **WHEN** the Hub constructs the production burner
|
||||
- **THEN** it supplies the exact reserves, minima and maxima as MoneyAmount values in USDT-then-SyrupUSDC priority, supplies minimum burn 0.1 EVE/reserve 0.01 EVE and the Production status service, and retains the other existing production arguments
|
||||
|
||||
#### Scenario: Production burn threshold
|
||||
- **WHEN** production EVE balance is below 0.11, exactly 0.11, or 0.135 with usable mint precision and fresh price
|
||||
- **THEN** below 0.11 produces no burn, 0.11 burns 0.1 and 0.135 burns 0.125; successful burns retain 0.01 EVE
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: New buybacks require a below-reference market price
|
||||
An otherwise eligible new buyback SHALL require a fresh canonical EVE/USDT observation within the existing maximum price age and a usable canonical EVE/USDT reference price from the injected corresponding Evelyn status service at the current decision instant. Both prices SHALL be positive. Only actual price strictly below reference SHALL permit submission; equality SHALL not qualify. The existing public reference operation SHALL be reused without copying curve calculations or reading stored history.
|
||||
|
||||
#### Scenario: Strict boundary below reference
|
||||
- **WHEN** an input is otherwise eligible and reference price is 15 USDT/EVE
|
||||
- **THEN** market price 14.99 permits the existing buyback, while 15.00 and 15.01 submit no new swap from any input in that iteration
|
||||
|
||||
#### Scenario: Required prices are unusable
|
||||
- **WHEN** market observation is missing, stale, incomplete, unavailable or has an invalid pair/non-positive price, or reference lookup throws, is missing or has an invalid pair/non-positive price
|
||||
- **THEN** no new swap is submitted and a safe diagnostic explains why the purchase was skipped
|
||||
|
||||
#### Scenario: Reference uses the current decision instant
|
||||
- **WHEN** the first usable input is ready for a new swap
|
||||
- **THEN** the corresponding status service is asked for its reference price at the current instant rather than using a sampled history point or the observation timestamp
|
||||
|
||||
### Requirement: Price skips preserve independent burn processing and recovery
|
||||
A rejected buyback price condition SHALL skip purchases only, continue complete EVE-balance processing under the existing independent burn requirements, and SHALL NOT create a suspension. Each subsequent otherwise eligible iteration SHALL re-evaluate prices. Pending-burn priority and read-only pending-swap reconciliation SHALL remain independent of the new-purchase condition; any newly permitted purchase after reconciliation SHALL pass the current price check.
|
||||
|
||||
#### Scenario: Existing EVE burns at or above reference
|
||||
- **WHEN** market price is equal to or above reference and existing EVE satisfies its own reserve, minimum, precision, fresh valuation and transaction requirements
|
||||
- **THEN** no new swap occurs and the existing eligible EVE is burned normally
|
||||
|
||||
#### Scenario: Reference unavailable but burn valuation is usable
|
||||
- **WHEN** reference lookup is unavailable or invalid but existing EVE and its fresh canonical valuation satisfy the burn requirements
|
||||
- **THEN** the purchase is skipped and the independent eligible burn still occurs
|
||||
|
||||
#### Scenario: Next scheduled iteration recovers
|
||||
- **WHEN** one iteration skips a purchase because its prices do not qualify and a later otherwise eligible iteration has usable prices below reference
|
||||
- **THEN** the later iteration can attempt its one new swap without a restart or manual reset
|
||||
|
||||
#### Scenario: Pending swap is resolved above reference
|
||||
- **WHEN** a pending swap resolves read-only at or above reference
|
||||
- **THEN** it is cleared normally, any otherwise eligible new purchase is price-checked and skipped, and existing EVE burn evaluation continues
|
||||
|
||||
#### Scenario: Unresolved swap and pending burn retain priority
|
||||
- **WHEN** a swap remains unresolved or the iteration starts with a pending burn
|
||||
- **THEN** existing transaction-priority rules apply without evaluating a new-purchase price condition or submitting a new swap
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
# Spec Delta
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Selected processes are constructed and started lazily in exact order
|
||||
The Hub SHALL support the modern process IDs `nenjim.registry.service`, `nenjim.journal.service`, `assetaz.ticker.default`, `jupiter-perps-alarm.default`, `evelyn.status.prod`, `evelyn.status.test`, `evelyn.iou-burner.prod`, `evelyn.liquidity.prod`, `nenjim.javafx-runtime.service`, `evelyn.mission-control.default`, and `nenjim.process-manager.default` through implementation-only construction capability that creates no process instance during validation. After passive composition, the Hub SHALL visit every configured entry after Registry in declared order, beginning with Journal. At each entry it SHALL construct only that process and any explicitly composed process-specific passive dependencies or administration view, register the process under the exact configured ID, resolve it through the canonical `NenjimProcess` contract where applicable, call `start()`, and record the ID only after successful return.
|
||||
|
||||
The production IOU burner SHALL receive the already constructed Production Evelyn status service, and SHALL require both Ticker and `evelyn.status.prod` to precede `evelyn.iou-burner.prod`. The configuration SHALL be the complete manually ordered process dependency closure. The Hub SHALL not discover, insert, recursively construct, reorder, or automatically start dependencies. A process omitted from `[AUTO_START]` SHALL not be constructed, registered, started, or recorded, even when its implementation is available. Legacy process prototypes outside the modern Hub graph SHALL not be selectable.
|
||||
|
||||
#### Scenario: Configured process starts successfully
|
||||
- **WHEN** a configured process entry is reached and its dependencies are already available
|
||||
- **THEN** that process is constructed, registered, started, and recorded before the next entry is reached
|
||||
|
||||
#### Scenario: Process is omitted
|
||||
- **WHEN** a known process ID does not appear in `[AUTO_START]`
|
||||
- **THEN** no instance of that process is constructed or registered and its lifecycle is never started or recorded
|
||||
|
||||
#### Scenario: Jupiter Perps Alarm is selected
|
||||
- **WHEN** `jupiter-perps-alarm.default` is explicitly listed after its required predecessors
|
||||
- **THEN** the otherwise inactive modern alarm process is constructed, registered, started and recorded at that exact position
|
||||
|
||||
#### Scenario: Dependency order is invalid
|
||||
- **WHEN** a selected process is reached before a process dependency required by its current constructor or startup behavior
|
||||
- **THEN** construction or startup fails clearly without implicit dependency construction, repair, or reordering
|
||||
|
||||
#### Scenario: JavaFX processes are omitted
|
||||
- **WHEN** a valid headless plan omits `nenjim.javafx-runtime.service`, `evelyn.mission-control.default`, and `nenjim.process-manager.default`
|
||||
- **THEN** none of those objects is constructed or registered and the JavaFX toolkit remains uninitialized by the Hub
|
||||
|
||||
#### Scenario: Renamed status services retain dependency wiring
|
||||
- **WHEN** `evelyn.status.prod` and `evelyn.status.test` are listed after Ticker and before Mission Control
|
||||
- **THEN** the Hub constructs separate Production/Test status services, registers the renamed IDs, and injects the corresponding typed service views into Mission Control in the configured order
|
||||
|
||||
#### Scenario: Deployment still contains an old status ID
|
||||
- **WHEN** a private deployment configuration contains `evelyn.service.prod` or `evelyn.service.test`
|
||||
- **THEN** validation rejects the unknown ID before startup; deployment migration replaces only that ID with `evelyn.status.prod` or `evelyn.status.test`, preserving selection and order
|
||||
|
||||
#### Scenario: Liquidity is explicitly selected
|
||||
- **WHEN** `evelyn.liquidity.prod` is reached in a valid startup plan
|
||||
- **THEN** the Hub loads its runtime configuration, binds the dedicated signer/wallet and Jupiter service to each other, constructs direct Raydium access and registers/starts the liquidity process without inserting a Ticker or status dependency or reordering the plan
|
||||
|
||||
#### Scenario: Production burner has its reference-price dependency
|
||||
- **WHEN** `evelyn.iou-burner.prod` is reached after Ticker and `evelyn.status.prod`
|
||||
- **THEN** the Hub injects the corresponding Production status service for buyback price decisions
|
||||
|
||||
#### Scenario: Production burner precedes its status dependency
|
||||
- **WHEN** the burner is reached without a previously constructed Production status service
|
||||
- **THEN** construction fails clearly without creating or starting the omitted dependency or the burner
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
# Tasks
|
||||
|
||||
## 1. Purchase policy and independent burns
|
||||
|
||||
- [x] 1.1 Require the corresponding Evelyn status service and gate a fully eligible new purchase on fresh canonical actual price strictly below its current reference; verify constructor validation, below/equal/above and unavailable/invalid prices with controlled offline checks.
|
||||
- [x] 1.2 Update constructor/domain JavaDoc and Evelyn operating documentation; verify price skips still burn eligible existing EVE, re-evaluate next iteration and preserve pending burn/swap priority in the same offline checks.
|
||||
|
||||
## 2. Hub composition
|
||||
|
||||
- [x] 2.1 Inject the Production status dependency through the existing Hub guard and document the explicit Ticker/status/burner order; verify production arguments and fail-fast dependency behavior without starting the Hub or editing deployment configuration.
|
||||
|
||||
## 3. Integrated verification
|
||||
|
||||
- [x] 3.1 Compile main and existing test sources through normal Gradle/Detag, execute focused offline purchase/burn/recovery checks and relevant prior burner harnesses, and record their final results without adding permanent tests or running live transactions.
|
||||
- [x] 3.2 Validate the scoped OpenSpec change strictly and check whitespace and changed-file scope; deliver completed planning artifacts and verification evidence for review.
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
# Verification
|
||||
|
||||
Verified on 2026-10-06 with `/usr/local/software/java/jfx-25`.
|
||||
|
||||
## Compilation and controlled acceptance checks
|
||||
|
||||
- `JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew compileJava compileTestJava --offline --no-daemon` → **BUILD SUCCESSFUL in 8s**. Normal Gradle-integrated Detag and both source compilations executed. The initial sandbox attempt could not create the existing Gradle cache lock outside the workspace; the same compilation passed with approved cache access.
|
||||
- Temporary sources in `/tmp/evelyn-issue94` compiled with `javac --enable-preview --release 25 -cp 'build/classes/java/main:build/libs/*' -d /tmp/evelyn-issue94`.
|
||||
- `java --enable-preview -cp '/tmp/evelyn-issue94:build/classes/java/main:build/libs/*' Issue94Verification` → **PASS: 459 focused issue #94 assertions**.
|
||||
- The copied `Issue93Verification` → **PASS: 246 burn-policy regression assertions**.
|
||||
- The copied `Issue92Verification` → **PASS: 115 input-minimum regression assertions**.
|
||||
- The copied `Issue91Verification`, restricted to burner checks → **PASS: 80 pending-swap/recovery regression assertions**. Unchanged Solana/Jupiter library checks were not rerun.
|
||||
- Production composition source check verifies the corresponding Production status dependency and preserved input reserves/minima/maxima, EVE burn minimum/reserve, hourly interval, slippage, freshness and finalization arguments.
|
||||
- An INFO/WARN-enabled controlled run verifies purchase-skip reasons for at/above reference, unavailable/invalid reference and unusable market price, without arbitrary reference/Ticker exception messages.
|
||||
|
||||
The **900** controlled assertions invoke the actual burner iteration with offline wallet, chain, swap, Ticker, reference and notification collaborators. No RPC, signing, live financial operation, application/Hub startup or deployment configuration read occurs. The Hub dependency guard is invoked directly without constructing a Hub. No permanent automated tests were added. Prior harness copies only add an always-below reference stub to constructor calls and update #92's expected canonical EVE/USDT lookup count for eligible purchases; original temporary files remain intact.
|
||||
|
||||
The #94 harness covers 14.99/15.00/15.01 against reference 15, numeric equality across scales, a close below-reference decimal, the current reference decision instant instead of observation/history time, complete independent burns and reserve preservation, missing/unavailable/non-positive/null-amount/wrong-pair market and reference data, stale market observations, fresh independent burn valuation after a purchase-price failure, stale burn valuation postponement, next-iteration recovery from every rejection category, input priority and amount limits, below-minimum input fallback, no-eligible-input/direct-EVE-only paths, manual swap suspension and null status validation. It verifies original-signature read-only reconciliation while price is above reference, unresolved retention, definitive success/failure/proven expiry followed by a newly rejected purchase, later below-reference recovery, and simultaneous pending burn/swap priority with no reference lookup or new transaction. PriceObservation itself rejects null observation timestamp/price before they can reach the burner.
|
||||
|
||||
An initial temporary harness compilation used the wrong fully qualified component-ID type; it was corrected to the repository's existing `com.r35157.nenjim.service.registry.valuetypes.NenjimComponentId`. Final compiled checks pass. Concurrent initial Java invocations reported harmless performance-data lock warnings; the controlled diagnostic run disabled performance data. No production fix was needed for either verification-only issue.
|
||||
|
||||
## Review and workflow
|
||||
|
||||
Parent independent review of the proposal/specs/design, implementation, operating documentation and controlled harness found no actionable findings. The existing malformed burn JavaDoc line was corrected while updating that same list item. Scope is limited to the required reference dependency, new-purchase condition, Hub composition, related documentation and two OpenSpec capability deltas. Transaction execution, reconciliation, burn valuation/policy, ValueTags, scheduling and private deployment files retain their existing behavior.
|
||||
|
||||
Strict validation of the active change and `git diff --check` pass. All four planning artifact types and all five implementation tasks are complete. The active change is ready for review; main specifications have not been synchronized and the change has not been archived.
|
||||
|
||||
No deployment, commit, push, Gitea write or live integration was performed.
|
||||
|
||||
## Follow-up: clearer purchase-skip logs
|
||||
|
||||
The owner's requested logging refinement keeps price eligibility unchanged. INFO price skips now show a direct labelled comparison, for example `Skipping buyback as EVE price is at or above reference (market=15.01 >= reference=15 USDT/EVE)`, followed by the trading pair, decision instant and next action. Prices use plain decimals without redundant trailing zeros. WARN skips retain their reason; unavailable or invalid reference-price diagnostics also include the known market price.
|
||||
|
||||
`JAVA_HOME=/usr/local/software/java/jfx-25 ./gradlew compileJava --offline --no-daemon` passed. The existing temporary #94 harness passed its 459 assertions again with INFO/WARN enabled. Its captured output in `/tmp/evelyn-issue94/issue94-logging-refinement.log` confirms the numeric comparison for both equality and higher prices, reference failure details, missing/stale market diagnostics and exclusion of arbitrary exception messages. No new tests were added.
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Purpose
|
||||
|
||||
Defines the Evelyn-specific service that safely converts prioritized wallet assets into Evelyn IOU, burns the complete visible EVE balance, and announces only finalized burns without automatically repeating transactions whose outcomes may be unknown.
|
||||
Defines the Evelyn-specific service that safely converts prioritized wallet assets into Evelyn IOU, accumulates EVE for burns above a configured minimum while retaining an EVE reserve, and announces only finalized burns without automatically repeating transactions whose outcomes may be unknown.
|
||||
## Requirements
|
||||
### Requirement: Evelyn-specific public service boundary
|
||||
The system SHALL expose an empty lifecycle-free `EvelynIOUBurnerService` under `com.fanitas.evelyn.service.burner` as the typed domain service and Registry view until external domain operations exist, and SHALL keep its reference implementation under `com.fanitas.evelyn.service.burner.impl.ref`. The same reference implementation object SHALL implement both `EvelynIOUBurnerService` and `NenjimProcess`; the Hub SHALL start it only through its `NenjimProcess` view. Neither the service interface nor the implementation SHALL expose a general stop, restart, worker-control, or other lifecycle operation. The service SHALL remain specific to Evelyn IOU and SHALL NOT introduce a generic token-burner abstraction, burner-specific input interfaces, records, or class hierarchies.
|
||||
@@ -144,7 +144,7 @@ For each entry the service SHALL subtract its configured reserve and skip non-po
|
||||
- **THEN** the service preserves interruption and ends processing without treating the input as an ordinary failure
|
||||
|
||||
### Requirement: At most one confirmed swap attempt per iteration
|
||||
After selecting a positive exact amount, the service SHALL invoke the existing Jupiter exact-input operation once with the selected CIS-resolved input mint, fixed EVE output mint, selected amount, and configured slippage. Normal return SHALL be treated as already `CONFIRMED` and SHALL NOT trigger a second confirmation wait. Once invoked, no later input currency SHALL be attempted in that iteration regardless of success or failure, and the service SHALL proceed to reload the complete EVE balance whenever processing was not interrupted.
|
||||
After selecting a positive exact amount and satisfying the current below-reference price condition, the service SHALL invoke the existing Jupiter exact-input operation once with the selected CIS-resolved input mint, fixed EVE output mint, selected amount, and configured slippage. Normal return SHALL be treated as already `CONFIRMED` and SHALL NOT trigger a second confirmation wait. Once invoked, no later input currency SHALL be attempted in that iteration regardless of success or failure, and the service SHALL proceed to reload the complete EVE balance whenever processing was not interrupted.
|
||||
|
||||
A structured definitive on-chain swap failure SHALL be logged without suspending later-process swaps. A timed-out outcome or known-signature confirmation communication error SHALL retain process-local pending swap signature, canonical input, attempted amount and original validity metadata for later read-only reconciliation. Proven pre-submission errors SHALL permit a fresh attempt in a later scheduled iteration. An ambiguous post-submission error without a trustworthy signature SHALL retain manual suspension. Interruption SHALL preserve available pending context and propagate normally. No unknown outcome SHALL cause automatic rebuilding, re-signing or resubmission; pending swaps SHALL still permit existing EVE burn evaluation.
|
||||
|
||||
@@ -161,40 +161,56 @@ A structured definitive on-chain swap failure SHALL be logged without suspending
|
||||
- **THEN** the burner performs no additional confirmation wait and reloads the wallet instead of deriving its burn amount only from the returned output amount
|
||||
|
||||
### Requirement: Complete visible EVE balance is eligible
|
||||
After the optional swap attempt, the service SHALL detect the fixed EVE mint's supported token program internally and reload the wallet's complete available EVE balance. A missing EVE mint account, unsupported program, absent wallet token account, zero balance, or negative/invalid balance SHALL cause no burn or notification in that iteration. Every positive complete balance SHALL be eligible without a minimum threshold, including EVE deposited before the iteration or visible after a failed or absent swap.
|
||||
After the optional swap attempt, the service SHALL detect the fixed EVE mint's supported token program internally and reload the wallet's complete available EVE balance. A missing EVE mint account, unsupported program, absent wallet token account, zero balance, or negative/invalid balance SHALL cause no burn or notification in that iteration. The service SHALL subtract its configured EVE reserve, clamp negative available amounts to zero and round down to validated EVE mint precision using the detected token program. It SHALL submit a new burn only when the actual rounded amount is positive and at least the configured minimum burn amount; equality qualifies. It SHALL burn the entire eligible amount above reserve, without a maximum or fixed batch size, including EVE deposited before the iteration or visible after a failed or absent swap. Below threshold or with unusable mint precision it SHALL retain the EVE and send no burn notification. This threshold SHALL NOT suppress independent buybacks.
|
||||
|
||||
#### Scenario: Existing EVE without buyback
|
||||
- **WHEN** no swap is performed but the wallet contains a positive EVE balance
|
||||
- **THEN** the service evaluates that complete balance for burning
|
||||
- **THEN** the service evaluates that complete balance under the EVE reserve, precision and burn-minimum rules
|
||||
|
||||
#### Scenario: EVE account is absent or empty
|
||||
- **WHEN** the wallet has no EVE token account or its complete balance is zero
|
||||
- **THEN** the iteration submits no burn and sends no notification
|
||||
|
||||
#### Scenario: Issue example threshold and reserve
|
||||
- **WHEN** minimum is 1 EVE and reserve is 0.1 EVE
|
||||
- **THEN** balances 0, 0.05, 0.1, 1.05 and 1.099999 produce no burn; balance 1.1 burns 1 and balance 1.35 burns 1.25, retaining 0.1 after success
|
||||
|
||||
#### Scenario: Accumulation across small buybacks
|
||||
- **WHEN** individually eligible small buybacks accumulate EVE over multiple iterations
|
||||
- **THEN** each below-threshold iteration still permits its buyback and sends no burn message; the first qualifying iteration burns all eligible EVE and subsequent accumulation starts with the retained reserve
|
||||
|
||||
#### Scenario: Exact configuration exceeds mint precision
|
||||
- **WHEN** six-decimal mint precision rounds available 1.0000009 to 1.000000 while minimum is 1.0000001
|
||||
- **THEN** no burn occurs; no amount is rounded upward and the reserve is never consumed to reach the minimum
|
||||
|
||||
### Requirement: Fresh EVE value is required before submission
|
||||
Immediately before a new burn, the service SHALL obtain the latest canonical EVE/USDT observation and require a positive price, non-null observation timestamp, and age no greater than `maximumPriceAge` milliseconds. USDT SHALL be treated as USD for notification value, calculated as the complete EVE amount multiplied by that fresh price. If the pair is unsupported or the observation is missing, invalid, or stale, the service SHALL warn, postpone the burn, send no notification, and retry only by re-evaluating state in a later scheduled iteration.
|
||||
Immediately before a new burn, the service SHALL obtain the latest canonical EVE/USDT observation and require a positive price, non-null observation timestamp, and age no greater than `maximumPriceAge` milliseconds. USDT SHALL be treated as USD for notification value, calculated as the actual rounded eligible EVE amount above reserve multiplied by that fresh price. If the pair is unsupported or the observation is missing, invalid, or stale, the service SHALL warn, postpone the burn, send no notification, and retry only by re-evaluating state in a later scheduled iteration.
|
||||
|
||||
#### Scenario: Fresh EVE price exists
|
||||
- **WHEN** a complete positive EVE balance and sufficiently fresh positive EVE/USDT observation are available
|
||||
- **THEN** the service calculates the notification's USD value from that complete balance before submitting the burn
|
||||
- **WHEN** a positive rounded EVE amount at or above the burn minimum and sufficiently fresh positive EVE/USDT observation are available
|
||||
- **THEN** the service calculates the notification's USD value from that actual eligible burn amount, excluding reserve before submitting the burn
|
||||
|
||||
#### Scenario: EVE price is stale
|
||||
- **WHEN** EVE is present but its observation is older than the configured age limit
|
||||
- **THEN** the service leaves EVE unburned and sends no notification in that iteration
|
||||
|
||||
### Requirement: Single complete-balance burn submission and pending state
|
||||
For a positive EVE balance with a usable price, the service SHALL call the wallet burn operation exactly once with the fixed EVE mint and complete observed balance. It SHALL NOT automatically retry, rebuild, re-sign, or resubmit within or across iterations. Immediately after a non-blank signature is returned, it SHALL retain process-local pending data containing that signature, submitted EVE amount, and its calculated notification value or complete notification text before awaiting the signature.
|
||||
For a positive rounded EVE amount at or above the minimum with a usable price, the service SHALL call the wallet burn operation exactly once with the fixed EVE mint and entire eligible rounded amount above reserve. It SHALL NOT automatically retry, rebuild, re-sign, or resubmit within or across iterations. Immediately after a non-blank signature is returned, it SHALL retain process-local pending data containing that signature, actual submitted EVE amount excluding reserve, and its calculated notification value or complete notification text before awaiting the signature.
|
||||
|
||||
If burn submission fails without a usable signature and its outcome may be unknown, including an I/O or interruption path whose contract permits post-submission uncertainty, the service SHALL conservatively suspend further burn submissions for the current process. It MAY continue ordinary iteration work that does not submit another burn. Recovery SHALL require external reconciliation followed by a complete process restart, not a Burner service stop/start operation.
|
||||
|
||||
#### Scenario: Burn returns a signature
|
||||
- **WHEN** the wallet submits the complete observed EVE balance and returns a non-blank signature
|
||||
- **WHEN** the wallet submits the entire eligible rounded amount above reserve and returns a non-blank signature
|
||||
- **THEN** pending state is retained before the service begins finalization waiting
|
||||
|
||||
#### Scenario: Submission outcome may be unknown without a signature
|
||||
- **WHEN** burn submission fails or is interrupted without providing a usable signature and may already have reached Solana
|
||||
- **THEN** the service performs no further burn submission until external reconciliation and a complete process restart
|
||||
|
||||
#### Scenario: Pending burn bypasses new threshold evaluation
|
||||
- **WHEN** a retained burn signature remains pending while balance is now below the minimum or at the reserve
|
||||
- **THEN** only the original signature is awaited, its original submitted amount/value are preserved and it is never re-evaluated, rebuilt or resubmitted
|
||||
|
||||
### Requirement: Finalized burn outcome governs completion
|
||||
Every known burn signature SHALL be awaited through the Solana blockchain at `FINALIZED` with exactly the configured finalization timeout. `SUCCEEDED` SHALL authorize notification; `FAILED` SHALL log the slot and complete on-chain failure details, clear pending state, and send no notification; `TIMED_OUT` and `IOException` SHALL keep the pending signature because its result remains unknown; and `InterruptedException` SHALL preserve interruption and pending state. None of these outcomes SHALL cause automatic transaction resubmission.
|
||||
|
||||
@@ -231,7 +247,7 @@ The EVE amount SHALL use plain decimal notation without unnecessary trailing zer
|
||||
- **THEN** the service logs a non-secret failure, clears completed state, and never retries either notification or burn
|
||||
|
||||
### Requirement: Exact token-capped amount and per-currency reserve
|
||||
The service SHALL select the lesser of positive `balance - minimumReserve` and the input UUID's maximum token amount, then round downward to mint precision. It SHALL skip non-positive results and actual rounded amounts below that UUID's minimum, continuing to later inputs in the same iteration without counting a swap attempt. Equality SHALL qualify. It SHALL never round upward, top up from reserves or exceed either existing bound. Input selection SHALL require no input/USDC Ticker observation. Minima and maxima SHALL constrain buyback inputs only; EVE burns SHALL remain uncapped and unaffected. Native SOL reserves SHALL use observed balance without estimating future fees.
|
||||
The service SHALL select the lesser of positive `balance - minimumReserve` and the input UUID's maximum token amount, then round downward to mint precision. It SHALL skip non-positive results and actual rounded amounts below that UUID's minimum, continuing to later inputs in the same iteration without counting a swap attempt. Equality SHALL qualify. It SHALL never round upward, top up from reserves or exceed either existing bound. Input selection SHALL require no input/USDC Ticker observation. Minima and maxima SHALL constrain buyback inputs only; EVE burns SHALL remain uncapped and governed independently by their own EVE minimum and reserve. Native SOL reserves SHALL use observed balance without estimating future fees.
|
||||
|
||||
#### Scenario: SyrupUSDC maximum is lower than spendable balance
|
||||
- **WHEN** SyrupUSDC balance is 0.5, its reserve is 0.095, its minimum is 0.045 and its maximum is 0.2
|
||||
@@ -275,14 +291,18 @@ The service SHALL select the lesser of positive `balance - minimumReserve` and t
|
||||
|
||||
#### Scenario: Input minima do not suppress direct EVE burn
|
||||
- **WHEN** every configured input is below its minimum but the wallet contains EVE with a usable EVE/USDT price
|
||||
- **THEN** the complete EVE balance is still evaluated and burned by the existing rules
|
||||
- **THEN** the complete EVE balance is still evaluated and its rounded eligible amount above the EVE reserve is burned if it meets the EVE burn minimum
|
||||
|
||||
### Requirement: Production burner accepts USDT and SyrupUSDC
|
||||
The production Hub SHALL configure USDT first with reserve 0.1, minimum input 0.05 and maximum input 0.25 USDT, and SyrupUSDC second with reserve 0.095, minimum input 0.045 and maximum input 0.2 SyrupUSDC. These SHALL be token-denominated limits without a USD-value guarantee. The existing burner wallet, Jupiter route selection, registration, hourly interval, slippage, EVE/USDT freshness, confirmation settings and burn recovery behavior SHALL be preserved without new Ticker sources or history storage.
|
||||
The production Hub SHALL configure USDT first with reserve 0.1, minimum input 0.05 and maximum input 0.25 USDT, and SyrupUSDC second with reserve 0.095, minimum input 0.045 and maximum input 0.2 SyrupUSDC. These SHALL be token-denominated limits without a USD-value guarantee. The production EVE burn minimum SHALL be 0.1 EVE and its retained reserve SHALL be 0.01 EVE, as selected by the owner on 2026-10-06, configured as MoneyAmount values using the canonical EVE UUID. The production Hub SHALL supply its corresponding Production Evelyn status service for current reference-price decisions. The existing burner wallet, Jupiter route selection, registration, hourly interval, slippage, EVE/USDT freshness, confirmation settings and burn recovery behavior SHALL be preserved without new Ticker sources or history storage.
|
||||
|
||||
#### Scenario: Production inputs and settings
|
||||
- **WHEN** the Hub constructs the production burner
|
||||
- **THEN** it supplies the exact reserves, minima and maxima as MoneyAmount values in USDT-then-SyrupUSDC priority and retains the other existing production arguments
|
||||
- **THEN** it supplies the exact reserves, minima and maxima as MoneyAmount values in USDT-then-SyrupUSDC priority, supplies minimum burn 0.1 EVE/reserve 0.01 EVE and the Production status service, and retains the other existing production arguments
|
||||
|
||||
#### Scenario: Production burn threshold
|
||||
- **WHEN** production EVE balance is below 0.11, exactly 0.11, or 0.135 with usable mint precision and fresh price
|
||||
- **THEN** below 0.11 produces no burn, 0.11 burns 0.1 and 0.135 burns 0.125; successful burns retain 0.01 EVE
|
||||
|
||||
### Requirement: Pending swaps reconcile before new input selection
|
||||
After pending-burn priority, a retained swap SHALL be checked read-only by its original signature at CONFIRMED or stronger in a bounded operation. Success, on-chain failure or demonstrated non-executed expiry SHALL clear it and permit newly read balances to drive at most one new swap. Unknown, absent without expiry proof or I/O outcomes SHALL remain pending without a new swap while allowing existing EVE burns. Interruption SHALL propagate with pending state intact.
|
||||
@@ -305,3 +325,52 @@ Swap diagnostics SHALL identify canonical input symbol/UUID, attempted amount, k
|
||||
#### Scenario: Unknown outcome is diagnosed
|
||||
- **WHEN** a swap outcome remains unknown
|
||||
- **THEN** diagnostics identify whether its original signature will be checked next iteration or manual investigation is required, without exposing secret or payload data
|
||||
|
||||
### Requirement: Independent EVE burn policy configuration
|
||||
The constructor SHALL accept minimumBurnAmount and eveReserve as EVE-denominated MoneyAmount values identified by canonical EVE UUID regardless of supplied display metadata. Both values, their amounts, currencies and UUIDs SHALL be non-null; minimum SHALL be strictly positive and reserve non-negative. Zero reserve SHALL be valid. Invalid burn configuration SHALL fail before worker startup without changing existing input validation.
|
||||
|
||||
#### Scenario: Invalid burn configuration
|
||||
- **WHEN** either value, amount, currency or UUID is null, a UUID is not EVE, the minimum is zero/negative, or the reserve is negative
|
||||
- **THEN** construction fails before creating a worker or transaction
|
||||
|
||||
#### Scenario: Stale EVE metadata and zero reserve
|
||||
- **WHEN** both values carry the EVE UUID with stale names/symbols and the reserve is zero
|
||||
- **THEN** construction succeeds and uses canonical EVE identity with the configured exact numeric amounts
|
||||
|
||||
### Requirement: New buybacks require a below-reference market price
|
||||
An otherwise eligible new buyback SHALL require a fresh canonical EVE/USDT observation within the existing maximum price age and a usable canonical EVE/USDT reference price from the injected corresponding Evelyn status service at the current decision instant. Both prices SHALL be positive. Only actual price strictly below reference SHALL permit submission; equality SHALL not qualify. The existing public reference operation SHALL be reused without copying curve calculations or reading stored history.
|
||||
|
||||
#### Scenario: Strict boundary below reference
|
||||
- **WHEN** an input is otherwise eligible and reference price is 15 USDT/EVE
|
||||
- **THEN** market price 14.99 permits the existing buyback, while 15.00 and 15.01 submit no new swap from any input in that iteration
|
||||
|
||||
#### Scenario: Required prices are unusable
|
||||
- **WHEN** market observation is missing, stale, incomplete, unavailable or has an invalid pair/non-positive price, or reference lookup throws, is missing or has an invalid pair/non-positive price
|
||||
- **THEN** no new swap is submitted and a safe diagnostic explains why the purchase was skipped
|
||||
|
||||
#### Scenario: Reference uses the current decision instant
|
||||
- **WHEN** the first usable input is ready for a new swap
|
||||
- **THEN** the corresponding status service is asked for its reference price at the current instant rather than using a sampled history point or the observation timestamp
|
||||
|
||||
### Requirement: Price skips preserve independent burn processing and recovery
|
||||
A rejected buyback price condition SHALL skip purchases only, continue complete EVE-balance processing under the existing independent burn requirements, and SHALL NOT create a suspension. Each subsequent otherwise eligible iteration SHALL re-evaluate prices. Pending-burn priority and read-only pending-swap reconciliation SHALL remain independent of the new-purchase condition; any newly permitted purchase after reconciliation SHALL pass the current price check.
|
||||
|
||||
#### Scenario: Existing EVE burns at or above reference
|
||||
- **WHEN** market price is equal to or above reference and existing EVE satisfies its own reserve, minimum, precision, fresh valuation and transaction requirements
|
||||
- **THEN** no new swap occurs and the existing eligible EVE is burned normally
|
||||
|
||||
#### Scenario: Reference unavailable but burn valuation is usable
|
||||
- **WHEN** reference lookup is unavailable or invalid but existing EVE and its fresh canonical valuation satisfy the burn requirements
|
||||
- **THEN** the purchase is skipped and the independent eligible burn still occurs
|
||||
|
||||
#### Scenario: Next scheduled iteration recovers
|
||||
- **WHEN** one iteration skips a purchase because its prices do not qualify and a later otherwise eligible iteration has usable prices below reference
|
||||
- **THEN** the later iteration can attempt its one new swap without a restart or manual reset
|
||||
|
||||
#### Scenario: Pending swap is resolved above reference
|
||||
- **WHEN** a pending swap resolves read-only at or above reference
|
||||
- **THEN** it is cleared normally, any otherwise eligible new purchase is price-checked and skipped, and existing EVE burn evaluation continues
|
||||
|
||||
#### Scenario: Unresolved swap and pending burn retain priority
|
||||
- **WHEN** a swap remains unresolved or the iteration starts with a pending burn
|
||||
- **THEN** existing transaction-priority rules apply without evaluating a new-purchase price condition or submitting a new swap
|
||||
|
||||
@@ -91,7 +91,7 @@ After validating the entire plan, the Hub SHALL construct the configured Registr
|
||||
### Requirement: Selected processes are constructed and started lazily in exact order
|
||||
The Hub SHALL support the modern process IDs `nenjim.registry.service`, `nenjim.journal.service`, `assetaz.ticker.default`, `jupiter-perps-alarm.default`, `evelyn.status.prod`, `evelyn.status.test`, `evelyn.iou-burner.prod`, `evelyn.liquidity.prod`, `nenjim.javafx-runtime.service`, `evelyn.mission-control.default`, and `nenjim.process-manager.default` through implementation-only construction capability that creates no process instance during validation. After passive composition, the Hub SHALL visit every configured entry after Registry in declared order, beginning with Journal. At each entry it SHALL construct only that process and any explicitly composed process-specific passive dependencies or administration view, register the process under the exact configured ID, resolve it through the canonical `NenjimProcess` contract where applicable, call `start()`, and record the ID only after successful return.
|
||||
|
||||
The configuration SHALL be the complete manually ordered process dependency closure. The Hub SHALL not discover, insert, recursively construct, reorder, or automatically start dependencies. A process omitted from `[AUTO_START]` SHALL not be constructed, registered, started, or recorded, even when its implementation is available. Legacy process prototypes outside the modern Hub graph SHALL not be selectable.
|
||||
The production IOU burner SHALL receive the already constructed Production Evelyn status service, and SHALL require both Ticker and `evelyn.status.prod` to precede `evelyn.iou-burner.prod`. The configuration SHALL be the complete manually ordered process dependency closure. The Hub SHALL not discover, insert, recursively construct, reorder, or automatically start dependencies. A process omitted from `[AUTO_START]` SHALL not be constructed, registered, started, or recorded, even when its implementation is available. Legacy process prototypes outside the modern Hub graph SHALL not be selectable.
|
||||
|
||||
#### Scenario: Configured process starts successfully
|
||||
- **WHEN** a configured process entry is reached and its dependencies are already available
|
||||
@@ -125,6 +125,14 @@ The configuration SHALL be the complete manually ordered process dependency clos
|
||||
- **WHEN** `evelyn.liquidity.prod` is reached in a valid startup plan
|
||||
- **THEN** the Hub loads its runtime configuration, binds the dedicated signer/wallet and Jupiter service to each other, constructs direct Raydium access and registers/starts the liquidity process without inserting a Ticker or status dependency or reordering the plan
|
||||
|
||||
#### Scenario: Production burner has its reference-price dependency
|
||||
- **WHEN** `evelyn.iou-burner.prod` is reached after Ticker and `evelyn.status.prod`
|
||||
- **THEN** the Hub injects the corresponding Production status service for buyback price decisions
|
||||
|
||||
#### Scenario: Production burner precedes its status dependency
|
||||
- **WHEN** the burner is reached without a previously constructed Production status service
|
||||
- **THEN** construction fails clearly without creating or starting the omitted dependency or the burner
|
||||
|
||||
### Requirement: Configured startup is fail-fast and reports actual milestones
|
||||
Registry and every selected process SHALL be registered before its `start()` call and appended to the Hub's immutable ordered startup snapshot only after `start()` returns successfully. Construction failure SHALL leave the selected process unregistered. Registration or startup failure SHALL stop the plan before any later entry is constructed or started; a startup failure MAY leave that process registered but SHALL not record it. Earlier successful processes SHALL remain in snapshots, no rollback or retry SHALL occur, and the Hub SHALL announce its existing online point and enter its blocking runtime wait only after every configured process succeeds.
|
||||
|
||||
|
||||
@@ -23,6 +23,11 @@ import java.util.List;
|
||||
* internally through the Currency Identity Service and Solana and are not
|
||||
* part of Evelyn's input configuration.</p>
|
||||
*
|
||||
* <p>Two independent EVE-denominated {@link MoneyAmount} values configure the
|
||||
* minimum per burn and retained EVE reserve. Their canonical EVE UUID is
|
||||
* authoritative; the minimum must be strictly positive and the reserve
|
||||
* non-negative. A burn threshold does not suppress independent buybacks.</p>
|
||||
*
|
||||
* <p>Every non-overlapping iteration has this order:</p>
|
||||
* <ol>
|
||||
* <li>If a prior burn has a pending signature, await only that exact
|
||||
@@ -46,7 +51,15 @@ import java.util.List;
|
||||
* below that input's minimum and continue to later inputs in the same
|
||||
* iteration; equality qualifies. Never round upward or use the reserve
|
||||
* to reach a minimum. Input minima do not suppress EVE burns.</li>
|
||||
* <li>Attempt no more than one exact-input Jupiter swap. Normal return is
|
||||
* <li>Immediately before a new purchase, require a fresh canonical
|
||||
* EVE/USDT price strictly below the corresponding Evelyn status service's
|
||||
* reference price at the current decision instant. Equality, higher price,
|
||||
* unavailable reference or unusable price data skips all new purchases
|
||||
* for this iteration and continues to independent EVE burn processing.
|
||||
* A price skip does not suspend subsequent iterations. Pending transaction
|
||||
* reconciliation is independent of this new-purchase condition.</li>
|
||||
* <li>When the price condition permits, attempt no more than one exact-input
|
||||
* Jupiter swap. Normal return is
|
||||
* already {@code CONFIRMED}; it is not awaited a second time. A definitive
|
||||
* failure or proven pre-submission error permits a later iteration to try
|
||||
* again. A known-signature timeout or communication error retains pending
|
||||
@@ -55,12 +68,19 @@ import java.util.List;
|
||||
* unknown swap is rebuilt, re-signed or resubmitted automatically.</li>
|
||||
* <li>Reload the complete visible Evelyn IOU balance, including tokens
|
||||
* deposited or acquired before this iteration. Absence or failure of a
|
||||
* swap does not suppress this step, and the input buyback limit never caps
|
||||
* the amount eligible for burning.</li>
|
||||
* swap, including a price-condition skip, does not suppress this step,
|
||||
* and the input buyback limit never caps
|
||||
* the amount eligible for burning. Subtract the retained EVE reserve and
|
||||
* round the positive remainder down to EVE mint precision. If the actual
|
||||
* rounded amount is zero or below the burn minimum, keep the EVE for a
|
||||
* later iteration and send no burn notification. Equality qualifies;
|
||||
* never round upward or consume reserve to reach the minimum.</li>
|
||||
* <li>Require a positive, sufficiently fresh canonical EVE/USDT price and
|
||||
* calculate the complete balance's dollar value before submitting a burn.
|
||||
* calculate the actual eligible burn amount's dollar value, excluding
|
||||
* the retained reserve, before submitting a burn.
|
||||
* Missing, invalid, or stale price data postpones the burn.</li>
|
||||
* <li>Submit the complete EVE balance once and retain its signature,
|
||||
* <li>Submit all eligible rounded EVE above reserve once, without a burn
|
||||
* maximum or fixed batch size, and retain its signature,
|
||||
* amount, and notification value as pending before waiting. The service
|
||||
* never automatically retries, rebuilds, re-signs, or resubmits a swap or
|
||||
* burn because timeout, interruption, or communication failure can leave
|
||||
|
||||
+164
-8
@@ -7,6 +7,7 @@ import com.r35157.assetaz.services.ticker.PriceObservation;
|
||||
import com.r35157.assetaz.services.ticker.TickerService;
|
||||
import com.r35157.assetaz.valuetypes.CurrencyType;
|
||||
import com.r35157.cryptowallet.solana.SolanaWallet;
|
||||
import com.r35157.evelyn.service.status.EvelynStatusService;
|
||||
import com.r35157.libs.jupiter.JupiterTransactionOutcomeException;
|
||||
import com.r35157.libs.jupiter.swap.JupiterSwapService;
|
||||
import com.r35157.libs.jupiter.swap.JupiterSwapSubmissionException;
|
||||
@@ -85,13 +86,31 @@ public final class EvelynIOUBurnerServiceImpl
|
||||
* equality qualifies without rounding up or spending the reserve. Three
|
||||
* empty input lists permit direct-EVE-only burning.</p>
|
||||
*
|
||||
* <p>{@code minimumBurnAmount} and {@code eveReserve} must identify EVE by
|
||||
* its canonical UUID, regardless of supplied name/symbol. The minimum must
|
||||
* be strictly positive and the reserve non-negative. A new burn uses the
|
||||
* entire balance above reserve, rounded down to EVE mint precision, only
|
||||
* when that actual amount is positive and at least the minimum. Equality
|
||||
* qualifies; fractional remainders stay in the wallet. This policy does
|
||||
* not suppress independent small buybacks or re-evaluate pending burns.</p>
|
||||
*
|
||||
* <p>A fully eligible new buyback additionally requires a fresh canonical
|
||||
* EVE/USDT price strictly below the corresponding status service's
|
||||
* reference price at the current decision instant. Equality, unavailable
|
||||
* or invalid prices skip purchases only, without suspending later
|
||||
* iterations or suppressing independent EVE burn processing. Pending
|
||||
* burn priority and read-only swap reconciliation remain unchanged.</p>
|
||||
*
|
||||
* @param burnerWallet dedicated wallet whose balances are inspected and
|
||||
* whose complete EVE balance is burned
|
||||
* whose eligible EVE above reserve is burned
|
||||
* @param swapService exact-input Jupiter service; normal return already
|
||||
* means the swap reached {@code CONFIRMED}
|
||||
* @param solanaBlockChain Solana access used for mint ownership, token
|
||||
* precision, and burn finalization
|
||||
* @param tickerService latest-price source for EVE/USDT before burning
|
||||
* @param tickerService latest-price source for EVE/USDT before new
|
||||
* buybacks and independently before burning
|
||||
* @param evelynStatusService corresponding Evelyn instance's reference
|
||||
* price source for new buyback decisions
|
||||
* @param currencyIdentityService authoritative resolver for AssetAZ UUIDs
|
||||
* and Solana mint references
|
||||
* @param notificationService fully bound synchronous destination for the
|
||||
@@ -108,6 +127,10 @@ public final class EvelynIOUBurnerServiceImpl
|
||||
* @param inputCurrencyMaximumSwapAmounts exactly one strictly positive
|
||||
* token-denominated buyback maximum per accepted input UUID;
|
||||
* it does not limit burns
|
||||
* @param minimumBurnAmount strictly positive EVE-denominated minimum for
|
||||
* the actual rounded amount of each new burn
|
||||
* @param eveReserve non-negative EVE-denominated amount retained after a
|
||||
* successful burn; zero is valid
|
||||
* @param maximumSlippageBps Jupiter slippage limit in basis points from 0
|
||||
* through 10000 inclusive
|
||||
* @param maximumPriceAge strictly positive maximum observation age in
|
||||
@@ -126,18 +149,23 @@ public final class EvelynIOUBurnerServiceImpl
|
||||
* lacks exactly one usable Solana mint,
|
||||
* or minimum/maximum UUIDs do not match inputs,
|
||||
* a swap bound is non-positive, or a
|
||||
* minimum exceeds its maximum
|
||||
* minimum exceeds its maximum, or the
|
||||
* burn minimum/reserve does not identify
|
||||
* EVE or has an invalid numeric value
|
||||
*/
|
||||
public EvelynIOUBurnerServiceImpl(
|
||||
@NotNull SolanaWallet burnerWallet,
|
||||
@NotNull JupiterSwapService swapService,
|
||||
@NotNull SolanaBlockChain solanaBlockChain,
|
||||
@NotNull TickerService tickerService,
|
||||
@NotNull EvelynStatusService evelynStatusService,
|
||||
@NotNull CurrencyIdentityService currencyIdentityService,
|
||||
@NotNull BoundNotificationService notificationService,
|
||||
@NotNull List<MoneyAmount> inputCurrencyMinimumReserves,
|
||||
@NotNull List<MoneyAmount> inputCurrencyMinimumSwapAmounts,
|
||||
@NotNull List<MoneyAmount> inputCurrencyMaximumSwapAmounts,
|
||||
@NotNull MoneyAmount minimumBurnAmount,
|
||||
@NotNull MoneyAmount eveReserve,
|
||||
int maximumSlippageBps,
|
||||
ΩmilliSecondsΩ maximumPriceAge,
|
||||
@NotNull Duration burnFinalizationTimeout,
|
||||
@@ -159,6 +187,10 @@ public final class EvelynIOUBurnerServiceImpl
|
||||
tickerService,
|
||||
"tickerService"
|
||||
);
|
||||
this.evelynStatusService = Objects.requireNonNull(
|
||||
evelynStatusService,
|
||||
"evelynStatusService"
|
||||
);
|
||||
this.currencyIdentityService = Objects.requireNonNull(
|
||||
currencyIdentityService,
|
||||
"currencyIdentityService"
|
||||
@@ -211,6 +243,12 @@ public final class EvelynIOUBurnerServiceImpl
|
||||
USDT_ID,
|
||||
"USDT"
|
||||
);
|
||||
this.minimumBurnAmount = validateEveAmount(
|
||||
minimumBurnAmount, "minimumBurnAmount", canonicalEve, true
|
||||
);
|
||||
this.eveReserve = validateEveAmount(
|
||||
eveReserve, "eveReserve", canonicalEve, false
|
||||
);
|
||||
this.eveUsdtTradingPair = new TradingPair(
|
||||
canonicalEve,
|
||||
canonicalUsdt
|
||||
@@ -414,6 +452,9 @@ public final class EvelynIOUBurnerServiceImpl
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isBuybackBelowReferencePrice()) {
|
||||
return;
|
||||
}
|
||||
attemptSwap(
|
||||
canonicalCurrency,
|
||||
inputMint,
|
||||
@@ -423,6 +464,70 @@ public final class EvelynIOUBurnerServiceImpl
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isBuybackBelowReferencePrice() {
|
||||
ΩPriceΩ actualPrice = findFreshPrice(eveUsdtTradingPair);
|
||||
if (actualPrice == null) {
|
||||
log.warn(
|
||||
"Skipping buyback as no fresh valid EVE/USDT price "
|
||||
+ "is available; nextAction=burn "
|
||||
+ "evaluation and price recheck next iteration"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
Instant decisionInstant = Instant.now();
|
||||
AssetPrice referencePrice;
|
||||
try {
|
||||
referencePrice = evelynStatusService.getReferencePrice(
|
||||
decisionInstant
|
||||
);
|
||||
} catch (RuntimeException exception) {
|
||||
log.warn(
|
||||
"Skipping buyback as reference price is unavailable "
|
||||
+ "(market={} USDT/EVE); decisionInstant={}, "
|
||||
+ "failureType={}; "
|
||||
+ "nextAction=burn evaluation and price recheck "
|
||||
+ "next iteration",
|
||||
actualPrice.stripTrailingZeros().toPlainString(),
|
||||
decisionInstant,
|
||||
exception.getClass().getName()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (referencePrice == null
|
||||
|| !eveUsdtTradingPair.equals(referencePrice.tradingPair())
|
||||
|| referencePrice.price() == null
|
||||
|| referencePrice.price().signum() <= 0) {
|
||||
log.warn(
|
||||
"Skipping buyback as reference price is missing or "
|
||||
+ "invalid (market={} USDT/EVE); tradingPair={}, "
|
||||
+ "decisionInstant={}; nextAction=burn evaluation "
|
||||
+ "and price recheck next iteration",
|
||||
actualPrice.stripTrailingZeros().toPlainString(),
|
||||
eveUsdtTradingPair,
|
||||
decisionInstant
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actualPrice.compareTo(referencePrice.price()) >= 0) {
|
||||
log.info(
|
||||
"Skipping buyback as EVE price is at or above reference "
|
||||
+ "(market={} >= reference={} USDT/EVE); "
|
||||
+ "tradingPair={}, "
|
||||
+ "decisionInstant={}; nextAction=burn evaluation "
|
||||
+ "and price recheck next iteration",
|
||||
actualPrice.stripTrailingZeros().toPlainString(),
|
||||
referencePrice.price().stripTrailingZeros().toPlainString(),
|
||||
eveUsdtTradingPair,
|
||||
decisionInstant
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void attemptSwap(
|
||||
CurrencyType canonicalCurrency,
|
||||
ΩSPLMintAddressΩ inputMint,
|
||||
@@ -519,9 +624,9 @@ public final class EvelynIOUBurnerServiceImpl
|
||||
|
||||
private void processCompleteEveBalance() throws InterruptedException {
|
||||
ΩAmountΩ eveBalance;
|
||||
SolanaSPLTokenProgram eveTokenProgram;
|
||||
try {
|
||||
SolanaSPLTokenProgram eveTokenProgram =
|
||||
resolveSupportedTokenProgram(EVELYN_IOU_MINT);
|
||||
eveTokenProgram = resolveSupportedTokenProgram(EVELYN_IOU_MINT);
|
||||
eveBalance = burnerWallet.getSPLTokenBalance(
|
||||
EVELYN_IOU_MINT,
|
||||
eveTokenProgram
|
||||
@@ -554,17 +659,40 @@ public final class EvelynIOUBurnerServiceImpl
|
||||
return;
|
||||
}
|
||||
|
||||
ΩAmountΩ availableForBurn = eveBalance.subtract(eveReserve.amount());
|
||||
if (availableForBurn.signum() <= 0) {
|
||||
return;
|
||||
}
|
||||
ΩAmountΩ amountToBurn;
|
||||
try {
|
||||
ΩamountDecimalsΩ decimals = resolveMintDecimals(
|
||||
EVELYN_IOU_MINT, eveTokenProgram
|
||||
);
|
||||
amountToBurn = availableForBurn.setScale(decimals, RoundingMode.DOWN);
|
||||
} catch (IOException | RuntimeException exception) {
|
||||
log.warn(
|
||||
"Could not resolve Evelyn IOU mint precision; burn is "
|
||||
+ "postponed: failureType={}",
|
||||
exception.getClass().getName()
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (amountToBurn.signum() <= 0
|
||||
|| amountToBurn.compareTo(minimumBurnAmount.amount()) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ΩPriceΩ evePriceInUsdt = findFreshPrice(eveUsdtTradingPair);
|
||||
if (evePriceInUsdt == null) {
|
||||
return;
|
||||
}
|
||||
ΩAmountΩ dollarValue = eveBalance.multiply(evePriceInUsdt);
|
||||
ΩAmountΩ dollarValue = amountToBurn.multiply(evePriceInUsdt);
|
||||
|
||||
ΩSolanaTransactionSignatureΩ burnSignature;
|
||||
try {
|
||||
burnSignature = burnerWallet.burnSPLToken(
|
||||
EVELYN_IOU_MINT,
|
||||
eveBalance
|
||||
amountToBurn
|
||||
);
|
||||
} catch (IOException exception) {
|
||||
burnsSuspended = true;
|
||||
@@ -607,7 +735,7 @@ public final class EvelynIOUBurnerServiceImpl
|
||||
|
||||
pendingBurn = new PendingBurn(
|
||||
burnSignature,
|
||||
eveBalance,
|
||||
amountToBurn,
|
||||
dollarValue
|
||||
);
|
||||
awaitPendingBurn();
|
||||
@@ -826,6 +954,31 @@ public final class EvelynIOUBurnerServiceImpl
|
||||
return supply.decimals();
|
||||
}
|
||||
|
||||
private static MoneyAmount validateEveAmount(
|
||||
MoneyAmount configuredAmount,
|
||||
String configurationName,
|
||||
CurrencyType canonicalEve,
|
||||
boolean strictlyPositive
|
||||
) {
|
||||
Objects.requireNonNull(configuredAmount, configurationName);
|
||||
Objects.requireNonNull(configuredAmount.amount(), configurationName + ".amount");
|
||||
CurrencyType currency = Objects.requireNonNull(
|
||||
configuredAmount.currencyType(), configurationName + ".currencyType"
|
||||
);
|
||||
UUID currencyId = Objects.requireNonNull(
|
||||
currency.id(), configurationName + ".currencyType.id"
|
||||
);
|
||||
if (!EVE_ID.equals(currencyId)) {
|
||||
throw new IllegalArgumentException(configurationName + " must identify EVE by UUID");
|
||||
}
|
||||
if (configuredAmount.amount().signum() < 0
|
||||
|| (strictlyPositive && configuredAmount.amount().signum() == 0)) {
|
||||
throw new IllegalArgumentException(configurationName
|
||||
+ (strictlyPositive ? " must be greater than zero" : " must be zero or greater"));
|
||||
}
|
||||
return new MoneyAmount(configuredAmount.amount(), canonicalEve);
|
||||
}
|
||||
|
||||
private List<MoneyAmount> validateAndCopyInputs(
|
||||
List<MoneyAmount> configuredInputs
|
||||
) {
|
||||
@@ -1054,11 +1207,14 @@ public final class EvelynIOUBurnerServiceImpl
|
||||
private final JupiterSwapService swapService;
|
||||
private final SolanaBlockChain solanaBlockChain;
|
||||
private final TickerService tickerService;
|
||||
private final EvelynStatusService evelynStatusService;
|
||||
private final CurrencyIdentityService currencyIdentityService;
|
||||
private final BoundNotificationService notificationService;
|
||||
private final List<MoneyAmount> inputCurrencyMinimumReserves;
|
||||
private final Map<UUID, MoneyAmount> inputCurrencyMinimumSwapAmounts;
|
||||
private final Map<UUID, MoneyAmount> inputCurrencyMaximumSwapAmounts;
|
||||
private final MoneyAmount minimumBurnAmount;
|
||||
private final MoneyAmount eveReserve;
|
||||
private final int maximumSlippageBps;
|
||||
private final ΩmilliSecondsΩ maximumPriceAge;
|
||||
private final Duration maximumPriceAgeDuration;
|
||||
|
||||
@@ -324,11 +324,17 @@ public final class NenjimHubImpl implements NenjimHub {
|
||||
tickerService,
|
||||
EVELYN_IOU_BURNER_PROD_ID,
|
||||
TICKER_SERVICE_ID),
|
||||
requireProcessDependency(
|
||||
evelynStatusServiceProd,
|
||||
EVELYN_IOU_BURNER_PROD_ID,
|
||||
EVELYN_STATUS_PROD_ID),
|
||||
cis,
|
||||
discordAssetAZTokenNotificationService,
|
||||
inputCurrencyMinimumReserves,
|
||||
inputCurrencyMinimumSwapAmounts,
|
||||
inputCurrencyMaximumSwapAmounts,
|
||||
new MoneyAmount(new ΩAmountΩ("0.1"), cis.resolve(CurrencyTypeIds.EVE_ID)),
|
||||
new MoneyAmount(new ΩAmountΩ("0.01"), cis.resolve(CurrencyTypeIds.EVE_ID)),
|
||||
200,
|
||||
2*1000*60L,
|
||||
Duration.ofSeconds(10),
|
||||
|
||||
Reference in New Issue
Block a user