diff --git a/src/models/pool.ts b/src/models/pool.ts index dfe8fa073..71209fe86 100644 --- a/src/models/pool.ts +++ b/src/models/pool.ts @@ -1,5 +1,5 @@ -import { AccountInfo, PublicKey } from "@solana/web3.js"; -import { TokenAccount } from "./account"; +import { AccountInfo, PublicKey } from '@solana/web3.js'; +import { TokenAccount } from './account'; export const DEFAULT_DENOMINATOR = 10_000; @@ -45,7 +45,8 @@ export interface PoolConfig { hostFeeNumerator: number; hostFeeDenominator: number; }; - + freezeAuthority: PublicKey | undefined; + freezeAuthorityBitMask: number; token_b_offset?: number; token_b_price?: number; } diff --git a/src/models/tokenSwap.ts b/src/models/tokenSwap.ts index 415119db6..31890489d 100644 --- a/src/models/tokenSwap.ts +++ b/src/models/tokenSwap.ts @@ -1,104 +1,101 @@ -import { Numberu64 } from "@solana/spl-token-swap"; -import { PublicKey, Account, TransactionInstruction } from "@solana/web3.js"; -import * as BufferLayout from "buffer-layout"; -import { CurveType, PoolConfig } from "./pool"; +import { Numberu64 } from '@solana/spl-token-swap'; +import { PublicKey, Account, TransactionInstruction } from '@solana/web3.js'; +import * as BufferLayout from 'buffer-layout'; +import { CurveType, PoolConfig } from './pool'; -export { TokenSwap } from "@solana/spl-token-swap"; +export { TokenSwap } from '@solana/spl-token-swap'; /** * Layout for a public key */ -export const publicKey = (property: string = "publicKey"): Object => { +export const publicKey = (property: string = 'publicKey'): Object => { return BufferLayout.blob(32, property); }; /** * Layout for a 64bit unsigned value */ -export const uint64 = (property: string = "uint64"): Object => { +export const uint64 = (property: string = 'uint64'): Object => { return BufferLayout.blob(8, property); }; const FEE_LAYOUT = BufferLayout.struct( [ - BufferLayout.nu64("tradeFeeNumerator"), - BufferLayout.nu64("tradeFeeDenominator"), - BufferLayout.nu64("ownerTradeFeeNumerator"), - BufferLayout.nu64("ownerTradeFeeDenominator"), - BufferLayout.nu64("ownerWithdrawFeeNumerator"), - BufferLayout.nu64("ownerWithdrawFeeDenominator"), - BufferLayout.nu64("hostFeeNumerator"), - BufferLayout.nu64("hostFeeDenominator"), + BufferLayout.nu64('tradeFeeNumerator'), + BufferLayout.nu64('tradeFeeDenominator'), + BufferLayout.nu64('ownerTradeFeeNumerator'), + BufferLayout.nu64('ownerTradeFeeDenominator'), + BufferLayout.nu64('ownerWithdrawFeeNumerator'), + BufferLayout.nu64('ownerWithdrawFeeDenominator'), + BufferLayout.nu64('hostFeeNumerator'), + BufferLayout.nu64('hostFeeDenominator'), ], - "fees" + 'fees' ); -export const TokenSwapLayoutLegacyV0 = BufferLayout.struct([ - BufferLayout.u8("isInitialized"), - BufferLayout.u8("nonce"), - publicKey("tokenAccountA"), - publicKey("tokenAccountB"), - publicKey("tokenPool"), - uint64("feesNumerator"), - uint64("feesDenominator"), +/// Note to the confused people: Bartosz's v0, v1, v2 do not line up +// To the backend's v0, v1, etc. +// TokenSwapLayoutV0 maps to an older independent contract +// TokenSwapLayoutV1 maps to SwapV1 on the backend +// TokenSwapLayout maps to SwapV2. +export const TokenSwapLayoutV0: typeof BufferLayout.Structure = BufferLayout.struct([ + BufferLayout.u8('isInitialized'), + BufferLayout.u8('nonce'), + publicKey('tokenProgramId'), + publicKey('tokenAccountA'), + publicKey('tokenAccountB'), + publicKey('tokenPool'), + publicKey('mintA'), + publicKey('mintB'), + publicKey('feeAccount'), + BufferLayout.u8('curveType'), + uint64('tradeFeeNumerator'), + uint64('tradeFeeDenominator'), + uint64('ownerTradeFeeNumerator'), + uint64('ownerTradeFeeDenominator'), + uint64('ownerWithdrawFeeNumerator'), + uint64('ownerWithdrawFeeDenominator'), + BufferLayout.blob(16, 'padding'), ]); -export const TokenSwapLayoutV1: typeof BufferLayout.Structure = BufferLayout.struct( - [ - BufferLayout.u8("isInitialized"), - BufferLayout.u8("nonce"), - publicKey("tokenProgramId"), - publicKey("tokenAccountA"), - publicKey("tokenAccountB"), - publicKey("tokenPool"), - publicKey("mintA"), - publicKey("mintB"), - publicKey("feeAccount"), - BufferLayout.u8("curveType"), - uint64("tradeFeeNumerator"), - uint64("tradeFeeDenominator"), - uint64("ownerTradeFeeNumerator"), - uint64("ownerTradeFeeDenominator"), - uint64("ownerWithdrawFeeNumerator"), - uint64("ownerWithdrawFeeDenominator"), - BufferLayout.blob(16, "padding"), - ] -); - -const CURVE_NODE = BufferLayout.union( - BufferLayout.u8(), - BufferLayout.blob(32), - "curve" -); -CURVE_NODE.addVariant(0, BufferLayout.struct([]), "constantProduct"); -CURVE_NODE.addVariant( - 1, - BufferLayout.struct([BufferLayout.nu64("token_b_price")]), - "constantPrice" -); -CURVE_NODE.addVariant(2, BufferLayout.struct([]), "stable"); -CURVE_NODE.addVariant( - 3, - BufferLayout.struct([BufferLayout.nu64("token_b_offset")]), - "offset" -); +const CURVE_NODE = BufferLayout.union(BufferLayout.u8(), BufferLayout.blob(32), 'curve'); +CURVE_NODE.addVariant(0, BufferLayout.struct([]), 'constantProduct'); +CURVE_NODE.addVariant(1, BufferLayout.struct([BufferLayout.nu64('token_b_price')]), 'constantPrice'); +CURVE_NODE.addVariant(2, BufferLayout.struct([]), 'stable'); +CURVE_NODE.addVariant(3, BufferLayout.struct([BufferLayout.nu64('token_b_offset')]), 'offset'); + +export const TokenSwapLayoutV1: typeof BufferLayout.Structure = BufferLayout.struct([ + BufferLayout.u8('version'), + BufferLayout.u8('isInitialized'), + BufferLayout.u8('nonce'), + publicKey('tokenProgramId'), + publicKey('tokenAccountA'), + publicKey('tokenAccountB'), + publicKey('tokenPool'), + publicKey('mintA'), + publicKey('mintB'), + publicKey('feeAccount'), + FEE_LAYOUT, + CURVE_NODE, +]); -export const TokenSwapLayout: typeof BufferLayout.Structure = BufferLayout.struct( - [ - BufferLayout.u8("version"), - BufferLayout.u8("isInitialized"), - BufferLayout.u8("nonce"), - publicKey("tokenProgramId"), - publicKey("tokenAccountA"), - publicKey("tokenAccountB"), - publicKey("tokenPool"), - publicKey("mintA"), - publicKey("mintB"), - publicKey("feeAccount"), - FEE_LAYOUT, - CURVE_NODE, - ] -); +export const TokenSwapLayout: typeof BufferLayout.Structure = BufferLayout.struct([ + BufferLayout.u8('version'), + BufferLayout.u8('isInitialized'), + BufferLayout.u8('nonce'), + publicKey('tokenProgramId'), + publicKey('tokenAccountA'), + publicKey('tokenAccountB'), + publicKey('tokenPool'), + publicKey('mintA'), + publicKey('mintB'), + publicKey('feeAccount'), + FEE_LAYOUT, + CURVE_NODE, + BufferLayout.u32('freezeAuthorityOption'), + publicKey('freezeAuthority'), + BufferLayout.u8('freezeAuthorityBitMask'), +]); export const createInitSwapInstruction = ( tokenSwapAccount: Account, @@ -128,29 +125,33 @@ export const createInitSwapInstruction = ( let data = Buffer.alloc(1024); if (isLatest) { const fields = [ - BufferLayout.u8("instruction"), - BufferLayout.u8("nonce"), - BufferLayout.nu64("tradeFeeNumerator"), - BufferLayout.nu64("tradeFeeDenominator"), - BufferLayout.nu64("ownerTradeFeeNumerator"), - BufferLayout.nu64("ownerTradeFeeDenominator"), - BufferLayout.nu64("ownerWithdrawFeeNumerator"), - BufferLayout.nu64("ownerWithdrawFeeDenominator"), - BufferLayout.nu64("hostFeeNumerator"), - BufferLayout.nu64("hostFeeDenominator"), - BufferLayout.u8("curveType"), + BufferLayout.u8('instruction'), + BufferLayout.u8('nonce'), + BufferLayout.nu64('tradeFeeNumerator'), + BufferLayout.nu64('tradeFeeDenominator'), + BufferLayout.nu64('ownerTradeFeeNumerator'), + BufferLayout.nu64('ownerTradeFeeDenominator'), + BufferLayout.nu64('ownerWithdrawFeeNumerator'), + BufferLayout.nu64('ownerWithdrawFeeDenominator'), + BufferLayout.nu64('hostFeeNumerator'), + BufferLayout.nu64('hostFeeDenominator'), + BufferLayout.u8('curveType'), ]; if (config.curveType === CurveType.ConstantProductWithOffset) { - fields.push(BufferLayout.nu64("token_b_offset")); - fields.push(BufferLayout.blob(24, "padding")); + fields.push(BufferLayout.nu64('token_b_offset')); + fields.push(BufferLayout.blob(24, 'padding')); } else if (config.curveType === CurveType.ConstantPrice) { - fields.push(BufferLayout.nu64("token_b_price")); - fields.push(BufferLayout.blob(24, "padding")); + fields.push(BufferLayout.nu64('token_b_price')); + fields.push(BufferLayout.blob(24, 'padding')); } else { - fields.push(BufferLayout.blob(32, "padding")); + fields.push(BufferLayout.blob(32, 'padding')); } + fields.push(BufferLayout.u8('freezeAuthorityOption')); + fields.push(publicKey('freezeAuthority')); + fields.push(BufferLayout.u8('freezeAuthorityBitMask')); + const commandDataLayout = BufferLayout.struct(fields); const { fees, ...rest } = config; @@ -161,22 +162,24 @@ export const createInitSwapInstruction = ( nonce, ...fees, ...rest, + freezeAuthority: config.freezeAuthority ? Buffer.from(config.freezeAuthority.toBuffer()) : undefined, + freezeAuthorityOption: config.freezeAuthority ? 1 : 0, }, data ); data = data.slice(0, encodeLength); } else { const commandDataLayout = BufferLayout.struct([ - BufferLayout.u8("instruction"), - BufferLayout.u8("nonce"), - BufferLayout.u8("curveType"), - BufferLayout.nu64("tradeFeeNumerator"), - BufferLayout.nu64("tradeFeeDenominator"), - BufferLayout.nu64("ownerTradeFeeNumerator"), - BufferLayout.nu64("ownerTradeFeeDenominator"), - BufferLayout.nu64("ownerWithdrawFeeNumerator"), - BufferLayout.nu64("ownerWithdrawFeeDenominator"), - BufferLayout.blob(16, "padding"), + BufferLayout.u8('instruction'), + BufferLayout.u8('nonce'), + BufferLayout.u8('curveType'), + BufferLayout.nu64('tradeFeeNumerator'), + BufferLayout.nu64('tradeFeeDenominator'), + BufferLayout.nu64('ownerTradeFeeNumerator'), + BufferLayout.nu64('ownerTradeFeeDenominator'), + BufferLayout.nu64('ownerWithdrawFeeNumerator'), + BufferLayout.nu64('ownerWithdrawFeeDenominator'), + BufferLayout.blob(16, 'padding'), ]); const encodeLength = commandDataLayout.encode( @@ -221,10 +224,10 @@ export const depositInstruction = ( isLatest: boolean ): TransactionInstruction => { const dataLayout = BufferLayout.struct([ - BufferLayout.u8("instruction"), - uint64("poolTokenAmount"), - uint64("maximumTokenA"), - uint64("maximumTokenB"), + BufferLayout.u8('instruction'), + uint64('poolTokenAmount'), + uint64('maximumTokenA'), + uint64('maximumTokenB'), ]); const data = Buffer.alloc(dataLayout.span); @@ -285,9 +288,9 @@ export const depositExactOneInstruction = ( isLatest: boolean ): TransactionInstruction => { const dataLayout = BufferLayout.struct([ - BufferLayout.u8("instruction"), - uint64("sourceTokenAmount"), - uint64("minimumPoolTokenAmount"), + BufferLayout.u8('instruction'), + uint64('sourceTokenAmount'), + uint64('minimumPoolTokenAmount'), ]); const data = Buffer.alloc(dataLayout.span); @@ -348,10 +351,10 @@ export const withdrawInstruction = ( isLatest: boolean ): TransactionInstruction => { const dataLayout = BufferLayout.struct([ - BufferLayout.u8("instruction"), - uint64("poolTokenAmount"), - uint64("minimumTokenA"), - uint64("minimumTokenB"), + BufferLayout.u8('instruction'), + uint64('poolTokenAmount'), + uint64('minimumTokenA'), + uint64('minimumTokenB'), ]); const data = Buffer.alloc(dataLayout.span); @@ -417,9 +420,9 @@ export const withdrawExactOneInstruction = ( isLatest: boolean ): TransactionInstruction => { const dataLayout = BufferLayout.struct([ - BufferLayout.u8("instruction"), - uint64("sourceTokenAmount"), - uint64("maximumTokenAmount"), + BufferLayout.u8('instruction'), + uint64('sourceTokenAmount'), + uint64('maximumTokenAmount'), ]); const data = Buffer.alloc(dataLayout.span); @@ -483,9 +486,9 @@ export const swapInstruction = ( isLatest: boolean ): TransactionInstruction => { const dataLayout = BufferLayout.struct([ - BufferLayout.u8("instruction"), - uint64("amountIn"), - uint64("minimumAmountOut"), + BufferLayout.u8('instruction'), + uint64('amountIn'), + uint64('minimumAmountOut'), ]); const keys = isLatest @@ -534,3 +537,32 @@ export const swapInstruction = ( data, }); }; + +export const setFreezeAuthorityBitMaskInstruction = ( + tokenSwap: PublicKey, + freezeAuthority: PublicKey, + swapProgramId: PublicKey, + freezeAuthorityBitMask: number +): TransactionInstruction => { + const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction'), BufferLayout.u8('freezeAuthorityBitMask')]); + + const keys = [ + { pubkey: tokenSwap, isSigner: false, isWritable: true }, + { pubkey: freezeAuthority, isSigner: true, isWritable: false }, + ]; + + const data = Buffer.alloc(dataLayout.span); + dataLayout.encode( + { + instruction: 6, // SetFreezeAuthorityBitMask instruction + freezeAuthorityBitMask, + }, + data + ); + + return new TransactionInstruction({ + keys, + programId: swapProgramId, + data, + }); +}; diff --git a/src/utils/currencyPair.tsx b/src/utils/currencyPair.tsx index ab8b3ff06..80797cea1 100644 --- a/src/utils/currencyPair.tsx +++ b/src/utils/currencyPair.tsx @@ -1,28 +1,13 @@ -import React, { - useCallback, - useContext, - useEffect, - useMemo, - useState, -} from "react"; -import { - calculateDependentAmount, - usePoolForBasket, - PoolOperation, -} from "./pools"; -import { cache, useAccountByMint } from "./accounts"; -import { MintInfo } from "@solana/spl-token"; -import { useConnection, useConnectionConfig } from "./connection"; -import { - CurveType, - PoolConfig, - TokenAccount, - DEFAULT_DENOMINATOR, -} from "../models"; -import { convert, getTokenIcon, getTokenName } from "./utils"; -import { useHistory, useLocation } from "react-router-dom"; -import bs58 from "bs58"; -import { TokenInfo } from "@solana/spl-token-registry"; +import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import { calculateDependentAmount, usePoolForBasket, PoolOperation } from './pools'; +import { cache, useAccountByMint } from './accounts'; +import { MintInfo } from '@solana/spl-token'; +import { useConnection, useConnectionConfig } from './connection'; +import { CurveType, PoolConfig, TokenAccount, DEFAULT_DENOMINATOR } from '../models'; +import { convert, getTokenIcon, getTokenName } from './utils'; +import { useHistory, useLocation } from 'react-router-dom'; +import bs58 from 'bs58'; +import { TokenInfo } from '@solana/spl-token-registry'; export interface CurrencyContextState { mintAddress: string; @@ -47,9 +32,7 @@ export interface CurrencyPairContextState { setOptions: (config: PoolConfig) => void; } -const CurrencyPairContext = React.createContext( - null -); +const CurrencyPairContext = React.createContext(null); const convertAmount = (amount: string, mint?: MintInfo) => { return parseFloat(amount) * Math.pow(10, mint?.decimals || 0); @@ -57,8 +40,8 @@ const convertAmount = (amount: string, mint?: MintInfo) => { export const useCurrencyLeg = (config: PoolConfig, defaultMint?: string) => { const { tokenMap } = useConnectionConfig(); - const [amount, setAmount] = useState(""); - const [mintAddress, setMintAddress] = useState(defaultMint || ""); + const [amount, setAmount] = useState(''); + const [mintAddress, setMintAddress] = useState(defaultMint || ''); const account = useAccountByMint(mintAddress); const mint = cache.getMint(mintAddress); @@ -75,19 +58,9 @@ export const useCurrencyLeg = (config: PoolConfig, defaultMint?: string) => { convertAmount: () => convertAmount(amount, mint), sufficientBalance: () => account !== undefined && - (convert(account, mint) >= parseFloat(amount) || - config.curveType === CurveType.ConstantProductWithOffset), + (convert(account, mint) >= parseFloat(amount) || config.curveType === CurveType.ConstantProductWithOffset), }), - [ - mintAddress, - account, - mint, - amount, - tokenMap, - setAmount, - setMintAddress, - config, - ] + [mintAddress, account, mint, amount, tokenMap, setAmount, setMintAddress, config] ); }; @@ -97,10 +70,8 @@ export function CurrencyPairProvider({ children = null as any }) { const history = useHistory(); const location = useLocation(); - const [lastTypedAccount, setLastTypedAccount] = useState(""); - const [poolOperation, setPoolOperation] = useState( - PoolOperation.Add - ); + const [lastTypedAccount, setLastTypedAccount] = useState(''); + const [poolOperation, setPoolOperation] = useState(PoolOperation.Add); const [options, setOptions] = useState({ curveType: CurveType.ConstantProduct, @@ -114,6 +85,8 @@ export function CurrencyPairProvider({ children = null as any }) { hostFeeNumerator: 20, hostFeeDenominator: 100, }, + freezeAuthority: undefined, + freezeAuthorityBitMask: 0, }); const base = useCurrencyLeg(options); @@ -131,10 +104,8 @@ export function CurrencyPairProvider({ children = null as any }) { const pool = usePoolForBasket([base.mintAddress, quote.mintAddress]); useEffect(() => { - const base = - tokens.find((t) => t.address === mintAddressA)?.symbol || mintAddressA; - const quote = - tokens.find((t) => t.address === mintAddressB)?.symbol || mintAddressB; + const base = tokens.find((t) => t.address === mintAddressA)?.symbol || mintAddressA; + const quote = tokens.find((t) => t.address === mintAddressB)?.symbol || mintAddressB; document.title = `Swap | Serum (${base}/${quote})`; }, [mintAddressA, mintAddressB, tokens, location]); @@ -142,12 +113,10 @@ export function CurrencyPairProvider({ children = null as any }) { // updates browser history on token changes useEffect(() => { // set history - const base = - tokens.find((t) => t.address === mintAddressA)?.symbol || mintAddressA; - const quote = - tokens.find((t) => t.address === mintAddressB)?.symbol || mintAddressB; + const base = tokens.find((t) => t.address === mintAddressA)?.symbol || mintAddressA; + const quote = tokens.find((t) => t.address === mintAddressB)?.symbol || mintAddressB; - if (base && quote && location.pathname.indexOf("info") < 0) { + if (base && quote && location.pathname.indexOf('info') < 0) { history.push({ search: `?pair=${base}-${quote}`, }); @@ -168,23 +137,16 @@ export function CurrencyPairProvider({ children = null as any }) { return; } - let { defaultBase, defaultQuote } = getDefaultTokens( - tokens, - location.search - ); + let { defaultBase, defaultQuote } = getDefaultTokens(tokens, location.search); if (!defaultBase || !defaultQuote) { return; } setMintAddressA( - tokens.find((t) => t.symbol === defaultBase)?.address || - (isValidAddress(defaultBase) ? defaultBase : "") || - "" + tokens.find((t) => t.symbol === defaultBase)?.address || (isValidAddress(defaultBase) ? defaultBase : '') || '' ); setMintAddressB( - tokens.find((t) => t.symbol === defaultQuote)?.address || - (isValidAddress(defaultQuote) ? defaultQuote : "") || - "" + tokens.find((t) => t.symbol === defaultQuote)?.address || (isValidAddress(defaultQuote) ? defaultQuote : '') || '' ); // mintAddressA and mintAddressB are not included here to prevent infinite loop // eslint-disable-next-line @@ -205,19 +167,13 @@ export function CurrencyPairProvider({ children = null as any }) { amount = parseFloat(amountB); } - const result = await calculateDependentAmount( - connection, - independent, - amount, - pool, - poolOperation - ); - if (typeof result === "string") { + const result = await calculateDependentAmount(connection, independent, amount, pool, poolOperation); + if (typeof result === 'string') { setDependent(result); } else if (result !== undefined && Number.isFinite(result)) { setDependent(result.toFixed(6)); } else { - setDependent(""); + setDependent(''); } } }, [ @@ -269,8 +225,8 @@ const isValidAddress = (address: string) => { }; function getDefaultTokens(tokens: TokenInfo[], search: string) { - let defaultBase = "SOL"; - let defaultQuote = "USDC"; + let defaultBase = 'SOL'; + let defaultQuote = 'USDC'; const nameToToken = tokens.reduce((map, item) => { map.set(item.symbol, item); @@ -279,9 +235,9 @@ function getDefaultTokens(tokens: TokenInfo[], search: string) { if (search) { const urlParams = new URLSearchParams(search); - const pair = urlParams.get("pair"); + const pair = urlParams.get('pair'); if (pair) { - let items = pair.split("-"); + let items = pair.split('-'); if (items.length > 1) { if (nameToToken.has(items[0]) || isValidAddress(items[0])) { diff --git a/src/utils/ids.tsx b/src/utils/ids.tsx index d98b56788..8fe0ce0c3 100644 --- a/src/utils/ids.tsx +++ b/src/utils/ids.tsx @@ -1,20 +1,14 @@ -import { PublicKey } from "@solana/web3.js"; -import { TokenSwapLayout, TokenSwapLayoutV1 } from "../models"; +import { PublicKey } from '@solana/web3.js'; +import { TokenSwapLayout } from '../models'; -export const WRAPPED_SOL_MINT = new PublicKey( - "So11111111111111111111111111111111111111112" -); -let TOKEN_PROGRAM_ID = new PublicKey( - "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" -); +export const WRAPPED_SOL_MINT = new PublicKey('So11111111111111111111111111111111111111112'); +let TOKEN_PROGRAM_ID = new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'); let SWAP_PROGRAM_ID: PublicKey; let SWAP_PROGRAM_LEGACY_IDS: PublicKey[]; let SWAP_PROGRAM_LAYOUT: any; -export const SWAP_PROGRAM_OWNER_FEE_ADDRESS = new PublicKey( - "HfoTxFR1Tm6kGmWgYWD6J7YHVy1UwqSULUGVLXkJqaKN" -); +export const SWAP_PROGRAM_OWNER_FEE_ADDRESS = new PublicKey('HfoTxFR1Tm6kGmWgYWD6J7YHVy1UwqSULUGVLXkJqaKN'); export const SWAP_HOST_FEE_ADDRESS = process.env.REACT_APP_SWAP_HOST_FEE_ADDRESS ? new PublicKey(`${process.env.REACT_APP_SWAP_HOST_FEE_ADDRESS}`) @@ -28,41 +22,41 @@ console.debug(`Owner address: ${SWAP_PROGRAM_OWNER_FEE_ADDRESS?.toBase58()}`); // legacy pools are used to show users contributions in those pools to allow for withdrawals of funds export const PROGRAM_IDS = [ { - name: "mainnet-beta", + name: 'mainnet-beta', swap: () => ({ current: { - pubkey: new PublicKey("SwaPpA9LAaLfeLi3a68M4DjnLqgtticKg6CnyNwgAC8"), + pubkey: new PublicKey('SwaPpA9LAaLfeLi3a68M4DjnLqgtticKg6CnyNwgAC8'), layout: TokenSwapLayout, }, - legacy: [new PublicKey("9qvG1zUp8xF1Bi4m6UdRNby1BAAuaDrUxSpv4CmRRMjL")], + legacy: [new PublicKey('9qvG1zUp8xF1Bi4m6UdRNby1BAAuaDrUxSpv4CmRRMjL')], }), }, { - name: "testnet", + name: 'testnet', swap: () => ({ current: { - pubkey: new PublicKey("SwaPpA9LAaLfeLi3a68M4DjnLqgtticKg6CnyNwgAC8"), + pubkey: new PublicKey('SwaPpA9LAaLfeLi3a68M4DjnLqgtticKg6CnyNwgAC8'), layout: TokenSwapLayout, }, legacy: [], }), }, { - name: "devnet", + name: 'devnet', swap: () => ({ current: { - pubkey: new PublicKey("SwaPpA9LAaLfeLi3a68M4DjnLqgtticKg6CnyNwgAC8"), + pubkey: new PublicKey('SwaPpA9LAaLfeLi3a68M4DjnLqgtticKg6CnyNwgAC8'), layout: TokenSwapLayout, }, - legacy: [new PublicKey("BSfTAcBdqmvX5iE2PW88WFNNp2DHhLUaBKk5WrnxVkcJ")], + legacy: [new PublicKey('BSfTAcBdqmvX5iE2PW88WFNNp2DHhLUaBKk5WrnxVkcJ')], }), }, { - name: "localnet", + name: 'localnet', swap: () => ({ current: { - pubkey: new PublicKey("369YmCWHGxznT7GGBhcLZDRcRoGWmGKFWdmtiPy78yj7"), - layout: TokenSwapLayoutV1, + pubkey: new PublicKey('369YmCWHGxznT7GGBhcLZDRcRoGWmGKFWdmtiPy78yj7'), + layout: TokenSwapLayout, }, legacy: [], }), diff --git a/src/utils/pools.tsx b/src/utils/pools.tsx index d09d20cd7..a3c0c950c 100644 --- a/src/utils/pools.tsx +++ b/src/utils/pools.tsx @@ -1,28 +1,10 @@ -import { - Account, - AccountInfo, - Connection, - PublicKey, - SystemProgram, - TransactionInstruction, -} from "@solana/web3.js"; -import { sendTransaction, useConnection } from "./connection"; -import { useEffect, useMemo, useState } from "react"; -import { Token, MintLayout, AccountLayout } from "@solana/spl-token"; -import { notify } from "./notifications"; -import { - cache, - getCachedAccount, - useUserAccounts, - useCachedPool, - getMultipleAccounts, -} from "./accounts"; -import { - programIds, - SWAP_HOST_FEE_ADDRESS, - SWAP_PROGRAM_OWNER_FEE_ADDRESS, - WRAPPED_SOL_MINT, -} from "./ids"; +import { Account, AccountInfo, Connection, PublicKey, SystemProgram, TransactionInstruction } from '@solana/web3.js'; +import { sendTransaction, useConnection } from './connection'; +import { useEffect, useMemo, useState } from 'react'; +import { Token, MintLayout, AccountLayout } from '@solana/spl-token'; +import { notify } from './notifications'; +import { cache, getCachedAccount, useUserAccounts, useCachedPool, getMultipleAccounts } from './accounts'; +import { programIds, SWAP_HOST_FEE_ADDRESS, SWAP_PROGRAM_OWNER_FEE_ADDRESS, WRAPPED_SOL_MINT } from './ids'; import { LiquidityComponent, PoolInfo, @@ -31,13 +13,14 @@ import { TokenSwapLayout, depositInstruction, withdrawInstruction, - TokenSwapLayoutLegacyV0 as TokenSwapLayoutV0, TokenSwapLayoutV1, swapInstruction, PoolConfig, depositExactOneInstruction, withdrawExactOneInstruction, -} from "./../models"; + setFreezeAuthorityBitMaskInstruction, + TokenSwapLayoutV0, +} from './../models'; const LIQUIDITY_TOKEN_PRECISION = 8; @@ -56,13 +39,13 @@ export const removeLiquidity = async ( pool?: PoolInfo ) => { if (!pool) { - throw new Error("Pool is required"); + throw new Error('Pool is required'); } notify({ - message: "Removing Liquidity...", - description: "Please review transactions to approve.", - type: "warn", + message: 'Removing Liquidity...', + description: 'Please review transactions to approve.', + type: 'warn', }); // TODO get min amounts based on total supply and liquidity @@ -70,16 +53,10 @@ export const removeLiquidity = async ( const minAmount1 = 0; const poolMint = await cache.queryMint(connection, pool.pubkeys.mint); - const accountA = await cache.queryAccount( - connection, - pool.pubkeys.holdingAccounts[0] - ); - const accountB = await cache.queryAccount( - connection, - pool.pubkeys.holdingAccounts[1] - ); + const accountA = await cache.queryAccount(connection, pool.pubkeys.holdingAccounts[0]); + const accountB = await cache.queryAccount(connection, pool.pubkeys.holdingAccounts[1]); if (!poolMint.mintAuthority) { - throw new Error("Mint doesnt have authority"); + throw new Error('Mint doesnt have authority'); } const authority = poolMint.mintAuthority; @@ -87,9 +64,7 @@ export const removeLiquidity = async ( const instructions: TransactionInstruction[] = []; const cleanupInstructions: TransactionInstruction[] = []; - const accountRentExempt = await connection.getMinimumBalanceForRentExemption( - AccountLayout.span - ); + const accountRentExempt = await connection.getMinimumBalanceForRentExemption(AccountLayout.span); const toAccounts: PublicKey[] = [ await findOrCreateAccountByMint( @@ -151,40 +126,25 @@ export const removeLiquidity = async ( const deleteAccount = liquidityAmount === account.info.amount.toNumber(); if (deleteAccount) { instructions.push( - Token.createCloseAccountInstruction( - programIds().token, - account.pubkey, - authority, - wallet.publicKey, - [] - ) + Token.createCloseAccountInstruction(programIds().token, account.pubkey, authority, wallet.publicKey, []) ); } - let tx = await sendTransaction( - connection, - wallet, - instructions.concat(cleanupInstructions), - signers - ); + let tx = await sendTransaction(connection, wallet, instructions.concat(cleanupInstructions), signers); if (deleteAccount) { cache.deleteAccount(account.pubkey); } notify({ - message: "Liquidity Returned. Thank you for your support.", - type: "success", + message: 'Liquidity Returned. Thank you for your support.', + type: 'success', description: `Transaction - ${tx}`, }); return [ - accountA.info.mint.equals(WRAPPED_SOL_MINT) - ? (wallet.publicKey as PublicKey) - : toAccounts[0], - accountB.info.mint.equals(WRAPPED_SOL_MINT) - ? (wallet.publicKey as PublicKey) - : toAccounts[1], + accountA.info.mint.equals(WRAPPED_SOL_MINT) ? (wallet.publicKey as PublicKey) : toAccounts[0], + accountB.info.mint.equals(WRAPPED_SOL_MINT) ? (wallet.publicKey as PublicKey) : toAccounts[1], ]; }; @@ -198,42 +158,33 @@ export const removeExactOneLiquidity = async ( pool?: PoolInfo ) => { if (!pool) { - throw new Error("Pool is required"); + throw new Error('Pool is required'); } notify({ - message: "Removing Liquidity...", - description: "Please review transactions to approve.", - type: "warn", + message: 'Removing Liquidity...', + description: 'Please review transactions to approve.', + type: 'warn', }); // Maximum number of LP tokens // needs to be different math because the new instruction const liquidityMaxAmount = liquidityAmount * (1 + SLIPPAGE); const poolMint = await cache.queryMint(connection, pool.pubkeys.mint); - const accountA = await cache.queryAccount( - connection, - pool.pubkeys.holdingAccounts[0] - ); - const accountB = await cache.queryAccount( - connection, - pool.pubkeys.holdingAccounts[1] - ); + const accountA = await cache.queryAccount(connection, pool.pubkeys.holdingAccounts[0]); + const accountB = await cache.queryAccount(connection, pool.pubkeys.holdingAccounts[1]); if (!poolMint.mintAuthority) { - throw new Error("Mint doesnt have authority"); + throw new Error('Mint doesnt have authority'); } - const tokenMatchAccount = - tokenMint === pool.pubkeys.holdingMints[0].toBase58() ? accountA : accountB; + const tokenMatchAccount = tokenMint === pool.pubkeys.holdingMints[0].toBase58() ? accountA : accountB; const authority = poolMint.mintAuthority; const signers: Account[] = []; const instructions: TransactionInstruction[] = []; const cleanupInstructions: TransactionInstruction[] = []; - const accountRentExempt = await connection.getMinimumBalanceForRentExemption( - AccountLayout.span - ); + const accountRentExempt = await connection.getMinimumBalanceForRentExemption(AccountLayout.span); const toAccount: PublicKey = await findOrCreateAccountByMint( wallet.publicKey, @@ -278,22 +229,15 @@ export const removeExactOneLiquidity = async ( ) ); - let tx = await sendTransaction( - connection, - wallet, - instructions.concat(cleanupInstructions), - signers - ); + let tx = await sendTransaction(connection, wallet, instructions.concat(cleanupInstructions), signers); notify({ - message: "Liquidity Returned. Thank you for your support.", - type: "success", + message: 'Liquidity Returned. Thank you for your support.', + type: 'success', description: `Transaction - ${tx}`, }); - return tokenMatchAccount.info.mint.equals(WRAPPED_SOL_MINT) - ? (wallet.publicKey as PublicKey) - : toAccount; + return tokenMatchAccount.info.mint.equals(WRAPPED_SOL_MINT) ? (wallet.publicKey as PublicKey) : toAccount; }; export const swap = async ( @@ -305,7 +249,7 @@ export const swap = async ( ) => { if (!pool || !components[0].account) { notify({ - type: "error", + type: 'error', message: `Pool doesn't exsist.`, description: `Swap trade cancelled`, }); @@ -318,18 +262,15 @@ export const swap = async ( const amountIn = components[0].amount; // these two should include slippage const minAmountOut = components[1].amount * (1 - SLIPPAGE); const holdingA = - pool.pubkeys.holdingMints[0]?.toBase58() === - components[0].account.info.mint.toBase58() + pool.pubkeys.holdingMints[0]?.toBase58() === components[0].account.info.mint.toBase58() ? pool.pubkeys.holdingAccounts[0] : pool.pubkeys.holdingAccounts[1]; const holdingB = - holdingA === pool.pubkeys.holdingAccounts[0] - ? pool.pubkeys.holdingAccounts[1] - : pool.pubkeys.holdingAccounts[0]; + holdingA === pool.pubkeys.holdingAccounts[0] ? pool.pubkeys.holdingAccounts[1] : pool.pubkeys.holdingAccounts[0]; const poolMint = await cache.queryMint(connection, pool.pubkeys.mint); if (!poolMint.mintAuthority || !pool.pubkeys.feeAccount) { - throw new Error("Mint doesnt have authority"); + throw new Error('Mint doesnt have authority'); } const authority = poolMint.mintAuthority; @@ -337,9 +278,7 @@ export const swap = async ( const cleanupInstructions: TransactionInstruction[] = []; const signers: Account[] = []; - const accountRentExempt = await connection.getMinimumBalanceForRentExemption( - AccountLayout.span - ); + const accountRentExempt = await connection.getMinimumBalanceForRentExemption(AccountLayout.span); const fromAccount = getWrappedAccount( instructions, @@ -407,16 +346,11 @@ export const swap = async ( ) ); - let tx = await sendTransaction( - connection, - wallet, - instructions.concat(cleanupInstructions), - signers - ); + let tx = await sendTransaction(connection, wallet, instructions.concat(cleanupInstructions), signers); notify({ - message: "Trade executed.", - type: "success", + message: 'Trade executed.', + type: 'success', description: `Transaction - ${tx}`, }); }; @@ -428,18 +362,13 @@ export const addLiquidity = async ( slippage: number, pool?: PoolInfo, options?: PoolConfig, - depositType: string = "both" + depositType: string = 'both' ) => { - if (depositType === "one" && pool) { - await _addLiquidityExactOneExistingPool( - pool, - components[0], - connection, - wallet - ); + if (depositType === 'one' && pool) { + await _addLiquidityExactOneExistingPool(pool, components[0], connection, wallet); } else if (!pool) { if (!options) { - throw new Error("Options are required to create new pool."); + throw new Error('Options are required to create new pool.'); } await _addLiquidityNewPool(wallet, connection, components, options); @@ -449,9 +378,7 @@ export const addLiquidity = async ( }; const getHoldings = (connection: Connection, accounts: string[]) => { - return accounts.map((acc) => - cache.queryAccount(connection, new PublicKey(acc)) - ); + return accounts.map((acc) => cache.queryAccount(connection, new PublicKey(acc))); }; const toPoolInfo = (item: any, program: PublicKey) => { @@ -462,9 +389,7 @@ const toPoolInfo = (item: any, program: PublicKey) => { program: program, mint, holdingMints: [] as PublicKey[], - holdingAccounts: [item.data.tokenAccountA, item.data.tokenAccountB].map( - (a) => new PublicKey(a) - ), + holdingAccounts: [item.data.tokenAccountA, item.data.tokenAccountB].map((a) => new PublicKey(a)), }, legacy: false, raw: item, @@ -484,9 +409,7 @@ export const usePools = () => { (await connection.getProgramAccounts(swapId)) .filter( (item) => - item.account.data.length === TokenSwapLayout.span || - item.account.data.length === TokenSwapLayoutV1.span || - item.account.data.length === TokenSwapLayoutV0.span + item.account.data.length === TokenSwapLayout.span || item.account.data.length === TokenSwapLayoutV1.span ) .map((item) => { let result = { @@ -496,52 +419,30 @@ export const usePools = () => { init: async () => {}, }; - const layout = - item.account.data.length === TokenSwapLayout.span - ? TokenSwapLayout - : item.account.data.length === TokenSwapLayoutV1.span - ? TokenSwapLayoutV1 - : TokenSwapLayoutV0; - - // handling of legacy layout can be removed soon... - if (layout === TokenSwapLayoutV0) { - result.data = layout.decode(item.account.data); - let pool = toPoolInfo(result, swapId); - pool.legacy = isLegacy; - poolsArray.push(pool as PoolInfo); - - result.init = async () => { - try { - // TODO: this is not great - // Ideally SwapLayout stores hash of all the mints to make finding of pool for a pair easier - const holdings = await Promise.all( - getHoldings(connection, [ - result.data.tokenAccountA, - result.data.tokenAccountB, - ]) - ); - - pool.pubkeys.holdingMints = [ - holdings[0].info.mint, - holdings[1].info.mint, - ] as PublicKey[]; - } catch (err) { - console.log(err); - } - }; - } else { - result.data = layout.decode(item.account.data); + let layout; + switch (item.account.data.length) { + case TokenSwapLayout.span: + layout = TokenSwapLayout; + break; + case TokenSwapLayoutV0.span: + layout = TokenSwapLayoutV0; + break; + case TokenSwapLayoutV1.span: + layout = TokenSwapLayoutV1; + break; + } - let pool = toPoolInfo(result, swapId); - pool.legacy = isLegacy; - pool.pubkeys.feeAccount = new PublicKey(result.data.feeAccount); - pool.pubkeys.holdingMints = [ - new PublicKey(result.data.mintA), - new PublicKey(result.data.mintB), - ] as PublicKey[]; + result.data = layout.decode(item.account.data); - poolsArray.push(pool as PoolInfo); - } + let pool = toPoolInfo(result, swapId); + pool.legacy = isLegacy; + pool.pubkeys.feeAccount = new PublicKey(result.data.feeAccount); + pool.pubkeys.holdingMints = [ + new PublicKey(result.data.mintA), + new PublicKey(result.data.mintB), + ] as PublicKey[]; + + poolsArray.push(pool as PoolInfo); return result; }); @@ -560,32 +461,29 @@ export const usePools = () => { // This will pre-cache all accounts used by pools // All those accounts are updated whenever there is a change - await getMultipleAccounts(connection, toQuery, "single").then( - ({ keys, array }) => { - return array.map((obj, index) => { - const pubKey = new PublicKey(keys[index]); - if (obj.data.length === AccountLayout.span) { - return cache.addAccount(pubKey, obj); - } else if (obj.data.length === MintLayout.span) { - if (!cache.getMint(pubKey)) { - return cache.addMint(pubKey, obj); - } + await getMultipleAccounts(connection, toQuery, 'single').then(({ keys, array }) => { + return array.map((obj, index) => { + const pubKey = new PublicKey(keys[index]); + if (obj.data.length === AccountLayout.span) { + return cache.addAccount(pubKey, obj); + } else if (obj.data.length === MintLayout.span) { + if (!cache.getMint(pubKey)) { + return cache.addMint(pubKey, obj); } + } - return obj; - }) as any[]; - } - ); + return obj; + }) as any[]; + }); return poolsArray; }; - Promise.all([ - queryPools(programIds().swap), - ...programIds().swap_legacy.map((leg) => queryPools(leg, true)), - ]).then((all) => { - setPools(all.flat()); - }); + Promise.all([queryPools(programIds().swap), ...programIds().swap_legacy.map((leg) => queryPools(leg, true))]).then( + (all) => { + setPools(all.flat()); + } + ); }, [connection]); useEffect(() => { @@ -601,9 +499,7 @@ export const usePools = () => { pubkey: new PublicKey(id), }; - const index = - pools && - pools.findIndex((p) => p.pubkeys.account.toBase58() === id); + const index = pools && pools.findIndex((p) => p.pubkeys.account.toBase58() === id); if (index && index >= 0 && pools) { // TODO: check if account is empty? @@ -622,7 +518,7 @@ export const usePools = () => { } } }, - "singleGossip" + 'singleGossip' ); return () => { @@ -659,17 +555,13 @@ export const usePoolForBasket = (mints: (string | undefined)[]) => { cache.queryAccount(connection, p.pubkeys.holdingAccounts[0]), cache.queryAccount(connection, p.pubkeys.holdingAccounts[1]), ]); - const amount = - (account0.info.amount.toNumber() || 0) + - (account1.info.amount.toNumber() || 0); + const amount = (account0.info.amount.toNumber() || 0) + (account1.info.amount.toNumber() || 0); if (amount > 0) { poolQuantities[i.toString()] = amount; } } if (Object.keys(poolQuantities).length > 0) { - const sorted = Object.entries( - poolQuantities - ).sort(([pool0Idx, amount0], [pool1Idx, amount1]) => + const sorted = Object.entries(poolQuantities).sort(([pool0Idx, amount0], [pool1Idx, amount1]) => amount0 > amount1 ? -1 : 1 ); const bestPool = matchingPool[parseInt(sorted[0][0])]; @@ -725,39 +617,30 @@ async function _addLiquidityExistingPool( wallet: any ) { notify({ - message: "Adding Liquidity...", - description: "Please review transactions to approve.", - type: "warn", + message: 'Adding Liquidity...', + description: 'Please review transactions to approve.', + type: 'warn', }); const poolMint = await cache.queryMint(connection, pool.pubkeys.mint); if (!poolMint.mintAuthority) { - throw new Error("Mint doesnt have authority"); + throw new Error('Mint doesnt have authority'); } if (!pool.pubkeys.feeAccount) { - throw new Error("Invald fee account"); + throw new Error('Invald fee account'); } - const accountA = await cache.queryAccount( - connection, - pool.pubkeys.holdingAccounts[0] - ); - const accountB = await cache.queryAccount( - connection, - pool.pubkeys.holdingAccounts[1] - ); + const accountA = await cache.queryAccount(connection, pool.pubkeys.holdingAccounts[0]); + const accountB = await cache.queryAccount(connection, pool.pubkeys.holdingAccounts[1]); const reserve0 = accountA.info.amount.toNumber(); const reserve1 = accountB.info.amount.toNumber(); - const fromA = - accountA.info.mint.toBase58() === components[0].mintAddress - ? components[0] - : components[1]; + const fromA = accountA.info.mint.toBase58() === components[0].mintAddress ? components[0] : components[1]; const fromB = fromA === components[0] ? components[1] : components[0]; if (!fromA.account || !fromB.account) { - throw new Error("Missing account info."); + throw new Error('Missing account info.'); } const supply = poolMint.supply.toNumber(); @@ -778,9 +661,7 @@ async function _addLiquidityExistingPool( const signers: Account[] = []; - const accountRentExempt = await connection.getMinimumBalanceForRentExemption( - AccountLayout.span - ); + const accountRentExempt = await connection.getMinimumBalanceForRentExemption(AccountLayout.span); const fromKeyA = getWrappedAccount( instructions, cleanupInstructions, @@ -853,16 +734,11 @@ async function _addLiquidityExistingPool( ) ); - let tx = await sendTransaction( - connection, - wallet, - instructions.concat(cleanupInstructions), - signers - ); + let tx = await sendTransaction(connection, wallet, instructions.concat(cleanupInstructions), signers); notify({ - message: "Pool Funded. Happy trading.", - type: "success", + message: 'Pool Funded. Happy trading.', + type: 'success', description: `Transaction - ${tx}`, }); } @@ -874,33 +750,27 @@ async function _addLiquidityExactOneExistingPool( wallet: any ) { notify({ - message: "Adding Liquidity...", - description: "Please review transactions to approve.", - type: "warn", + message: 'Adding Liquidity...', + description: 'Please review transactions to approve.', + type: 'warn', }); const poolMint = await cache.queryMint(connection, pool.pubkeys.mint); if (!poolMint.mintAuthority) { - throw new Error("Mint doesnt have authority"); + throw new Error('Mint doesnt have authority'); } if (!pool.pubkeys.feeAccount) { - throw new Error("Invald fee account"); + throw new Error('Invald fee account'); } - const accountA = await cache.queryAccount( - connection, - pool.pubkeys.holdingAccounts[0] - ); - const accountB = await cache.queryAccount( - connection, - pool.pubkeys.holdingAccounts[1] - ); + const accountA = await cache.queryAccount(connection, pool.pubkeys.holdingAccounts[0]); + const accountB = await cache.queryAccount(connection, pool.pubkeys.holdingAccounts[1]); const from = component; if (!from.account) { - throw new Error("Missing account info."); + throw new Error('Missing account info.'); } const reserve = accountA.info.mint.toBase58() === from.mintAddress @@ -924,9 +794,7 @@ async function _addLiquidityExactOneExistingPool( const signers: Account[] = []; - const accountRentExempt = await connection.getMinimumBalanceForRentExemption( - AccountLayout.span - ); + const accountRentExempt = await connection.getMinimumBalanceForRentExemption(AccountLayout.span); const fromKey = getWrappedAccount( instructions, cleanupInstructions, @@ -980,16 +848,11 @@ async function _addLiquidityExactOneExistingPool( ) ); - let tx = await sendTransaction( - connection, - wallet, - instructions.concat(cleanupInstructions), - signers - ); + let tx = await sendTransaction(connection, wallet, instructions.concat(cleanupInstructions), signers); notify({ - message: "Pool Funded. Happy trading.", - type: "success", + message: 'Pool Funded. Happy trading.', + type: 'success', description: `Transaction - ${tx}`, }); } @@ -1018,28 +881,13 @@ function findOrCreateAccountByMint( toAccount = account.pubkey; } else { // creating depositor pool account - const newToAccount = createSplAccount( - instructions, - payer, - accountRentExempt, - mint, - owner, - AccountLayout.span - ); + const newToAccount = createSplAccount(instructions, payer, accountRentExempt, mint, owner, AccountLayout.span); toAccount = newToAccount.publicKey; signers.push(newToAccount); if (isWrappedSol) { - cleanupInstructions.push( - Token.createCloseAccountInstruction( - programIds().token, - toAccount, - payer, - payer, - [] - ) - ); + cleanupInstructions.push(Token.createCloseAccountInstruction(programIds().token, toAccount, payer, payer, [])); } } @@ -1051,9 +899,7 @@ function estimateProceedsFromInput( proceedsQuantityInPool: number, inputAmount: number ): number { - return ( - (proceedsQuantityInPool * inputAmount) / (inputQuantityInPool + inputAmount) - ); + return (proceedsQuantityInPool * inputAmount) / (inputQuantityInPool + inputAmount); } function estimateInputFromProceeds( @@ -1062,13 +908,10 @@ function estimateInputFromProceeds( proceedsAmount: number ): number | string { if (proceedsAmount >= proceedsQuantityInPool) { - return "Not possible"; + return 'Not possible'; } - return ( - (inputQuantityInPool * proceedsAmount) / - (proceedsQuantityInPool - proceedsAmount) - ); + return (inputQuantityInPool * proceedsAmount) / (proceedsQuantityInPool - proceedsAmount); } export enum PoolOperation { @@ -1085,20 +928,14 @@ export async function calculateDependentAmount( op: PoolOperation ): Promise { const poolMint = await cache.queryMint(connection, pool.pubkeys.mint); - const accountA = await cache.queryAccount( - connection, - pool.pubkeys.holdingAccounts[0] - ); + const accountA = await cache.queryAccount(connection, pool.pubkeys.holdingAccounts[0]); const amountA = accountA.info.amount.toNumber(); - const accountB = await cache.queryAccount( - connection, - pool.pubkeys.holdingAccounts[1] - ); + const accountB = await cache.queryAccount(connection, pool.pubkeys.holdingAccounts[1]); let amountB = accountB.info.amount.toNumber(); if (!poolMint.mintAuthority) { - throw new Error("Mint doesnt have authority"); + throw new Error('Mint doesnt have authority'); } if (poolMint.supply.eqn(0)) { @@ -1120,14 +957,8 @@ export async function calculateDependentAmount( } const isFirstIndependent = accountA.info.mint.toBase58() === independent; - const depPrecision = Math.pow( - 10, - isFirstIndependent ? mintB.decimals : mintA.decimals - ); - const indPrecision = Math.pow( - 10, - isFirstIndependent ? mintA.decimals : mintB.decimals - ); + const depPrecision = Math.pow(10, isFirstIndependent ? mintB.decimals : mintA.decimals); + const indPrecision = Math.pow(10, isFirstIndependent ? mintA.decimals : mintB.decimals); const indAdjustedAmount = amount * indPrecision; let indBasketQuantity = isFirstIndependent ? amountA : amountB; @@ -1142,27 +973,18 @@ export async function calculateDependentAmount( } else { switch (+op) { case PoolOperation.Add: - depAdjustedAmount = - (depBasketQuantity / indBasketQuantity) * indAdjustedAmount; + depAdjustedAmount = (depBasketQuantity / indBasketQuantity) * indAdjustedAmount; break; case PoolOperation.SwapGivenProceeds: - depAdjustedAmount = estimateInputFromProceeds( - depBasketQuantity, - indBasketQuantity, - indAdjustedAmount - ); + depAdjustedAmount = estimateInputFromProceeds(depBasketQuantity, indBasketQuantity, indAdjustedAmount); break; case PoolOperation.SwapGivenInput: - depAdjustedAmount = estimateProceedsFromInput( - indBasketQuantity, - depBasketQuantity, - indAdjustedAmount - ); + depAdjustedAmount = estimateProceedsFromInput(indBasketQuantity, depBasketQuantity, indAdjustedAmount); break; } } - if (typeof depAdjustedAmount === "string") { + if (typeof depAdjustedAmount === 'string') { return depAdjustedAmount; } if (depAdjustedAmount === undefined) { @@ -1179,16 +1001,16 @@ async function _addLiquidityNewPool( options: PoolConfig ) { notify({ - message: "Creating new pool...", - description: "Please review transactions to approve.", - type: "warn", + message: 'Creating new pool...', + description: 'Please review transactions to approve.', + type: 'warn', }); if (components.some((c) => !c.account)) { notify({ - message: "You need to have balance for all legs in the basket...", - description: "Please review inputs.", - type: "error", + message: 'You need to have balance for all legs in the basket...', + description: 'Please review inputs.', + type: 'error', }); return; } @@ -1202,9 +1024,7 @@ async function _addLiquidityNewPool( SystemProgram.createAccount({ fromPubkey: wallet.publicKey, newAccountPubkey: liquidityTokenMint.publicKey, - lamports: await connection.getMinimumBalanceForRentExemption( - MintLayout.span - ), + lamports: await connection.getMinimumBalanceForRentExemption(MintLayout.span), space: MintLayout.span, programId: programIds().token, }) @@ -1231,9 +1051,7 @@ async function _addLiquidityNewPool( ); // Create holding accounts for - const accountRentExempt = await connection.getMinimumBalanceForRentExemption( - AccountLayout.span - ); + const accountRentExempt = await connection.getMinimumBalanceForRentExemption(AccountLayout.span); const holdingAccounts: Account[] = []; let signers: Account[] = []; @@ -1245,14 +1063,7 @@ async function _addLiquidityNewPool( const mintPublicKey = leg.account.info.mint; // component account to store tokens I of N in liquidity poll holdingAccounts.push( - createSplAccount( - instructions, - wallet.publicKey, - accountRentExempt, - mintPublicKey, - authority, - AccountLayout.span - ) + createSplAccount(instructions, wallet.publicKey, accountRentExempt, mintPublicKey, authority, AccountLayout.span) ); }); @@ -1287,15 +1098,15 @@ async function _addLiquidityNewPool( ]); notify({ - message: "Accounts created", + message: 'Accounts created', description: `Transaction ${tx}`, - type: "success", + type: 'success', }); notify({ - message: "Adding Liquidity...", - description: "Please review transactions to approve.", - type: "warn", + message: 'Adding Liquidity...', + description: 'Please review transactions to approve.', + type: 'warn', }); signers = []; @@ -1306,9 +1117,7 @@ async function _addLiquidityNewPool( SystemProgram.createAccount({ fromPubkey: wallet.publicKey, newAccountPubkey: tokenSwapAccount.publicKey, - lamports: await connection.getMinimumBalanceForRentExemption( - programIds().swapLayout.span - ), + lamports: await connection.getMinimumBalanceForRentExemption(programIds().swapLayout.span), space: programIds().swapLayout.span, programId: programIds().swap, }) @@ -1360,16 +1169,44 @@ async function _addLiquidityNewPool( // All instructions didn't fit in single transaction // initialize and provide inital liquidity to swap in 2nd (this prevents loss of funds) - tx = await sendTransaction( - connection, - wallet, - instructions.concat(cleanupInstructions), - [tokenSwapAccount, ...signers] - ); + tx = await sendTransaction(connection, wallet, instructions.concat(cleanupInstructions), [ + tokenSwapAccount, + ...signers, + ]); notify({ - message: "Pool Funded. Happy trading.", - type: "success", + message: 'Pool Funded. Happy trading.', + type: 'success', + description: `Transaction - ${tx}`, + }); +} + +export async function setFreezeAuthorityBitMask( + wallet: any, + connection: Connection, + tokenSwap: PublicKey, + freezeAuthorityBitMask: number +) { + notify({ + message: 'Updating freeze authority bit mask...', + description: 'Please review transactions to approve.', + type: 'warn', + }); + + let instructions: TransactionInstruction[] = []; + + // Create account for pool liquidity token + instructions.push( + setFreezeAuthorityBitMaskInstruction(tokenSwap, wallet.publicKey, programIds().swap, freezeAuthorityBitMask) + ); // create all accounts in one transaction + + // All instructions didn't fit in single transaction + // initialize and provide inital liquidity to swap in 2nd (this prevents loss of funds) + let tx = await sendTransaction(connection, wallet, instructions, [], true); + + notify({ + message: 'Freeze authority changed. Happy trading.', + type: 'success', description: `Transaction - ${tx}`, }); } @@ -1388,19 +1225,10 @@ function approveAmount( const transferAuthority = new Account(); instructions.push( - Token.createApproveInstruction( - tokenProgram, - account, - delegate ?? transferAuthority.publicKey, - owner, - [], - amount - ) + Token.createApproveInstruction(tokenProgram, account, delegate ?? transferAuthority.publicKey, owner, [], amount) ); - cleanupInstructions.push( - Token.createRevokeInstruction(tokenProgram, account, owner, []) - ); + cleanupInstructions.push(Token.createRevokeInstruction(tokenProgram, account, owner, [])); return transferAuthority; } @@ -1428,23 +1256,10 @@ function getWrappedAccount( }) ); - instructions.push( - Token.createInitAccountInstruction( - programIds().token, - WRAPPED_SOL_MINT, - account.publicKey, - payer - ) - ); + instructions.push(Token.createInitAccountInstruction(programIds().token, WRAPPED_SOL_MINT, account.publicKey, payer)); cleanupInstructions.push( - Token.createCloseAccountInstruction( - programIds().token, - account.publicKey, - payer, - payer, - [] - ) + Token.createCloseAccountInstruction(programIds().token, account.publicKey, payer, payer, []) ); signers.push(account); @@ -1471,14 +1286,7 @@ function createSplAccount( }) ); - instructions.push( - Token.createInitAccountInstruction( - programIds().token, - mint, - account.publicKey, - owner - ) - ); + instructions.push(Token.createInitAccountInstruction(programIds().token, mint, account.publicKey, owner)); return account; }