chore: cleanup unused code

This commit is contained in:
bartosz-lipinski 2021-01-05 11:35:02 -06:00
parent 4d16a1c182
commit 16d6cd60a8
5 changed files with 9 additions and 1251 deletions

View File

@ -131,12 +131,12 @@ export const AppLayout = React.memo((props: any) => {
</Menu>
<Menu theme={theme} defaultSelectedKeys={[defaultKey]} selectable={false} mode="inline" className="bottom-links">
<Menu.Item key="16" icon={<ForkOutlined />}>
<a title="Fork" href={`${config.repository.url}/fork`} target="_blank">
<a title="Fork" href={`${config.repository.url}/fork`} target="_blank" rel="noopener noreferrer" >
Fork
</a>
</Menu.Item>,
<Menu.Item key="15" icon={<GithubOutlined />}>
<a title="Gtihub" href={config.repository.url} target="_blank">
<a title="Gtihub" href={config.repository.url} target="_blank" rel="noopener noreferrer" >
Github
</a>
</Menu.Item>

View File

@ -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<any>(null);
@ -347,7 +346,6 @@ export function AccountsProvider({ children = null as any }) {
const [tokenAccounts, setTokenAccounts] = useState<TokenAccount[]>([]);
const [userAccounts, setUserAccounts] = useState<TokenAccount[]>([]);
const { nativeAccount } = UseNativeAccount();
const { pools } = usePools();
const selectUserAccounts = useCallback(() => {
return cache
@ -419,7 +417,6 @@ export function AccountsProvider({ children = null as any }) {
<AccountsContext.Provider
value={{
userAccounts,
pools,
nativeAccount,
}}
>

View File

@ -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,
});
};

View File

@ -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<PoolInfo>();
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<string, TokenAccount[]>());
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<string>([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<string>([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<string>
): 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;
}

View File

@ -74,7 +74,7 @@ export const BorrowReserveView = () => {
reserve={lendingReserve}
/>
</Col>
<Col xs={24} xl={9}>
<Col xs={24} xl={8}>
<SideReserveOverview
className="card-fill"
reserve={lendingReserve}