89: Add EvelynLiquidityService to withdraw CLMM proceeds, swap to USDC and send payouts
This commit is contained in:
+56
@@ -0,0 +1,56 @@
|
||||
## Purpose
|
||||
|
||||
Realize Evelyn's manually funded EVE/SyrupUSDC concentrated liquidity into USDC payouts using one dedicated wallet and current on-chain state.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Owned positions qualify by their normalized upper boundary
|
||||
The service SHALL discover positions owned by its wallet in its configured CLMM pool and withdraw positive liquidity only when the upper EVE/SyrupUSDC boundary U satisfies U <= P*(1-h/100). P SHALL come from current on-chain pool state, both orientations SHALL be normalized, and decisions SHALL use pool precision. Unusable P SHALL skip withdrawals while allowing later steps.
|
||||
|
||||
#### Scenario: Inclusive threshold and upper boundary
|
||||
- **WHEN** P is 15, h is 10 and positions span 13–13.5 and 13.5–14
|
||||
- **THEN** only the first qualifies, including equality
|
||||
|
||||
#### Scenario: Reversed pool ordering
|
||||
- **WHEN** mint A is SyrupUSDC and mint B is EVE
|
||||
- **THEN** the inverted lower raw boundary determines the normalized upper price and the same eligibility rule applies
|
||||
|
||||
#### Scenario: Other or empty positions and falling market
|
||||
- **WHEN** a position belongs to another wallet or pool, has zero liquidity, or its upper boundary exceeds the threshold after a price decline
|
||||
- **THEN** it retains its state and the service neither withdraws it nor creates or refills positions
|
||||
|
||||
### Requirement: Swap and payout use independent current balances
|
||||
The service SHALL read SyrupUSDC at step 2 and USDC at step 3 and operate on the entire respective balance above its nonnegative reserve. Absent or at/below-reserve balances SHALL be skipped. Exact-input swap SHALL use explicit maximum slippage. SPL payout SHALL target the configured wallet owner with existing account-creation support. Amounts SHALL never round upward. EVE and reward tokens SHALL remain untouched by this pipeline.
|
||||
|
||||
#### Scenario: Nonzero reserves
|
||||
- **WHEN** SyrupUSDC balance is 1.01 with reserve 0.01 and USDC balance is 2.5 with reserve 0.5
|
||||
- **THEN** 1.00 SyrupUSDC is swapped and 2.0 USDC is sent
|
||||
|
||||
#### Scenario: Zero reserves and empty accounts
|
||||
- **WHEN** reserves are zero
|
||||
- **THEN** full positive balances are eligible, absent/zero balances are skipped and empty source accounts are not closed
|
||||
|
||||
### Requirement: Failure recovery uses on-chain state
|
||||
Each iteration SHALL attempt withdrawal, swap and payout independently in that order, isolating ordinary per-position failures. Subsequent iterations and restarts SHALL reread positions and balances without a persistent queue, attribution, pending-signature gate, immediate retries or replay. Logs SHALL identify operations, positions, amounts and returned signatures without signer secrets. Interruption SHALL stop work.
|
||||
|
||||
#### Scenario: Independent failures and restart
|
||||
- **WHEN** withdrawal or swap fails or returns an unknown outcome
|
||||
- **THEN** independent later work can proceed and the next iteration or restart acts on current state, skipping completed empty positions
|
||||
|
||||
### Requirement: Configuration is validated before worker start
|
||||
A dedicated version-1 runtime configuration SHALL validate wallet/signer binding, pool type/token identity, address syntax, 0<=h<100, positive fixed delay, nonnegative reserves with mint precision and explicit slippage limits. Currencies, mints and programs SHALL use canonical identity and chain metadata. The public example SHALL have zero reserves and SHALL never be a runtime fallback.
|
||||
|
||||
#### Scenario: Invalid input or missing deployment file
|
||||
- **WHEN** configuration is invalid or only the example exists
|
||||
- **THEN** startup fails before financial work
|
||||
|
||||
### Requirement: Optional process owns a sequential worker
|
||||
The Hub SHALL select evelyn.liquidity.prod through its existing catalogue and Registry, constructing deployment configuration and dedicated signer/swap dependencies only when selected. Existing selections and ordering SHALL be preserved. The domain interface SHALL expose no lifecycle operations. A single worker SHALL run the first and subsequent iterations identically, with a fixed delay after completion and ordinary shutdown interruption.
|
||||
|
||||
#### Scenario: Optional process is omitted
|
||||
- **WHEN** the Hub plan omits evelyn.liquidity.prod
|
||||
- **THEN** no liquidity configuration or dedicated signer is loaded and the checked-in Hub example activates no financial operation
|
||||
|
||||
#### Scenario: Slow iteration and shutdown
|
||||
- **WHEN** an iteration exceeds the configured interval or shutdown interrupts it
|
||||
- **THEN** no iterations overlap and interruption prevents further steps
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: NenjimHub owns composition and runtime startup
|
||||
`NenjimHub` SHALL remain the global runtime owner exposing only `start() throws Exception` plus an immutable ordered snapshot of successfully started process IDs. It SHALL be neither component nor process and SHALL not register itself. Its only accepted startup attempt SHALL validate the complete fixed-path Hub configuration before starting anything, construct and start Registry first, register Registry as `nenjim.registry.service`, record it, construct and register the existing shared passive graph, then lazily construct, register, start, and record only the remaining configured processes in exact order beginning with Journal. The selected liquidity process SHALL additionally compose its dedicated passive dependencies lazily at its configured entry. Registry and Journal managers SHALL remain distinct passive, unregistered handles retained privately by the Hub. Registration SHALL never imply startup. The Hub SHALL preserve the online point and blocking wait only after all configured processes succeed, without retry, rollback, dependency inference, or reordering.
|
||||
|
||||
#### Scenario: Registry starts before passive graph construction
|
||||
- **WHEN** the Hub has validated a complete startup plan and performs its first startup attempt
|
||||
- **THEN** it starts, registers, and records Registry first before constructing the passive component graph or any selected non-Registry process
|
||||
|
||||
#### Scenario: Journal follows passive composition but starts second
|
||||
- **WHEN** Registry bootstrap and passive graph construction have succeeded
|
||||
- **THEN** the Hub constructs, registers, starts, and records Journal before any later configured process, so Journal remains the second successful process
|
||||
|
||||
#### Scenario: Registry is process-discoverable
|
||||
- **WHEN** the Registry registration has completed
|
||||
- **THEN** the same object is retrievable as both `NenjimRegistryService` and `NenjimProcess`
|
||||
|
||||
#### Scenario: Journal is process-discoverable
|
||||
- **WHEN** the configured Journal construction and registration have completed
|
||||
- **THEN** the same object is retrievable as both `JournalService` and `NenjimProcess` under `nenjim.journal.service`, while its manager is not registered
|
||||
|
||||
#### Scenario: Selected JavaFX runtime is process-discoverable
|
||||
- **WHEN** `nenjim.javafx-runtime.service` is selected and its registration has completed
|
||||
- **THEN** one object is retrievable as both `JavaFxRuntimeService` and `NenjimProcess` under that ID
|
||||
|
||||
#### Scenario: Selected JavaFX applications share the runtime
|
||||
- **WHEN** JavaFX runtime precedes selected Mission Control or Process Manager entries
|
||||
- **THEN** each selected application retains the same already-constructed `JavaFxRuntimeService` instance registered by the Hub
|
||||
|
||||
#### Scenario: Selected Process Manager is process-discoverable
|
||||
- **WHEN** `nenjim.process-manager.default` is selected after JavaFX runtime and its construction and registration complete
|
||||
- **THEN** the object constructed with the current Hub and shared JavaFX runtime is retrievable as both `NenjimProcessManager` and `NenjimProcess`
|
||||
|
||||
#### Scenario: Required passive bindings are registered
|
||||
- **WHEN** Hub passive composition completes
|
||||
- **THEN** all current passive component IDs and implementation choices from issue #76 are present exactly as configured, while process IDs other than Registry remain absent until selected
|
||||
|
||||
#### Scenario: Configured startup subset preserves order
|
||||
- **WHEN** Hub startup completes successfully
|
||||
- **THEN** successful process IDs exactly equal the validated `[AUTO_START]` entries in their declared order
|
||||
|
||||
#### Scenario: Omitted processes do not enter the Registry
|
||||
- **WHEN** a modern process ID is omitted from `[AUTO_START]`
|
||||
- **THEN** that process is neither constructed, registered, started, nor recorded even though its implementation is known to the Hub
|
||||
|
||||
#### Scenario: Journal failure is fail-fast
|
||||
- **WHEN** Journal construction, registration, or process startup throws
|
||||
- **THEN** Registry remains the only recorded process, no later configured process is constructed or started, the exception propagates, and the Hub neither announces online nor enters its final wait
|
||||
|
||||
#### Scenario: JavaFX runtime failure is fail-fast
|
||||
- **WHEN** selected JavaFX runtime construction, registration, or startup throws
|
||||
- **THEN** its ID is not recorded, no later configured process is constructed or started, the exception propagates, and the Hub neither announces online nor enters its final wait
|
||||
|
||||
#### Scenario: Hub start is not retried
|
||||
- **WHEN** any first Hub startup attempt has begun and `start()` is requested again
|
||||
- **THEN** the later request fails with `IllegalStateException` whether the first attempt succeeded or failed
|
||||
|
||||
#### Scenario: Startup failure is fail-fast
|
||||
- **WHEN** any passive composition, selected construction, registration, or process startup operation throws
|
||||
- **THEN** the exception propagates, no later process is constructed or started, the Hub neither announces online nor enters its final wait, and no generic rollback or retry is attempted
|
||||
|
||||
#### Scenario: Dedicated liquidity bindings use the liquidity signer
|
||||
- **WHEN** `evelyn.liquidity.prod` is selected and its construction succeeds
|
||||
- **THEN** `solana.wallet.evelyn-liquidity-prod`, `jupiter.swap.evelyn-liquidity-prod` and `raydium.service.evelyn-liquidity-prod` are registered, the swap service uses the dedicated liquidity wallet and Raydium uses direct chain reads
|
||||
|
||||
#### Scenario: Liquidity service has orthogonal domain and lifecycle views
|
||||
- **WHEN** the selected liquidity process is registered
|
||||
- **THEN** the same object resolves as `EvelynLiquidityService` and `NenjimProcess`, while the domain interface exposes no lifecycle operations
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Registry bootstraps eager passive composition
|
||||
After validating the entire plan, the Hub SHALL construct the configured Registry subsystem, start Registry, register its service and process object as `nenjim.registry.service`, and record that ID only after successful startup. The Hub SHALL then eagerly construct and register the existing shared passive component graph. Dedicated liquidity deployment configuration, wallet signer, Jupiter swap service and direct Raydium service SHALL instead be constructed and registered only when the configured `evelyn.liquidity.prod` entry is reached. This passive phase SHALL not construct any canonical `NenjimProcess`, start background workers, initialize JavaFX, add startup IDs, infer process dependencies, or implicitly create an omitted process. Plain internal dependencies that are not `NenjimComponent` objects MAY be constructed as part of passive composition.
|
||||
|
||||
#### Scenario: Registry surrounds passive composition
|
||||
- **WHEN** a valid Hub startup begins
|
||||
- **THEN** Registry starts, is registered, and is recorded before the existing shared passive component graph is constructed and registered
|
||||
|
||||
#### Scenario: Passive composition completes
|
||||
- **WHEN** Registry bootstrap succeeds
|
||||
- **THEN** all existing shared non-process building blocks, adapters, and services are constructed and registered without starting a process
|
||||
|
||||
#### Scenario: Passive construction fails
|
||||
- **WHEN** any passive component cannot be constructed or registered
|
||||
- **THEN** startup stops immediately with Registry remaining recorded and no configured process after Registry constructed or started
|
||||
|
||||
#### Scenario: Liquidity is omitted
|
||||
- **WHEN** the plan omits `evelyn.liquidity.prod`
|
||||
- **THEN** its deployment configuration, dedicated signer/wallet, Jupiter service and direct Raydium service are neither loaded nor constructed nor registered
|
||||
|
||||
### 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.
|
||||
|
||||
#### 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
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
## Purpose
|
||||
|
||||
Provide a narrowly scoped protected Raydium CLMM full liquidity decrease using the owner's existing Solana signing and submission primitives.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Full withdrawal preserves the reusable position
|
||||
The operation SHALL validate the NFT owner, pool and positive current liquidity, decrease that complete liquidity into the same wallet, collect pool-token fees and preserve the position account, NFT and NFT token account. Transaction/account details SHALL remain behind Raydium. It SHALL submit once and use existing confirmation behavior without close, burn or service-level retry.
|
||||
|
||||
#### Scenario: Full protected withdrawal
|
||||
- **WHEN** a wallet withdraws its position's complete liquidity
|
||||
- **THEN** the transaction names the expected wallet, pool, liquidity and nonzero minimum output protection and contains no close-position, NFT burn or token-account close instruction
|
||||
|
||||
#### Scenario: Wrong owner or changed position
|
||||
- **WHEN** ownership, pool or current position state is inconsistent with the requested withdrawal
|
||||
- **THEN** the operation fails before signing
|
||||
|
||||
### Requirement: Minimum output derives from precise state and explicit slippage
|
||||
Withdrawal minimums SHALL derive from current Q64.64 pool state, exact CLMM tick/liquidity math and configured slippage with downward raw-token rounding. Both minimums SHALL NOT be unconditionally zero. Required legacy/Token-2022 accounts, tick arrays and bitmap extension SHALL follow the official decrease instruction. Unsupported token extensions SHALL fail safely before signing.
|
||||
|
||||
#### Scenario: One-sided principal and fees
|
||||
- **WHEN** the range is fully below the normalized current price
|
||||
- **THEN** SyrupUSDC principal is protected, any EVE fees return to the wallet and all received EVE remains there
|
||||
|
||||
#### Scenario: Precision and tiny position
|
||||
- **WHEN** expected principal is too small to provide a positive raw-unit minimum
|
||||
- **THEN** the operation fails rather than submitting an unchecked withdrawal
|
||||
Reference in New Issue
Block a user