X: Clean-up and documentation updates

This commit is contained in:
2026-08-14 13:50:10 +02:00
parent 3d5de0e09b
commit f5a2183a40
24 changed files with 515 additions and 541 deletions
+135 -48
View File
@@ -4,28 +4,39 @@ The Jupiter Perps Price Alarm monitors the Jupiter Perps aggregated oracle accou
Oracle prices are received through Solana WebSocket `accountSubscribe` using `processed` commitment. The application does not poll prices once per second. It also fetches the current oracle account state whenever a WebSocket connection opens, reconnects automatically, and supports multiple RPC endpoints for redundancy.
The alarm is currently initialized and started by `NenjimHubImpl`. Its
`SolanaWallet` and `JupiterPerpsService` are selected by the Hub and injected
before startup. The alarm action configuration files do not select a wallet or
signing key.
> **Important:** This application is not necessarily notification-only. Every active entry in a Jupiter Perps position action file can sign and submit a real transaction. Review both transaction action files before running it.
## Startup flow
The application performs the following steps before it starts monitoring prices:
The alarm performs the following steps before it starts monitoring prices:
1. Parse command-line options and the alarm configuration file.
2. Fetch the wallet's current open Jupiter Perps positions.
3. Populate entry-price and liquidation-price variables.
1. Parse the configured alarm path, WebSocket endpoint environment and
`alarms.conf`.
2. Parse the position-increase and position-decrease action files, including
their structure and reserve settings.
3. Fetch the injected wallet's current open Jupiter Perps positions and populate
entry-price and liquidation-price variables.
4. Resolve and validate every alarm condition.
5. Start the periodic variable refresher and file-trigger watcher.
6. Parse and validate the four action configuration files.
6. Parse the console and Pushover action files and construct all alarm actions.
7. Open one oracle WebSocket connection per configured asset and RPC endpoint.
Startup fails before WebSockets are opened if an alarm condition or action configuration contains an unresolved variable or invalid value.
Startup fails before WebSockets are opened if an alarm condition or an action
value that is resolved during startup contains an unavailable variable or
invalid value. Jupiter transaction-row expressions are resolved and validated
when their alarm triggers.
## Requirements and build
The project uses:
- JDK 25
- Gradle 9.3.1 through the Gradle wrapper
- Gradle 9.6.1 through the Gradle wrapper
- Java preview features
- Detag-generated Java sources from `src/main/tjava`
@@ -43,35 +54,45 @@ To create the application JAR and collect runtime dependencies in `build/libs`:
There is currently no alarm-specific automated self-test task.
## Running the alarm application
## Running through Nenjim
The alarm entry point is:
The alarm no longer has a standalone `main` method. `NenjimHubImpl` constructs
the wallet, Jupiter service and alarm, then starts the alarm as part of normal
Hub startup. Run the configured application with:
```text
com.r35157.jupiterperpsalarm.impl.ref.JupiterPerpsAlarmImpl
```bash
./gradlew run
```
The Gradle application plugin currently points at the Nenjim Hub main class, so `./gradlew run` does **not** directly start the alarm application. Run `JupiterPerpsAlarmImpl` from the IDE, or run the prepared JAR and dependencies explicitly:
After `./gradlew prepareLibs`, it can also be started explicitly with the Nenjim
Hub main class:
```bash
java --enable-preview \
-cp "build/libs/*" \
com.r35157.jupiterperpsalarm.impl.ref.JupiterPerpsAlarmImpl \
--config=conf/alarms.conf
com.r35157.nenjim.hubd.impl.ref.Main
```
The repository contains example files for the alarm configuration and every action configuration. The real `conf/*.conf` files are ignored by Git; keep credentials and operational wallet details out of version control.
The current Hub composition binds the alarm to `conf/alarms.conf` and to the
test Jupiter Perps wallet and service. Until Nenjim provides configured
injection, a different configuration or wallet must be selected in the Hub's
composition code, not in an alarm action file.
### Command-line options and environment variables
The repository contains example files for the alarm configuration and every
action configuration. The real `conf/*.conf` files are ignored by Git; keep
credentials and operational details out of version control.
| Purpose | Command-line option | Environment variable | Default |
|---|---|---|---|
| Alarm configuration | `--config=<path>` | `PRICE_ALARMS_CONFIG` | `price-alarms.conf` |
| Solana WebSocket endpoints | `--ws=<url1,url2,...>` | `SOLANA_WS_URLS` | `wss://api.mainnet-beta.solana.com` |
### Environment variables
The command-line option takes precedence over its environment variable.
The Hub does not currently forward alarm-specific command-line options. Solana
WebSocket endpoints can be overridden with:
The variable-refresh file watcher uses the configuration file's parent directory. Until that is changed, use a configuration path with an explicit directory component, such as `conf/alarms.conf` or `./price-alarms.conf`, rather than relying on the bare default filename.
```bash
export SOLANA_WS_URLS='wss://first-provider.example,wss://second-provider.example'
```
If `SOLANA_WS_URLS` is absent, the alarm uses
`wss://api.mainnet-beta.solana.com`.
## Configuration files
@@ -79,11 +100,11 @@ The application uses five configuration files:
| File | Purpose |
|---|---|
| `alarms.conf` | Variables and price-alarm definitions |
| `alarms.conf` | Shared variables and price-alarm definitions |
| `alarmaction_Console.conf` | Alarm IDs written to the console |
| `alarmaction_Pushover.conf` | Pushover credentials and notifications |
| `alarmaction_JupiterPerpsPositionIncreaseAlarmAction.conf` | Jupiter wallet, signer, and position increases |
| `alarmaction_JupiterPerpsPositionDecreaseAlarmAction.conf` | Jupiter wallet, signer, and position decreases |
| `alarmaction_JupiterPerpsPositionIncreaseAlarmAction.conf` | USDC reserve and position increases |
| `alarmaction_JupiterPerpsPositionDecreaseAlarmAction.conf` | Position-size reserve and position decreases |
The four action files are resolved in the same directory as the file supplied through `--config`. All five files are required at startup, although an action can contain no alarm IDs. Blank lines and lines beginning with `#` are ignored.
@@ -97,12 +118,16 @@ Example files:
### Format version
Every configuration file must declare its format version as its first actual
configuration entry:
Every configuration file must declare its own format version as its first
actual configuration entry:
```text
FORMAT_VERSION=1
```
| File | Format version |
|---|---:|
| `alarms.conf` | `2` |
| `alarmaction_Console.conf` | `1` |
| `alarmaction_Pushover.conf` | `1` |
| `alarmaction_JupiterPerpsPositionIncreaseAlarmAction.conf` | `2` |
| `alarmaction_JupiterPerpsPositionDecreaseAlarmAction.conf` | `2` |
Blank lines and full-line comments may appear before it. The application
rejects a missing, duplicate, malformed, misplaced, older, or newer version
@@ -121,12 +146,15 @@ the project-wide policy.
Variable definitions live in `alarms.conf`. A definition consists of a name and a single-token value:
```text
{{JUPITER_PERPS_WALLET}} vj98roDZ7744EBfxyuDFkKpEGCsKQLr7K8UFRumJNHf
{{SOL_WARNING_PRICE}} 150.00
```
Variable names must use uppercase letters, digits, and underscores. User-defined variables can be referenced from alarm conditions and action configuration values as `{{NAME}}`.
`JUPITER_PERPS_WALLET` is required for the initial and periodic Jupiter position fetches. For open SOL, BTC, and ETH positions, the refresher supplies these dynamic variables:
The wallet address used for initial and periodic Jupiter position fetches comes
from the injected `SolanaWallet`; it is not a shared configuration variable.
For open SOL, BTC, and ETH positions belonging to that wallet, the refresher
supplies these dynamic variables:
```text
{{SOL_LONG_ENTRY_PRICE}} {{SOL_LONG_LIQ_PRICE}}
@@ -137,9 +165,17 @@ Variable names must use uppercase letters, digits, and underscores. User-defined
{{ETH_SHORT_ENTRY_PRICE}} {{ETH_SHORT_LIQ_PRICE}}
```
The refresher replaces user-supplied variables with these names when position data is fetched. Only variables for positions returned by Jupiter are populated. Startup validation fails if a condition or action value references a variable that is unavailable after the initial position fetch.
The refresher replaces user-supplied variables with these names when position
data is fetched. Only variables for positions returned by Jupiter are
populated. Startup validation fails if an alarm condition references a variable
that is unavailable after the initial position fetch. Transaction-row
expressions are resolved later, when their alarm triggers.
Alarm conditions, Pushover notes, and Jupiter position-increase rows are resolved again when an alarm triggers, so refreshed values take effect without restarting the application. Pushover credentials and severity plus the Jupiter wallet and signer are resolved during startup.
Alarm conditions, Pushover notes, and Jupiter position-increase and
position-decrease rows are resolved again when an alarm triggers, so refreshed
values take effect without restarting the application. Pushover credentials
and severity are resolved during startup. The wallet and signer belong to the
injected `SolanaWallet` and are not resolved from alarm configuration.
## Alarm definitions
@@ -362,13 +398,13 @@ Credentials, severity, and notes may reference shared variables. Credentials and
### Automatic position increase
`alarmaction_JupiterPerpsPositionIncreaseAlarmAction.conf` contains the wallet and signer followed by zero or more position increases:
`alarmaction_JupiterPerpsPositionIncreaseAlarmAction.conf` contains a minimum
USDC reserve followed by zero or more position increases:
```text
FORMAT_VERSION=1
FORMAT_VERSION=2
WALLET_ID {{JUPITER_PERPS_WALLET}}
SIGNER_KEY_NAME <jup-key-name>
RESERVE_USDC_LIMIT 10
# ID ASSET DIRECTION COLLATERAL_USDC SIZE_DELTA_USD MAX_SLIPPAGE_BPS
4 SOL LONG 0.25 2.50 200
@@ -382,11 +418,55 @@ Each active row can submit a real transaction when its alarm triggers. Supported
- `SIZE_DELTA_USD`: zero or greater.
- `MAX_SLIPPAGE_BPS`: from `0` to `10000`.
Wallet, signer, asset, direction, amounts, and slippage may reference shared variables. Wallet and signer are resolved during startup. Every transaction row is validated during startup and resolved again at trigger time, allowing refreshed variables to change the transaction parameters without a restart.
`RESERVE_USDC_LIMIT` is the amount of USDC that must remain after collateral is
deducted. An increase is skipped unless the injected wallet's USDC balance can
cover both the requested collateral and this reserve.
For a configured ID, the action builds a Jupiter position-increase transaction, signs it by running `jup sign -f json --key <SIGNER_KEY_NAME> --tx <serialized-transaction>`, verifies that the returned signer matches `WALLET_ID`, and submits the signed transaction. IDs absent from this file never perform a transaction.
Asset, direction, amounts, and slippage may reference shared variables. Every
transaction row is structurally parsed during startup, then resolved and
validated at trigger time. This allows refreshed variables to change the
transaction parameters without a restart.
The example transaction rows are commented out. Do not activate a row until its alarm condition, wallet, signer, asset, direction, amounts, and slippage have been reviewed.
For a configured ID, the injected `JupiterPerpsService` builds the position
increase, uses its injected wallet to sign it, submits it and awaits the
transaction confirmation. IDs absent from this file never perform a
transaction.
The example transaction rows are commented out. Do not activate a row until
its alarm condition, injected wallet binding, asset, direction, amounts,
reserve, and slippage have been reviewed.
### Automatic position decrease
`alarmaction_JupiterPerpsPositionDecreaseAlarmAction.conf` contains a minimum
remaining position-size reserve followed by zero or more position decreases:
```text
FORMAT_VERSION=2
RESERVE_POSITION_SIZE_USD_LIMIT 10
# ID ASSET DIRECTION RECEIVE_TOKEN SIZE_DELTA_USD MAX_SLIPPAGE_BPS
3 SOL SHORT USDC 5.00 200
```
Supported values are:
- `ASSET`: `SOL`, `ETH`, or `BTC`.
- `DIRECTION`: `LONG` or `SHORT`.
- `RECEIVE_TOKEN`: `USDC` or the token matching `ASSET`.
- `SIZE_DELTA_USD`: greater than zero.
- `MAX_SLIPPAGE_BPS`: from `0` to `10000`.
The action finds the injected wallet's single matching open position. It skips
the decrease if the resulting position size would be below
`RESERVE_POSITION_SIZE_USD_LIMIT`. Asset, direction, receive token, amount and
slippage may reference shared variables and are resolved again at trigger time.
For a configured ID, the injected `JupiterPerpsService` builds the position
decrease, uses its injected wallet to sign it, submits it and awaits the
transaction confirmation. IDs absent from this file never perform a
transaction.
## RPC redundancy and reconnect behavior
@@ -407,7 +487,11 @@ Each connection:
## Validation and errors
Structural configuration errors include the file path and line number. After the initial Jupiter position fetch, every condition is resolved and parsed. The action files are then parsed and validated before WebSockets are opened.
Structural configuration errors include the file path and line number. The two
Jupiter action files are parsed before the initial position fetch. After that
fetch, every alarm condition is resolved and parsed, and the console and
Pushover files are loaded before WebSockets are opened. Jupiter transaction-row
expressions are resolved and validated only when their alarm triggers.
Startup validation rejects, among other things:
@@ -418,12 +502,14 @@ Startup validation rejects, among other things:
- missing range targets;
- negative target values or percentages;
- ranges whose lower target exceeds the upper target;
- missing action credentials or wallet settings;
- missing action credentials or reserve settings;
- duplicate action IDs;
- invalid Pushover severities;
- invalid Jupiter assets, directions, amounts, or slippage.
- invalid Pushover severities.
At runtime, a later condition resolution/parsing failure skips that price event for the affected alarm. A later action-variable failure is logged by the action dispatcher, and the remaining actions are still attempted.
At runtime, a later condition resolution/parsing failure skips that price event
for the affected alarm. Invalid Jupiter assets, directions, receive tokens,
amounts, slippage, or unavailable action variables are logged by the action
dispatcher, and the remaining actions are still attempted.
## Current limitations
@@ -434,6 +520,7 @@ At runtime, a later condition resolution/parsing failure skips that price event
- Target expressions support only a decimal value with an optional single percentage adjustment; they are not a general expression language.
- Configuration files are read only during startup; editing an action file requires a restart.
- Action IDs are not cross-validated against `alarms.conf`; an unknown ID is accepted but never triggered.
- Pushover credentials and severity plus the Jupiter wallet and signer are resolved only during startup. Dynamic re-resolution applies to conditions, Pushover notes, and Jupiter transaction rows.
- A bare default configuration path has no parent directory for the refresh watcher; use an explicit path such as `./price-alarms.conf` or `conf/alarms.conf`.
- Pushover credentials and severity are resolved only during startup. Dynamic
re-resolution applies to conditions, Pushover notes, and Jupiter transaction
rows. Wallet and signer selection requires changing the Hub composition.
- This application is a monitoring and automation aid, not a substitute for independent risk controls.