@symmetry-hq/liquidity-sdk
Version:
Exchange functionality using symmetry funds liquidity
204 lines (183 loc) • 6.75 kB
text/typescript
import {struct, u8 } from '@coral-xyz/borsh';
import { Wallet } from "@coral-xyz/anchor/dist/cjs/provider";
import { TOKEN_PROGRAM_ID, createAssociatedTokenAccountInstruction, createCloseAccountInstruction, getAssociatedTokenAddressSync } from "@solana/spl-token";
import { Connection, PublicKey, SystemProgram, Transaction, TransactionInstruction, } from "@solana/web3.js";
import { TokenSettings, RouteData } from "./types";
export async function applyBlockHashAndPartialSign(
connection: Connection, wallet: Wallet,
preTransaction:Transaction, transaction:Transaction, postTransaction:Transaction,
preSigners:Array<any>, signers:Array<any>
) {
let { blockhash } = await connection.getRecentBlockhash('processed');
let result: any = [];
let txIndex = 0;
if (preTransaction.instructions.length > 0) {
txIndex = 1;
preTransaction.recentBlockhash = blockhash;
preTransaction.feePayer = wallet.publicKey;
preTransaction.setSigners(wallet.publicKey, ...preSigners.map((s) => s.publicKey))
if (preSigners.length > 0)
preTransaction.partialSign(...preSigners);
result.push(preTransaction);
}
transaction.recentBlockhash = blockhash;
transaction.feePayer = wallet.publicKey;
transaction.setSigners(wallet.publicKey, ...signers.map((s) => s.publicKey))
if (signers.length > 0)
transaction.partialSign(...signers);
result.push(transaction);
if (postTransaction.instructions.length > 0) {
postTransaction.recentBlockhash = blockhash;
postTransaction.feePayer = wallet.publicKey;
postTransaction.setSigners(wallet.publicKey, ...signers.map((s) => s.publicKey))
if (signers.length > 0)
postTransaction.partialSign(...signers);
result.push(postTransaction);
}
return {
txIndex: txIndex,
transactions: result
}
}
export async function sendAndConfirmTransaction(connection: Connection, serialized: any) {
let tries = 0;
let signature: any = null;
let signatureResponse: any = null;
while (true) {
connection.sendRawTransaction(serialized, {skipPreflight: true}).catch((e) => {
return null;
}).then(
result => { if (!signature) signature = result; }
);
if (signature) {
if (tries % 2 == 0) {
let response = await connection.getTransaction(
signature, { commitment: "confirmed" }
).catch(
() => { return "Error"; }
)
if (response) {
signatureResponse = response;
break;
}
}
}
tries = tries + 1;
if (tries == 30)
break;
await delay(1000);
}
return {
signature: signature,
response: signatureResponse
}
}
export function delay(ms: number) {
return new Promise( resolve => setTimeout(resolve, ms) );
}
export function createSyncNativeInstruction(
nativeAccount: PublicKey
): TransactionInstruction {
const dataLayout = struct([
u8("instruction"),
]);
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode(
{
instruction: 17, // SyncNative instruction
},
data
);
const keys = [{ pubkey: nativeAccount, isSigner: false, isWritable: true }];
return new TransactionInstruction({
keys,
programId: TOKEN_PROGRAM_ID,
data,
});
}
export async function createAssociatedTokenAccount(
account: string | undefined | null,
owner: PublicKey,
mintAddress: string,
connection: Connection,
transaction: Transaction,
atas: string[] = []
) {
let publicKey
if (account) {
publicKey = new PublicKey(account)
}
const mint = new PublicKey(mintAddress)
const ata = getAssociatedTokenAddressSync(mint, owner)
const infoAta = await connection.getAccountInfo(ata);
if ((!publicKey || !ata.equals(publicKey)) && !atas.includes(ata.toBase58()) && !infoAta) {
transaction.add(
createAssociatedTokenAccountInstruction(
owner,
ata,
owner,
mint,
)
)
atas.push(ata.toBase58())
}
return ata
}
export async function prepareAccounts(
wallet: Wallet,
connection: Connection,
tokenListData: TokenSettings[],
routeData: RouteData,
fromTokenAccount: PublicKey|undefined,
toTokenAccount: PublicKey|undefined,
): Promise<{
preTransaction: Transaction,
postTransaction: Transaction,
fromTokenAccount: PublicKey,
toTokenAccount: PublicKey,
}> {
let preTransaction = new Transaction();
let postTransaction = new Transaction();
if (fromTokenAccount == undefined || fromTokenAccount.equals(wallet.publicKey)){
if (tokenListData[routeData.fromTokenId].tokenMint === "So11111111111111111111111111111111111111112") {
fromTokenAccount = await createAssociatedTokenAccount(fromTokenAccount?.toBase58(), wallet.publicKey, tokenListData[routeData.fromTokenId].tokenMint, connection, preTransaction);
preTransaction.add(
SystemProgram.transfer({
fromPubkey: wallet.publicKey,
toPubkey: fromTokenAccount,
lamports: routeData.fromAmount * 1e9 + 1e7, // 1 sol
})
);
preTransaction.add(
createSyncNativeInstruction(fromTokenAccount)
);
postTransaction.add(
createCloseAccountInstruction(
fromTokenAccount,
wallet.publicKey,
wallet.publicKey,
)
)
} else
fromTokenAccount = await createAssociatedTokenAccount(fromTokenAccount?.toBase58(), wallet.publicKey, tokenListData[routeData.fromTokenId].tokenMint, connection, preTransaction)
}
if (toTokenAccount == undefined || toTokenAccount.equals(wallet.publicKey)){
if (tokenListData[routeData.toTokenId].tokenMint === "So11111111111111111111111111111111111111112") {
toTokenAccount = await createAssociatedTokenAccount(toTokenAccount?.toBase58(), wallet.publicKey, tokenListData[routeData.toTokenId].tokenMint, connection, preTransaction)
postTransaction.add(
createCloseAccountInstruction(
toTokenAccount,
wallet.publicKey,
wallet.publicKey,
)
)
} else
toTokenAccount = await createAssociatedTokenAccount(toTokenAccount?.toBase58(), wallet.publicKey, tokenListData[routeData.toTokenId].tokenMint, connection, preTransaction)
}
return {
preTransaction: preTransaction,
postTransaction: postTransaction,
fromTokenAccount: fromTokenAccount,
toTokenAccount: toTokenAccount,
};
}