@solsdk/xswap_sdk
Version:
Universal cross-chain swaps SDK
164 lines • 8.53 kB
JavaScript
import { getTransferSolInstruction, SYSTEM_PROGRAM_ADDRESS } from '@solana-program/system';
import { ASSOCIATED_TOKEN_PROGRAM_ADDRESS, fetchMint, getCreateAssociatedTokenInstructionAsync, getSyncNativeInstruction, } from '@solana-program/token';
import { address, addSignersToTransactionMessage, appendTransactionMessageInstructions, createNoopSigner, createSolanaRpc, createTransactionMessage, fetchEncodedAccount, generateKeyPairSigner, getAddressEncoder, getProgramDerivedAddress, getTransactionCodec, partiallySignTransactionMessageWithSigners, pipe, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, } from '@solana/kit';
import { ChainID } from '../../chains.js';
import { CROSS_CHAIN_GUARD_ADDRESSES, NATIVE_SOLANA_TOKEN_ADDRESS, SINGLE_CHAIN_GUARD_ADDRESSES, WRAPPED_SOL_MINT_ADDRESS, } from '../../constants.js';
import { getDefaultSolanaRPC } from './client.js';
import { getCreateOrderInstruction } from './generated/cross-chain/index.js';
import { getCreateLimitOrderInstructionAsync } from './generated/single-chain/index.js';
import { genSecretHashAndNumber } from './utils.js';
export async function getSolanaSingleChainOrderInstructions(order, options) {
const rpc = options?.rpcUrl ? createSolanaRpc(options.rpcUrl) : getDefaultSolanaRPC();
const orderSigner = await generateKeyPairSigner();
const signer = createNoopSigner(order.user);
let tokenInMint = address(order.tokenIn);
const { secretHash, secretNumber } = genSecretHashAndNumber(order);
const orderUserAddress = address(order.user);
const spendingNative = tokenInMint === NATIVE_SOLANA_TOKEN_ADDRESS;
if (spendingNative) {
tokenInMint = WRAPPED_SOL_MINT_ADDRESS;
order.tokenIn = WRAPPED_SOL_MINT_ADDRESS;
}
const tokenMintProgram = await fetchMint(rpc, tokenInMint);
const guardAddress = address(SINGLE_CHAIN_GUARD_ADDRESSES[ChainID.Solana]);
const addressEncoder = getAddressEncoder();
const instructions = [];
const [tokenInProgramAccount] = await getProgramDerivedAddress({
programAddress: ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
seeds: [
addressEncoder.encode(orderUserAddress),
addressEncoder.encode(tokenMintProgram.programAddress),
addressEncoder.encode(tokenInMint),
],
});
const userTokenInAccount = await fetchEncodedAccount(rpc, tokenInProgramAccount);
if (!userTokenInAccount.exists) {
const createAccountIx = await getCreateAssociatedTokenInstructionAsync({
payer: signer,
ata: tokenInProgramAccount,
owner: orderUserAddress,
mint: tokenMintProgram.address,
tokenProgram: tokenMintProgram.programAddress,
});
instructions.push(createAccountIx);
}
if (spendingNative) {
const transferIx = getTransferSolInstruction({
amount: order.amountIn,
destination: userTokenInAccount.address,
source: signer,
});
instructions.push(transferIx);
const syncNativeIx = getSyncNativeInstruction({
account: userTokenInAccount.address,
});
instructions.push(syncNativeIx);
}
const createSingleChainLimitOrderIx = await getCreateLimitOrderInstructionAsync({
user: signer,
order: orderSigner,
guard: guardAddress,
tokenInMint: tokenInMint,
userTokenInAccount: userTokenInAccount.address,
tokenInProgram: tokenMintProgram.programAddress,
amountIn: order.amountIn,
deadline: order.deadline,
amountOutMin: order.amountOutMin,
secretHash: Uint8Array.from(secretHash),
extraTransfersAmounts: [], // TODO
});
instructions.push(createSingleChainLimitOrderIx);
const { value: latestBlockhash } = await rpc.getLatestBlockhash({ commitment: 'confirmed' }).send();
const txMessage = pipe(createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayerSigner(signer, tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => appendTransactionMessageInstructions(instructions, tx), (tx) => addSignersToTransactionMessage([orderSigner], tx));
const partiallySignedTransaction = await partiallySignTransactionMessageWithSigners(txMessage);
const txBytes = getTransactionCodec().encode(partiallySignedTransaction);
return {
orderAddress: orderSigner.address,
txBytes,
secretNumber,
};
}
export async function getSolanaCrossChainOrderInstructions(order, options) {
const rpc = options?.rpcUrl ? createSolanaRpc(options.rpcUrl) : getDefaultSolanaRPC();
const orderSigner = await generateKeyPairSigner();
const signer = createNoopSigner(order.user);
let tokenInMint = address(order.sourceTokenAddress);
const orderUserAddress = address(order.user);
const spendingNative = tokenInMint === NATIVE_SOLANA_TOKEN_ADDRESS;
if (spendingNative) {
tokenInMint = WRAPPED_SOL_MINT_ADDRESS;
}
const tokenMintProgram = await fetchMint(rpc, tokenInMint);
const guardAddress = address(CROSS_CHAIN_GUARD_ADDRESSES[ChainID.Solana]);
const addressEncoder = getAddressEncoder();
const [tokenInProgramAccount] = await getProgramDerivedAddress({
programAddress: ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
seeds: [
addressEncoder.encode(orderUserAddress),
addressEncoder.encode(tokenMintProgram.programAddress),
addressEncoder.encode(tokenInMint),
],
});
const [guardProgramAccount] = await getProgramDerivedAddress({
programAddress: ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
seeds: [
// Owner
addressEncoder.encode(guardAddress),
// Token program
addressEncoder.encode(tokenMintProgram.programAddress),
// mint address
addressEncoder.encode(tokenInMint),
],
});
const instructions = [];
const userTokenInAccount = await fetchEncodedAccount(rpc, tokenInProgramAccount);
if (spendingNative) {
order.sourceTokenAddress = WRAPPED_SOL_MINT_ADDRESS;
if (!userTokenInAccount.exists) {
const createAccountIx = await getCreateAssociatedTokenInstructionAsync({
payer: signer,
ata: tokenInProgramAccount,
owner: orderUserAddress,
mint: tokenMintProgram.address,
tokenProgram: tokenMintProgram.programAddress,
});
instructions.push(createAccountIx);
}
const transferIx = getTransferSolInstruction({
source: signer,
destination: userTokenInAccount.address,
amount: order.sourceTokenAmount,
});
const syncNativeIx = getSyncNativeInstruction({
account: userTokenInAccount.address,
});
instructions.push(transferIx);
instructions.push(syncNativeIx);
}
const executionHashUint8Array = order.executionDetailsHashToBytes();
const createOrderIx = getCreateOrderInstruction({
user: signer,
order: orderSigner,
guard: address(CROSS_CHAIN_GUARD_ADDRESSES[ChainID.Solana]),
systemProgram: SYSTEM_PROGRAM_ADDRESS,
associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
tokenInMint: tokenInMint,
userTokenInAccount: userTokenInAccount.address,
guardTokenInAccount: guardProgramAccount,
tokenInProgram: tokenMintProgram.programAddress,
amountIn: order.sourceTokenAmount,
deadline: order.deadline,
executionDetailsHash: executionHashUint8Array,
minStablecoinsAmount: order.minStablecoinAmount,
});
instructions.push(createOrderIx);
const { value: latestBlockhash } = await rpc.getLatestBlockhash({ commitment: 'confirmed' }).send();
const txMessage = pipe(createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayerSigner(signer, tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => appendTransactionMessageInstructions(instructions, tx), (tx) => addSignersToTransactionMessage([orderSigner], tx));
const partiallySignedTransaction = await partiallySignTransactionMessageWithSigners(txMessage);
const txBytes = getTransactionCodec().encode(partiallySignedTransaction);
return {
orderAddress: orderSigner.address,
txBytes: Uint8Array.from(txBytes),
};
}
//# sourceMappingURL=order-instructions.js.map