@solsdk/xswap_sdk
Version:
Universal cross-chain swaps SDK
177 lines • 10 kB
JavaScript
import { address, appendTransactionMessageInstructions, compileTransactionMessage, createNoopSigner, createSolanaRpc, createTransactionMessage, fetchEncodedAccount, getAddressEncoder, getCompiledTransactionMessageEncoder, getProgramDerivedAddress, pipe, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, } from '@solana/kit';
import { getDefaultSolanaRPC } from './client.js';
import { fetchMaybeLimitOrder, getCancelLimitOrderInstruction } from './generated/single-chain/index.js';
import { ASSOCIATED_TOKEN_PROGRAM_ADDRESS, fetchMint, getCreateAssociatedTokenInstructionAsync, } from '@solana-program/token';
import { CROSS_CHAIN_GUARD_ADDRESSES, SINGLE_CHAIN_GUARD_ADDRESSES, SOLANA_MINT_TOKEN } from '../../constants.js';
import { ChainID } from '../../chains.js';
import { fetchMaybeOrder, getCancelOrderInstruction } from './generated/cross-chain/index.js';
export async function cancelSingleChainOrderInstructions(orderAddress, options) {
const rpc = options?.rpcUrl ? createSolanaRpc(options.rpcUrl) : getDefaultSolanaRPC();
const orderId = address(orderAddress);
const chainOrder = await fetchMaybeLimitOrder(rpc, orderId);
if (!chainOrder.exists) {
throw new Error(`Order with address ${orderAddress} not found`);
}
const instructions = [];
const orderUserAddress = chainOrder.data.user;
const tokenInMint = chainOrder.data.tokenInMint;
const tokenMintProgram = await fetchMint(rpc, tokenInMint);
const addressEncoder = getAddressEncoder();
const [tokenInProgramAccount] = await getProgramDerivedAddress({
programAddress: ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
seeds: [
addressEncoder.encode(orderUserAddress),
addressEncoder.encode(tokenMintProgram.programAddress),
addressEncoder.encode(tokenInMint),
],
});
const guardAddress = SINGLE_CHAIN_GUARD_ADDRESSES[ChainID.Solana];
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 userTokenInAccount = await fetchEncodedAccount(rpc, tokenInProgramAccount);
if (!userTokenInAccount.exists) {
const createTokenIx = await getCreateAssociatedTokenInstructionAsync({
mint: tokenInMint,
owner: orderUserAddress,
payer: createNoopSigner(orderUserAddress),
tokenProgram: tokenMintProgram.programAddress,
});
instructions.push(createTokenIx);
}
const cancelLimitOrderIx = getCancelLimitOrderInstruction({
user: createNoopSigner(orderUserAddress),
order: orderId,
guard: guardAddress,
tokenInMint: chainOrder.data.tokenInMint,
userTokenInAccount: userTokenInAccount.address,
guardTokenInAccount: guardProgramAccount,
tokenInProgram: tokenMintProgram.programAddress,
});
instructions.push(cancelLimitOrderIx);
return instructions;
}
export async function cancelCrossChainOrderInstructionsAsBytes(orderAddress, signerAddress, options) {
const rpc = options?.rpcUrl ? createSolanaRpc(options.rpcUrl) : getDefaultSolanaRPC();
const instructions = await cancelCrossChainOrderInstructions(orderAddress, options);
const { value: latestBlockhash } = await rpc.getLatestBlockhash({ commitment: 'confirmed' }).send();
const txMessage = pipe(createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayerSigner(createNoopSigner(address(signerAddress)), tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => appendTransactionMessageInstructions(instructions, tx));
const compiledTxMessage = compileTransactionMessage(txMessage);
const txBytes = getCompiledTransactionMessageEncoder().encode(compiledTxMessage);
return { versionedMessageBytes: txBytes };
}
export async function cancelSingleChainOrderInstructionsAsBytes(orderAddress, signerAddress, options) {
const rpc = options?.rpcUrl ? createSolanaRpc(options.rpcUrl) : getDefaultSolanaRPC();
const instructions = await cancelSingleChainOrderInstructions(orderAddress, options);
const { value: latestBlockhash } = await rpc.getLatestBlockhash({ commitment: 'confirmed' }).send();
const txMessage = pipe(createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayerSigner(createNoopSigner(address(signerAddress)), tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => appendTransactionMessageInstructions(instructions, tx));
const compiledTxMessage = compileTransactionMessage(txMessage);
const txBytes = getCompiledTransactionMessageEncoder().encode(compiledTxMessage);
return { versionedMessageBytes: txBytes };
}
export async function cancelCrossChainOrderInstructions(orderAddress, options) {
const rpc = options?.rpcUrl ? createSolanaRpc(options.rpcUrl) : getDefaultSolanaRPC();
const orderId = address(orderAddress);
const chainOrder = await fetchMaybeOrder(rpc, orderId);
if (!chainOrder.exists) {
throw new Error(`Order with address ${orderAddress} not found`);
}
const instructions = [];
const orderUserAddress = chainOrder.data.user;
const guardAddress = CROSS_CHAIN_GUARD_ADDRESSES[ChainID.Solana];
const addressEncoder = getAddressEncoder();
const isRecoveringTokenIn = chainOrder.data.lockedStablecoins === 0n;
const recoverTokenMint = isRecoveringTokenIn ? chainOrder.data.tokenInMint : address(SOLANA_MINT_TOKEN.mint); // TODO: Use USDC for production
const recoverTokenMintProgram = await fetchMint(rpc, recoverTokenMint);
const [userRecoveredTokenAccount] = await getProgramDerivedAddress({
programAddress: ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
seeds: [
addressEncoder.encode(orderUserAddress),
addressEncoder.encode(recoverTokenMintProgram.programAddress),
addressEncoder.encode(recoverTokenMint),
],
});
const [guardRecoveredTokenAccount] = await getProgramDerivedAddress({
programAddress: ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
seeds: [
addressEncoder.encode(guardAddress),
addressEncoder.encode(recoverTokenMintProgram.programAddress),
addressEncoder.encode(recoverTokenMint),
],
});
// Check if user's recovered token account exists, create if needed
const userRecoveredTokenAccountInfo = await fetchEncodedAccount(rpc, userRecoveredTokenAccount);
if (!userRecoveredTokenAccountInfo.exists) {
const createRecoveredTokenAccountIx = await getCreateAssociatedTokenInstructionAsync({
payer: createNoopSigner(orderUserAddress),
ata: userRecoveredTokenAccount,
owner: orderUserAddress,
mint: recoverTokenMint,
tokenProgram: recoverTokenMintProgram.programAddress,
});
instructions.push(createRecoveredTokenAccountIx);
}
let userCollateralTokenAccount;
const willClaimCollateral = chainOrder.data.lockedCollateral > 0n;
if (willClaimCollateral) {
// Using SOLANA_DEFAULT_STABLECOIN as collateral mint for now
const collateralTokenMint = address(SOLANA_MINT_TOKEN.mint);
const collateralTokenMintProgram = await fetchMint(rpc, collateralTokenMint);
const [userCollateralAccount] = await getProgramDerivedAddress({
programAddress: ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
seeds: [
addressEncoder.encode(orderUserAddress),
addressEncoder.encode(collateralTokenMintProgram.programAddress),
addressEncoder.encode(collateralTokenMint),
],
});
userCollateralTokenAccount = userCollateralAccount;
// Check if user's collateral token account exists, create if needed
const userCollateralTokenAccountInfo = await fetchEncodedAccount(rpc, userCollateralTokenAccount);
if (!userCollateralTokenAccountInfo.exists) {
const createCollateralTokenAccountIx = await getCreateAssociatedTokenInstructionAsync({
payer: createNoopSigner(orderUserAddress),
ata: userCollateralTokenAccount,
owner: orderUserAddress,
mint: collateralTokenMint,
tokenProgram: collateralTokenMintProgram.programAddress,
});
instructions.push(createCollateralTokenAccountIx);
}
}
const collateralTokenMint = address(SOLANA_MINT_TOKEN.mint);
const collateralTokenMintProgram = await fetchMint(rpc, collateralTokenMint);
const [guardCollateralTokenAccount] = await getProgramDerivedAddress({
programAddress: ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
seeds: [
addressEncoder.encode(guardAddress),
addressEncoder.encode(collateralTokenMintProgram.programAddress),
addressEncoder.encode(collateralTokenMint),
],
});
// Create the cancel order instruction
const cancelOrderIx = getCancelOrderInstruction({
user: createNoopSigner(orderUserAddress),
order: orderId,
guard: guardAddress,
recoveredTokenMint: recoverTokenMint,
userRecoveredTokenAccount: userRecoveredTokenAccount,
guardRecoveredTokenAccount: guardRecoveredTokenAccount,
recoveredTokenProgram: recoverTokenMintProgram.programAddress,
collateralTokenMint: collateralTokenMint,
userCollateralTokenAccount: userCollateralTokenAccount,
guardCollateralTokenAccount: guardCollateralTokenAccount,
collateralTokenProgram: collateralTokenMintProgram.programAddress,
});
instructions.push(cancelOrderIx);
return instructions;
}
//# sourceMappingURL=cancel-order.js.map