Support SyrupUSDC in the Evelyn IOU burner with per-currency swap limits #90

Closed
opened 2026-10-05 17:33:24 +02:00 by minimons · 0 comments
Owner

Problem and outcome

The Evelyn IOU burner currently accepts only USDT in its production Hub configuration. Its common USDC-equivalent swap ceiling also requires a fresh input-token/USDC price for any other token. Add SyrupUSDC as an accepted input and replace that common ceiling with a maximum input amount for each configured currency, expressed in that currency's own units.

This lets SyrupUSDC deposited into the existing burner wallet be exchanged for EVE and burned using the existing Jupiter pipeline, without adding SyrupUSDC price sources or changing the Ticker.

Production configuration

Keep configuration in NenjimHubImpl.constructEvelynIOUBurnerProdProcess() and use the existing MoneyAmount type for both reserves and maximum amounts.

Currency Minimum reserve Maximum swap input per iteration
USDT (CurrencyTypeIds.USDT_ID) 0.1 USDT 0.25 USDT
SyrupUSDC (CurrencyTypeIds.SYRUPUSDC_ID) 0.095 SyrupUSDC 0.2 SyrupUSDC

Preserve USDT as the first priority and add SyrupUSDC after it. Preserve the existing burner wallet, process registration and all other production settings, including the hourly interval and slippage limit.

These are token-denominated limits. In particular, 0.2 SyrupUSDC is not a guaranteed USD or USDC value. Do not treat SyrupUSDC as equal to USDC.

Constructor and amount calculation

  • Replace the scalar maximumSwapAmountInUSDC constructor argument with a List<MoneyAmount> of per-currency maximum input amounts, for example inputCurrencyMaximumSwapAmounts.

  • Keep the ordered inputCurrencyMinimumReserves list as the authoritative accepted-input list and priority order. Match maxima by canonical currency UUID, not by list position, name or symbol.

  • Defensively copy the configuration. Require exactly one strictly positive maximum for every accepted input, with no duplicates or extra currencies. Reject null entries, amounts, currencies or UUIDs and invalid/unknown identities before starting a worker. Preserve the existing reserve and input validation. Empty reserves and maxima lists remain valid and permit direct EVE deposits to be burned.

  • Remove input-token/USDC price lookup and the conversion from the common USDC ceiling. Remove configuration names, fields and documentation that imply the swap limit is denominated in USD or USDC.

  • For each input, calculate:

    availableForSwap = max(balance - minimumReserve, 0)
    amountToSwap = min(availableForSwap, maximumAmountForThisCurrency)
    

    Round downward to the input mint's precision using the existing precision handling. Skip a non-positive amount, including one that rounds to zero. Never round upward beyond the spendable balance or maximum.

Preserve the existing execution and burn behavior

All swaps continue through the existing exact-input JupiterSwapService, from the selected input mint to the fixed EVE mint. Jupiter chooses the pool and route; this issue does not constrain it to one hop or a specific Raydium pool.

Keep the existing behavior for ordered input selection, at most one swap attempt per iteration, SOL/SPL balance and mint resolution, confirmed swap outcomes, suspension after unknown swap outcomes, reloading and burning the complete EVE balance, pending burn handling, FINALIZED confirmation and notifications.

The Ticker dependency remains necessary: preserve the fresh EVE/USDT observation required before burning and used to calculate the notification value. Only the input-token/USDC price dependency is removed. Preserve direct-EVE deposit handling and the existing Hub-owned NenjimProcess lifecycle.

Verification and acceptance

  • Production configuration contains the exact reserves and maxima above, including 0.095, not 0.1, for SyrupUSDC.
  • With SyrupUSDC balance 0.5, select 0.2 SyrupUSDC. With balance 0.245, select 0.150. At or below 0.095, skip SyrupUSDC.
  • With USDT balance 0.35, select 0.25 USDT, preserving the existing reserve and maximum.
  • Verify matching by UUID even when the maxima list uses a different order, invalid/missing/duplicate maxima rejection, and downward mint-precision rounding.
  • SyrupUSDC selection requires no SyrupUSDC/USDC Ticker observation. Missing or stale EVE/USDT observations still postpone burning exactly as before.
  • Verify that the existing swap and burn failure/recovery behavior is preserved using controlled dependencies or offline verification. Do not submit live financial transactions for verification.
  • Update directly affected OpenSpec requirements, constructor JavaDoc and documentation to describe per-currency token limits. Compile the relevant sources, validate the OpenSpec change and run the normal diff/whitespace checks.

Scope

No new PriceSource implementation or instances, Ticker API/selection changes, price history files or directories, derived cross-prices, direct Raydium swaps, generic token discovery, new configuration framework, new ValueTags, burner package rename, input-priority changes or unrelated burner refactoring.

Related work: #72 (original burner) and #83 (domain/lifecycle separation). This issue records the agreed implementation scope; implementation and deployment are separate tasks.

