diff --git a/src/main/tjava/com/r35157/libs/jupiter/perps/impl/anchoridl/AnchorIdlJupiterPerpsCustodyDecoder.tjava b/src/main/tjava/com/r35157/libs/jupiter/perps/impl/anchoridl/AnchorIdlJupiterPerpsCustodyDecoder.tjava new file mode 100644 index 0000000..04fcda1 --- /dev/null +++ b/src/main/tjava/com/r35157/libs/jupiter/perps/impl/anchoridl/AnchorIdlJupiterPerpsCustodyDecoder.tjava @@ -0,0 +1,50 @@ +package com.r35157.libs.jupiter.perps.impl.anchoridl; + +import com.r35157.libs.codec.Base58Codec; +import com.r35157.libs.codec.impl.ref.Base58CodecImpl; +import com.r35157.libs.solana.SolanaAccountInfo; + +import java.util.Base64; + +class AnchorIdlJupiterPerpsCustodyDecoder { + + ΩSPLMintAddressΩ decodeMint( + SolanaAccountInfo custodyAccountInfo + ) { + byte[] data = Base64.getDecoder().decode(custodyAccountInfo.dataBase64()); + + if (data.length < MINT_OFFSET + PUBLIC_KEY_LENGTH) { + throw new IllegalArgumentException( + "Jupiter Perps custody account data is too short: " + data.length + ); + } + + return readPublicKey(data, MINT_OFFSET); + } + + private ΩSPLMintAddressΩ readPublicKey( + byte[] data, + int offset + ) { + byte[] publicKeyBytes = new byte[PUBLIC_KEY_LENGTH]; + + System.arraycopy( + data, + offset, + publicKeyBytes, + 0, + PUBLIC_KEY_LENGTH + ); + + return base58.encode(publicKeyBytes); + } + + private static final int ANCHOR_DISCRIMINATOR_LENGTH = 8; + private static final int PUBLIC_KEY_LENGTH = 32; + + private static final int MINT_OFFSET = + ANCHOR_DISCRIMINATOR_LENGTH + + PUBLIC_KEY_LENGTH; // pool + + private static final Base58Codec base58 = new Base58CodecImpl(); +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/jupiter/perps/impl/anchoridl/AnchorIdlJupiterPerpsPositionDecoder.tjava b/src/main/tjava/com/r35157/libs/jupiter/perps/impl/anchoridl/AnchorIdlJupiterPerpsPositionDecoder.tjava index 096af67..7661a5b 100644 --- a/src/main/tjava/com/r35157/libs/jupiter/perps/impl/anchoridl/AnchorIdlJupiterPerpsPositionDecoder.tjava +++ b/src/main/tjava/com/r35157/libs/jupiter/perps/impl/anchoridl/AnchorIdlJupiterPerpsPositionDecoder.tjava @@ -1,5 +1,7 @@ package com.r35157.libs.jupiter.perps.impl.anchoridl; +import com.r35157.libs.codec.Base58Codec; +import com.r35157.libs.codec.impl.ref.Base58CodecImpl; import com.r35157.libs.jupiter.perps.JupiterPerpsPosition; import com.r35157.libs.jupiter.perps.JupiterPerpsPositionDirection; import com.r35157.libs.solana.SolanaAccountInfo; @@ -13,7 +15,8 @@ class AnchorIdlJupiterPerpsPositionDecoder { JupiterPerpsPosition decode( ΩJupiterPerpsPositionAccountΩ positionAccount, - SolanaAccountInfo accountInfo + SolanaAccountInfo accountInfo, + ΩSPLMintAddressΩ tradedTokenMint ) { byte[] data = Base64.getDecoder().decode(accountInfo.dataBase64()); @@ -38,12 +41,28 @@ class AnchorIdlJupiterPerpsPositionDecoder { JupiterPerpsPosition pos = new JupiterPerpsPosition( positionAccount, entryPrice, - direction + direction, + tradedTokenMint ); return pos; } + ΩSolanaAddressΩ decodeCustodyAccount(SolanaAccountInfo accountInfo) { + byte[] data = Base64.getDecoder().decode(accountInfo.dataBase64()); + + if (data.length < CUSTODY_OFFSET + PUBLIC_KEY_LENGTH) { + throw new IllegalArgumentException( + "Jupiter Perps position account data is too short: " + data.length + ); + } + + return readPublicKey( + data, + CUSTODY_OFFSET + ); + } + private JupiterPerpsPositionDirection decodeDirection( byte rawSide ) { @@ -59,22 +78,37 @@ class AnchorIdlJupiterPerpsPositionDecoder { return direction; } + private ΩSolanaAddressΩ readPublicKey( + byte[] data, + int offset + ) { + byte[] publicKeyBytes = new byte[PUBLIC_KEY_LENGTH]; + + System.arraycopy( + data, + offset, + publicKeyBytes, + 0, + PUBLIC_KEY_LENGTH + ); + + return base58.encode(publicKeyBytes); + } + 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 U64_LENGTH = 8; - private static final int SIDE_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 + private static final int OWNER_OFFSET = ANCHOR_DISCRIMINATOR_LENGTH; + private static final int POOL_OFFSET = OWNER_OFFSET + PUBLIC_KEY_LENGTH; + private static final int CUSTODY_OFFSET = POOL_OFFSET + PUBLIC_KEY_LENGTH; + private static final int COLLATERAL_CUSTODY_OFFSET = CUSTODY_OFFSET + PUBLIC_KEY_LENGTH; // custody + private static final int OPEN_TIME_OFFSET = COLLATERAL_CUSTODY_OFFSET + PUBLIC_KEY_LENGTH; + private static final int UPDATE_TIME_OFFSET = OPEN_TIME_OFFSET + I64_LENGTH; // openTime + private static final int SIDE_OFFSET = UPDATE_TIME_OFFSET + I64_LENGTH; + private static final int PRICE_OFFSET = SIDE_OFFSET + SIDE_ENUM_LENGTH; - private static final int PRICE_OFFSET = - SIDE_OFFSET - + SIDE_ENUM_LENGTH; // side + private static final Base58Codec base58 = new Base58CodecImpl(); } \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/jupiter/perps/impl/anchoridl/AnchorIdlJupiterPerpsServiceImpl.tjava b/src/main/tjava/com/r35157/libs/jupiter/perps/impl/anchoridl/AnchorIdlJupiterPerpsServiceImpl.tjava index 4f8bbb3..f3349f3 100644 --- a/src/main/tjava/com/r35157/libs/jupiter/perps/impl/anchoridl/AnchorIdlJupiterPerpsServiceImpl.tjava +++ b/src/main/tjava/com/r35157/libs/jupiter/perps/impl/anchoridl/AnchorIdlJupiterPerpsServiceImpl.tjava @@ -17,6 +17,7 @@ public class AnchorIdlJupiterPerpsServiceImpl implements JupiterPerpsService { ) { this.solanaBlockChain = solanaBlockChain; this.positionDecoder = new AnchorIdlJupiterPerpsPositionDecoder(); + this.custodyDecoder = new AnchorIdlJupiterPerpsCustodyDecoder(); } @Override @@ -34,7 +35,9 @@ public class AnchorIdlJupiterPerpsServiceImpl implements JupiterPerpsService { ); } - JupiterPerpsPosition pos = positionDecoder.decode(positionAccount, accountInfo); + ΩSPLMintAddressΩ tradedTokenMint = getTradedTokenMint(accountInfo); + JupiterPerpsPosition pos = positionDecoder.decode(positionAccount, accountInfo, tradedTokenMint); + return pos; } @@ -61,20 +64,36 @@ public class AnchorIdlJupiterPerpsServiceImpl implements JupiterPerpsService { throw new IllegalArgumentException(errorMsg); } - JupiterPerpsPosition position = positionDecoder.decode( - address, - accountInfo - ); + ΩSPLMintAddressΩ tradedTokenMint = getTradedTokenMint(accountInfo); + + JupiterPerpsPosition position = positionDecoder.decode(address, accountInfo, tradedTokenMint); positions.add(position); } return Set.copyOf(positions); } + private ΩSPLMintAddressΩ getTradedTokenMint(SolanaAccountInfo positionAccountInfo) + throws IOException, InterruptedException + { + ΩSolanaAddressΩ custodyAccount = positionDecoder.decodeCustodyAccount(positionAccountInfo); + SolanaAccountInfo custodyAccountInfo = solanaBlockChain.getAccountInfo(custodyAccount); + + if (custodyAccountInfo == null) { + throw new IllegalArgumentException( + "Jupiter Perps custody account does not exist: " + custodyAccount + ); + } + + ΩSPLMintAddressΩ mintAddress = custodyDecoder.decodeMint(custodyAccountInfo); + return mintAddress; + } + private static final ΩJupiterPerpsProgramIdΩ JUPITER_PERPS_PROGRAM_ID = "PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu"; private static final int POSITION_OWNER_OFFSET = 8; private final SolanaBlockChain solanaBlockChain; private final AnchorIdlJupiterPerpsPositionDecoder positionDecoder; + private final AnchorIdlJupiterPerpsCustodyDecoder custodyDecoder; } \ No newline at end of file