From e283f0b8f2b957010bb8a7459a9a4d6ee688706f Mon Sep 17 00:00:00 2001 From: Minimons Date: Tue, 6 Oct 2026 14:01:17 +0200 Subject: [PATCH] 94: Buy back Evelyn IOU tokens only below the Evelyn reference price --- docs/Evelyn.md | 37 ++++++-- docs/Nenjim-public.html | 4 +- docs/Nenjim.md | 5 ++ .../.openspec.yaml | 2 + .../design.md | 29 +++++++ .../proposal.md | 28 ++++++ .../specs/evelyn-iou-burner-service/spec.md | 71 +++++++++++++++ .../specs/nenjim-hub-configuration/spec.md | 48 +++++++++++ .../tasks.md | 15 ++++ .../verification.md | 40 +++++++++ .../.openspec.yaml | 2 + .../design.md | 29 +++++++ .../proposal.md | 28 ++++++ .../specs/evelyn-iou-burner-service/spec.md | 71 +++++++++++++++ .../specs/nenjim-hub-configuration/spec.md | 48 +++++++++++ .../tasks.md | 15 ++++ .../verification.md | 34 ++++++++ .../specs/evelyn-iou-burner-service/spec.md | 44 +++++++++- .../specs/nenjim-hub-configuration/spec.md | 10 ++- .../burner/EvelynIOUBurnerService.tjava | 16 +++- .../impl/ref/EvelynIOUBurnerServiceImpl.tjava | 86 ++++++++++++++++++- .../nenjim/hubd/impl/ref/NenjimHubImpl.tjava | 4 + 22 files changed, 649 insertions(+), 17 deletions(-) create mode 100644 openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/.openspec.yaml create mode 100644 openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/design.md create mode 100644 openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/proposal.md create mode 100644 openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/specs/evelyn-iou-burner-service/spec.md create mode 100644 openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/specs/nenjim-hub-configuration/spec.md create mode 100644 openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/tasks.md create mode 100644 openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/verification.md create mode 100644 openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/.openspec.yaml create mode 100644 openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/design.md create mode 100644 openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/proposal.md create mode 100644 openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/specs/evelyn-iou-burner-service/spec.md create mode 100644 openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/specs/nenjim-hub-configuration/spec.md create mode 100644 openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/tasks.md create mode 100644 openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/verification.md diff --git a/docs/Evelyn.md b/docs/Evelyn.md index 491afaa..11abb3a 100644 --- a/docs/Evelyn.md +++ b/docs/Evelyn.md @@ -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,7 +148,8 @@ 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. -Efter buyback genlæses hele den synlige EVE-saldo, også ved direkte +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; @@ -141,8 +159,9 @@ 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 -uafhængigt og samler EVE op mellem burns. Under tærsklen sker hverken burn eller +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 | @@ -165,7 +184,9 @@ 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. diff --git a/docs/Nenjim-public.html b/docs/Nenjim-public.html index c795fdf..3cd2f03 100644 --- a/docs/Nenjim-public.html +++ b/docs/Nenjim-public.html @@ -177,7 +177,9 @@ nenjim.journal.service start(), and recorded only after successful return. An omitted process is therefore neither constructed, registered, started, nor displayed by Process Manager.

[AUTO_START] 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.

