Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JUP-setup jupiter program to swap solana tokens and added functionali… #41

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
2d4f179
JUP-setup jupiter program to swap solana tokens and added functionali…
Aug 13, 2024
fbf186b
Adding a capability to call getSignaturesForAddress rpc call with bef…
Aug 16, 2024
dfaa37e
Added an ability to read unsigned 128 bits value
Aug 17, 2024
94b9f3f
Added some properties to parse the data received from raydium lp market
Aug 18, 2024
28c5922
Added logsSubscribeWithId and logsUnSubscribe rpc method calls
Sep 11, 2024
df7f9b7
Updated onClose Subscription websocket so it can reconnect when conne…
Sep 14, 2024
39e6d2e
Refactored Jupiter program swap token local field name to avoid confu…
Nov 12, 2024
3f79b4d
Fixed merged conflict and changed naming for legacy transaction and l…
Nov 12, 2024
6daac37
Fixed new merged conflict created by me force pushing commit that rem…
Nov 12, 2024
c388174
Fixed conflict that was not removed from comments and the compiler di…
Nov 12, 2024
6f77a4c
Updated getSignaturesForAddress Rpc call to include before parameter
Nov 13, 2024
646ec91
Added digital asset standard api getTokenAccounts to fetch all holder…
Nov 30, 2024
9697334
Added digital asset standard api searchAssets method to search assets…
i-contemplator Dec 1, 2024
301a150
Added cursor for searchAssets rpc das call
i-contemplator Dec 3, 2024
c377e2d
Made Holder Asset class public
i-contemplator Dec 3, 2024
0f4f828
Changed enum tokenType to string to be correctly parsed by rpc reques…
i-contemplator Dec 3, 2024
a6531a9
Modified the search asset response dto
i-contemplator Dec 3, 2024
1668103
Modified inner classes of search asset request and response to be ac…
i-contemplator Dec 3, 2024
bff7f38
Updated searchassetsresponse dto type
i-contemplator Dec 16, 2024
55d59a5
Merge branch 'main' into JUP-swap-functionality
i-contemplator Jan 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ int lamports = 3000;

Account signer = new Account(secret_key);

Transaction transaction = new Transaction();
transaction.addInstruction(SystemProgram.transfer(fromPublicKey, toPublickKey, lamports));
Transaction legacyTransaction = new Transaction();
legacyTransaction.addInstruction(SystemProgram.transfer(fromPublicKey, toPublickKey, lamports));

String signature = client.getApi().sendTransaction(transaction, signer);
String signature = client.getApi().sendTransaction(legacyTransaction, signer);
```

##### Get balance
Expand All @@ -77,18 +77,18 @@ final Market solUsdcMarket = new MarketBuilder()
final OrderBook bids = solUsdcMarket.getBidOrderBook();
```

##### Send a transaction with call to the "Memo" program
##### Send a legacyTransaction with call to the "Memo" program
```java
// Create account from private key
final Account feePayer = new Account(Base58.decode(new String(data)));
final Transaction transaction = new Transaction();
final Transaction legacyTransaction = new Transaction();

// Add instruction to write memo
transaction.addInstruction(
legacyTransaction.addInstruction(
MemoProgram.writeUtf8(feePayer.getPublicKey(),"Hello from SolanaJ :)")
);

String response = result = client.getApi().sendTransaction(transaction, feePayer);
String response = result = client.getApi().sendTransaction(legacyTransaction, feePayer);
```

## License
Expand Down
14 changes: 13 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,18 @@
<artifactId>jackson-databind</artifactId>
<version>2.17.2</version>
</dependency>

<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.2.1</version>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.15.0</version>
</dependency>
</dependencies>

<distributionManagement>
Expand Down Expand Up @@ -186,7 +198,7 @@
<goal>sign</goal>
</goals>
<configuration>
<homedir>c:/Users/Michael/.gnupg/</homedir>
<homedir>/Users/chintan_mbp/.gnupg/</homedir>
<keyname>0x27FAE7D2</keyname>
</configuration>
</execution>
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/p2p/solanaj/core/Account.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import org.p2p.solanaj.utils.bip32.wallet.DerivableType;