## Problem and outcome The Evelyn IOU burner currently accepts only USDT in its production Hub configuration. Its common USDC-equivalent swap ceiling also requires a fresh input-token/USDC price for any other token. Add SyrupUSDC as an accepted input and replace that common ceiling with a maximum input amount for each configured currency, expressed in that currency's own units. This lets SyrupUSDC deposited into the existing burner wallet be exchanged for EVE and burned using the existing Jupiter pipeline, without adding SyrupUSDC price sources or changing the Ticker. ## Production configuration Keep configuration in `NenjimHubImpl.constructEvelynIOUBurnerProdProcess()` and use the existing `MoneyAmount` type for both reserves and maximum amounts. | Currency | Minimum reserve | Maximum swap input per iteration | | --- | ---: | ---: | | USDT (`CurrencyTypeIds.USDT_ID`) | 0.1 USDT | 0.25 USDT | | SyrupUSDC (`CurrencyTypeIds.SYRUPUSDC_ID`) | **0.095 SyrupUSDC** | **0.2 SyrupUSDC** | Preserve USDT as the first priority and add SyrupUSDC after it. Preserve the existing burner wallet, process registration and all other production settings, including the hourly interval and slippage limit. These are token-denominated limits. In particular, 0.2 SyrupUSDC is not a guaranteed USD or USDC value. Do not treat SyrupUSDC as equal to USDC. ## Constructor and amount calculation - Replace the scalar `maximumSwapAmountInUSDC` constructor argument with a `List<MoneyAmount>` of per-currency maximum input amounts, for example `inputCurrencyMaximumSwapAmounts`. - Keep the ordered `inputCurrencyMinimumReserves` list as the authoritative accepted-input list and priority order. Match maxima by canonical currency UUID, not by list position, name or symbol. - Defensively copy the configuration. Require exactly one strictly positive maximum for every accepted input, with no duplicates or extra currencies. Reject null entries, amounts, currencies or UUIDs and invalid/unknown identities before starting a worker. Preserve the existing reserve and input validation. Empty reserves and maxima lists remain valid and permit direct EVE deposits to be burned. - Remove input-token/USDC price lookup and the conversion from the common USDC ceiling. Remove configuration names, fields and documentation that imply the swap limit is denominated in USD or USDC. - For each input, calculate: ```text availableForSwap = max(balance - minimumReserve, 0) amountToSwap = min(availableForSwap, maximumAmountForThisCurrency) ``` Round downward to the input mint's precision using the existing precision handling. Skip a non-positive amount, including one that rounds to zero. Never round upward beyond the spendable balance or maximum. ## Preserve the existing execution and burn behavior All swaps continue through the existing exact-input `JupiterSwapService`, from the selected input mint to the fixed EVE mint. Jupiter chooses the pool and route; this issue does not constrain it to one hop or a specific Raydium pool. Keep the existing behavior for ordered input selection, at most one swap attempt per iteration, SOL/SPL balance and mint resolution, confirmed swap outcomes, suspension after unknown swap outcomes, reloading and burning the complete EVE balance, pending burn handling, `FINALIZED` confirmation and notifications. The Ticker dependency remains necessary: preserve the fresh EVE/USDT observation required before burning and used to calculate the notification value. Only the input-token/USDC price dependency is removed. Preserve direct-EVE deposit handling and the existing Hub-owned `NenjimProcess` lifecycle. ## Verification and acceptance - Production configuration contains the exact reserves and maxima above, including **0.095**, not 0.1, for SyrupUSDC. - With SyrupUSDC balance 0.5, select 0.2 SyrupUSDC. With balance 0.245, select 0.150. At or below 0.095, skip SyrupUSDC. - With USDT balance 0.35, select 0.25 USDT, preserving the existing reserve and maximum. - Verify matching by UUID even when the maxima list uses a different order, invalid/missing/duplicate maxima rejection, and downward mint-precision rounding. - SyrupUSDC selection requires no SyrupUSDC/USDC Ticker observation. Missing or stale EVE/USDT observations still postpone burning exactly as before. - Verify that the existing swap and burn failure/recovery behavior is preserved using controlled dependencies or offline verification. Do not submit live financial transactions for verification. - Update directly affected OpenSpec requirements, constructor JavaDoc and documentation to describe per-currency token limits. Compile the relevant sources, validate the OpenSpec change and run the normal diff/whitespace checks. ## Scope No new PriceSource implementation or instances, Ticker API/selection changes, price history files or directories, derived cross-prices, direct Raydium swaps, generic token discovery, new configuration framework, new ValueTags, burner package rename, input-priority changes or unrelated burner refactoring. Related work: #72 (original burner) and #83 (domain/lifecycle separation). This issue records the agreed implementation scope; implementation and deployment are separate tasks.
minimons added the enhancement label 2026-10-05 17:33:24 +02:00
minimons added this to the Evelyn project 2026-10-05 17:34:05 +02:00
minimons self-assigned this 2026-10-05 17:34:10 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: r35157/com_r35157_nenjim-hubd-impl_ref#90