UNPKG

@solsdk/xswap_sdk

Version:

Universal cross-chain swaps SDK

252 lines (251 loc) 8.64 kB
import { address, appendTransactionMessageInstructions, compileTransaction, createKeyPairFromBytes, createNoopSigner, createSignableMessage, createSignerFromKeyPair, createTransactionMessage, getBase58Encoder, getBase64EncodedWireTransaction, getTransactionCodec, partiallySignTransaction, pipe, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, signTransaction, } from "@solana/kit"; import { BaseSDK } from "../sdk.js"; import { createSolanaClient } from "./client.js"; import { cancelCrossChainOrderInstructions, cancelSingleChainOrderInstructions, } from "./cancel-order.js"; import { getSolanaCrossChainOrderInstructions, getSolanaSingleChainOrderInstructions, } from "./order-instructions.js"; import { fetchJWTToken, fetchSiweMessage } from "../../auth/siwe.js"; import { ChainID } from "../../chains.js"; import { bytesToHex } from "viem"; import { fetchUserOrders } from "../orders/api/fetch.js"; import { Keypair as UtilsKeypair } from "@nealireverse_dev/utils"; /** * Solana-specific SDK implementation * * Handles Solana-specific aspects of cross-chain swaps using Solana blockchain. * Uses @solana/kit for transaction creation, signing, and submission. * Supports cross-chain swaps from Solana to other supported chains. */ export class SolanaSDK extends BaseSDK { /** * Creates a new instance of the Solana SDK * * @param config Solana configuration including privateKey, commitment level, and optional RPC URL */ constructor(config) { super(); /** Configuration for Solana connection and authentication */ Object.defineProperty(this, "config", { enumerable: true, configurable: true, writable: true, value: void 0, }); Object.defineProperty(this, "token", { enumerable: true, configurable: true, writable: true, value: void 0, }); /** Client for Solana RPC interactions and transaction handling */ Object.defineProperty(this, "client", { enumerable: true, configurable: true, writable: true, value: void 0, }); this.config = config; this.client = createSolanaClient(config); UtilsKeypair.from({ keypair: config.privateKey }); } /** * Gets the user's Solana wallet address derived from their private key * * Uses @solana/kit to securely derive the wallet address from the private key * * @returns Promise resolving to the user's Solana address as a Base58-encoded string * @throws {SolanaError} If address derivation fails */ async getUserAddress() { const signer = await this.getUserSigner(); return signer.address; } setToken(token) { this.token = token; return this; } async cancelCrossChainOrder(orderId) { const instructions = await cancelCrossChainOrderInstructions(orderId, { rpcUrl: this.config.rpcProviderUrl, }); const signer = await this.getUserSigner(); const signerKeyPair = await this.getUserCryptoKeypair(); const noopSigner = createNoopSigner(signer.address); const { value: latestBlockhash } = await this.client.rpc .getLatestBlockhash({ commitment: this.config.commitment }) .send(); const transactionMessage = pipe( createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayerSigner(noopSigner, tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => appendTransactionMessageInstructions(instructions, tx) ); const myTx = compileTransaction(transactionMessage); const signature = await signTransaction([signerKeyPair], myTx); await this.client.sendAndConfirmTransaction(signature, { commitment: this.config.commitment, }); return orderId; } async cancelSingleChainOrder(orderId) { const instructions = await cancelSingleChainOrderInstructions(orderId, { rpcUrl: this.config.rpcProviderUrl, }); const signer = await this.getUserSigner(); const signerKeyPair = await this.getUserCryptoKeypair(); const noopSigner = createNoopSigner(signer.address); const { value: latestBlockhash } = await this.client.rpc .getLatestBlockhash({ commitment: this.config.commitment }) .send(); const transactionMessage = pipe( createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayerSigner(noopSigner, tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => appendTransactionMessageInstructions(instructions, tx) ); const myTx = compileTransaction(transactionMessage); const signature = await signTransaction([signerKeyPair], myTx); await this.client.sendAndConfirmTransaction(signature, { commitment: this.config.commitment, }); return orderId; } /** * Creates a CryptoKeyPair from the user's private key * * Converts the Base58-encoded private key to the format required by WebCrypto API * * @returns Promise resolving to a CryptoKeyPair for transaction signing * @private */ async getUserCryptoKeypair() { const encoder = getBase58Encoder(); const bytesWithPrefix = encoder.encode(this.config.privateKey); return createKeyPairFromBytes(bytesWithPrefix); } /** * Creates a KeyPairSigner from the user's crypto keypair * * The KeyPairSigner is used for transaction signing and verification * * @returns Promise resolving to a KeyPairSigner for transaction operations * @private */ async getUserSigner() { const signer = await this.getUserCryptoKeypair(); return createSignerFromKeyPair(signer); } async authenticate(token) { const wallet = await this.getUserAddress(); const response = await fetchSiweMessage({ chainId: ChainID.Solana, wallet, }); const message = response.data; const signableMessage = createSignableMessage(message); const signer = await this.getUserSigner(); const signatureArray = await signer.signMessages([signableMessage]); const signatureBytes = signatureArray.map( (signature) => signature[address(wallet)] )[0]; if (!signatureBytes) { throw new Error("No signature bytes found"); } const hexSignature = bytesToHex(signatureBytes); const jwt = await fetchJWTToken( { message, signature: hexSignature, }, token ); const newToken = jwt.data; return newToken; } async getOrders() { if (!this.token) { throw new Error("No token provided"); } const orders = await fetchUserOrders(this.token); return orders; } /** * Prepares a Solana order for submission * * This method: * 1. Gets the user's signer from their private key * 2. Generates Solana-specific instructions for the order * 3. Creates, signs, and submits the transaction to the Solana blockchain * 4. Returns the prepared order with the orderPubkey for tracking * * @param order The validated order to prepare * @returns Promise resolving to a prepared order with Solana-specific data * @protected */ async prepareCrossChainOrder(order) { const signerKeyPair = await this.getUserCryptoKeypair(); const { orderAddress, txBytes } = await getSolanaCrossChainOrderInstructions(order); const transactionCodec = getTransactionCodec(); const tx = transactionCodec.decode(txBytes); const signedTx = await signTransaction([signerKeyPair], tx); const encodedTransaction = getBase64EncodedWireTransaction(signedTx); await this.client.rpc .sendTransaction(encodedTransaction, { preflightCommitment: this.config.commitment, encoding: "base64", }) .send(); return { order, preparedData: { orderPubkey: orderAddress, }, }; } async prepareSingleChainOrder(order) { const signerKeyPair = await this.getUserCryptoKeypair(); const { orderAddress, txBytes, secretNumber } = await getSolanaSingleChainOrderInstructions(order); const transactionCodec = getTransactionCodec(); const tx = transactionCodec.decode(txBytes); const signedTx = await partiallySignTransaction([signerKeyPair], tx); const encodedTransaction = getBase64EncodedWireTransaction(signedTx); await this.client.rpc .sendTransaction(encodedTransaction, { preflightCommitment: this.config.commitment, encoding: "base64", }) .send(); return { order, preparedData: { orderPubkey: orderAddress, secretNumber, }, }; } } //# sourceMappingURL=sdk.js.map