UNPKG

@saros-finance/sdk

Version:

Saros swap - A DeFi Super-Network Built on Solana

355 lines (329 loc) 9.85 kB
/* eslint-disable prefer-promise-reject-errors */ /* eslint-disable no-undef */ import { PublicKey, Transaction, Connection } from '@solana/web3.js'; import { ASSOCIATED_TOKEN_PROGRAM_ID, Token, AccountLayout, u64, } from '@solana/spl-token'; import * as BufferLayout from 'buffer-layout'; import { get } from 'lodash'; import { TOKEN_PROGRAM_ID } from '../constants'; import { sleep } from '../functions'; const bs58 = require('bs58'); const txsFail = 'txsFail'; export const messFail = [ 'gasSolNotEnough', 'tradeErrFund', 'sizeTooSmall', 'txsFail', 'tooLarge', 'exceedsLimit', ]; export async function awaitTransactionSignatureConfirmation( connection, txid, timeout = 20000 ) { let done = false; const connectionOrca = genConnectionSolana(); const result = await new Promise((resolve, reject) => { (async () => { setTimeout(() => { if (done) { return; } done = true; console.log('Timed out for txid', txid); const timeout = { timeout: true }; reject(timeout); }, timeout); try { connectionOrca.onSignature( txid, (result) => { done = true; if (result.err) { const isExceedsLimit = get(result.err, 'InstructionError[1].Custom', 0) === 30 || get(result.err, 'InstructionError[1].Custom', 0) === 16; const isNotEnoughSol = get(result.err, 'InstructionError[1].Custom', 0) === 1; done = true; if (isNotEnoughSol) { reject({ isError: true, mess: isNotEnoughSol ? 'Error gasSolNotEnough' : txsFail, }); } reject({ isError: true, mess: isExceedsLimit ? 'Error exceedsLimit' : txsFail, }); } else { resolve(result); } }, 'recent' ); } catch (e) { done = true; console.log('WS error in setup', txid, e); } while (!done) { (async () => { try { const signatureStatuses = await connectionOrca.getSignatureStatuses( [txid] ); const result = signatureStatuses && signatureStatuses.value[0]; if (!done) { if (!result) { console.log('REST null result for', txid, result); } else if (result.err) { const isExceedsLimit = get(result.err, 'InstructionError[1].Custom', 0) === 30 || get(result.err, 'InstructionError[1].Custom', 0) === 16; const isNotEnoughSol = get(result.err, 'InstructionError[1].Custom', 0) === 1; done = true; if (isNotEnoughSol) { reject({ isError: true, mess: isNotEnoughSol ? 'Error gasSolNotEnough' : txsFail, }); } reject({ isError: true, mess: isExceedsLimit ? 'Error exceedsLimit' : txsFail, }); } else if (!result.confirmations) { done = true; resolve(result); } else { done = true; resolve(result); } } } catch (e) { if (!done) { console.log('REST connection error: txid', txid, e); } } })(); await sleep(300); } })(); }); done = true; return result; } export function encodeMessErr(mess) { const stringResult = mess ? get(mess, 'mess', mess).toString() : ''; if (stringResult.includes('Error')) { let mess = txsFail; switch (true) { case stringResult.includes('exceedsLimit'): mess = 'exceedsLimit'; break; // case stringResult.includes('gasSolNotEnough'): // mess = 'gasSolNotEnough'; // break; case stringResult.includes('Insufficient funds'): mess = 'tradeErrFund'; break; case stringResult.includes('size too small'): mess = 'sizeTooSmall'; break; case stringResult.includes('Transaction too large'): mess = 'tooLarge'; break; case stringResult.includes('Attempt to debit an account but'): case stringResult.includes('gasSolNotEnough'): case stringResult.includes('0x1'): mess = 'gasSolNotEnough'; break; // case stringResult.includes('0x1'): // mess = 'gasSolNotEnough'; // break; case stringResult.includes('the capitalization checksum'): mess = null; break; } return mess; } else { return txsFail; } } const LAYOUT = BufferLayout.union(BufferLayout.u8('instruction')); LAYOUT.addVariant( 0, BufferLayout.struct([ BufferLayout.u8('decimals'), BufferLayout.blob(32, 'mintAuthority'), BufferLayout.u8('freezeAuthorityOption'), BufferLayout.blob(32, 'freezeAuthority'), ]), 'initializeMint' ); LAYOUT.addVariant(1, BufferLayout.struct([]), 'initializeAccount'); LAYOUT.addVariant( 3, BufferLayout.struct([BufferLayout.nu64('amount')]), 'transfer' ); LAYOUT.addVariant( 7, BufferLayout.struct([BufferLayout.nu64('amount')]), 'mintTo' ); LAYOUT.addVariant( 8, BufferLayout.struct([BufferLayout.nu64('amount')]), 'burn' ); export const genConnectionSolana = () => { const connectionSolana = new Connection( 'https://api.mainnet-beta.solana.com', 'singleGossip' ); return connectionSolana; }; export async function genOwnerSolana(wallet) { const publicKey = new PublicKey(wallet); return { publicKey }; } export async function signTransaction(transaction) { return window.coin98.sol .request({ method: 'sol_sign', params: [transaction] }) .then((res) => { console.log({ res }); const sig = bs58.decode(res.signature); const publicKey = new PublicKey(res.publicKey); transaction.addSignature(publicKey, sig); return transaction; }) .catch((err) => { console.log({ err }); }); } export async function sendTransaction( connection, transaction, signers = [], toastNotiWait ) { try { console.log({ transaction, signers }); transaction = await signTransaction(transaction); if (signers.length > 1) { const getSignerValid = signers.slice().filter((it) => it.secretKey); transaction.partialSign(...getSignerValid); } const rawTransaction = transaction.serialize(); const hash = await connection.sendRawTransaction(rawTransaction, { skipPreflight: true, }); console.log({ hash }); await awaitTransactionSignatureConfirmation(connection, hash); toastNotiWait && toastNotiWait(); return hash; } catch (mess) { console.log({ mess }); return { isError: true, mess: encodeMessErr(mess.mess) }; } } export const getInfoTokenByMint = async (mintAddress, accountSol) => { const connection = genConnectionSolana(); const parsedTokenAccounts = await connection.getParsedTokenAccountsByOwner( new PublicKey(accountSol), { mint: new PublicKey(mintAddress), }, 'confirmed' ); if (!parsedTokenAccounts) { return null; } return get(parsedTokenAccounts, 'value[0]', null); }; export const createAssociatedTokenAccountIfNotExist = async ( account, owner, mintAddress, transaction, atas = [] ) => { let publicKey; if (account) { publicKey = new PublicKey(account); } const mint = new PublicKey(mintAddress); const ata = await Token.getAssociatedTokenAddress( ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, mint, owner, true ); if ( (!publicKey || !ata.equals(publicKey)) && !atas.includes(ata.toBase58()) ) { transaction.add( Token.createAssociatedTokenAccountInstruction( ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, mint, ata, owner, owner ) ); atas.push(ata.toBase58()); } return ata; }; export const deserializeAccount = (data) => { if (data === undefined || data.length === 0) { return undefined; } const accountInfo = AccountLayout.decode(data); accountInfo.mint = new PublicKey(accountInfo.mint); accountInfo.owner = new PublicKey(accountInfo.owner); accountInfo.amount = u64.fromBuffer(accountInfo.amount); if (accountInfo.delegateOption === 0) { accountInfo.delegate = null; accountInfo.delegatedAmount = new u64(0); } else { accountInfo.delegate = new PublicKey(accountInfo.delegate); accountInfo.delegatedAmount = u64.fromBuffer(accountInfo.delegatedAmount); } accountInfo.isInitialized = accountInfo.state !== 0; accountInfo.isFrozen = accountInfo.state === 2; if (accountInfo.isNativeOption === 1) { accountInfo.rentExemptReserve = u64.fromBuffer(accountInfo.isNative); accountInfo.isNative = true; } else { accountInfo.rentExemptReserve = null; accountInfo.isNative = false; } if (accountInfo.closeAuthorityOption === 0) { accountInfo.closeAuthority = null; } else { accountInfo.closeAuthority = new PublicKey(accountInfo.closeAuthority); } return accountInfo; }; export const createTransactions = async (connection, accountSol) => { const publicKey = new PublicKey(accountSol); const { blockhash } = await connection.getRecentBlockhash(); const transaction = new Transaction({ recentBlockhash: blockhash }); transaction.feePayer = publicKey; return transaction; }; export const isAddressInUse = async (connection, address) => { const programInf = await connection.getAccountInfo(address); return programInf !== null; };