Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@

### 1.0.1 (2024-7-25)

- Updated entry point in json package
- Updated entry point in json package

### 1.0.2 (2025-05-05)

- Updating solana transaction function for lefi swap which supports different type of solana transaction
103 changes: 84 additions & 19 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@getsafle/vault-sol-controller",
"version": "1.0.1",
"version": "1.0.2",
"description": "Solana chain controller for Safle Vault",
"main": "src/index.js",
"scripts": {
Expand All @@ -17,7 +17,7 @@
"license": "MIT",
"dependencies": {
"@solana/spl-token": "^0.4.8",
"@solana/web3.js": "^1.95.1",
"@solana/web3.js": "^1.98.2",
"bip39": "^3.1.0",
"bs58": "^5.0.0",
"ed25519-hd-key": "^1.3.0",
Expand Down
29 changes: 22 additions & 7 deletions src/helper/generateTransactionObject.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
const solanaWeb3 = require('@solana/web3.js');
const { getOrCreateAssociatedTokenAccount, createTransferInstruction} = require("@solana/spl-token");
const solanaWeb3 = require("@solana/web3.js");
const {
getOrCreateAssociatedTokenAccount,
createTransferInstruction,
} = require("@solana/spl-token");

const { solana_transaction: { NATIVE_TRANSFER, TOKEN_TRANSFER }} = require("../config");
const {
solana_transaction: { NATIVE_TRANSFER, TOKEN_TRANSFER },
} = require("../config");

async function generateTransactionObject(transaction, signer, connection) {
// ✅ Handle base64 versioned transaction (bypasses normal logic)
if (transaction.serializedTx) {
const rawBuffer = Buffer.from(transaction.serializedTx, "base64");
const versionedTx = solanaWeb3.VersionedTransaction.deserialize(rawBuffer);

// ✅ Sign the deserialized versioned transaction
versionedTx.sign([signer]);

return versionedTx;
}

// 🔁 Legacy transaction types
const { txnType } = transaction;
let rawTransaction = {};

Expand Down Expand Up @@ -48,16 +65,14 @@ async function generateTransactionObject(transaction, signer, connection) {
);
}

// set the desired priority fee in microLamport
if(transaction?.priorityFee && transaction?.priorityFee > 0) {
// ⏫ Optional: Add priority fee if needed
if (transaction?.priorityFee && transaction?.priorityFee > 0) {
const addPriorityFee = solanaWeb3.ComputeBudgetProgram.setComputeUnitPrice({
microLamports: transaction.priorityFee,
});

rawTransaction.add(addPriorityFee);
}

return rawTransaction;
}

module.exports = generateTransactionObject;
99 changes: 66 additions & 33 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
const bs58 = require("bs58");
const nacl = require('tweetnacl');
const nacl = require("tweetnacl");
const helper = require("./helper");
const ObservableStore = require("obs-store");
const solanaWeb3 = require('@solana/web3.js');

const solanaWeb3 = require("@solana/web3.js");

const {
solana: { HD_PATH },
Expand Down Expand Up @@ -61,7 +60,7 @@ class KeyringController {
}

async signMessage(message, _address) {
const { mnemonic, address } = this.store.getState()
const { mnemonic, address } = this.store.getState();
const idx = address.indexOf(_address);

if (idx < 0)
Expand All @@ -87,34 +86,55 @@ class KeyringController {

try {
const signer = helper.setupAccount(mnemonic, helper.getHDPath(idx));

const connection = new solanaWeb3.Connection(network, "confirmed");

const rawTx = await helper.generateTransactionObject(transaction, signer, connection);

const rawSignedTxn = await helper.signTransaction(rawTx, signer, connection, []);

const signedTxn = rawSignedTxn.serialize().toString("hex");

return { signedTransaction: signedTxn };
// 🔄 Generate the transaction object (legacy or versioned)
const rawTx = await helper.generateTransactionObject(
transaction,
signer,
connection
);

// 🔁 Return serialized transaction in hex
if (transaction.serializedTx) {
// 🔐 Sign if it's not already signed
if (
rawTx.signatures &&
rawTx.signatures.some((sig) => sig === undefined || sig.length === 0)
) {
rawTx.sign([signer]);
}
const signedTxn = Buffer.from(rawTx.serialize()).toString("hex");
return { signedTransaction: signedTxn };
} else {
const rawSignedTxn = await helper.signTransaction(
rawTx,
signer,
connection,
[]
);

const signedTxn = rawSignedTxn.serialize().toString("hex");
return { signedTransaction: signedTxn };
}
} catch (err) {
throw err;
}
}

async sendTransaction(rawTransaction) {

try {
const { network } = this.store.getState()
const stringBuff = Buffer.from(rawTransaction, 'hex')

const connection = new solanaWeb3.Connection(network, "confirmed")
const transactionDetails = await connection.sendRawTransaction(stringBuff)
return { transactionDetails: transactionDetails }
const { network } = this.store.getState();
const stringBuff = Buffer.from(rawTransaction, "hex");

const connection = new solanaWeb3.Connection(network, "confirmed");
const transactionDetails = await connection.sendRawTransaction(
stringBuff
);
return { transactionDetails: transactionDetails };
} catch (err) {
console.log(err);
throw err
throw err;
}
}

Expand All @@ -126,18 +146,28 @@ class KeyringController {
if (idx < 0)
throw "Invalid address, the address is not available in the wallet";

const signer = helper.setupAccount(mnemonic, helper.getHDPath(idx));
const signer = helper.setupAccount(mnemonic, helper.getHDPath(idx));

const connection = new solanaWeb3.Connection(network, "confirmed");
const connection = new solanaWeb3.Connection(network, "confirmed");

const rawTx = await helper.generateTransactionObject(rawTransaction, signer, connection);

const rawSignedTxn = await helper.signTransaction(rawTx, signer, connection, []);
const rawTx = await helper.generateTransactionObject(
rawTransaction,
signer,
connection
);

const fees = await connection.getFeeForMessage(rawSignedTxn.compileMessage());
const rawSignedTxn = await helper.signTransaction(
rawTx,
signer,
connection,
[]
);

return { fees: fees.value };
const fees = await connection.getFeeForMessage(
rawSignedTxn.compileMessage()
);

return { fees: fees.value };
}

persistAllAddress(_address) {
Expand All @@ -156,13 +186,16 @@ class KeyringController {

const getBalance = async (address, network) => {
try {
const _network = helper.getNetwork(network)
const connection = new solanaWeb3.Connection(_network, "confirmed")
const accInfo = await connection.getAccountInfo(new solanaWeb3.PublicKey(address), 'confirmed')
return { balance: accInfo ? accInfo.lamports : 0 }
const _network = helper.getNetwork(network);
const connection = new solanaWeb3.Connection(_network, "confirmed");
const accInfo = await connection.getAccountInfo(
new solanaWeb3.PublicKey(address),
"confirmed"
);
return { balance: accInfo ? accInfo.lamports : 0 };
} catch (err) {
throw err
throw err;
}
}
};

module.exports = { KeyringController, getBalance };