225 lines
7.9 KiB
TypeScript
225 lines
7.9 KiB
TypeScript
import React, { useState } from "react";
|
|
import { Button, Spin, Popover } from "antd";
|
|
import { LoadingOutlined } from "@ant-design/icons";
|
|
// Auto generate button label
|
|
import {
|
|
ADD_LIQUIDITY_LABEL,
|
|
generateActionLabel,
|
|
} from "./labels";
|
|
// Issue set helper
|
|
import issueSet from '../utils/issueSet';
|
|
// Connect to wallet
|
|
import { useWallet } from '../utils/wallet';
|
|
// Create connection to wallet
|
|
import {
|
|
useConnection, useConnectionConfig, useSlippageConfig
|
|
} from '../utils/connection';
|
|
// Currency pair on this market
|
|
import { useCurrencyLeg } from "../utils/currencyPair";
|
|
// Input box
|
|
import { CurrencyInput } from "./currencyInput";
|
|
// Opertions on the pool
|
|
import { addLiquidity, PoolForBasketPromise, usePoolForBasket, calculateDependentAmount, PoolOperation } from '../utils/pools';
|
|
import { useMint } from '../utils/accounts';
|
|
// Make notifications
|
|
import { notify } from '../utils/notifications'
|
|
//
|
|
import { DEFAULT_DENOMINATOR } from "./pool/config";
|
|
// For setting the config of the pool
|
|
import { PoolConfig } from "../models";
|
|
// Lists of known pools
|
|
import { useCachedPool } from '../utils/accounts';
|
|
// Typescript: type of mint
|
|
import { MintInfo } from "@solana/spl-token";
|
|
// Create icons
|
|
const antIcon = <LoadingOutlined style={{ fontSize: 24 }} spin />;
|
|
|
|
|
|
// TODO: Allow market change
|
|
// TODO: Check for no pool
|
|
// TODO: Only allow usdc account
|
|
export const AddLiquidityView = (props: {
|
|
market: any,
|
|
baseMintAddress: string,
|
|
outcomes: Array<any>
|
|
}) => {
|
|
const connection = useConnection();
|
|
const { wallet, connected } = useWallet();
|
|
const [pendingTx, setPendingTx] = useState(false);
|
|
let pendingTxNum = 0;
|
|
const { tokenMap } = useConnectionConfig();
|
|
const { slippage } = useSlippageConfig();
|
|
// Create useful methods on our mint address
|
|
const baseMintAddress = useCurrencyLeg(props.baseMintAddress);
|
|
const outcome0 = useCurrencyLeg(props.outcomes[0].mint_pk);
|
|
const outcome1 = useCurrencyLeg(props.outcomes[1].mint_pk);
|
|
const baseMint = useMint(props.baseMintAddress);
|
|
const outcome0Mint = useMint(props.outcomes[0].mint_pk);
|
|
const outcome1Mint = useMint(props.outcomes[1].mint_pk);
|
|
const [options, setOptions] = useState<PoolConfig>({
|
|
curveType: 0,
|
|
tradeFeeNumerator: 25,
|
|
tradeFeeDenominator: DEFAULT_DENOMINATOR,
|
|
ownerTradeFeeNumerator: 5,
|
|
ownerTradeFeeDenominator: DEFAULT_DENOMINATOR,
|
|
ownerWithdrawFeeNumerator: 0,
|
|
ownerWithdrawFeeDenominator: DEFAULT_DENOMINATOR,
|
|
});
|
|
const { pools } = useCachedPool();
|
|
|
|
const fundPool = async (components: Array<any>, pool: any) => {
|
|
pendingTxNum += 1;
|
|
// Add the liquidity to the first pool
|
|
addLiquidity(connection, wallet, components, slippage, pool, options)
|
|
.then(() => {
|
|
pendingTxNum -= 1;
|
|
if (pendingTxNum === 0) {
|
|
setPendingTx(false);
|
|
}
|
|
})
|
|
.catch((e) => {
|
|
console.log("Transaction failed", e);
|
|
notify({
|
|
description:
|
|
"Please try again and approve transactions from your wallet",
|
|
message: "Adding liquidity cancelled.",
|
|
type: "error",
|
|
});
|
|
pendingTxNum -= 1;
|
|
if (pendingTxNum === 0) {
|
|
setPendingTx(false);
|
|
}
|
|
});
|
|
}
|
|
const hasSufficientBalance = baseMintAddress.sufficientBalance()
|
|
|
|
function parseAmount(mint: MintInfo | undefined, amount: string) {
|
|
return parseFloat(amount) * Math.pow(10, mint?.decimals || 0);
|
|
}
|
|
// Swap usdc for market tokens
|
|
const executeAction =
|
|
!connected
|
|
? wallet.connect :
|
|
async () => {
|
|
// TODO: Confirm mint address exists for outcome pk
|
|
// TODO: Should fail fast be implemented here i.e if one pool was not funded, can the other pool be ?
|
|
if (baseMintAddress.account && baseMintAddress.mint) {
|
|
setPendingTx(true);
|
|
pendingTxNum += 1;
|
|
// @ts-ignore
|
|
issueSet(props.market, parseAmount(baseMint, baseMintAddress.amount) / 2, wallet, connection)
|
|
.then(async () => {
|
|
// Check that the outcome token accounts exists
|
|
if (!outcome0.account || !outcome1.account) {
|
|
notify({
|
|
description:
|
|
"Token account does not exists",
|
|
message: "Adding liquidity cancelled.",
|
|
type: "error",
|
|
});
|
|
}
|
|
pendingTxNum -= 1;
|
|
// Fund pools of the outcome pk
|
|
[outcome0, outcome1].forEach(async (outcome, i) => {
|
|
PoolForBasketPromise([baseMintAddress.mintAddress, outcome.mintAddress].sort(), connection, pools).then(async (pool) => {
|
|
const components = [
|
|
{
|
|
account: baseMintAddress.account,
|
|
mintAddress: baseMintAddress.mintAddress,
|
|
amount: await calculateDependentAmount(connection, outcome.mintAddress, parseAmount(i === 0 ? outcome0Mint : outcome1Mint, baseMintAddress.amount) / 2, pool, PoolOperation.Add),
|
|
},
|
|
{
|
|
account: outcome.account,
|
|
mintAddress: outcome.mintAddress,
|
|
amount: parseAmount(i === 0 ? outcome0Mint : outcome1Mint, baseMintAddress.amount) / 2,
|
|
},
|
|
];
|
|
fundPool(components, pool);
|
|
}).catch((e) => {
|
|
console.log("Transaction failed", e);
|
|
notify({
|
|
description:
|
|
"Please try again and approve transactions from your wallet",
|
|
message: "Adding liquidity cancelled.",
|
|
type: "error",
|
|
});
|
|
pendingTxNum -= 1;
|
|
if (pendingTxNum === 0) {
|
|
setPendingTx(false);
|
|
}
|
|
});
|
|
})
|
|
})
|
|
.catch((e) => {
|
|
console.log("Transaction failed", e);
|
|
notify({
|
|
description:
|
|
"Please try again and approve transactions from your wallet",
|
|
message: "Adding liquidity cancelled.",
|
|
type: "error",
|
|
});
|
|
pendingTxNum -= 1;
|
|
if (pendingTxNum === 0) {
|
|
setPendingTx(false);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
const colStyle: React.CSSProperties = { padding: "1em" };
|
|
|
|
|
|
return (
|
|
<>
|
|
<div>
|
|
<div>
|
|
<Popover
|
|
trigger="hover"
|
|
content={
|
|
<div style={{ width: 300 }}>
|
|
Liquidity providers earn a fixed percentage fee on all trades
|
|
proportional to their share of the pool. Fees are added to the
|
|
pool, accrue in real time and can be claimed by withdrawing your
|
|
liquidity.
|
|
</div>
|
|
}
|
|
>
|
|
<Button type="text">Provide Liquidity to both pools</Button>
|
|
</Popover>
|
|
</div>
|
|
<CurrencyInput
|
|
title="Input"
|
|
onInputChange={(val: any) => {
|
|
baseMintAddress.setAmount(val);
|
|
}}
|
|
amount={baseMintAddress.amount}
|
|
mint={baseMintAddress.mintAddress}
|
|
onMintChange={(item) => {
|
|
baseMintAddress.setMint(item);
|
|
}}
|
|
forceMint={baseMintAddress.mintAddress}
|
|
renderOneTokenItem={baseMintAddress.mintAddress}
|
|
/>
|
|
|
|
<Button
|
|
size="large"
|
|
type="primary"
|
|
onClick={executeAction}
|
|
disabled={
|
|
connected &&
|
|
(pendingTx ||
|
|
!baseMintAddress.account ||
|
|
baseMintAddress.account === outcome1.account ||
|
|
baseMintAddress.account === outcome1.account ||
|
|
!hasSufficientBalance)
|
|
}
|
|
>
|
|
{generateActionLabel(ADD_LIQUIDITY_LABEL, connected, tokenMap, baseMintAddress, baseMintAddress)}
|
|
{pendingTx && <Spin indicator={antIcon} className="add-spinner" />}
|
|
</Button>
|
|
|
|
</div>
|
|
</>
|
|
);
|
|
};
|