41: Skip Jupiter Perps position increase when USDC balance is insufficient

This commit is contained in:
2026-08-05 10:12:39 +02:00
parent 5d940d37cc
commit 3ba92bae4b
4 changed files with 78 additions and 3 deletions
@@ -1,8 +1,8 @@
# Wallet and signer used for position-increase transactions
################################################################
WALLET_ID {{JUPITER_PERPS_WALLET}}
SIGNER_KEY_NAME <SIGNER>
WALLET_ID {{JUPITER_PERPS_WALLET}}
SIGNER_KEY_NAME <SIGNER>
RESERVE_USDC_LIMIT 10
# Id Asset Direction CollateralUSDC SizeDeltaUSD MaxSlippageBPS
############################################################################
@@ -6,11 +6,15 @@ import com.r35157.libs.jupiter.perps.JupiterPerpsService;
import com.r35157.libs.solana.SolanaBlockChain;
import com.r35157.libs.solana.SolanaSignedTransaction;
import com.r35157.libs.solana.SolanaUnsignedTransaction;
import com.r35157.libs.valuetypes.basic.MoneyAmount;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Path;
import java.util.Objects;
import static com.r35157.libs.solana.valuetypes.economic.SolanaSPLTokenProgram.SPL_TOKEN_PROGRAM;
public final class JupiterPerpsPositionIncreaseAlarmAction implements ConfiguredAlarmAction {
public JupiterPerpsPositionIncreaseAlarmAction(
Path configurationDirectory,
@@ -51,6 +55,10 @@ public final class JupiterPerpsPositionIncreaseAlarmAction implements Configured
JupiterPerpsPositionIncreaseAlarmActionConfiguration.ResolvedPositionIncrease
positionIncrease = definition.resolve(variableResolver);
if (!isUSDCBalanceOk(positionIncrease)) {
return;
}
SolanaUnsignedTransaction utrans =
jupiter.buildPositionIncreaseTransaction(
wallet.getAddress(),
@@ -60,6 +68,7 @@ public final class JupiterPerpsPositionIncreaseAlarmAction implements Configured
positionIncrease.sizeUsdDelta(),
positionIncrease.maxSlippageBps()
);
SolanaSignedTransaction strans = wallet.signTransaction(utrans);
ΩSolanaTransactionSignatureΩ signature = jupiter.executePositionIncreaseTransaction(strans);
System.out.println("Position Increase Signature: " + signature);
@@ -68,6 +77,45 @@ public final class JupiterPerpsPositionIncreaseAlarmAction implements Configured
}
}
private boolean isUSDCBalanceOk(JupiterPerpsPositionIncreaseAlarmActionConfiguration.ResolvedPositionIncrease positionIncrease)
throws IOException, InterruptedException {
ΩAmountΩ usdcBalance = wallet.getSPLTokenBalance(
JupiterPerpsReceiveToken.USDC.receiveTokenMint(),
SPL_TOKEN_PROGRAM
);
if (usdcBalance == null) {
System.err.println("Position Increase of "
+ positionIncrease.collateralUsd()
+ " USDC skipped: Wallet has no USDC token account!"
);
return false;
}
ΩUSDCAmountΩ collateralUsd = positionIncrease.collateralUsd();
ΩUSDCAmountΩ reserveUsdcLimit = configuration.reserveUsdcLimit();
ΩUSDCAmountΩ requiredUsdcBalance = collateralUsd.add(reserveUsdcLimit);
if (usdcBalance.compareTo(requiredUsdcBalance) < 0) {
System.err.println(
"Position Increase of "
+ positionIncrease.collateralUsd()
+ " USDC skipped: Wallet has "
+ usdcBalance
+ " USDC, but "
+ requiredUsdcBalance
+ " USDC is required ("
+ collateralUsd
+ " collateral + "
+ reserveUsdcLimit
+ " reserve)."
);
return false;
}
return true;
}
private final JupiterPerpsPositionIncreaseAlarmActionConfiguration configuration;
private final SolanaWallet wallet;
private final JupiterPerpsService jupiter;
@@ -10,11 +10,13 @@ import java.util.Objects;
public record JupiterPerpsPositionIncreaseAlarmActionConfiguration(
ΩSolanaWalletIdΩ walletId,
String signerKeyName,
ΩUSDCAmountΩ reserveUsdcLimit,
Map<Integer, PositionIncrease> positionIncreases
) {
public JupiterPerpsPositionIncreaseAlarmActionConfiguration {
Objects.requireNonNull(walletId, "walletId");
Objects.requireNonNull(signerKeyName, "signerKeyName");
Objects.requireNonNull(reserveUsdcLimit, "reserveUsdcLimit");
positionIncreases = Map.copyOf(positionIncreases);
if (walletId.isBlank()) {
@@ -23,6 +25,11 @@ public record JupiterPerpsPositionIncreaseAlarmActionConfiguration(
if (signerKeyName.isBlank()) {
throw new IllegalArgumentException("Signer key name cannot be blank");
}
if (reserveUsdcLimit.signum() < 0) {
throw new IllegalArgumentException(
"USDC reserve limit cannot be negative: " + reserveUsdcLimit
);
}
}
public record PositionIncrease(
@@ -1,6 +1,7 @@
package com.r35157.jupiterperpsalarm.impl.ref;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedHashMap;
@@ -20,6 +21,7 @@ public final class JupiterPerpsPositionIncreaseAlarmActionConfigurationParser {
positionIncreases = new LinkedHashMap<>();
ΩSolanaWalletIdΩ walletId = null;
String signerKeyName = null;
ΩUSDCAmountΩ reserveUsdcLimit = null;
for (int lineNumber = 1; lineNumber <= lines.size(); lineNumber++) {
String line = removeComment(lines.get(lineNumber - 1)).trim();
@@ -43,6 +45,16 @@ public final class JupiterPerpsPositionIncreaseAlarmActionConfigurationParser {
throw new IllegalArgumentException("Duplicate SIGNER_KEY_NAME");
}
signerKeyName = variableResolver.resolve(columns[1]);
} else if (firstColumn.equals("RESERVE_USDC_LIMIT")) {
requireColumnCount(columns, 2, "RESERVE_USDC_LIMIT value");
if (reserveUsdcLimit != null) {
throw new IllegalArgumentException(
"Duplicate RESERVE_USDC_LIMIT"
);
}
reserveUsdcLimit = new ΩUSDCAmountΩ(
variableResolver.resolve(columns[1])
);
} else {
parsePositionIncrease(
columns,
@@ -61,13 +73,21 @@ public final class JupiterPerpsPositionIncreaseAlarmActionConfigurationParser {
if (walletId == null) {
throw new IllegalArgumentException("Missing WALLET_ID in " + path);
}
if (signerKeyName == null) {
throw new IllegalArgumentException("Missing SIGNER_KEY_NAME in " + path);
}
if (reserveUsdcLimit == null) {
throw new IllegalArgumentException(
"Missing RESERVE_USDC_LIMIT in " + path
);
}
return new JupiterPerpsPositionIncreaseAlarmActionConfiguration(
walletId,
signerKeyName,
reserveUsdcLimit,
positionIncreases
);
}