92: Add per-currency minimum buyback amounts to the Evelyn IOU burner
This commit is contained in:
@@ -16,14 +16,14 @@ The system SHALL expose an empty lifecycle-free `EvelynIOUBurnerService` under `
|
||||
- **THEN** it uses existing `MoneyAmount` values rather than burner-specific Solana, SPL-token, or input-asset types
|
||||
|
||||
### Requirement: Ordered AssetAZ input configuration
|
||||
The authoritative constructor SHALL accept a non-null ordered `List<MoneyAmount>` named `inputCurrencyMinimumReserves`, defensively copy it without changing order, and interpret inclusion as acceptance, list position as priority, each `currencyType().id()` as the authoritative AssetAZ identity, and each `amount()` as the minimum balance that must remain. It SHALL ignore supplied currency name and symbol metadata by resolving each UUID through the injected Currency Identity Service and using the canonical returned currency for processing. The constructor SHALL also accept a non-null `List<MoneyAmount>` named `inputCurrencyMaximumSwapAmounts`, defensively copy its values, and match each maximum to an accepted input by canonical currency UUID, independently of maximum-list order or supplied metadata. Each maximum SHALL be expressed in that same currency's own units. Empty reserves and maxima lists SHALL be valid and SHALL still permit directly deposited EVE to be burned.
|
||||
The authoritative constructor SHALL accept a non-null ordered `List<MoneyAmount>` named `inputCurrencyMinimumReserves`, defensively copy it without changing order, and interpret inclusion as acceptance, list position as priority, each `currencyType().id()` as the authoritative AssetAZ identity, and each `amount()` as the minimum balance that must remain. It SHALL ignore supplied currency name and symbol metadata by resolving each UUID through the injected Currency Identity Service and using the canonical returned currency for processing. The constructor SHALL also accept a non-null `List<MoneyAmount>` named `inputCurrencyMaximumSwapAmounts`, defensively copy its values, and match each maximum to an accepted input by canonical currency UUID, independently of maximum-list order or supplied metadata. Each maximum SHALL be expressed in that same currency's own units. The constructor SHALL additionally accept `inputCurrencyMinimumSwapAmounts`, defensively copy its values and match strictly positive token-denominated minima by canonical UUID independently of order/name/symbol. Empty reserves, minima and maxima lists SHALL be valid and SHALL still permit directly deposited EVE to be burned.
|
||||
|
||||
#### Scenario: Stale configured metadata
|
||||
- **WHEN** a configured `MoneyAmount` carries a known UUID but stale name or symbol metadata
|
||||
- **THEN** the service processes the current canonical currency resolved for that UUID
|
||||
|
||||
#### Scenario: Priority is preserved
|
||||
- **WHEN** multiple configured entries have spendable balances and usable Solana data
|
||||
- **WHEN** multiple configured entries have rounded amounts at/above their own minimum and usable Solana data
|
||||
- **THEN** the earliest entry in the defensively copied list is the only input selected in that iteration
|
||||
|
||||
#### Scenario: Empty input list
|
||||
@@ -35,16 +35,20 @@ The authoritative constructor SHALL accept a non-null ordered `List<MoneyAmount>
|
||||
- **THEN** each input receives the maximum for its UUID and reserve-list priority is preserved
|
||||
|
||||
#### Scenario: Caller changes its configuration lists
|
||||
- **WHEN** a caller changes either supplied list after construction
|
||||
- **THEN** the accepted currencies, their priority, reserves and maxima remain as validated during construction
|
||||
- **WHEN** a caller changes any supplied list after construction
|
||||
- **THEN** the accepted currencies, their priority, reserves, minima and maxima remain as validated during construction
|
||||
|
||||
#### Scenario: Minimum list order and display metadata are independent
|
||||
- **WHEN** minima are reordered or use stale names/symbols for known canonical UUIDs
|
||||
- **THEN** each input receives its UUID's minimum and only reserve-list order controls priority
|
||||
|
||||
### Requirement: Complete constructor validation
|
||||
Construction SHALL require all injected services, both input configuration lists, and durations to be non-null; every list entry, entry amount, entry currency, and currency UUID to be non-null; each reserve to be zero or greater; each maximum to be strictly positive; each UUID to be known to the injected Currency Identity Service; UUIDs to be unique within each list; the maximum UUID set to exactly equal the accepted reserve UUID set; and EVE not to be an input currency. It SHALL require slippage to be from 0 through 10000 basis points inclusive, `maximumPriceAge` to be greater than zero milliseconds, and both finalization timeout and iteration interval to be positive and non-zero.
|
||||
Construction SHALL require all injected services, all three input configuration lists, and durations to be non-null; every list entry, entry amount, entry currency, and currency UUID to be non-null; each reserve to be zero or greater; each minimum and maximum to be strictly positive; each minimum to be no greater than its matched maximum; each UUID to be known to the injected Currency Identity Service; UUIDs to be unique within each list; both minimum and maximum UUID sets to exactly equal the accepted reserve UUID set; and EVE not to be an input currency. It SHALL require slippage to be from 0 through 10000 basis points inclusive, `maximumPriceAge` to be greater than zero milliseconds, and both finalization timeout and iteration interval to be positive and non-zero.
|
||||
|
||||
Every configured non-SOL UUID SHALL resolve through reverse CIS lookup to exactly one external reference in the `solana-mint` namespace, and that reference's external ID SHALL be non-null and non-blank. Construction SHALL fail for a missing, ambiguous, null, or blank non-SOL mint representation. These burner-specific rules SHALL NOT change the general-purpose `MoneyAmount` contract.
|
||||
|
||||
#### Scenario: Valid configuration
|
||||
- **WHEN** every dependency and value is valid, reserves are non-negative, every accepted input has exactly one positive maximum, UUIDs are distinct and known within each list, EVE is absent, and every non-SOL input has one non-blank Solana mint reference
|
||||
- **WHEN** every dependency and value is valid, reserves are non-negative, every accepted input has exactly one positive minimum and maximum with minimum <= maximum, UUIDs are distinct and known within each list, EVE is absent, and every non-SOL input has one non-blank Solana mint reference
|
||||
- **THEN** construction succeeds and preserves the configured priority order
|
||||
|
||||
#### Scenario: Invalid reserve or operating limit
|
||||
@@ -67,6 +71,18 @@ Every configured non-SOL UUID SHALL resolve through reverse CIS lookup to exactl
|
||||
- **WHEN** a maximum entry, amount, currency or UUID is null, or its UUID is unknown or resolves to an invalid canonical identity
|
||||
- **THEN** construction fails with a diagnostic identifying the invalid maximum configuration
|
||||
|
||||
#### Scenario: Invalid minimum configuration
|
||||
- **WHEN** the minimum list or an entry, amount, currency or UUID is null; a UUID is unknown, duplicated, extra or missing; a minimum is non-positive; or it exceeds that UUID's maximum
|
||||
- **THEN** construction fails before a worker or transaction is created
|
||||
|
||||
#### Scenario: Minimum equals maximum
|
||||
- **WHEN** a strictly positive minimum is numerically equal to its matched maximum
|
||||
- **THEN** that amount configuration is accepted without requiring equal decimal scales
|
||||
|
||||
#### Scenario: Direct EVE only configuration
|
||||
- **WHEN** all three configuration lists are empty
|
||||
- **THEN** construction succeeds and direct EVE remains eligible for burning
|
||||
|
||||
### Requirement: Fixed Evelyn domain values and internally resolved integration values
|
||||
The service SHALL fix the EVE mint as `meveYG2iXYSkgSUn1T1uxcthH1EGMZdRHGgCntXZA3Y`, construct the EVE/USDT notification-price pair from canonical CIS resolutions of the stable EVE and USDT UUIDs, and use fixed notification wording. Native SOL SHALL be identified only by the stable SOL UUID; when sent to Jupiter, its input mint SHALL be the canonical CIS Solana-mint reference `So11111111111111111111111111111111111111112`. Mint addresses, token programs, price pairs, notification wording, and a separate SOL reserve SHALL NOT be constructor configuration.
|
||||
|
||||
@@ -215,14 +231,14 @@ The EVE amount SHALL use plain decimal notation without unnecessary trailing zer
|
||||
- **THEN** the service logs a non-secret failure, clears completed state, and never retries either notification or burn
|
||||
|
||||
### Requirement: Exact token-capped amount and per-currency reserve
|
||||
The service SHALL select the lesser of positive `balance - minimumReserve` and the input UUID's maximum token amount, then round downward to mint precision. It SHALL skip non-positive results and SHALL never exceed either bound through rounding. Input selection SHALL require no input/USDC Ticker observation. Maxima SHALL limit buybacks only; EVE burns SHALL remain uncapped. Native SOL reserves SHALL use observed balance without estimating future fees.
|
||||
The service SHALL select the lesser of positive `balance - minimumReserve` and the input UUID's maximum token amount, then round downward to mint precision. It SHALL skip non-positive results and actual rounded amounts below that UUID's minimum, continuing to later inputs in the same iteration without counting a swap attempt. Equality SHALL qualify. It SHALL never round upward, top up from reserves or exceed either existing bound. Input selection SHALL require no input/USDC Ticker observation. Minima and maxima SHALL constrain buyback inputs only; EVE burns SHALL remain uncapped and unaffected. Native SOL reserves SHALL use observed balance without estimating future fees.
|
||||
|
||||
#### Scenario: SyrupUSDC maximum is lower than spendable balance
|
||||
- **WHEN** SyrupUSDC balance is 0.5, its reserve is 0.095, and its maximum is 0.2
|
||||
- **WHEN** SyrupUSDC balance is 0.5, its reserve is 0.095, its minimum is 0.045 and its maximum is 0.2
|
||||
- **THEN** 0.2 SyrupUSDC is selected without requesting a SyrupUSDC/USDC observation
|
||||
|
||||
#### Scenario: SyrupUSDC spendable balance is lower than the maximum
|
||||
- **WHEN** SyrupUSDC balance is 0.245, its reserve is 0.095, and its maximum is 0.2
|
||||
- **WHEN** SyrupUSDC balance is 0.245, its reserve is 0.095, its minimum is 0.045 and its maximum is 0.2
|
||||
- **THEN** 0.150 SyrupUSDC is selected before downward mint-precision rounding
|
||||
|
||||
#### Scenario: Reserve consumes the balance
|
||||
@@ -230,23 +246,43 @@ The service SHALL select the lesser of positive `balance - minimumReserve` and t
|
||||
- **THEN** no amount of that currency is intentionally swapped
|
||||
|
||||
#### Scenario: USDT maximum preserves the production bound
|
||||
- **WHEN** USDT balance is 0.35, its reserve is 0.1, and its maximum is 0.25
|
||||
- **WHEN** USDT balance is 0.35, its reserve is 0.1, its minimum is 0.05 and its maximum is 0.25
|
||||
- **THEN** 0.25 USDT is selected before downward mint-precision rounding
|
||||
|
||||
#### Scenario: Precision rounding never increases the spend
|
||||
- **WHEN** the lesser of spendable balance and maximum is 0.1509999 and the mint supports six decimals
|
||||
- **WHEN** the lesser of spendable balance and maximum is 0.1509999, the mint supports six decimals and its minimum is no greater than 0.150999
|
||||
- **THEN** the service selects 0.150999, and if downward rounding instead produces zero the input is skipped
|
||||
|
||||
#### Scenario: Native transaction fees
|
||||
- **WHEN** native SOL is selected with a configured reserve
|
||||
- **THEN** the service enforces the reserve against the observed balance only and does not estimate fees, even though later fees may reduce the final balance or cause execution to fail
|
||||
|
||||
#### Scenario: USDT below and at its minimum
|
||||
- **WHEN** USDT reserve is 0.1 and minimum is 0.05
|
||||
- **THEN** balances 0.14 and 0.149999 are skipped, while balance 0.15 selects exactly 0.05
|
||||
|
||||
#### Scenario: Tiny first input permits useful later input
|
||||
- **WHEN** USDT balance is 0.100001 with reserve 0.1/minimum 0.05, and SyrupUSDC balance is 0.502048 with reserve 0.095/minimum 0.045/maximum 0.2
|
||||
- **THEN** the USDT remainder is skipped and 0.2 SyrupUSDC is the only attempted swap
|
||||
|
||||
#### Scenario: SyrupUSDC minimum boundary
|
||||
- **WHEN** SyrupUSDC reserve is 0.095 and minimum is 0.045
|
||||
- **THEN** balance 0.14 selects exactly 0.045 while 0.139999 is skipped
|
||||
|
||||
#### Scenario: Actual rounded candidate falls below a fractional minimum
|
||||
- **WHEN** six-decimal input precision rounds candidate 0.0500009 down to 0.050000 and its minimum is 0.0500001
|
||||
- **THEN** the candidate is skipped without rounding up or using reserves
|
||||
|
||||
#### Scenario: Input minima do not suppress direct EVE burn
|
||||
- **WHEN** every configured input is below its minimum but the wallet contains EVE with a usable EVE/USDT price
|
||||
- **THEN** the complete EVE balance is still evaluated and burned by the existing rules
|
||||
|
||||
### Requirement: Production burner accepts USDT and SyrupUSDC
|
||||
The production Hub SHALL configure USDT first with reserve 0.1 and maximum input 0.25 USDT, and SyrupUSDC second with reserve 0.095 and maximum input 0.2 SyrupUSDC. These SHALL be token-denominated limits without a USD-value guarantee. The existing burner wallet, Jupiter route selection, registration, hourly interval, slippage, EVE/USDT freshness, confirmation settings and burn recovery behavior SHALL be preserved without new Ticker sources or history storage.
|
||||
The production Hub SHALL configure USDT first with reserve 0.1, minimum input 0.05 and maximum input 0.25 USDT, and SyrupUSDC second with reserve 0.095, minimum input 0.045 and maximum input 0.2 SyrupUSDC. These SHALL be token-denominated limits without a USD-value guarantee. The 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 two exact reserves and maxima as MoneyAmount values in USDT-then-SyrupUSDC priority and retains the other existing production arguments
|
||||
- **THEN** it supplies the exact reserves, minima and maxima as MoneyAmount values in USDT-then-SyrupUSDC priority and retains the other existing production arguments
|
||||
|
||||
### Requirement: Pending swaps reconcile before new input selection
|
||||
After pending-burn priority, a retained swap SHALL be checked read-only by its original signature at CONFIRMED or stronger in a bounded operation. Success, on-chain failure or demonstrated non-executed expiry SHALL clear it and permit newly read balances to drive at most one new swap. Unknown, absent without expiry proof or I/O outcomes SHALL remain pending without a new swap while allowing existing EVE burns. Interruption SHALL propagate with pending state intact.
|
||||
|
||||
Reference in New Issue
Block a user