UNPKG

@bronlabs/intents-sdk

Version:
141 lines 6.43 kB
import { Connection, Transaction, SystemProgram, Keypair, PublicKey } from "@solana/web3.js"; import { createTransferInstruction, getOrCreateAssociatedTokenAccount } from "@solana/spl-token"; import bs58 from "bs58"; import { log } from "../utils.js"; export class SolNetwork { constructor(rpcUrl, confirmations = 20) { this.nativeAssetDecimals = 9; // SOL has 9 decimals this.retryDelay = 5000; this.rpcUrl = rpcUrl; this.confirmations = confirmations; this.connection = new Connection(rpcUrl, { commitment: "confirmed" }); } async getDecimals(tokenAddress) { if (tokenAddress === "0x0") { return this.nativeAssetDecimals; } const { result } = await fetch(this.rpcUrl, { method: 'POST', body: JSON.stringify({ id: 1, jsonrpc: "2.0", method: "getAccountInfo", params: [tokenAddress, { "encoding": "jsonParsed" }] }) }).then((res) => res.json()); return result.value.data.parsed.info.decimals; } async getTxData(txHash, tokenAddress) { const currentBlock = await fetch(this.rpcUrl, { method: 'POST', body: JSON.stringify({ id: 1, jsonrpc: "2.0", method: "getLatestBlockhash", params: [] }) }).then((res) => res.json()).then((res) => res.result.context.slot); const { result } = await fetch(this.rpcUrl, { method: 'POST', body: JSON.stringify({ id: 1, jsonrpc: "2.0", method: "getTransaction", params: [txHash, { "commitment": "confirmed", "maxSupportedTransactionVersion": 0, "encoding": "json" }] }) }).then((res) => res.json()); if (!result) return; const blockNumber = result.slot || currentBlock; const confirmed = currentBlock - blockNumber >= this.confirmations; if (result.meta?.err) { log.warn(`Transaction ${txHash} failed on blockchain: ${result}`); return { to: "", token: "", amount: 0n, confirmed }; } log.info(`Confirmations ${txHash}: ${currentBlock - blockNumber}`); // Native token - SOL if (tokenAddress === "0x0") { if (Number(result.meta.postBalances[0] - result.meta.preBalances[0]) > 0) { return { to: result.transaction.message.accountKeys[0], token: tokenAddress, amount: BigInt(result.meta.postBalances[0]) - BigInt(result.meta.preBalances[0]), confirmed }; } else { return { to: result.transaction.message.accountKeys[1], token: tokenAddress, amount: BigInt(result.meta.postBalances[1]) - BigInt(result.meta.preBalances[1]), confirmed }; } } // ERC20 token const postTokenBalances = result.meta.postTokenBalances.filter((balance) => balance.mint === tokenAddress); const preTokenBalances = result.meta.preTokenBalances.filter((balance) => balance.mint === tokenAddress); const senderAddress = (() => { // Find the account that has less tokens in postTokenBalances than in preTokenBalances for (const postBalance of postTokenBalances) { const preBalance = preTokenBalances.find((balance) => balance.owner === postBalance.owner); if (preBalance && BigInt(postBalance.uiTokenAmount.amount) < BigInt(preBalance.uiTokenAmount.amount)) { return preBalance.owner; } } throw new Error("No sender found"); })(); const postTokenBalanceOfReceiver = postTokenBalances.find((balance) => balance.owner != senderAddress); const preTokenBalanceOfReceiver = preTokenBalances.find((balance) => balance.owner != senderAddress); let preBalance = 0n; if (preTokenBalanceOfReceiver?.uiTokenAmount.amount) { preBalance = BigInt(preTokenBalanceOfReceiver.uiTokenAmount.amount); } return { to: postTokenBalanceOfReceiver.owner, token: postTokenBalanceOfReceiver.mint, amount: BigInt(postTokenBalanceOfReceiver.uiTokenAmount.amount) - preBalance, confirmed }; } async transfer(privateKey, to, value, tokenAddress) { const keypair = this.base58ToKeypair(privateKey); if (tokenAddress === "0x0") { // Send SOL (native token) const transaction = new Transaction().add(SystemProgram.transfer({ fromPubkey: keypair.publicKey, toPubkey: new PublicKey(to), lamports: value })); transaction.recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash; return await this.connection.sendTransaction(transaction, [keypair]); } const senderTokenAccount = await getOrCreateAssociatedTokenAccount(this.connection, keypair, new PublicKey(tokenAddress), keypair.publicKey); const receiverTokenAccount = await getOrCreateAssociatedTokenAccount(this.connection, keypair, new PublicKey(tokenAddress), new PublicKey(to), true // Allow creating a token account for the receiver if it doesn't exist ); const transaction = new Transaction().add(createTransferInstruction(senderTokenAccount.address, receiverTokenAccount.address, keypair.publicKey, value)); transaction.recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash; return await this.connection.sendTransaction(transaction, [keypair]); } base58ToKeypair(base58PrivateKey) { try { const privateKeyBuffer = bs58.decode(base58PrivateKey); return Keypair.fromSecretKey(privateKeyBuffer); } catch (error) { throw new Error("Invalid base58 private key."); } } } //# sourceMappingURL=sol.js.map