22: Investigate and expose borrowFeeUsd in JupiterPerpsPosition

This commit is contained in:
2026-08-05 10:12:38 +02:00
parent f6afbd322b
commit 27c161bb6b
8 changed files with 123 additions and 36 deletions
@@ -31,8 +31,8 @@ public record JupiterPerpsPosition(
ΩJupiterPerpsPositionAccountΩ positionAccount,
ΩSPLMintAddressΩ tradedTokenMint,
JupiterPerpsPositionDirection direction,
ΩUSDCAmountΩ value,
ΩUSDCAmountΩ size,
ΩUSDCAmountΩ positionValue,
ΩUSDCAmountΩ positionSize,
ΩUSDCAmountΩ pnl,
BigDecimal pnlPercent,
BigDecimal leverage,
@@ -17,7 +17,7 @@ class AnchorIdlJupiterPerpsCustodyDecoder {
ΩJupiterPerpsPostionIdlΩ idl = decodeIdl(encodedCustodyAccountInfo);
JupiterCustodyAccountInfo cai = new JupiterCustodyAccountInfo(
readPublicKey(idl, MINT_OFFSET),
decodePublicKey(idl, MINT_OFFSET),
decodeCumulativeInterestRate(idl)
);
@@ -35,15 +35,15 @@ class AnchorIdlJupiterPerpsCustodyDecoder {
return idl;
}
private BigInteger decodeCumulativeInterestRate(byte[] data) {
byte[] bytes = readU128(data, CUMULATIVE_INTEREST_RATE_OFFSET);
BigInteger cumulativeInterestRate = new BigInteger(1, bytes);
private @NotNull ΩJupiterCumulativeInterestRateΩ decodeCumulativeInterestRate(ΩJupiterPerpsCustodyAccountIdlΩ idl) {
byte[] bytes = readU128(idl, CUMULATIVE_INTEREST_RATE_OFFSET);
ΩJupiterCumulativeInterestRateΩ cumulativeInterestRate = new ΩJupiterCumulativeInterestRateΩ(1, bytes);
return cumulativeInterestRate;
}
// Offsets:
// 8 discriminator
// 8 discriminator
// +32 pool
// +32 mint
// +32 token_account
@@ -55,8 +55,8 @@ class AnchorIdlJupiterPerpsCustodyDecoder {
// +8 target_ratio_bps
// +48 assets
private static final int ANCHOR_DISCRIMINATOR_LENGTH = 8;
private static final int MINT_OFFSET = ANCHOR_DISCRIMINATOR_LENGTH + PUBLIC_KEY_LENGTH;
private static final int DISCRIMINATOR_LENGTH = 8;
private static final int MINT_OFFSET = DISCRIMINATOR_LENGTH + PUBLIC_KEY_LENGTH;
private static final int FUNDING_RATE_STATE_OFFSET = 262;
private static final int CUMULATIVE_INTEREST_RATE_OFFSET = FUNDING_RATE_STATE_OFFSET;
@@ -6,6 +6,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Base64;
@@ -21,47 +22,63 @@ class AnchorIdlJupiterPerpsPositionDecoder {
JupiterPerpsPositionDirection direction = decodeDirection(idl);
long rawEntryPrice = ByteBuffer
ΩjupiterRawEntryPriceUsdΩ rawEntryPrice = ByteBuffer
.wrap(idl, PRICE_OFFSET, U64_LENGTH)
.order(ByteOrder.LITTLE_ENDIAN)
.getLong();
ΩUSDCPriceΩ entryPrice = BigDecimal
ΩUSDCPriceΩ entryPrice = ΩUSDCPriceΩ
.valueOf(rawEntryPrice)
.movePointLeft(6);
long rawCollateralUsd = ByteBuffer
ΩjupiterRawCollateralUsdΩ rawCollateralUsd = ByteBuffer
.wrap(idl, COLLATERAL_USD_OFFSET, U64_LENGTH)
.order(ByteOrder.LITTLE_ENDIAN)
.getLong();
ΩUSDCAmountΩ collateralUsd = BigDecimal
ΩUSDCAmountΩ collateral = ΩUSDCAmountΩ
.valueOf(rawCollateralUsd)
.movePointLeft(6);
long rawSizeUsd = ByteBuffer
ΩjupiterRawSizeUsdΩ rawSizeUsd = ByteBuffer
.wrap(idl, SIZE_USD_OFFSET, U64_LENGTH)
.order(ByteOrder.LITTLE_ENDIAN)
.getLong();
ΩUSDCAmountΩ sizeUsd = BigDecimal
ΩUSDCAmountΩ positionSize = ΩUSDCAmountΩ
.valueOf(rawSizeUsd)
.movePointLeft(6);
ΩJupiterCustodyAccountAddressΩ custodyAccountAddress = readPublicKey(idl, CUSTODY_OFFSET);
ΩJupiterPerpsCustodyAccountAddressΩ custodyAccountAddress =
decodePublicKey(idl, CUSTODY_OFFSET);
ΩJupiterPerpsCollateralCustodyAccountAddressΩ collateralCustodyAccountAddress =
decodePublicKey(idl, COLLATERAL_CUSTODY_OFFSET);
ΩJupiterCumulativeInterestSnapshotΩ cumulativeInterestSnapshot = decodeCumulativeInterestSnapshot(idl);
JupiterPerpsPositionInfo posInfo = new JupiterPerpsPositionInfo(
entryPrice,
direction,
sizeUsd,
collateralUsd,
custodyAccountAddress
positionSize,
collateral,
custodyAccountAddress,
collateralCustodyAccountAddress,
cumulativeInterestSnapshot
);
return posInfo;
}
ΩJupiterCustodyAccountAddressΩ decodeCustodyAccountAddress(SolanaAccountInfo accountInfo) {
private @NotNull ΩJupiterCumulativeInterestSnapshotΩ decodeCumulativeInterestSnapshot(ΩJupiterPerpsPostionIdlΩ idl) {
byte[] bytes = readU128(idl, CUMULATIVE_INTEREST_SNAPSHOT_OFFSET);
ΩJupiterCumulativeInterestSnapshotΩ cumulativeInterestSnapshot
= new ΩJupiterCumulativeInterestSnapshotΩ(1, bytes);
return cumulativeInterestSnapshot;
}
ΩJupiterPerpsCustodyAccountAddressΩ decodeCustodyAccountAddress(SolanaAccountInfo accountInfo) {
byte[] data = base64.decode(accountInfo.data());
if (data.length < CUSTODY_OFFSET + PUBLIC_KEY_LENGTH) {
@@ -69,7 +86,7 @@ class AnchorIdlJupiterPerpsPositionDecoder {
throw new IllegalArgumentException(errorMsg);
}
ΩJupiterCustodyAccountAddressΩ addr = readPublicKey(data, CUSTODY_OFFSET);
ΩJupiterPerpsCustodyAccountAddressΩ addr = decodePublicKey(data, CUSTODY_OFFSET);
return addr;
}
@@ -117,6 +134,8 @@ class AnchorIdlJupiterPerpsPositionDecoder {
private static final int PRICE_OFFSET = SIDE_OFFSET + SIDE_ENUM_LENGTH;
private static final int SIZE_USD_OFFSET = PRICE_OFFSET + U64_LENGTH;
private static final int COLLATERAL_USD_OFFSET = SIZE_USD_OFFSET + U64_LENGTH;
private static final int REALISED_PNL_USD_OFFSET = COLLATERAL_USD_OFFSET + U64_LENGTH;
private static final int CUMULATIVE_INTEREST_SNAPSHOT_OFFSET = REALISED_PNL_USD_OFFSET + I64_LENGTH;
private static final Base64.Decoder base64 = Base64.getDecoder();
}
@@ -12,6 +12,8 @@ import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.HashSet;
import java.util.Set;
@@ -48,7 +50,7 @@ public class AnchorIdlJupiterPerpsServiceImpl implements JupiterPerpsService {
if(perpsPositionInfo == null) {
pos = null;
} else {
ΩJupiterCustodyAccountAddressΩ custodyAccountAddress = positionDecoder.decodeCustodyAccountAddress(accountInfo);
ΩJupiterPerpsCustodyAccountAddressΩ custodyAccountAddress = positionDecoder.decodeCustodyAccountAddress(accountInfo);
ΩJupiterCustodyAccountInfoEncodedΩ encodedCustodyAccountInfo = solanaBlockChain.getAccountInfo(custodyAccountAddress);
if (encodedCustodyAccountInfo == null) {
@@ -59,13 +61,31 @@ public class AnchorIdlJupiterPerpsServiceImpl implements JupiterPerpsService {
JupiterCustodyAccountInfo custodyAccountInfo = custodyDecoder.decode(encodedCustodyAccountInfo);
ΩSPLMintAddressΩ mintAddress = custodyAccountInfo.mintAddress();
ΩJupiterPerpsCollateralCustodyAccountAddressΩ collateralCustodyAccountAddress =
perpsPositionInfo.collateralCustodyAccountAddress();
ΩJupiterCustodyAccountInfoEncodedΩ encodedCollateralCustodyAccountInfo =
solanaBlockChain.getAccountInfo(collateralCustodyAccountAddress);
JupiterCustodyAccountInfo collateralCustodyAccountInfo =
custodyDecoder.decode(encodedCollateralCustodyAccountInfo);
ΩJupiterPostionInterestΩ positionInterest =
collateralCustodyAccountInfo.currentCumulativeInterestRate()
.subtract(perpsPositionInfo.cumulativeInterestSnapshot());
ΩUSDCAmountΩ borrowFeesDue =
calculateBorrowFeesDue(
positionInterest,
perpsPositionInfo.positionSize()
);
ΩUSDCAmountΩ value = ZERO; // TODO - Dummy
ΩUSDCAmountΩ pnl = ZERO; // TODO - Dummy
BigDecimal pnlPercent = ZERO; // TODO - Dummy
BigDecimal leverage = ZERO; // TODO - Dummy
ΩUSDCPriceΩ marketPrice = ZERO; // TODO - Dummy
ΩUSDCAmountΩ totalFees = ZERO; // TODO - Dummy
ΩUSDCAmountΩ borrowFeesDue = ZERO; // TODO - Dummy
ΩUSDCAmountΩ closeFeePending = ZERO; // TODO - Dummy
ΩSolanaAmountΩ accountRent = new MoneyAmount(ZERO, WellKnownCurrencyTypes.SOLANA.getCurrencyType()); // TODO - Dummy
@@ -74,7 +94,7 @@ public class AnchorIdlJupiterPerpsServiceImpl implements JupiterPerpsService {
mintAddress,
perpsPositionInfo.direction(),
value,
perpsPositionInfo.size(),
perpsPositionInfo.positionSize(),
pnl,
pnlPercent,
leverage,
@@ -115,12 +135,13 @@ public class AnchorIdlJupiterPerpsServiceImpl implements JupiterPerpsService {
}
JupiterPerpsPositionInfo perpsPositionInfo = positionDecoder.decode(accountInfo);
if(perpsPositionInfo == null || perpsPositionInfo.size().compareTo(ZERO) == 0) {
if(perpsPositionInfo == null || perpsPositionInfo.positionSize().compareTo(ZERO) == 0) {
continue;
}
ΩJupiterCustodyAccountAddressΩ custodyAccountAddress = perpsPositionInfo.custodyAccountAddress();
ΩJupiterCustodyAccountInfoEncodedΩ encodedCustodyAccountInfo = solanaBlockChain.getAccountInfo(custodyAccountAddress);
ΩJupiterPerpsCustodyAccountAddressΩ custodyAccountAddress = perpsPositionInfo.custodyAccountAddress();
ΩJupiterCustodyAccountInfoEncodedΩ encodedCustodyAccountInfo
= solanaBlockChain.getAccountInfo(custodyAccountAddress);
if (encodedCustodyAccountInfo == null) {
String errorMsg = "Jupiter Perps custody account does not exist: " + custodyAccountAddress;
@@ -130,13 +151,31 @@ public class AnchorIdlJupiterPerpsServiceImpl implements JupiterPerpsService {
JupiterCustodyAccountInfo custodyAccountInfo = custodyDecoder.decode(encodedCustodyAccountInfo);
ΩSPLMintAddressΩ mintAddress = custodyAccountInfo.mintAddress();
ΩJupiterPerpsCollateralCustodyAccountAddressΩ collateralCustodyAccountAddress =
perpsPositionInfo.collateralCustodyAccountAddress();
ΩJupiterCustodyAccountInfoEncodedΩ encodedCollateralCustodyAccountInfo =
solanaBlockChain.getAccountInfo(collateralCustodyAccountAddress);
JupiterCustodyAccountInfo collateralCustodyAccountInfo =
custodyDecoder.decode(encodedCollateralCustodyAccountInfo);
ΩJupiterPostionInterestΩ positionInterest =
collateralCustodyAccountInfo.currentCumulativeInterestRate()
.subtract(perpsPositionInfo.cumulativeInterestSnapshot());
ΩUSDCAmountΩ borrowFeesDue =
calculateBorrowFeesDue(
positionInterest,
perpsPositionInfo.positionSize()
);
ΩUSDCAmountΩ value = ZERO; // TODO - Dummy
ΩUSDCAmountΩ pnl = ZERO; // TODO - Dummy
BigDecimal pnlPercent = ZERO; // TODO - Dummy
BigDecimal leverage = ZERO; // TODO - Dummy
ΩUSDCPriceΩ marketPrice = ZERO; // TODO - Dummy
ΩUSDCAmountΩ totalFees = ZERO; // TODO - Dummy
ΩUSDCAmountΩ borrowFeesDue = ZERO; // TODO - Dummy
ΩUSDCAmountΩ closeFeePending = ZERO; // TODO - Dummy
ΩSolanaAmountΩ accountRent = new MoneyAmount(ZERO, WellKnownCurrencyTypes.SOLANA.getCurrencyType()); // TODO - Dummy
@@ -145,7 +184,7 @@ public class AnchorIdlJupiterPerpsServiceImpl implements JupiterPerpsService {
mintAddress,
perpsPositionInfo.direction(),
value,
perpsPositionInfo.size(),
perpsPositionInfo.positionSize(),
pnl,
pnlPercent,
leverage,
@@ -164,6 +203,21 @@ public class AnchorIdlJupiterPerpsServiceImpl implements JupiterPerpsService {
return Set.copyOf(positions);
}
private static ΩUSDCAmountΩ calculateBorrowFeesDue(
ΩJupiterPostionInterestΩ positionInterest,
ΩUSDCAmountΩ size
) {
ΩUSDCAmountΩ fees = new BigDecimal(positionInterest)
.multiply(size)
.divide(
BigDecimal.valueOf(RATE_POWER),
6,
RoundingMode.CEILING
);
return fees;
}
private static final long RATE_POWER = 1_000_000_000L;
private static final ΩJupiterPerpsProgramIdΩ JUPITER_PERPS_PROGRAM_ID =
"PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu";
private static final int POSITION_OWNER_OFFSET = 8;
@@ -2,6 +2,9 @@ package com.r35157.libs.jupiter.perps.impl.anchoridl;
import com.r35157.libs.codec.Base58Codec;
import com.r35157.libs.codec.impl.ref.Base58CodecImpl;
import org.jetbrains.annotations.NotNull;
import java.math.BigInteger;
public class DecodingPrimitives {
@@ -15,7 +18,7 @@ public class DecodingPrimitives {
return bytes;
}
public static ΩSolanaAddressΩ readPublicKey(byte[] data, int offset) {
public static @NotNull ΩSolanaAddressΩ decodePublicKey(ΩJupiterPerpsPostionIdlΩ data, int offset) {
byte[] bytes = new byte[PUBLIC_KEY_LENGTH];
System.arraycopy(
@@ -4,6 +4,6 @@ import java.math.BigInteger;
public record JupiterCustodyAccountInfo(
ΩSPLMintAddressΩ mintAddress,
BigInteger currentCumulativeInterestRate
ΩJupiterCumulativeInterestRateΩ currentCumulativeInterestRate
) {
}
@@ -3,12 +3,15 @@ package com.r35157.libs.jupiter.perps.impl.anchoridl;
import com.r35157.libs.jupiter.perps.JupiterPerpsPositionDirection;
import java.math.BigDecimal;
import java.math.BigInteger;
public record JupiterPerpsPositionInfo(
ΩUSDCPriceΩ entryPrice,
JupiterPerpsPositionDirection direction,
ΩUSDCAmountΩ size,
ΩUSDCAmountΩ positionSize,
ΩUSDCAmountΩ collateral,
ΩJupiterCustodyAccountAddressΩ custodyAccountAddress
ΩJupiterPerpsCustodyAccountAddressΩ custodyAccountAddress,
ΩJupiterPerpsCollateralCustodyAccountAddressΩ collateralCustodyAccountAddress,
ΩJupiterCumulativeInterestSnapshotΩ cumulativeInterestSnapshot
) {
}
@@ -15,6 +15,8 @@ import java.lang.management.ManagementFactory;
import com.r35157.nenjim.hubd.ctx.ContextManager;
import com.r35157.nenjim.hubd.journal.JournalManager;
import com.r35157.nenjim.hubd.impl.ref.JournalManagerImpl;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Set;
@@ -28,10 +30,16 @@ public class Main {
SolanaBlockChain sbc = new SolanaBlockChainImpl();
JupiterPerpsService jupiter = new AnchorIdlJupiterPerpsServiceImpl(sbc);
ΩSolanaWalletIdΩ walletId = "vj98roDZ7744EBfxyuDFkKpEGCsKQLr7K8UFRumJNHf";
Set<JupiterPerpsPosition> positions = jupiter.getOpenPositions(walletId);
int a=0;
//Set<JupiterPerpsPosition> positions = jupiter.getOpenPositions(walletId);
nenjimHub.awaitShutdown();
while(true) {
String addr = "2z2oExdggoRPCcojntHrGbRiDWeJgVSRbasfMbPfprQi";
JupiterPerpsPosition pos = jupiter.getPosition(addr);
ΩUSDCAmountΩ a = pos.borrowFeesDue();
int i = 0;
}
// nenjimHub.awaitShutdown();
/* try {
log.info("Auto-starting 2 Nenjim application(s)...");