diff --git a/docs/Nenjim.md b/docs/Nenjim.md index c41b6b3..fcd8558 100644 --- a/docs/Nenjim.md +++ b/docs/Nenjim.md @@ -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//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 diff --git a/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/.openspec.yaml b/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/.openspec.yaml new file mode 100644 index 0000000..aa32566 --- /dev/null +++ b/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-06 diff --git a/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/design.md b/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/design.md new file mode 100644 index 0000000..3b4f7a7 --- /dev/null +++ b/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/design.md @@ -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. diff --git a/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/proposal.md b/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/proposal.md new file mode 100644 index 0000000..225515b --- /dev/null +++ b/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/proposal.md @@ -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. diff --git a/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/specs/evelyn-iou-burner-service/spec.md b/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/specs/evelyn-iou-burner-service/spec.md new file mode 100644 index 0000000..c1885ba --- /dev/null +++ b/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/specs/evelyn-iou-burner-service/spec.md @@ -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 diff --git a/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/specs/nenjim-hub-configuration/spec.md b/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/specs/nenjim-hub-configuration/spec.md new file mode 100644 index 0000000..e529d28 --- /dev/null +++ b/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/specs/nenjim-hub-configuration/spec.md @@ -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 diff --git a/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/tasks.md b/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/tasks.md new file mode 100644 index 0000000..1ff5df9 --- /dev/null +++ b/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/tasks.md @@ -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. diff --git a/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/verification.md b/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/verification.md new file mode 100644 index 0000000..4042f3b --- /dev/null +++ b/openspec/changes/archive/2026-10-06-94-gate-buybacks-on-evelyn-reference-price/verification.md @@ -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. diff --git a/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/.openspec.yaml b/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/.openspec.yaml new file mode 100644 index 0000000..aa32566 --- /dev/null +++ b/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-06 diff --git a/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/design.md b/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/design.md new file mode 100644 index 0000000..3b4f7a7 --- /dev/null +++ b/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/design.md @@ -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. diff --git a/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/proposal.md b/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/proposal.md new file mode 100644 index 0000000..225515b --- /dev/null +++ b/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/proposal.md @@ -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. diff --git a/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/specs/evelyn-iou-burner-service/spec.md b/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/specs/evelyn-iou-burner-service/spec.md new file mode 100644 index 0000000..c1885ba --- /dev/null +++ b/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/specs/evelyn-iou-burner-service/spec.md @@ -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 diff --git a/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/specs/nenjim-hub-configuration/spec.md b/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/specs/nenjim-hub-configuration/spec.md new file mode 100644 index 0000000..e529d28 --- /dev/null +++ b/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/specs/nenjim-hub-configuration/spec.md @@ -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 diff --git a/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/tasks.md b/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/tasks.md new file mode 100644 index 0000000..1ff5df9 --- /dev/null +++ b/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/tasks.md @@ -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. diff --git a/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/verification.md b/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/verification.md new file mode 100644 index 0000000..6367f97 --- /dev/null +++ b/openspec/changes/archive/2026-10-07-94-gate-buybacks-on-evelyn-reference-price/verification.md @@ -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. diff --git a/openspec/specs/evelyn-iou-burner-service/spec.md b/openspec/specs/evelyn-iou-burner-service/spec.md index 1bf45fa..e9f55ca 100644 --- a/openspec/specs/evelyn-iou-burner-service/spec.md +++ b/openspec/specs/evelyn-iou-burner-service/spec.md @@ -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. @@ -294,11 +294,11 @@ The service SHALL select the lesser of positive `balance - minimumReserve` and t - **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. +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 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 @@ -336,3 +336,41 @@ The constructor SHALL accept minimumBurnAmount and eveReserve as EVE-denominated #### 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 diff --git a/openspec/specs/nenjim-hub-configuration/spec.md b/openspec/specs/nenjim-hub-configuration/spec.md index 7b3518f..039fa58 100644 --- a/openspec/specs/nenjim-hub-configuration/spec.md +++ b/openspec/specs/nenjim-hub-configuration/spec.md @@ -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. diff --git a/src/main/tjava/com/fanitas/evelyn/service/burner/EvelynIOUBurnerService.tjava b/src/main/tjava/com/fanitas/evelyn/service/burner/EvelynIOUBurnerService.tjava index c2ac73a..1a145cb 100644 --- a/src/main/tjava/com/fanitas/evelyn/service/burner/EvelynIOUBurnerService.tjava +++ b/src/main/tjava/com/fanitas/evelyn/service/burner/EvelynIOUBurnerService.tjava @@ -51,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. - *
  • Attempt no more than one exact-input Jupiter swap. Normal return is + *
  • 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.
  • + *
  • 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 @@ -60,11 +68,13 @@ import java.util.List; * unknown swap is rebuilt, re-signed or resubmitted automatically.
  • *
  • 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 + * 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; + * later iteration and send no burn notification. Equality qualifies; + * never round upward or consume reserve to reach the minimum.
  • *
  • Require a positive, sufficiently fresh canonical EVE/USDT price and * calculate the actual eligible burn amount's dollar value, excluding * the retained reserve, before submitting a burn. diff --git a/src/main/tjava/com/fanitas/evelyn/service/burner/impl/ref/EvelynIOUBurnerServiceImpl.tjava b/src/main/tjava/com/fanitas/evelyn/service/burner/impl/ref/EvelynIOUBurnerServiceImpl.tjava index aeac7d9..b32164d 100644 --- a/src/main/tjava/com/fanitas/evelyn/service/burner/impl/ref/EvelynIOUBurnerServiceImpl.tjava +++ b/src/main/tjava/com/fanitas/evelyn/service/burner/impl/ref/EvelynIOUBurnerServiceImpl.tjava @@ -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; @@ -93,13 +94,23 @@ public final class EvelynIOUBurnerServiceImpl * qualifies; fractional remainders stay in the wallet. This policy does * not suppress independent small buybacks or re-evaluate pending burns.

    * + *

    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.

    + * * @param burnerWallet dedicated wallet whose balances are inspected and * 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 @@ -147,6 +158,7 @@ public final class EvelynIOUBurnerServiceImpl @NotNull JupiterSwapService swapService, @NotNull SolanaBlockChain solanaBlockChain, @NotNull TickerService tickerService, + @NotNull EvelynStatusService evelynStatusService, @NotNull CurrencyIdentityService currencyIdentityService, @NotNull BoundNotificationService notificationService, @NotNull List inputCurrencyMinimumReserves, @@ -175,6 +187,10 @@ public final class EvelynIOUBurnerServiceImpl tickerService, "tickerService" ); + this.evelynStatusService = Objects.requireNonNull( + evelynStatusService, + "evelynStatusService" + ); this.currencyIdentityService = Objects.requireNonNull( currencyIdentityService, "currencyIdentityService" @@ -436,6 +452,9 @@ public final class EvelynIOUBurnerServiceImpl continue; } + if (!isBuybackBelowReferencePrice()) { + return; + } attemptSwap( canonicalCurrency, inputMint, @@ -445,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, @@ -1124,6 +1207,7 @@ 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 inputCurrencyMinimumReserves; diff --git a/src/main/tjava/com/r35157/nenjim/hubd/impl/ref/NenjimHubImpl.tjava b/src/main/tjava/com/r35157/nenjim/hubd/impl/ref/NenjimHubImpl.tjava index 4eebc18..6b315e2 100644 --- a/src/main/tjava/com/r35157/nenjim/hubd/impl/ref/NenjimHubImpl.tjava +++ b/src/main/tjava/com/r35157/nenjim/hubd/impl/ref/NenjimHubImpl.tjava @@ -324,6 +324,10 @@ 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,