3 Commits

5 changed files with 189 additions and 19 deletions
@@ -0,0 +1,70 @@
package com.r35157.libs.codec.impl.ref;
import com.r35157.libs.codec.Base58Codec;
public class Base58CodecImpl implements Base58Codec {
public String encode(byte[] input) {
if (input.length == 0) {
return "";
}
byte[] copy = input.clone();
int zeros = 0;
while (zeros < copy.length && copy[zeros] == 0) {
zeros++;
}
char[] encoded = new char[copy.length * 2];
int outputStart = encoded.length;
int inputStart = zeros;
while (inputStart < copy.length) {
int remainder = divmod58(
copy,
inputStart
);
if (copy[inputStart] == 0) {
inputStart++;
}
encoded[--outputStart] = ALPHABET[remainder];
}
while (outputStart < encoded.length && encoded[outputStart] == ENCODED_ZERO) {
outputStart++;
}
while (zeros-- > 0) {
encoded[--outputStart] = ENCODED_ZERO;
}
return new String(
encoded,
outputStart,
encoded.length - outputStart
);
}
private static int divmod58(byte[] number, int startAt) {
int remainder = 0;
for (int i = startAt; i < number.length; i++) {
int digit = number[i] & 0xff;
int temp = remainder * 256 + digit;
number[i] = (byte) (temp / 58);
remainder = temp % 58;
}
return remainder;
}
private static final char[] ALPHABET =
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
private static final char ENCODED_ZERO =
ALPHABET[0];
}
@@ -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();
}
@@ -1,5 +1,7 @@
package com.r35157.libs.jupiter.perps.impl.anchoridl; 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.JupiterPerpsPosition;
import com.r35157.libs.jupiter.perps.JupiterPerpsPositionDirection; import com.r35157.libs.jupiter.perps.JupiterPerpsPositionDirection;
import com.r35157.libs.solana.SolanaAccountInfo; import com.r35157.libs.solana.SolanaAccountInfo;
@@ -46,6 +48,21 @@ class AnchorIdlJupiterPerpsPositionDecoder {
return pos; 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( private JupiterPerpsPositionDirection decodeDirection(
byte rawSide byte rawSide
) { ) {
@@ -61,22 +78,37 @@ class AnchorIdlJupiterPerpsPositionDecoder {
return direction; 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 ANCHOR_DISCRIMINATOR_LENGTH = 8;
private static final int PUBLIC_KEY_LENGTH = 32; private static final int PUBLIC_KEY_LENGTH = 32;
private static final int I64_LENGTH = 8; 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 SIDE_ENUM_LENGTH = 1;
private static final int U64_LENGTH = 8;
private static final int SIDE_OFFSET = private static final int OWNER_OFFSET = ANCHOR_DISCRIMINATOR_LENGTH;
ANCHOR_DISCRIMINATOR_LENGTH private static final int POOL_OFFSET = OWNER_OFFSET + PUBLIC_KEY_LENGTH;
+ PUBLIC_KEY_LENGTH // owner private static final int CUSTODY_OFFSET = POOL_OFFSET + PUBLIC_KEY_LENGTH;
+ PUBLIC_KEY_LENGTH // pool private static final int COLLATERAL_CUSTODY_OFFSET = CUSTODY_OFFSET + PUBLIC_KEY_LENGTH; // custody
+ PUBLIC_KEY_LENGTH // custody private static final int OPEN_TIME_OFFSET = COLLATERAL_CUSTODY_OFFSET + PUBLIC_KEY_LENGTH;
+ PUBLIC_KEY_LENGTH // collateralCustody private static final int UPDATE_TIME_OFFSET = OPEN_TIME_OFFSET + I64_LENGTH; // openTime
+ I64_LENGTH // openTime private static final int SIDE_OFFSET = UPDATE_TIME_OFFSET + I64_LENGTH;
+ I64_LENGTH; // updateTime private static final int PRICE_OFFSET = SIDE_OFFSET + SIDE_ENUM_LENGTH;
private static final int PRICE_OFFSET = private static final Base58Codec base58 = new Base58CodecImpl();
SIDE_OFFSET
+ SIDE_ENUM_LENGTH; // side
} }
@@ -17,6 +17,7 @@ public class AnchorIdlJupiterPerpsServiceImpl implements JupiterPerpsService {
) { ) {
this.solanaBlockChain = solanaBlockChain; this.solanaBlockChain = solanaBlockChain;
this.positionDecoder = new AnchorIdlJupiterPerpsPositionDecoder(); this.positionDecoder = new AnchorIdlJupiterPerpsPositionDecoder();
this.custodyDecoder = new AnchorIdlJupiterPerpsCustodyDecoder();
} }
@Override @Override
@@ -34,9 +35,9 @@ public class AnchorIdlJupiterPerpsServiceImpl implements JupiterPerpsService {
); );
} }
ΩSPLMintAddressΩ tradedTokenMint = ""; ΩSPLMintAddressΩ tradedTokenMint = getTradedTokenMint(accountInfo);
JupiterPerpsPosition pos = positionDecoder.decode(positionAccount, accountInfo, tradedTokenMint); JupiterPerpsPosition pos = positionDecoder.decode(positionAccount, accountInfo, tradedTokenMint);
return pos; return pos;
} }
@@ -63,20 +64,36 @@ public class AnchorIdlJupiterPerpsServiceImpl implements JupiterPerpsService {
throw new IllegalArgumentException(errorMsg); throw new IllegalArgumentException(errorMsg);
} }
JupiterPerpsPosition position = positionDecoder.decode( ΩSPLMintAddressΩ tradedTokenMint = getTradedTokenMint(accountInfo);
address,
accountInfo JupiterPerpsPosition position = positionDecoder.decode(address, accountInfo, tradedTokenMint);
);
positions.add(position); positions.add(position);
} }
return Set.copyOf(positions); 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 = private static final ΩJupiterPerpsProgramIdΩ JUPITER_PERPS_PROGRAM_ID =
"PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu"; "PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu";
private static final int POSITION_OWNER_OFFSET = 8; private static final int POSITION_OWNER_OFFSET = 8;
private final SolanaBlockChain solanaBlockChain; private final SolanaBlockChain solanaBlockChain;
private final AnchorIdlJupiterPerpsPositionDecoder positionDecoder; private final AnchorIdlJupiterPerpsPositionDecoder positionDecoder;
private final AnchorIdlJupiterPerpsCustodyDecoder custodyDecoder;
} }
@@ -24,9 +24,10 @@ public class Main {
// TODO: Consider if we really need a Main class or we just need to move the main method to NenjimHubImpl? // TODO: Consider if we really need a Main class or we just need to move the main method to NenjimHubImpl?
static void main(String[] args) throws Exception { static void main(String[] args) throws Exception {
NenjimHubImpl nenjimHub = new NenjimHubImpl(); NenjimHubImpl nenjimHub = new NenjimHubImpl();
/* /*
SolanaBlockChain sbc = new SolanaBlockChainImpl(); SolanaBlockChain sbc = new SolanaBlockChainImpl();
JupiterPerpsPositionService jupiter = new AnchorIdlJupiterPerpsPositionServiceImpl(sbc); JupiterPerpsService jupiter = new AnchorIdlJupiterPerpsServiceImpl(sbc);
ΩSolanaWalletIdΩ walletId = "vj98roDZ7744EBfxyuDFkKpEGCsKQLr7K8UFRumJNHf"; ΩSolanaWalletIdΩ walletId = "vj98roDZ7744EBfxyuDFkKpEGCsKQLr7K8UFRumJNHf";
Set<JupiterPerpsPosition> positions = jupiter.getOpenPositions(walletId); Set<JupiterPerpsPosition> positions = jupiter.getOpenPositions(walletId);
int a=0; int a=0;