13: Create initial Jupiter Perps position API
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
package com.r35157.libs.jupiter.perps;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Represents a Jupiter Perps position.
|
||||
*
|
||||
* <p>A Jupiter Perps position is represented on-chain by a Solana account owned by
|
||||
* the Jupiter Perps program. This record contains the public API view of such a
|
||||
* position.</p>
|
||||
*
|
||||
* <p>The initial version of this API only exposes the position account and the entry
|
||||
* price. More position fields may be added later as the Jupiter Perps API matures.</p>
|
||||
*
|
||||
* @param positionAccount the Solana account address of the Jupiter Perps position
|
||||
* @param entryPrice the entry price of the position, denominated in USDC
|
||||
*/
|
||||
public record JupiterPerpsPosition(
|
||||
ΩJupiterPerpsPositionAccountΩ positionAccount,
|
||||
ΩUSDCPriceΩ entryPrice
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.r35157.libs.jupiter.perps;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Service for reading Jupiter Perps positions.
|
||||
*
|
||||
* <p>This service is read-only. It does not open, close, modify, or sign transactions
|
||||
* for Jupiter Perps positions.</p>
|
||||
*
|
||||
* <p>The first supported operation is reading a known Jupiter Perps position account
|
||||
* and returning its decoded position data.</p>
|
||||
*/
|
||||
public interface JupiterPerpsPositionService {
|
||||
/**
|
||||
* Reads a Jupiter Perps position from a known position account.
|
||||
*
|
||||
* <p>The supplied account must be the Solana account that stores the Jupiter Perps
|
||||
* position state. It is not the wallet address, token account, custody account, pool
|
||||
* account, or position request account.</p>
|
||||
*
|
||||
* @param positionAccount the Solana account address of the Jupiter Perps position
|
||||
* @return the decoded Jupiter Perps position
|
||||
* @throws IOException if the position account could not be fetched or decoded
|
||||
* @throws InterruptedException if the calling thread is interrupted while fetching
|
||||
* the position account
|
||||
*/
|
||||
JupiterPerpsPosition getPosition(ΩJupiterPerpsPositionAccountΩ positionAccount)
|
||||
throws IOException, InterruptedException;
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package com.r35157.libs.jupiter.perps.impl.anchoridl;
|
||||
|
||||
import com.r35157.libs.jupiter.perps.JupiterPerpsPosition;
|
||||
import com.r35157.libs.solana.SolanaAccountInfo;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Base64;
|
||||
|
||||
class AnchorIdlJupiterPerpsPositionDecoder {
|
||||
|
||||
JupiterPerpsPosition decode(
|
||||
ΩJupiterPerpsPositionAccountΩ positionAccount,
|
||||
SolanaAccountInfo accountInfo
|
||||
) {
|
||||
byte[] data = Base64.getDecoder().decode(accountInfo.dataBase64());
|
||||
|
||||
if (data.length < PRICE_OFFSET + U64_LENGTH) {
|
||||
throw new IllegalArgumentException(
|
||||
"Jupiter Perps position account data is too short: " + data.length
|
||||
);
|
||||
}
|
||||
|
||||
long rawEntryPrice = ByteBuffer
|
||||
.wrap(data, PRICE_OFFSET, U64_LENGTH)
|
||||
.order(ByteOrder.LITTLE_ENDIAN)
|
||||
.getLong();
|
||||
|
||||
ΩUSDCPriceΩ entryPrice = BigDecimal
|
||||
.valueOf(rawEntryPrice)
|
||||
.movePointLeft(6);
|
||||
|
||||
JupiterPerpsPosition pos = new JupiterPerpsPosition(
|
||||
positionAccount,
|
||||
entryPrice
|
||||
);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
private static final int ANCHOR_DISCRIMINATOR_LENGTH = 8;
|
||||
private static final int PUBLIC_KEY_LENGTH = 32;
|
||||
private static final int I64_LENGTH = 8;
|
||||
private static final int U64_LENGTH = 8;
|
||||
private static final int SIDE_ENUM_LENGTH = 1;
|
||||
|
||||
private static final int PRICE_OFFSET =
|
||||
ANCHOR_DISCRIMINATOR_LENGTH
|
||||
+ PUBLIC_KEY_LENGTH // owner
|
||||
+ PUBLIC_KEY_LENGTH // pool
|
||||
+ PUBLIC_KEY_LENGTH // custody
|
||||
+ PUBLIC_KEY_LENGTH // collateralCustody
|
||||
+ I64_LENGTH // openTime
|
||||
+ I64_LENGTH // updateTime
|
||||
+ SIDE_ENUM_LENGTH; // side
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package com.r35157.libs.jupiter.perps.impl.anchoridl;
|
||||
|
||||
import com.r35157.libs.jupiter.perps.JupiterPerpsPosition;
|
||||
import com.r35157.libs.jupiter.perps.JupiterPerpsPositionService;
|
||||
import com.r35157.libs.solana.SolanaAccountInfo;
|
||||
import com.r35157.libs.solana.SolanaBlockChain;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class AnchorIdlJupiterPerpsPositionServiceImpl implements JupiterPerpsPositionService {
|
||||
|
||||
public AnchorIdlJupiterPerpsPositionServiceImpl(
|
||||
SolanaBlockChain solanaBlockChain
|
||||
) {
|
||||
this.solanaBlockChain = solanaBlockChain;
|
||||
this.positionDecoder = new AnchorIdlJupiterPerpsPositionDecoder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JupiterPerpsPosition getPosition(ΩJupiterPerpsPositionAccountΩ positionAccount)
|
||||
throws IOException, InterruptedException {
|
||||
SolanaAccountInfo accountInfo = solanaBlockChain.getAccountInfo(positionAccount);
|
||||
|
||||
if (accountInfo == null) {
|
||||
throw new IllegalArgumentException("Jupiter Perps position account does not exist: " + positionAccount);
|
||||
}
|
||||
|
||||
if (!JUPITER_PERPS_PROGRAM_ID.equals(accountInfo.owner())) {
|
||||
throw new IllegalArgumentException(
|
||||
"Account is not owned by Jupiter Perps program: " + positionAccount
|
||||
);
|
||||
}
|
||||
|
||||
JupiterPerpsPosition pos = positionDecoder.decode(positionAccount, accountInfo);
|
||||
return pos;
|
||||
}
|
||||
|
||||
private static final ΩJupiterPerpsProgramIdΩ JUPITER_PERPS_PROGRAM_ID =
|
||||
"PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu";
|
||||
|
||||
private final SolanaBlockChain solanaBlockChain;
|
||||
private final AnchorIdlJupiterPerpsPositionDecoder positionDecoder;
|
||||
}
|
||||
Reference in New Issue
Block a user