Files
com_r35157_nenjim-hubd-impl…/docs/EvelynLiquidityService.md
T

151 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Evelyn liquidity proceeds
`com.r35157.evelyn.service.liquidity.EvelynLiquidityService` is a domain-only
Nenjim component. `impl.ref.EvelynLiquidityServiceImpl` additionally implements
`NenjimProcess`; NenjimHub owns startup. The optional process ID is
`evelyn.liquidity.prod`.
The owner creates/funds EVE/SyrupUSDC CLMM positions manually (for example,
10 EVE each) using the dedicated liquidity wallet. This service withdraws,
converts and pays proceeds; it never creates, refills, reranges, closes or burns
positions, NFTs or source token accounts. A falling price leaves nonqualifying
positions intact, including positions whose inventory has returned to EVE.
## Deployment
Copy `conf/evelyn-liquidity.conf.example` to `conf/evelyn-liquidity.conf` and
replace every placeholder. The local runtime file is intentionally ignored by
Git. Both begin with `FORMAT_VERSION=1`; the example is never loaded as a
fallback. Unknown, duplicate and missing settings fail validation.
| Setting | Contract |
| --- | --- |
| `LIQUIDITY_WALLET` | Dedicated public Solana wallet address |
| `SIGNER_KEY_NAME` | Existing external jup key name controlling exactly that address |
| `CLMM_POOL` | Existing EVE/SyrupUSDC Raydium CLMM pool address |
| `HARVEST_DISTANCE_PERCENT` | `0 <= h < 100`; `10` means ten percent |
| `ITERATION_INTERVAL_SECONDS` | Positive integer delay after an iteration completes; example `300` |
| `USDC_RECIPIENT` | Recipient wallet owner address, not a token account; must differ from the liquidity wallet |
| `MINIMUM_SYRUPUSDC_RESERVE` | Nonnegative SyrupUSDC amount; example `0` |
| `MINIMUM_USDC_RESERVE` | Nonnegative USDC amount; example `0` |
| `MAXIMUM_SWAP_SLIPPAGE_BPS` | Explicit Jupiter limit in `[0,10000]` basis points |
| `MAXIMUM_WITHDRAWAL_SLIPPAGE_BPS` | Explicit CLMM limit in `[0,10000)` basis points |
The existing SolanaWallet implementation checks the external jup version and
key/address binding during construction. No key material belongs in these files.
Fund the wallet with SOL for fees and any destination token-account rent.
Reserve amounts must be exactly representable at the corresponding mint's
precision; `0.01` SyrupUSDC and `0.5` USDC are permitted operating choices.
No hidden positive reserve is added.
Only after configuring and funding the wallet, explicitly add
`evelyn.liquidity.prod` to the existing `[AUTO_START]` section of private
`conf/NenjimHub.conf`. Keep Registry first and Journal second, with no blank line
inside the active section. Preserve all existing process selections and their
order. This process requires no Ticker, Evelyn status, JavaFX or burner process.
The checked-in Hub example continues to select only Registry and Journal.
Deployment configuration and the dedicated signer/wallet, Jupiter and direct
Raydium dependencies are constructed only when the selected entry is reached.
Registry IDs are `solana.wallet.evelyn-liquidity-prod`,
`jupiter.swap.evelyn-liquidity-prod`, `raydium.service.evelyn-liquidity-prod`
and `evelyn.liquidity.prod`. Jupiter signs with this wallet. Currency/mint
identities come from CIS; token programs and precision come from chain metadata.
## Iteration and recovery
A single worker runs immediately on startup and then with fixed delay after
completion. Every iteration follows the same three independent steps:
1. Discover the wallet's position NFTs. For each owned, positive-liquidity
position in the configured pool, read fresh on-chain pool state in
SyrupUSDC per EVE after discovery and any earlier withdrawals. Withdraw complete
liquidity when its normalized **upper** boundary satisfies
`U <= P * (1 - h/100)`. Equality qualifies. Both token orientations use
exact squared Q64.64 comparisons, without display-price rounding. At
`P=15, h=10`, `13–13.5` qualifies and `13.5–14` does not. Withdrawals retain
the position and NFT, and return pool-token fees to the same wallet.
Each position uses its own fresh state for both eligibility and withdrawal
minimums; the price is not reused across positions.
2. Read current SyrupUSDC balance and swap the full balance above its reserve
into USDC through the confirmed exact-input Jupiter operation.
3. Read current USDC balance and send the full balance above its reserve to
the recipient owner, creating its ATA at the sender's expense if needed.
The transfer submits once and is awaited at `CONFIRMED`.
Missing/zero balances and balances equal to their reserves are skipped. Amounts
are rounded down to mint precision. An unavailable or unusable pool price stops
the remaining withdrawals in that iteration while retaining steps 2 and 3. EVE returned as principal/fees and
other reward tokens remain in the wallet and never enter the conversion/payout
pipeline. All available SyrupUSDC/USDC is eligible, including manual deposits.
An ordinary position failure does not block other readable positions. An
ordinary step failure does not block later independent steps. Logs identify
steps, position NFTs, liquidity/token amounts and returned signatures. Unknown
outcomes are logged with signatures when available. Subsequent iterations and
restarts reread chain state; there is no service journal, attribution, pending
signature gate, immediate retry or transaction replay. Empty positions are
skipped and can be manually reused. Shutdown interrupts the current operation
and prevents following steps; the reference implementation can also be closed
by its local owner through `AutoCloseable`.
## Withdrawal support
The transaction follows the official
[decrease_liquidity_v2 accounts](https://github.com/raydium-io/raydium-clmm/blob/master/programs/amm/src/instructions/decrease_liquidity_v2.rs)
and [decrease/minimum-output behavior](https://github.com/raydium-io/raydium-clmm/blob/master/programs/amm/src/instructions/decrease_liquidity.rs),
also checked against the
[official SDK encoder](https://github.com/raydium-io/raydium-sdk-V2/blob/master/src/raydium/clmm/instrument.ts).
Withdrawal and closure are separate actions in
[Raydium's liquidity documentation](https://github.com/raydium-io/raydium-docs-v1/blob/main/user-flows/add-remove-liquidity.mdx).
Raydium validates pool/personal-position discriminators, the owned NFT holding,
current complete liquidity, mint precision, vaults and tick arrays before signing.
For every pool or reward mint, existing owner token accounts are reused after
validating their mint, owner, token program and initialized state, including
empty accounts and accounts outside the owner's ATA. Only when no owner holding
exists does the withdrawal use or idempotently create its ATA. A failed holdings
read aborts rather than creating an additional account. This preserves the
existing wallet's single-account-per-mint balance/transfer contract. The
transaction requests a 600,000 compute-unit limit.
The bitmap extension and initialized reward triplets are included when required.
The integer principal math matches Raydium's
[tick algorithm](https://github.com/raydium-io/raydium-clmm/blob/master/programs/amm/src/libraries/tick_math.rs).
Minimum principal amounts are floored after applying explicit withdrawal
slippage. An amount too small to protect at least one raw unit is rejected.
No withdrawal always substitutes zero for both minimums.
Price movement after the per-position read remains possible before execution;
principal minimums protect received amounts, not the harvest-distance threshold.
The current EVE mint uses Token-2022 metadata pointer/token metadata extensions;
SyrupUSDC uses legacy SPL. Read-only mainnet inspection on 2026-10-04 found the
EVE/SyrupUSDC pool `EpZXeeShz64DK8HfKnjWPTkmhN7dZe77SjvzNDX8CARq` with decimals
8/6, tick spacing 60 and no initialized rewards. Deployment still requires an
explicit pool setting and repeats validation. Mint close-authority and metadata/
group extensions are supported; other transfer-affecting extensions, including
transfer fees/hooks, fail before signing. The withdrawal encoder supports legacy
transactions fitting Solana's packet limit and rejects larger account sets.
## Offline verification
The Gradle JUnit fixtures exercise exact threshold equality, inversion, raw
principal amounts, owner/pool filtering, reserves, independent failures,
restart recovery and shutdown. Captured serialized transactions verify account
order, full liquidity, minimum output, NFT retention, bitmap/reward accounts and
one submission/confirmation attempt. Review regression fixtures also cover price
changes during discovery or earlier withdrawals in both token orientations,
quote consistency, and reuse/validation of existing pool and reward token accounts
under both token programs. All financial dependencies are controlled;
no verification submits live withdrawals, swaps or transfers.
```sh
./gradlew compileJava compileTestJava test \
--tests '*RaydiumClmm*Test' --tests '*EvelynLiquidityServiceImplTest' \
--tests '*NenjimHubLiquidityConfigurationTest'
openspec validate 89-add-evelyn-liquidity-service --strict
git diff --check
```
Stop the process/JVM and remove its explicit Hub selection to disable operation.
Existing positions and wallet balances remain the recovery state.