From 16d6cd60a855be3002c2a7530182154af3b4354c Mon Sep 17 00:00:00 2001 From: bartosz-lipinski <264380+bartosz-lipinski@users.noreply.github.com> Date: Tue, 5 Jan 2021 11:35:02 -0600 Subject: [PATCH] chore: cleanup unused code --- src/components/Layout/index.tsx | 4 +- src/contexts/accounts.tsx | 3 - src/models/tokenSwap.ts | 370 ------------- src/utils/pools.ts | 881 +----------------------------- src/views/borrowReserve/index.tsx | 2 +- 5 files changed, 9 insertions(+), 1251 deletions(-) diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx index 7313c00..e01ca67 100644 --- a/src/components/Layout/index.tsx +++ b/src/components/Layout/index.tsx @@ -131,12 +131,12 @@ export const AppLayout = React.memo((props: any) => { }> - + Fork , }> - + Github diff --git a/src/contexts/accounts.tsx b/src/contexts/accounts.tsx index 592965a..c2a42b7 100644 --- a/src/contexts/accounts.tsx +++ b/src/contexts/accounts.tsx @@ -7,7 +7,6 @@ import { PoolInfo, TokenAccount } from './../models'; import { chunks } from './../utils/utils'; import { EventEmitter } from './../utils/eventEmitter'; import { useUserAccounts } from '../hooks/useUserAccounts'; -import { usePools } from '../utils/pools'; import { WRAPPED_SOL_MINT, programIds } from '../utils/ids'; const AccountsContext = React.createContext(null); @@ -347,7 +346,6 @@ export function AccountsProvider({ children = null as any }) { const [tokenAccounts, setTokenAccounts] = useState([]); const [userAccounts, setUserAccounts] = useState([]); const { nativeAccount } = UseNativeAccount(); - const { pools } = usePools(); const selectUserAccounts = useCallback(() => { return cache @@ -419,7 +417,6 @@ export function AccountsProvider({ children = null as any }) { diff --git a/src/models/tokenSwap.ts b/src/models/tokenSwap.ts index 0f7653b..e095ddd 100644 --- a/src/models/tokenSwap.ts +++ b/src/models/tokenSwap.ts @@ -1,9 +1,5 @@ -import { Numberu64 } from '@solana/spl-token-swap'; -import { PublicKey, Account, TransactionInstruction } from '@solana/web3.js'; import * as BufferLayout from 'buffer-layout'; -import { programIds } from '../utils/ids'; import { publicKey, uint64 } from '../utils/layout'; -import { CurveType, PoolConfig } from './pool'; export { TokenSwap } from '@solana/spl-token-swap'; @@ -70,369 +66,3 @@ export const TokenSwapLayout: typeof BufferLayout.Structure = BufferLayout.struc FEE_LAYOUT, CURVE_NODE, ]); - -export const createInitSwapInstruction = ( - tokenSwapAccount: Account, - authority: PublicKey, - tokenAccountA: PublicKey, - tokenAccountB: PublicKey, - tokenPool: PublicKey, - feeAccount: PublicKey, - destinationAccount: PublicKey, - tokenProgramId: PublicKey, - swapProgramId: PublicKey, - nonce: number, - config: PoolConfig -): TransactionInstruction => { - const keys = [ - { pubkey: tokenSwapAccount.publicKey, isSigner: false, isWritable: true }, - { pubkey: authority, isSigner: false, isWritable: false }, - { pubkey: tokenAccountA, isSigner: false, isWritable: false }, - { pubkey: tokenAccountB, isSigner: false, isWritable: false }, - { pubkey: tokenPool, isSigner: false, isWritable: true }, - { pubkey: feeAccount, isSigner: false, isWritable: false }, - { pubkey: destinationAccount, isSigner: false, isWritable: true }, - { pubkey: tokenProgramId, isSigner: false, isWritable: false }, - ]; - - let data = Buffer.alloc(1024); - { - const isLatestLayout = programIds().swapLayout === TokenSwapLayout; - if (isLatestLayout) { - 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'), - ]; - - if (config.curveType === CurveType.ConstantProductWithOffset) { - 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')); - } else { - fields.push(BufferLayout.blob(32, 'padding')); - } - - const commandDataLayout = BufferLayout.struct(fields); - - const { fees, ...rest } = config; - - const encodeLength = commandDataLayout.encode( - { - instruction: 0, // InitializeSwap instruction - nonce, - ...fees, - ...rest, - }, - 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'), - ]); - - const encodeLength = commandDataLayout.encode( - { - instruction: 0, // InitializeSwap instruction - nonce, - curveType: config.curveType, - tradeFeeNumerator: config.fees.tradeFeeNumerator, - tradeFeeDenominator: config.fees.tradeFeeDenominator, - ownerTradeFeeNumerator: config.fees.ownerTradeFeeNumerator, - ownerTradeFeeDenominator: config.fees.ownerTradeFeeDenominator, - ownerWithdrawFeeNumerator: config.fees.ownerWithdrawFeeNumerator, - ownerWithdrawFeeDenominator: config.fees.ownerWithdrawFeeDenominator, - }, - data - ); - data = data.slice(0, encodeLength); - } - } - - return new TransactionInstruction({ - keys, - programId: swapProgramId, - data, - }); -}; - -export const depositPoolInstruction = ( - tokenSwap: PublicKey, - authority: PublicKey, - sourceA: PublicKey, - sourceB: PublicKey, - intoA: PublicKey, - intoB: PublicKey, - poolToken: PublicKey, - poolAccount: PublicKey, - swapProgramId: PublicKey, - tokenProgramId: PublicKey, - poolTokenAmount: number | Numberu64, - maximumTokenA: number | Numberu64, - maximumTokenB: number | Numberu64 -): TransactionInstruction => { - const dataLayout = BufferLayout.struct([ - BufferLayout.u8('instruction'), - uint64('poolTokenAmount'), - uint64('maximumTokenA'), - uint64('maximumTokenB'), - ]); - - const data = Buffer.alloc(dataLayout.span); - dataLayout.encode( - { - instruction: 2, // Deposit instruction - poolTokenAmount: new Numberu64(poolTokenAmount).toBuffer(), - maximumTokenA: new Numberu64(maximumTokenA).toBuffer(), - maximumTokenB: new Numberu64(maximumTokenB).toBuffer(), - }, - data - ); - - const keys = [ - { pubkey: tokenSwap, isSigner: false, isWritable: false }, - { pubkey: authority, isSigner: false, isWritable: false }, - { pubkey: sourceA, isSigner: false, isWritable: true }, - { pubkey: sourceB, isSigner: false, isWritable: true }, - { pubkey: intoA, isSigner: false, isWritable: true }, - { pubkey: intoB, isSigner: false, isWritable: true }, - { pubkey: poolToken, isSigner: false, isWritable: true }, - { pubkey: poolAccount, isSigner: false, isWritable: true }, - { pubkey: tokenProgramId, isSigner: false, isWritable: false }, - ]; - return new TransactionInstruction({ - keys, - programId: swapProgramId, - data, - }); -}; - -export const depositExactOneInstruction = ( - tokenSwap: PublicKey, - authority: PublicKey, - source: PublicKey, - intoA: PublicKey, - intoB: PublicKey, - poolToken: PublicKey, - poolAccount: PublicKey, - swapProgramId: PublicKey, - tokenProgramId: PublicKey, - sourceTokenAmount: number | Numberu64, - minimumPoolTokenAmount: number | Numberu64 -): TransactionInstruction => { - const dataLayout = BufferLayout.struct([ - BufferLayout.u8('instruction'), - uint64('sourceTokenAmount'), - uint64('minimumPoolTokenAmount'), - ]); - - const data = Buffer.alloc(dataLayout.span); - dataLayout.encode( - { - instruction: 4, // DepositExactOne instruction - sourceTokenAmount: new Numberu64(sourceTokenAmount).toBuffer(), - minimumPoolTokenAmount: new Numberu64(minimumPoolTokenAmount).toBuffer(), - }, - data - ); - - const keys = [ - { pubkey: tokenSwap, isSigner: false, isWritable: false }, - { pubkey: authority, isSigner: false, isWritable: false }, - { pubkey: source, isSigner: false, isWritable: true }, - { pubkey: intoA, isSigner: false, isWritable: true }, - { pubkey: intoB, isSigner: false, isWritable: true }, - { pubkey: poolToken, isSigner: false, isWritable: true }, - { pubkey: poolAccount, isSigner: false, isWritable: true }, - { pubkey: tokenProgramId, isSigner: false, isWritable: false }, - ]; - return new TransactionInstruction({ - keys, - programId: swapProgramId, - data, - }); -}; - -export const withdrawPoolInstruction = ( - tokenSwap: PublicKey, - authority: PublicKey, - poolMint: PublicKey, - feeAccount: PublicKey | undefined, - sourcePoolAccount: PublicKey, - fromA: PublicKey, - fromB: PublicKey, - userAccountA: PublicKey, - userAccountB: PublicKey, - swapProgramId: PublicKey, - tokenProgramId: PublicKey, - poolTokenAmount: number | Numberu64, - minimumTokenA: number | Numberu64, - minimumTokenB: number | Numberu64 -): TransactionInstruction => { - const dataLayout = BufferLayout.struct([ - BufferLayout.u8('instruction'), - uint64('poolTokenAmount'), - uint64('minimumTokenA'), - uint64('minimumTokenB'), - ]); - - const data = Buffer.alloc(dataLayout.span); - dataLayout.encode( - { - instruction: 3, // Withdraw instruction - poolTokenAmount: new Numberu64(poolTokenAmount).toBuffer(), - minimumTokenA: new Numberu64(minimumTokenA).toBuffer(), - minimumTokenB: new Numberu64(minimumTokenB).toBuffer(), - }, - data - ); - - const keys = [ - { pubkey: tokenSwap, isSigner: false, isWritable: false }, - { pubkey: authority, isSigner: false, isWritable: false }, - { pubkey: poolMint, isSigner: false, isWritable: true }, - { pubkey: sourcePoolAccount, isSigner: false, isWritable: true }, - { pubkey: fromA, isSigner: false, isWritable: true }, - { pubkey: fromB, isSigner: false, isWritable: true }, - { pubkey: userAccountA, isSigner: false, isWritable: true }, - { pubkey: userAccountB, isSigner: false, isWritable: true }, - ]; - - if (feeAccount) { - keys.push({ pubkey: feeAccount, isSigner: false, isWritable: true }); - } - keys.push({ pubkey: tokenProgramId, isSigner: false, isWritable: false }); - - return new TransactionInstruction({ - keys, - programId: swapProgramId, - data, - }); -}; - -export const withdrawExactOneInstruction = ( - tokenSwap: PublicKey, - authority: PublicKey, - poolMint: PublicKey, - sourcePoolAccount: PublicKey, - fromA: PublicKey, - fromB: PublicKey, - userAccount: PublicKey, - feeAccount: PublicKey | undefined, - swapProgramId: PublicKey, - tokenProgramId: PublicKey, - sourceTokenAmount: number | Numberu64, - maximumTokenAmount: number | Numberu64 -): TransactionInstruction => { - const dataLayout = BufferLayout.struct([ - BufferLayout.u8('instruction'), - uint64('sourceTokenAmount'), - uint64('maximumTokenAmount'), - ]); - - const data = Buffer.alloc(dataLayout.span); - dataLayout.encode( - { - instruction: 5, // WithdrawExactOne instruction - sourceTokenAmount: new Numberu64(sourceTokenAmount).toBuffer(), - maximumTokenAmount: new Numberu64(maximumTokenAmount).toBuffer(), - }, - data - ); - - const keys = [ - { pubkey: tokenSwap, isSigner: false, isWritable: false }, - { pubkey: authority, isSigner: false, isWritable: false }, - { pubkey: poolMint, isSigner: false, isWritable: true }, - { pubkey: sourcePoolAccount, isSigner: false, isWritable: true }, - { pubkey: fromA, isSigner: false, isWritable: true }, - { pubkey: fromB, isSigner: false, isWritable: true }, - { pubkey: userAccount, isSigner: false, isWritable: true }, - ]; - - if (feeAccount) { - keys.push({ pubkey: feeAccount, isSigner: false, isWritable: true }); - } - keys.push({ pubkey: tokenProgramId, isSigner: false, isWritable: false }); - - return new TransactionInstruction({ - keys, - programId: swapProgramId, - data, - }); -}; - -export const swapInstruction = ( - tokenSwap: PublicKey, - authority: PublicKey, - userSource: PublicKey, - poolSource: PublicKey, - poolDestination: PublicKey, - userDestination: PublicKey, - poolMint: PublicKey, - feeAccount: PublicKey, - swapProgramId: PublicKey, - tokenProgramId: PublicKey, - amountIn: number | Numberu64, - minimumAmountOut: number | Numberu64, - programOwner?: PublicKey -): TransactionInstruction => { - const dataLayout = BufferLayout.struct([ - BufferLayout.u8('instruction'), - uint64('amountIn'), - uint64('minimumAmountOut'), - ]); - - const keys = [ - { pubkey: tokenSwap, isSigner: false, isWritable: false }, - { pubkey: authority, isSigner: false, isWritable: false }, - { pubkey: userSource, isSigner: false, isWritable: true }, - { pubkey: poolSource, isSigner: false, isWritable: true }, - { pubkey: poolDestination, isSigner: false, isWritable: true }, - { pubkey: userDestination, isSigner: false, isWritable: true }, - { pubkey: poolMint, isSigner: false, isWritable: true }, - { pubkey: feeAccount, isSigner: false, isWritable: true }, - { pubkey: tokenProgramId, isSigner: false, isWritable: false }, - ]; - - // optional depending on the build of token-swap program - if (programOwner) { - keys.push({ pubkey: programOwner, isSigner: false, isWritable: true }); - } - - const data = Buffer.alloc(dataLayout.span); - dataLayout.encode( - { - instruction: 1, // Swap instruction - amountIn: new Numberu64(amountIn).toBuffer(), - minimumAmountOut: new Numberu64(minimumAmountOut).toBuffer(), - }, - data - ); - - return new TransactionInstruction({ - keys, - programId: swapProgramId, - data, - }); -}; diff --git a/src/utils/pools.ts b/src/utils/pools.ts index 0a6ded9..f3ade1e 100644 --- a/src/utils/pools.ts +++ b/src/utils/pools.ts @@ -1,350 +1,19 @@ -import { Account, Connection, PublicKey, SystemProgram, TransactionInstruction } from '@solana/web3.js'; +import { Connection, PublicKey } from '@solana/web3.js'; import { useEffect, useMemo, useState } from 'react'; -import { Token, MintLayout, AccountLayout } from '@solana/spl-token'; -import { notify } from './notifications'; -import { programIds, SWAP_HOST_FEE_ADDRESS, SWAP_PROGRAM_OWNER_FEE_ADDRESS, WRAPPED_SOL_MINT } from './ids'; +import { MintLayout, AccountLayout } from '@solana/spl-token'; +import { programIds } from './ids'; import { - LiquidityComponent, PoolInfo, - TokenAccount, - createInitSwapInstruction, TokenSwapLayout, - depositPoolInstruction, TokenSwapLayoutLegacyV0 as TokenSwapLayoutV0, TokenSwapLayoutV1, - swapInstruction, - PoolConfig, - depositExactOneInstruction, - withdrawExactOneInstruction, - withdrawPoolInstruction, } from './../models'; -import { sendTransaction, useConnection } from '../contexts/connection'; -import { cache, getCachedAccount, getMultipleAccounts, TokenAccountParser, useCachedPool } from '../contexts/accounts'; -import { useUserAccounts } from '../hooks/useUserAccounts'; - -const LIQUIDITY_TOKEN_PRECISION = 8; +import { useConnection } from '../contexts/connection'; +import { cache, getMultipleAccounts, TokenAccountParser } from '../contexts/accounts'; export const LIQUIDITY_PROVIDER_FEE = 0.003; export const SERUM_FEE = 0.0005; -export const removeLiquidity = async ( - connection: Connection, - wallet: any, - liquidityAmount: number, - account: TokenAccount, - pool?: PoolInfo -) => { - if (!pool) { - throw new Error('Pool is required'); - } - - notify({ - message: 'Removing Liquidity...', - description: 'Please review transactions to approve.', - type: 'warn', - }); - - // TODO get min amounts based on total supply and liquidity - const minAmount0 = 0; - const minAmount1 = 0; - - const poolMint = await cache.queryMint(connection, pool.pubkeys.mint); - const accountA = await cache.query(connection, pool.pubkeys.holdingAccounts[0]); - const accountB = await cache.query(connection, pool.pubkeys.holdingAccounts[1]); - if (!poolMint.mintAuthority) { - throw new Error('Mint doesnt have authority'); - } - const authority = poolMint.mintAuthority; - - const signers: Account[] = []; - const instructions: TransactionInstruction[] = []; - const cleanupInstructions: TransactionInstruction[] = []; - - const accountRentExempt = await connection.getMinimumBalanceForRentExemption(AccountLayout.span); - - const toAccounts: PublicKey[] = [ - await findOrCreateAccountByMint( - wallet.publicKey, - wallet.publicKey, - instructions, - cleanupInstructions, - accountRentExempt, - accountA.info.mint, - signers - ), - await findOrCreateAccountByMint( - wallet.publicKey, - wallet.publicKey, - instructions, - cleanupInstructions, - accountRentExempt, - accountB.info.mint, - signers - ), - ]; - - instructions.push( - Token.createApproveInstruction(programIds().token, account.pubkey, authority, wallet.publicKey, [], liquidityAmount) - ); - - // withdraw - instructions.push( - withdrawPoolInstruction( - pool.pubkeys.account, - authority, - pool.pubkeys.mint, - pool.pubkeys.feeAccount, - account.pubkey, - pool.pubkeys.holdingAccounts[0], - pool.pubkeys.holdingAccounts[1], - toAccounts[0], - toAccounts[1], - pool.pubkeys.program, - programIds().token, - liquidityAmount, - minAmount0, - minAmount1 - ) - ); - - const deleteAccount = liquidityAmount === account.info.amount.toNumber(); - if (deleteAccount) { - instructions.push( - Token.createCloseAccountInstruction(programIds().token, account.pubkey, authority, wallet.publicKey, []) - ); - } - - let tx = await sendTransaction(connection, wallet, instructions.concat(cleanupInstructions), signers); - - if (deleteAccount) { - cache.delete(account.pubkey); - } - - notify({ - 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], - ]; -}; - -export const removeExactOneLiquidity = async ( - connection: Connection, - wallet: any, - account: TokenAccount, - liquidityAmount: number, - tokenAmount: number, - tokenMint: string, - pool?: PoolInfo -) => { - if (!pool) { - throw new Error('Pool is required'); - } - - notify({ - 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.query(connection, pool.pubkeys.holdingAccounts[0]); - const accountB = await cache.query(connection, pool.pubkeys.holdingAccounts[1]); - if (!poolMint.mintAuthority) { - throw new Error('Mint doesnt have authority'); - } - - 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 toAccount: PublicKey = await findOrCreateAccountByMint( - wallet.publicKey, - wallet.publicKey, - instructions, - cleanupInstructions, - accountRentExempt, - tokenMatchAccount.info.mint, - signers - ); - - instructions.push( - Token.createApproveInstruction( - programIds().token, - account.pubkey, - authority, - wallet.publicKey, - [], - account.info.amount.toNumber() // liquidityAmount <- need math tuning - ) - ); - - // withdraw exact one - instructions.push( - withdrawExactOneInstruction( - pool.pubkeys.account, - authority, - pool.pubkeys.mint, - account.pubkey, - pool.pubkeys.holdingAccounts[0], - pool.pubkeys.holdingAccounts[1], - toAccount, - pool.pubkeys.feeAccount, - pool.pubkeys.program, - programIds().token, - tokenAmount, - liquidityMaxAmount - ) - ); - - let tx = await sendTransaction(connection, wallet, instructions.concat(cleanupInstructions), signers); - - notify({ - 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; -}; - -export const swap = async ( - connection: Connection, - wallet: any, - components: LiquidityComponent[], - SLIPPAGE: number, - pool?: PoolInfo -) => { - if (!pool || !components[0].account) { - notify({ - type: 'error', - message: `Pool doesn't exsist.`, - description: `Swap trade cancelled`, - }); - return; - } - - // Uniswap whitepaper: https://uniswap.org/whitepaper.pdf - // see: https://uniswap.org/docs/v2/advanced-topics/pricing/ - // as well as native uniswap v2 oracle: https://uniswap.org/docs/v2/core-concepts/oracles/ - 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.holdingAccounts[0] - : pool.pubkeys.holdingAccounts[1]; - const holdingB = - 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'); - } - const authority = poolMint.mintAuthority; - - const instructions: TransactionInstruction[] = []; - const cleanupInstructions: TransactionInstruction[] = []; - const signers: Account[] = []; - - const accountRentExempt = await connection.getMinimumBalanceForRentExemption(AccountLayout.span); - - const fromAccount = getWrappedAccount( - instructions, - cleanupInstructions, - components[0].account, - wallet.publicKey, - amountIn + accountRentExempt, - signers - ); - - let toAccount = findOrCreateAccountByMint( - wallet.publicKey, - wallet.publicKey, - instructions, - cleanupInstructions, - accountRentExempt, - new PublicKey(components[1].mintAddress), - signers - ); - - // create approval for transfer transactions - instructions.push( - Token.createApproveInstruction(programIds().token, fromAccount, authority, wallet.publicKey, [], amountIn) - ); - - let hostFeeAccount = SWAP_HOST_FEE_ADDRESS - ? findOrCreateAccountByMint( - wallet.publicKey, - SWAP_HOST_FEE_ADDRESS, - instructions, - cleanupInstructions, - accountRentExempt, - pool.pubkeys.mint, - signers - ) - : undefined; - - // swap - instructions.push( - swapInstruction( - pool.pubkeys.account, - authority, - fromAccount, - holdingA, - holdingB, - toAccount, - pool.pubkeys.mint, - pool.pubkeys.feeAccount, - pool.pubkeys.program, - programIds().token, - amountIn, - minAmountOut, - hostFeeAccount - ) - ); - - let tx = await sendTransaction(connection, wallet, instructions.concat(cleanupInstructions), signers); - - notify({ - message: 'Trade executed.', - type: 'success', - description: `Transaction - ${tx}`, - }); -}; - -export const addLiquidity = async ( - connection: Connection, - wallet: any, - components: LiquidityComponent[], - slippage: number, - pool?: PoolInfo, - options?: PoolConfig, - depositType: string = 'both' -) => { - 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.'); - } - - await _addLiquidityNewPool(wallet, connection, components, options); - } else { - await _addLiquidityExistingPool(pool, components, connection, wallet); - } -}; const getHoldings = (connection: Connection, accounts: string[]) => { return accounts.map((acc) => cache.query(connection, new PublicKey(acc))); @@ -510,7 +179,7 @@ export const usePools = () => { export const usePoolForBasket = (mints: (string | undefined)[]) => { const connection = useConnection(); - const { pools } = useCachedPool(); + const { pools } = usePools(); const [pool, setPool] = useState(); const sortedMints = useMemo(() => [...mints].sort(), [...mints]); // eslint-disable-line useEffect(() => { @@ -542,299 +211,6 @@ export const usePoolForBasket = (mints: (string | undefined)[]) => { return pool; }; -export const useOwnedPools = (legacy = false) => { - const { pools } = useCachedPool(legacy); - const { userAccounts } = useUserAccounts(); - - const ownedPools = useMemo(() => { - const map = userAccounts.reduce((acc, item) => { - const key = item.info.mint.toBase58(); - acc.set(key, [...(acc.get(key) || []), item]); - return acc; - }, new Map()); - - return pools - .filter((p) => map.has(p.pubkeys.mint.toBase58()) && p.legacy === legacy) - .map((item) => { - let feeAccount = item.pubkeys.feeAccount?.toBase58(); - return map.get(item.pubkeys.mint.toBase58())?.map((a) => { - return { - account: a as TokenAccount, - isFeeAccount: feeAccount === a.pubkey.toBase58(), - pool: item, - }; - }) as { - account: TokenAccount; - isFeeAccount: boolean; - pool: PoolInfo; - }[]; - }) - .flat(); - }, [pools, userAccounts, legacy]); - - return ownedPools; -}; - -// Allow for this much price movement in the pool before adding liquidity to the pool aborts -const SLIPPAGE = 0.005; - -async function _addLiquidityExistingPool( - pool: PoolInfo, - components: LiquidityComponent[], - connection: Connection, - wallet: any -) { - notify({ - 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'); - } - - if (!pool.pubkeys.feeAccount) { - throw new Error('Invald fee account'); - } - - const accountA = await cache.query(connection, pool.pubkeys.holdingAccounts[0]); - const accountB = await cache.query(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 fromB = fromA === components[0] ? components[1] : components[0]; - - if (!fromA.account || !fromB.account) { - throw new Error('Missing account info.'); - } - - const supply = poolMint.supply.toNumber(); - const authority = poolMint.mintAuthority; - - // Uniswap whitepaper: https://uniswap.org/whitepaper.pdf - // see: https://uniswap.org/docs/v2/advanced-topics/pricing/ - // as well as native uniswap v2 oracle: https://uniswap.org/docs/v2/core-concepts/oracles/ - const amount0 = fromA.amount; - const amount1 = fromB.amount; - - const liquidity = Math.min( - (amount0 * (1 - SLIPPAGE) * supply) / reserve0, - (amount1 * (1 - SLIPPAGE) * supply) / reserve1 - ); - const instructions: TransactionInstruction[] = []; - const cleanupInstructions: TransactionInstruction[] = []; - - const signers: Account[] = []; - - const accountRentExempt = await connection.getMinimumBalanceForRentExemption(AccountLayout.span); - const fromKeyA = getWrappedAccount( - instructions, - cleanupInstructions, - fromA.account, - wallet.publicKey, - amount0 + accountRentExempt, - signers - ); - const fromKeyB = getWrappedAccount( - instructions, - cleanupInstructions, - fromB.account, - wallet.publicKey, - amount1 + accountRentExempt, - signers - ); - - let toAccount = findOrCreateAccountByMint( - wallet.publicKey, - wallet.publicKey, - instructions, - [], - accountRentExempt, - pool.pubkeys.mint, - signers, - new Set([pool.pubkeys.feeAccount.toBase58()]) - ); - - // create approval for transfer transactions - instructions.push( - Token.createApproveInstruction(programIds().token, fromKeyA, authority, wallet.publicKey, [], amount0) - ); - - instructions.push( - Token.createApproveInstruction(programIds().token, fromKeyB, authority, wallet.publicKey, [], amount1) - ); - - // deposit - instructions.push( - depositPoolInstruction( - pool.pubkeys.account, - authority, - fromKeyA, - fromKeyB, - pool.pubkeys.holdingAccounts[0], - pool.pubkeys.holdingAccounts[1], - pool.pubkeys.mint, - toAccount, - pool.pubkeys.program, - programIds().token, - liquidity, - amount0, - amount1 - ) - ); - - let tx = await sendTransaction(connection, wallet, instructions.concat(cleanupInstructions), signers); - - notify({ - message: 'Pool Funded. Happy trading.', - type: 'success', - description: `Transaction - ${tx}`, - }); -} - -async function _addLiquidityExactOneExistingPool( - pool: PoolInfo, - component: LiquidityComponent, - connection: Connection, - wallet: any -) { - notify({ - 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'); - } - - if (!pool.pubkeys.feeAccount) { - throw new Error('Invald fee account'); - } - - const accountA = await cache.query(connection, pool.pubkeys.holdingAccounts[0]); - const accountB = await cache.query(connection, pool.pubkeys.holdingAccounts[1]); - - const from = component; - - if (!from.account) { - throw new Error('Missing account info.'); - } - const reserve = - accountA.info.mint.toBase58() === from.mintAddress - ? accountA.info.amount.toNumber() - : accountB.info.amount.toNumber(); - - const supply = poolMint.supply.toNumber(); - const authority = poolMint.mintAuthority; - - // Uniswap whitepaper: https://uniswap.org/whitepaper.pdf - // see: https://uniswap.org/docs/v2/advanced-topics/pricing/ - // as well as native uniswap v2 oracle: https://uniswap.org/docs/v2/core-concepts/oracles/ - const amount = from.amount; - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const _liquidityTokenTempMath = (amount * (1 - SLIPPAGE) * supply) / reserve; - const liquidityToken = 0; - - const instructions: TransactionInstruction[] = []; - const cleanupInstructions: TransactionInstruction[] = []; - - const signers: Account[] = []; - - const accountRentExempt = await connection.getMinimumBalanceForRentExemption(AccountLayout.span); - const fromKey = getWrappedAccount( - instructions, - cleanupInstructions, - from.account, - wallet.publicKey, - amount + accountRentExempt, - signers - ); - - let toAccount = findOrCreateAccountByMint( - wallet.publicKey, - wallet.publicKey, - instructions, - [], - accountRentExempt, - pool.pubkeys.mint, - signers, - new Set([pool.pubkeys.feeAccount.toBase58()]) - ); - - // create approval for transfer transactions - instructions.push( - Token.createApproveInstruction(programIds().token, fromKey, authority, wallet.publicKey, [], amount) - ); - - // deposit - instructions.push( - depositExactOneInstruction( - pool.pubkeys.account, - authority, - fromKey, - pool.pubkeys.holdingAccounts[0], - pool.pubkeys.holdingAccounts[1], - pool.pubkeys.mint, - toAccount, - pool.pubkeys.program, - programIds().token, - amount, - liquidityToken - ) - ); - - let tx = await sendTransaction(connection, wallet, instructions.concat(cleanupInstructions), signers); - - notify({ - message: 'Pool Funded. Happy trading.', - type: 'success', - description: `Transaction - ${tx}`, - }); -} - -function findOrCreateAccountByMint( - payer: PublicKey, - owner: PublicKey, - instructions: TransactionInstruction[], - cleanupInstructions: TransactionInstruction[], - accountRentExempt: number, - mint: PublicKey, // use to identify same type - signers: Account[], - excluded?: Set -): PublicKey { - const accountToFind = mint.toBase58(); - const account = getCachedAccount( - (acc) => - acc.info.mint.toBase58() === accountToFind && - acc.info.owner.toBase58() === owner.toBase58() && - (excluded === undefined || !excluded.has(acc.pubkey.toBase58())) - ); - const isWrappedSol = accountToFind === WRAPPED_SOL_MINT.toBase58(); - - let toAccount: PublicKey; - if (account && !isWrappedSol) { - toAccount = account.pubkey; - } else { - // creating depositor pool account - 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, [])); - } - } - - return toAccount; -} - function estimateProceedsFromInput( inputQuantityInPool: number, proceedsQuantityInPool: number, @@ -934,248 +310,3 @@ export async function calculateDependentAmount( } return depAdjustedAmount / depPrecision; } - -// TODO: add ui to customize curve type -async function _addLiquidityNewPool( - wallet: any, - connection: Connection, - components: LiquidityComponent[], - options: PoolConfig -) { - notify({ - 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', - }); - return; - } - - let instructions: TransactionInstruction[] = []; - let cleanupInstructions: TransactionInstruction[] = []; - - const liquidityTokenMint = new Account(); - // Create account for pool liquidity token - instructions.push( - SystemProgram.createAccount({ - fromPubkey: wallet.publicKey, - newAccountPubkey: liquidityTokenMint.publicKey, - lamports: await connection.getMinimumBalanceForRentExemption(MintLayout.span), - space: MintLayout.span, - programId: programIds().token, - }) - ); - - const tokenSwapAccount = new Account(); - - const [authority, nonce] = await PublicKey.findProgramAddress( - [tokenSwapAccount.publicKey.toBuffer()], - programIds().swap - ); - - // create mint for pool liquidity token - instructions.push( - Token.createInitMintInstruction( - programIds().token, - liquidityTokenMint.publicKey, - LIQUIDITY_TOKEN_PRECISION, - // pass control of liquidity mint to swap program - authority, - // swap program can freeze liquidity token mint - null - ) - ); - - // Create holding accounts for - const accountRentExempt = await connection.getMinimumBalanceForRentExemption(AccountLayout.span); - const holdingAccounts: Account[] = []; - let signers: Account[] = []; - - components.forEach((leg) => { - if (!leg.account) { - return; - } - - 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) - ); - }); - - // creating depositor pool account - const depositorAccount = createSplAccount( - instructions, - wallet.publicKey, - accountRentExempt, - liquidityTokenMint.publicKey, - wallet.publicKey, - AccountLayout.span - ); - - // creating fee pool account its set from env variable or to creater of the pool - // creater of the pool is not allowed in some versions of token-swap program - const feeAccount = createSplAccount( - instructions, - wallet.publicKey, - accountRentExempt, - liquidityTokenMint.publicKey, - SWAP_PROGRAM_OWNER_FEE_ADDRESS || wallet.publicKey, - AccountLayout.span - ); - - // create all accounts in one transaction - let tx = await sendTransaction(connection, wallet, instructions, [ - liquidityTokenMint, - depositorAccount, - feeAccount, - ...holdingAccounts, - ...signers, - ]); - - notify({ - message: 'Accounts created', - description: `Transaction ${tx}`, - type: 'success', - }); - - notify({ - message: 'Adding Liquidity...', - description: 'Please review transactions to approve.', - type: 'warn', - }); - - signers = []; - instructions = []; - cleanupInstructions = []; - - instructions.push( - SystemProgram.createAccount({ - fromPubkey: wallet.publicKey, - newAccountPubkey: tokenSwapAccount.publicKey, - lamports: await connection.getMinimumBalanceForRentExemption(programIds().swapLayout.span), - space: programIds().swapLayout.span, - programId: programIds().swap, - }) - ); - - components.forEach((leg, i) => { - if (!leg.account) { - return; - } - - // create temporary account for wrapped sol to perform transfer - const from = getWrappedAccount( - instructions, - cleanupInstructions, - leg.account, - wallet.publicKey, - leg.amount + accountRentExempt, - signers - ); - - instructions.push( - Token.createTransferInstruction( - programIds().token, - from, - holdingAccounts[i].publicKey, - wallet.publicKey, - [], - leg.amount - ) - ); - }); - - instructions.push( - createInitSwapInstruction( - tokenSwapAccount, - authority, - holdingAccounts[0].publicKey, - holdingAccounts[1].publicKey, - liquidityTokenMint.publicKey, - feeAccount.publicKey, - depositorAccount.publicKey, - programIds().token, - programIds().swap, - nonce, - options - ) - ); - - // 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, - ]); - - notify({ - message: 'Pool Funded. Happy trading.', - type: 'success', - description: `Transaction - ${tx}`, - }); -} - -function getWrappedAccount( - instructions: TransactionInstruction[], - cleanupInstructions: TransactionInstruction[], - toCheck: TokenAccount, - payer: PublicKey, - amount: number, - signers: Account[] -) { - if (!toCheck.info.isNative) { - return toCheck.pubkey; - } - - const account = new Account(); - instructions.push( - SystemProgram.createAccount({ - fromPubkey: payer, - newAccountPubkey: account.publicKey, - lamports: amount, - space: AccountLayout.span, - programId: programIds().token, - }) - ); - - instructions.push(Token.createInitAccountInstruction(programIds().token, WRAPPED_SOL_MINT, account.publicKey, payer)); - - cleanupInstructions.push( - Token.createCloseAccountInstruction(programIds().token, account.publicKey, payer, payer, []) - ); - - signers.push(account); - - return account.publicKey; -} - -function createSplAccount( - instructions: TransactionInstruction[], - payer: PublicKey, - accountRentExempt: number, - mint: PublicKey, - owner: PublicKey, - space: number -) { - const account = new Account(); - instructions.push( - SystemProgram.createAccount({ - fromPubkey: payer, - newAccountPubkey: account.publicKey, - lamports: accountRentExempt, - space, - programId: programIds().token, - }) - ); - - instructions.push(Token.createInitAccountInstruction(programIds().token, mint, account.publicKey, owner)); - - return account; -} diff --git a/src/views/borrowReserve/index.tsx b/src/views/borrowReserve/index.tsx index 86d0b16..1f1f2cb 100644 --- a/src/views/borrowReserve/index.tsx +++ b/src/views/borrowReserve/index.tsx @@ -74,7 +74,7 @@ export const BorrowReserveView = () => { reserve={lendingReserve} /> - +