public class Account {
private TweetNaclFast.Signature.KeyPair keyPair;
private final TweetNaclFast.Signature.KeyPair keyPair;

public Account() {
this.keyPair = TweetNaclFast.Signature.keyPair();
Expand Down
36 changes: 19 additions & 17 deletions src/main/java/org/p2p/solanaj/core/AccountKeysList.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
import java.util.HashMap;

public class AccountKeysList {
private HashMap<String, AccountMeta> accounts;
private final HashMap<String, AccountMeta> accounts;

public AccountKeysList() {
accounts = new HashMap<String, AccountMeta>();
accounts = new HashMap<>();
}

public void add(AccountMeta accountMeta) {
Expand All @@ -31,29 +31,31 @@ public void addAll(Collection<AccountMeta> metas) {
}

public ArrayList<AccountMeta> getList() {
ArrayList<AccountMeta> accountKeysList = new ArrayList<AccountMeta>(accounts.values());
ArrayList<AccountMeta> accountKeysList = new ArrayList<>(accounts.values());
accountKeysList.sort(metaComparator);

return accountKeysList;
}

private static final Comparator<AccountMeta> metaComparator = new Comparator<AccountMeta>() {
private static final Comparator<AccountMeta> metaComparator = (am1, am2) -> {

@Override
public int compare(AccountMeta am1, AccountMeta am2) {

int cmpSigner = am1.isSigner() == am2.isSigner() ? 0 : am1.isSigner() ? -1 : 1;
if (cmpSigner != 0) {
return cmpSigner;
}

int cmpkWritable = am1.isWritable() == am2.isWritable() ? 0 : am1.isWritable() ? -1 : 1;
if (cmpkWritable != 0) {
return cmpkWritable;
}
int cmpSigner = am1.isSigner() == am2.isSigner() ? 0 : am1.isSigner() ? -1 : 1;
if (cmpSigner != 0) {
return cmpSigner;
}

return Integer.compare(cmpSigner, cmpkWritable);
int cmpkWritable = am1.isWritable() == am2.isWritable() ? 0 : am1.isWritable() ? -1 : 1;
if (cmpkWritable != 0) {
return cmpkWritable;
}

return Integer.compare(cmpSigner, cmpkWritable);
};

@Override
public String toString() {
return "AccountKeysList{" +
"accounts=" + accounts +
'}';
}
}
9 changes: 9 additions & 0 deletions src/main/java/org/p2p/solanaj/core/AccountMeta.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,13 @@ public class AccountMeta {
private boolean isSigner;

private boolean isWritable;

@Override
public String toString() {
return "AccountMeta{" +
"publicKey=" + publicKey +
", isSigner=" + isSigner +
", isWritable=" + isWritable +
'}';
}
}
170 changes: 170 additions & 0 deletions src/main/java/org/p2p/solanaj/core/LegacyMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package org.p2p.solanaj.core;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

import org.bitcoinj.core.Base58;

import org.p2p.solanaj.utils.Shortvec;

public class LegacyMessage {
private static class MessageHeader {
static final int HEADER_LENGTH = 3;

byte numRequiredSignatures = 0;
byte numReadonlySignedAccounts = 0;
byte numReadonlyUnsignedAccounts = 0;

byte[] toByteArray() {
return new byte[] { numRequiredSignatures, numReadonlySignedAccounts, numReadonlyUnsignedAccounts };
}
}

private static class CompiledInstruction {
byte programIdIndex;
byte[] keyIndicesCount;
byte[] keyIndices;
byte[] dataLength;
byte[] data;

int getLength() {
// 1 = programIdIndex length
return 1 + keyIndicesCount.length + keyIndices.length + dataLength.length + data.length;
}
}

private static final int RECENT_BLOCK_HASH_LENGTH = 32;

private String recentBlockhash;
private final AccountKeysList accountKeys;
private final List<TransactionInstruction> instructions;
private Account feePayer;

public LegacyMessage() {
this.accountKeys = new AccountKeysList();
this.instructions = new ArrayList<>();
}

public LegacyMessage addInstruction(TransactionInstruction instruction) {
accountKeys.addAll(instruction.getKeys());
accountKeys.add(new AccountMeta(instruction.getProgramId(), false, false));
instructions.add(instruction);

return this;
}

public void setRecentBlockHash(String recentBlockhash) {
this.recentBlockhash = recentBlockhash;
}

public byte[] serialize() {

if (recentBlockhash == null) {
throw new IllegalArgumentException("recentBlockhash required");
}

if (instructions.isEmpty()) {
throw new IllegalArgumentException("No instructions provided");
}

MessageHeader messageHeader = new MessageHeader();

List<AccountMeta> keysList = getAccountKeys();
int accountKeysSize = keysList.size();

byte[] accountAddressesLength = Shortvec.encodeLength(accountKeysSize);

int compiledInstructionsLength = 0;
List<CompiledInstruction> compiledInstructions = new ArrayList<>();

for (TransactionInstruction instruction : instructions) {
int keysSize = instruction.getKeys().size();

byte[] keyIndices = new byte[keysSize];
for (int i = 0; i < keysSize; i++) {
keyIndices[i] = (byte) findAccountIndex(keysList, instruction.getKeys().get(i).getPublicKey());
}

CompiledInstruction compiledInstruction = new CompiledInstruction();
compiledInstruction.programIdIndex = (byte) findAccountIndex(keysList, instruction.getProgramId());
compiledInstruction.keyIndicesCount = Shortvec.encodeLength(keysSize);
compiledInstruction.keyIndices = keyIndices;
compiledInstruction.dataLength = Shortvec.encodeLength(instruction.getData().length);
compiledInstruction.data = instruction.getData();

compiledInstructions.add(compiledInstruction);

compiledInstructionsLength += compiledInstruction.getLength();
}

byte[] instructionsLength = Shortvec.encodeLength(compiledInstructions.size());

int bufferSize = MessageHeader.HEADER_LENGTH + RECENT_BLOCK_HASH_LENGTH + accountAddressesLength.length
+ (accountKeysSize * PublicKey.PUBLIC_KEY_LENGTH) + instructionsLength.length
+ compiledInstructionsLength;

ByteBuffer out = ByteBuffer.allocate(bufferSize);

ByteBuffer accountKeysBuff = ByteBuffer.allocate(accountKeysSize * PublicKey.PUBLIC_KEY_LENGTH);
for (AccountMeta accountMeta : keysList) {
accountKeysBuff.put(accountMeta.getPublicKey().toByteArray());

if (accountMeta.isSigner()) {
messageHeader.numRequiredSignatures += 1;
if (!accountMeta.isWritable()) {
messageHeader.numReadonlySignedAccounts += 1;
}
} else {
if (!accountMeta.isWritable()) {
messageHeader.numReadonlyUnsignedAccounts += 1;
}
}
}

out.put(messageHeader.toByteArray());

out.put(accountAddressesLength);
out.put(accountKeysBuff.array());

out.put(Base58.decode(recentBlockhash));

out.put(instructionsLength);
for (CompiledInstruction compiledInstruction : compiledInstructions) {
out.put(compiledInstruction.programIdIndex);
out.put(compiledInstruction.keyIndicesCount);
out.put(compiledInstruction.keyIndices);
out.put(compiledInstruction.dataLength);
out.put(compiledInstruction.data);
}

return out.array();
}

protected void setFeePayer(Account feePayer) {
this.feePayer = feePayer;
}

private List<AccountMeta> getAccountKeys() {
List<AccountMeta> keysList = accountKeys.getList();
int feePayerIndex = findAccountIndex(keysList, feePayer.getPublicKey());

List<AccountMeta> newList = new ArrayList<>();
AccountMeta feePayerMeta = keysList.get(feePayerIndex);
newList.add(new AccountMeta(feePayerMeta.getPublicKey(), true, true));
keysList.remove(feePayerIndex);
newList.addAll(keysList);

return newList;
}

private int findAccountIndex(List<AccountMeta> accountMetaList, PublicKey key) {
for (int i = 0; i < accountMetaList.size(); i++) {
if (accountMetaList.get(i).getPublicKey().equals(key)) {
return i;
}
}

throw new RuntimeException("unable to find account index");
}
}
76 changes: 76 additions & 0 deletions src/main/java/org/p2p/solanaj/core/LegacyTransaction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.p2p.solanaj.core;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.bitcoinj.core.Base58;
import org.p2p.solanaj.utils.Shortvec;
import org.p2p.solanaj.utils.TweetNaclFast;

public class LegacyTransaction {

public static final int SIGNATURE_LENGTH = 64;

private final LegacyMessage legacyMessage;
private final List<String> signatures;
private byte[] serializedLegacyMessage;

public LegacyTransaction() {
this.legacyMessage = new LegacyMessage();
this.signatures = new ArrayList<>();
}

public LegacyTransaction addInstruction(TransactionInstruction instruction) {
legacyMessage.addInstruction(instruction);

return this;
}

public void setRecentBlockHash(String recentBlockhash) {
legacyMessage.setRecentBlockHash(recentBlockhash);
}

public void sign(Account signer) {
sign(Collections.singletonList(signer));
}

public void sign(List<Account> signers) {

if (signers.isEmpty()) {
throw new IllegalArgumentException("No signers");
}

Account feePayer = signers.get(0);
legacyMessage.setFeePayer(feePayer);

serializedLegacyMessage = legacyMessage.serialize();

for (Account signer : signers) {
TweetNaclFast.Signature signatureProvider = new TweetNaclFast.Signature(new byte[0], signer.getSecretKey());
byte[] signature = signatureProvider.detached(serializedLegacyMessage);

signatures.add(Base58.encode(signature));
}
}

public byte[] serialize() {
int signaturesSize = signatures.size();
byte[] signaturesLength = Shortvec.encodeLength(signaturesSize);

ByteBuffer out = ByteBuffer
.allocate(signaturesLength.length + signaturesSize * SIGNATURE_LENGTH + serializedLegacyMessage.length);

out.put(signaturesLength);

for (String signature : signatures) {
byte[] rawSignature = Base58.decode(signature);
out.put(rawSignature);
}

out.put(serializedLegacyMessage);

return out.array();
}
}
Loading