mirror of https://github.com/certusone/oyster.git
feat: hookup liquidate
This commit is contained in:
parent
552b5c4527
commit
f24645810a
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 216 KiB |
28
src/App.less
28
src/App.less
|
@ -17,7 +17,10 @@ body {
|
||||||
.footer {
|
.footer {
|
||||||
background-color: black;
|
background-color: black;
|
||||||
color: lightgray;
|
color: lightgray;
|
||||||
padding: 20px;
|
padding: 10px 10px;
|
||||||
|
max-height: 60px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-spinner {
|
.action-spinner {
|
||||||
|
@ -156,7 +159,6 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.wallet-wrapper {
|
.wallet-wrapper {
|
||||||
background: @background-color-base;
|
|
||||||
padding-left: 0.7rem;
|
padding-left: 0.7rem;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -165,7 +167,6 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.wallet-key {
|
.wallet-key {
|
||||||
background: @background-color-base;
|
|
||||||
padding: 0.1rem 0.5rem 0.1rem 0.7rem;
|
padding: 0.1rem 0.5rem 0.1rem 0.7rem;
|
||||||
margin-left: 0.3rem;
|
margin-left: 0.3rem;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
|
@ -268,6 +269,27 @@ body {
|
||||||
padding: 8px 8px;
|
padding: 8px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-pro-global-header {
|
||||||
|
.ant-pro-global-header-logo a h1 {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
background-color: black !important;
|
||||||
|
color: white !important;
|
||||||
|
|
||||||
|
.ant-btn {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-statistic {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-statistic-content {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.token-input {
|
.token-input {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -61,31 +61,31 @@ export const borrow = async (
|
||||||
const obligation = existingObligation
|
const obligation = existingObligation
|
||||||
? existingObligation.pubkey
|
? existingObligation.pubkey
|
||||||
: createUninitializedObligation(
|
: createUninitializedObligation(
|
||||||
instructions,
|
instructions,
|
||||||
wallet.publicKey,
|
wallet.publicKey,
|
||||||
await connection.getMinimumBalanceForRentExemption(
|
await connection.getMinimumBalanceForRentExemption(
|
||||||
LendingObligationLayout.span
|
LendingObligationLayout.span
|
||||||
),
|
),
|
||||||
signers
|
signers
|
||||||
);
|
);
|
||||||
|
|
||||||
const obligationMint = existingObligation
|
const obligationMint = existingObligation
|
||||||
? existingObligation.info.tokenMint
|
? existingObligation.info.tokenMint
|
||||||
: createUninitializedMint(
|
: createUninitializedMint(
|
||||||
instructions,
|
instructions,
|
||||||
wallet.publicKey,
|
wallet.publicKey,
|
||||||
await connection.getMinimumBalanceForRentExemption(MintLayout.span),
|
await connection.getMinimumBalanceForRentExemption(MintLayout.span),
|
||||||
signers
|
signers
|
||||||
);
|
);
|
||||||
|
|
||||||
const obligationTokenOutput = obligationAccount
|
const obligationTokenOutput = obligationAccount
|
||||||
? obligationAccount
|
? obligationAccount
|
||||||
: createUninitializedAccount(
|
: createUninitializedAccount(
|
||||||
instructions,
|
instructions,
|
||||||
wallet.publicKey,
|
wallet.publicKey,
|
||||||
accountRentExempt,
|
accountRentExempt,
|
||||||
signers
|
signers
|
||||||
);
|
);
|
||||||
|
|
||||||
let toAccount = await findOrCreateAccountByMint(
|
let toAccount = await findOrCreateAccountByMint(
|
||||||
wallet.publicKey,
|
wallet.publicKey,
|
||||||
|
@ -172,10 +172,6 @@ export const borrow = async (
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const market = cache.get(depositReserve.info.lendingMarket) as ParsedAccount<
|
|
||||||
LendingMarket
|
|
||||||
>;
|
|
||||||
|
|
||||||
const dexMarketAddress = borrowReserve.info.dexMarketOption
|
const dexMarketAddress = borrowReserve.info.dexMarketOption
|
||||||
? borrowReserve.info.dexMarket
|
? borrowReserve.info.dexMarket
|
||||||
: depositReserve.info.dexMarket;
|
: depositReserve.info.dexMarket;
|
||||||
|
@ -185,6 +181,9 @@ export const borrow = async (
|
||||||
throw new Error(`Dex market doesn't exist.`);
|
throw new Error(`Dex market doesn't exist.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const market = cache.get(depositReserve.info.lendingMarket) as ParsedAccount<
|
||||||
|
LendingMarket
|
||||||
|
>;
|
||||||
const dexOrderBookSide = market.info.quoteMint.equals(
|
const dexOrderBookSide = market.info.quoteMint.equals(
|
||||||
depositReserve.info.liquidityMint
|
depositReserve.info.liquidityMint
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,4 +2,5 @@ export { borrow } from "./borrow";
|
||||||
export { deposit } from "./deposit";
|
export { deposit } from "./deposit";
|
||||||
export { repay } from "./repay";
|
export { repay } from "./repay";
|
||||||
export { withdraw } from "./withdraw";
|
export { withdraw } from "./withdraw";
|
||||||
|
export { liquidate } from "./liquidate";
|
||||||
export * from "./account";
|
export * from "./account";
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
import {
|
||||||
|
Account,
|
||||||
|
Connection,
|
||||||
|
PublicKey,
|
||||||
|
TransactionInstruction,
|
||||||
|
} from "@solana/web3.js";
|
||||||
|
import { sendTransaction } from "../contexts/connection";
|
||||||
|
import { notify } from "../utils/notifications";
|
||||||
|
import { LendingReserve } from "./../models/lending/reserve";
|
||||||
|
import { liquidateInstruction } from "./../models/lending/liquidate";
|
||||||
|
import { AccountLayout, Token } from "@solana/spl-token";
|
||||||
|
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../constants/ids";
|
||||||
|
import { createTempMemoryAccount, ensureSplAccount, findOrCreateAccountByMint } from "./account";
|
||||||
|
import { LendingMarket, LendingObligation, TokenAccount } from "../models";
|
||||||
|
import { cache, ParsedAccount } from "../contexts/accounts";
|
||||||
|
|
||||||
|
export const liquidate = async (
|
||||||
|
|
||||||
|
connection: Connection,
|
||||||
|
wallet: any,
|
||||||
|
from: TokenAccount, // liquidity account
|
||||||
|
amountLamports: number, // in liquidty token (lamports)
|
||||||
|
|
||||||
|
// which loan to repay
|
||||||
|
obligation: ParsedAccount<LendingObligation>,
|
||||||
|
|
||||||
|
repayReserve: ParsedAccount<LendingReserve>,
|
||||||
|
|
||||||
|
withdrawReserve: ParsedAccount<LendingReserve>,
|
||||||
|
) => {
|
||||||
|
notify({
|
||||||
|
message: "Repaing funds...",
|
||||||
|
description: "Please review transactions to approve.",
|
||||||
|
type: "warn",
|
||||||
|
});
|
||||||
|
|
||||||
|
// user from account
|
||||||
|
const signers: Account[] = [];
|
||||||
|
const instructions: TransactionInstruction[] = [];
|
||||||
|
const cleanupInstructions: TransactionInstruction[] = [];
|
||||||
|
|
||||||
|
const accountRentExempt = await connection.getMinimumBalanceForRentExemption(
|
||||||
|
AccountLayout.span
|
||||||
|
);
|
||||||
|
|
||||||
|
const [authority] = await PublicKey.findProgramAddress(
|
||||||
|
[repayReserve.info.lendingMarket.toBuffer()],
|
||||||
|
LENDING_PROGRAM_ID
|
||||||
|
);
|
||||||
|
|
||||||
|
const fromAccount = ensureSplAccount(
|
||||||
|
instructions,
|
||||||
|
cleanupInstructions,
|
||||||
|
from,
|
||||||
|
wallet.publicKey,
|
||||||
|
amountLamports + accountRentExempt,
|
||||||
|
signers
|
||||||
|
);
|
||||||
|
|
||||||
|
// create approval for transfer transactions
|
||||||
|
instructions.push(
|
||||||
|
Token.createApproveInstruction(
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
fromAccount,
|
||||||
|
authority,
|
||||||
|
wallet.publicKey,
|
||||||
|
[],
|
||||||
|
amountLamports
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// get destination account
|
||||||
|
const toAccount = await findOrCreateAccountByMint(
|
||||||
|
wallet.publicKey,
|
||||||
|
wallet.publicKey,
|
||||||
|
instructions,
|
||||||
|
cleanupInstructions,
|
||||||
|
accountRentExempt,
|
||||||
|
withdrawReserve.info.collateralMint,
|
||||||
|
signers
|
||||||
|
);
|
||||||
|
|
||||||
|
const dexMarketAddress = repayReserve.info.dexMarketOption
|
||||||
|
? repayReserve.info.dexMarket
|
||||||
|
: withdrawReserve.info.dexMarket;
|
||||||
|
const dexMarket = cache.get(dexMarketAddress);
|
||||||
|
|
||||||
|
if (!dexMarket) {
|
||||||
|
throw new Error(`Dex market doesn't exist.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const market = cache.get(withdrawReserve.info.lendingMarket) as ParsedAccount<
|
||||||
|
LendingMarket
|
||||||
|
>;
|
||||||
|
|
||||||
|
const dexOrderBookSide = market.info.quoteMint.equals(
|
||||||
|
repayReserve.info.liquidityMint
|
||||||
|
)
|
||||||
|
? dexMarket?.info.bids
|
||||||
|
: dexMarket?.info.asks;
|
||||||
|
|
||||||
|
|
||||||
|
console.log(dexMarketAddress.toBase58())
|
||||||
|
|
||||||
|
const memory = createTempMemoryAccount(
|
||||||
|
instructions,
|
||||||
|
wallet.publicKey,
|
||||||
|
signers
|
||||||
|
);
|
||||||
|
|
||||||
|
instructions.push(
|
||||||
|
liquidateInstruction(
|
||||||
|
amountLamports,
|
||||||
|
fromAccount,
|
||||||
|
toAccount,
|
||||||
|
repayReserve.pubkey,
|
||||||
|
repayReserve.info.liquiditySupply,
|
||||||
|
withdrawReserve.pubkey,
|
||||||
|
withdrawReserve.info.collateralSupply,
|
||||||
|
obligation.pubkey,
|
||||||
|
authority,
|
||||||
|
dexMarketAddress,
|
||||||
|
dexOrderBookSide,
|
||||||
|
memory
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
let tx = await sendTransaction(
|
||||||
|
connection,
|
||||||
|
wallet,
|
||||||
|
instructions.concat(cleanupInstructions),
|
||||||
|
signers,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
notify({
|
||||||
|
message: "Funds liquidated.",
|
||||||
|
type: "success",
|
||||||
|
description: `Transaction - ${tx}`,
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { Statistic } from "antd";
|
||||||
|
import React, { } from "react";
|
||||||
|
|
||||||
|
export const BarChartStatistic = <T, >(props: {
|
||||||
|
items: T[];
|
||||||
|
title: string;
|
||||||
|
name: (item: T) => string;
|
||||||
|
getPct: (item: T) => number;
|
||||||
|
}) => {
|
||||||
|
const colors = [
|
||||||
|
'#003f5c',
|
||||||
|
'#2f4b7c',
|
||||||
|
'#665191',
|
||||||
|
'#a05195',
|
||||||
|
'#d45087',
|
||||||
|
'#f95d6a',
|
||||||
|
'#ff7c43',
|
||||||
|
'#ffa600',
|
||||||
|
].reverse();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Statistic
|
||||||
|
title={props.title}
|
||||||
|
valueRender={() =>
|
||||||
|
<div style={{ width: '100%', height: 40, display: 'flex', backgroundColor: 'lightgrey',
|
||||||
|
fontSize: 12,
|
||||||
|
lineHeight: '40px', }}>
|
||||||
|
{props.items.map((item, i) =>
|
||||||
|
<div key={props.name(item)}
|
||||||
|
title={props.name(item)}
|
||||||
|
style={{
|
||||||
|
width: `${100 * props.getPct(item)}%` ,
|
||||||
|
backgroundColor: colors[i % props.items.length] }} >
|
||||||
|
{props.name(item)}
|
||||||
|
</div>)}
|
||||||
|
</div>}
|
||||||
|
>
|
||||||
|
</Statistic>
|
||||||
|
);
|
||||||
|
};
|
|
@ -42,7 +42,7 @@ export const AppLayout = (props: any) => {
|
||||||
</div>
|
</div>
|
||||||
<BasicLayout
|
<BasicLayout
|
||||||
title={LABELS.APP_TITLE}
|
title={LABELS.APP_TITLE}
|
||||||
footerRender={() => <div className="footer">{LABELS.FOOTER}</div>}
|
footerRender={() => <div className="footer" title={LABELS.FOOTER}>{LABELS.FOOTER}</div>}
|
||||||
navTheme={theme}
|
navTheme={theme}
|
||||||
headerTheme={theme}
|
headerTheme={theme}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
|
|
|
@ -4,25 +4,61 @@ import React, { useCallback } from "react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { LABELS } from "../../constants";
|
import { LABELS } from "../../constants";
|
||||||
import { ParsedAccount } from "../../contexts/accounts";
|
import { ParsedAccount } from "../../contexts/accounts";
|
||||||
import { EnrichedLendingObligation } from "../../hooks";
|
import { EnrichedLendingObligation, useUserBalance } from "../../hooks";
|
||||||
import { LendingReserve } from "../../models";
|
import { LendingReserve } from "../../models";
|
||||||
|
import { ActionConfirmation } from "../ActionConfirmation";
|
||||||
import { BackButton } from "../BackButton";
|
import { BackButton } from "../BackButton";
|
||||||
import { CollateralSelector } from "../CollateralSelector";
|
import { CollateralSelector } from "../CollateralSelector";
|
||||||
|
import { liquidate } from "../../actions";
|
||||||
import "./style.less";
|
import "./style.less";
|
||||||
|
import { useConnection } from "../../contexts/connection";
|
||||||
|
import { useWallet } from "../../contexts/wallet";
|
||||||
|
import { wadToLamports } from "../../utils/utils";
|
||||||
|
|
||||||
export const LiquidateInput = (props: {
|
export const LiquidateInput = (props: {
|
||||||
className?: string;
|
className?: string;
|
||||||
reserve: ParsedAccount<LendingReserve>;
|
repayReserve: ParsedAccount<LendingReserve>;
|
||||||
collateralReserve?: ParsedAccount<LendingReserve>;
|
withdrawReserve?: ParsedAccount<LendingReserve>;
|
||||||
obligation: EnrichedLendingObligation;
|
obligation: EnrichedLendingObligation;
|
||||||
}) => {
|
}) => {
|
||||||
const { reserve, collateralReserve } = props;
|
const connection = useConnection();
|
||||||
|
const { wallet } = useWallet();
|
||||||
|
const { repayReserve, withdrawReserve, obligation } = props;
|
||||||
const [pendingTx, setPendingTx] = useState(false);
|
const [pendingTx, setPendingTx] = useState(false);
|
||||||
|
const [showConfirmation, setShowConfirmation] = useState(false);
|
||||||
|
|
||||||
|
const { accounts: fromAccounts } = useUserBalance(
|
||||||
|
repayReserve?.info.liquidityMint
|
||||||
|
);
|
||||||
|
|
||||||
const onLiquidate = useCallback(() => {
|
const onLiquidate = useCallback(() => {
|
||||||
|
if (!withdrawReserve) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setPendingTx(true);
|
setPendingTx(true);
|
||||||
setPendingTx(false);
|
|
||||||
}, []);
|
(async () => {
|
||||||
|
try {
|
||||||
|
await liquidate(
|
||||||
|
connection,
|
||||||
|
wallet,
|
||||||
|
fromAccounts[0],
|
||||||
|
// TODO: ensure user has available amount
|
||||||
|
wadToLamports(obligation.info.borrowAmountWad).toNumber(),
|
||||||
|
obligation.account,
|
||||||
|
repayReserve,
|
||||||
|
withdrawReserve,
|
||||||
|
);
|
||||||
|
|
||||||
|
setShowConfirmation(true);
|
||||||
|
} catch {
|
||||||
|
// TODO:
|
||||||
|
} finally {
|
||||||
|
setPendingTx(false);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, [withdrawReserve, fromAccounts, obligation, repayReserve, wallet, connection]);
|
||||||
|
|
||||||
const bodyStyle: React.CSSProperties = {
|
const bodyStyle: React.CSSProperties = {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
@ -34,24 +70,30 @@ export const LiquidateInput = (props: {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className={props.className} bodyStyle={bodyStyle}>
|
<Card className={props.className} bodyStyle={bodyStyle}>
|
||||||
<div
|
{showConfirmation ? (
|
||||||
style={{
|
<ActionConfirmation onClose={() => setShowConfirmation(false)} />
|
||||||
display: "flex",
|
) : (
|
||||||
flexDirection: "column",
|
<div
|
||||||
justifyContent: "space-around",
|
style={{
|
||||||
}}
|
display: "flex",
|
||||||
>
|
flexDirection: "column",
|
||||||
<div className="liquidate-input-title">{LABELS.SELECT_COLLATERAL}</div>
|
justifyContent: "space-around",
|
||||||
<CollateralSelector
|
}}
|
||||||
reserve={reserve.info}
|
>
|
||||||
collateralReserve={collateralReserve?.pubkey.toBase58()}
|
<div className="liquidate-input-title">{LABELS.SELECT_COLLATERAL}</div>
|
||||||
disabled={true}
|
<CollateralSelector
|
||||||
/>
|
reserve={repayReserve.info}
|
||||||
<Button type="primary" onClick={onLiquidate} loading={pendingTx}>
|
collateralReserve={withdrawReserve?.pubkey.toBase58()}
|
||||||
{LABELS.LIQUIDATE_ACTION}
|
disabled={true}
|
||||||
</Button>
|
/>
|
||||||
<BackButton />
|
<Button type="primary"
|
||||||
</div>
|
onClick={onLiquidate}
|
||||||
|
disabled={fromAccounts.length === 0}
|
||||||
|
loading={pendingTx}>
|
||||||
|
{LABELS.LIQUIDATE_ACTION}
|
||||||
|
</Button>
|
||||||
|
<BackButton />
|
||||||
|
</div>)}
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
import React from "react";
|
||||||
|
import { LABELS } from "../../../constants";
|
||||||
|
import { useUserDeposits } from "./../../../hooks";
|
||||||
|
import { DepositItem } from "./item";
|
||||||
|
|
||||||
|
export const DashboardDeposits = () => {
|
||||||
|
const { userDeposits } = useUserDeposits();
|
||||||
|
|
||||||
|
return (<>
|
||||||
|
<span>{LABELS.DASHBOARD_TITLE_DEPOSITS}</span>
|
||||||
|
<div className="dashboard-item dashboard-header">
|
||||||
|
<div>{LABELS.TABLE_TITLE_ASSET}</div>
|
||||||
|
<div>{LABELS.TABLE_TITLE_DEPOSIT_BALANCE}</div>
|
||||||
|
<div>{LABELS.TABLE_TITLE_APY}</div>
|
||||||
|
<div>{LABELS.TABLE_TITLE_ACTION}</div>
|
||||||
|
</div>
|
||||||
|
{userDeposits.map((deposit) => (
|
||||||
|
<DepositItem reserve={deposit.reserve} account={deposit.account} />
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,53 @@
|
||||||
|
import React, { useMemo } from "react";
|
||||||
|
import { useUserCollateralBalance, useTokenName } from "../../../hooks";
|
||||||
|
import { calculateDepositAPY, LendingReserve } from "../../../models/lending";
|
||||||
|
import { TokenIcon } from "../../../components/TokenIcon";
|
||||||
|
import { formatNumber, formatPct } from "../../../utils/utils";
|
||||||
|
import { Button, Card } from "antd";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { TokenAccount } from "../../../models";
|
||||||
|
import { ParsedAccount } from "../../../contexts/accounts";
|
||||||
|
import { LABELS } from "../../../constants";
|
||||||
|
|
||||||
|
export const DepositItem = (props: {
|
||||||
|
reserve: ParsedAccount<LendingReserve>;
|
||||||
|
account: TokenAccount;
|
||||||
|
}) => {
|
||||||
|
const mintAddress = props.reserve.info.liquidityMint;
|
||||||
|
const name = useTokenName(mintAddress);
|
||||||
|
const { balance: collateralBalance } = useUserCollateralBalance(
|
||||||
|
props.reserve.info,
|
||||||
|
props.account.pubkey
|
||||||
|
);
|
||||||
|
|
||||||
|
const depositAPY = useMemo(() => calculateDepositAPY(props.reserve.info), [
|
||||||
|
props.reserve,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<div className="dashboard-item">
|
||||||
|
<span style={{ display: "flex" }}>
|
||||||
|
<TokenIcon mintAddress={mintAddress} />
|
||||||
|
{name}
|
||||||
|
</span>
|
||||||
|
<div>
|
||||||
|
{formatNumber.format(collateralBalance)} {name}
|
||||||
|
</div>
|
||||||
|
<div>{formatPct.format(depositAPY)}</div>
|
||||||
|
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
||||||
|
<Link to={`/deposit/${props.reserve.pubkey.toBase58()}`}>
|
||||||
|
<Button>
|
||||||
|
<span>{LABELS.DEPOSIT_ACTION}</span>
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<Link to={`/withdraw/${props.reserve.pubkey.toBase58()}`}>
|
||||||
|
<Button>
|
||||||
|
<span>{LABELS.WITHDRAW_ACTION}</span>
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,9 +1,10 @@
|
||||||
|
import { Col, Row } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { LABELS } from "../../constants";
|
import { LABELS } from "../../constants";
|
||||||
import { useWallet } from "../../contexts/wallet";
|
import { useWallet } from "../../contexts/wallet";
|
||||||
import { useUserDeposits, useUserObligations } from "./../../hooks";
|
import { useUserDeposits, useUserObligations } from "./../../hooks";
|
||||||
import { DepositItem } from "./depositItem";
|
import { DashboardObligations } from "./obligation";
|
||||||
import { ObligationItem } from "./obligationItem";
|
import { DashboardDeposits } from "./deposit";
|
||||||
import "./style.less";
|
import "./style.less";
|
||||||
|
|
||||||
export const DashboardView = () => {
|
export const DashboardView = () => {
|
||||||
|
@ -13,44 +14,32 @@ export const DashboardView = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dashboard-container">
|
<div className="dashboard-container">
|
||||||
{!connected && (
|
{!connected && (
|
||||||
<div className="dashboard-info">{LABELS.DASHBOARD_INFO}</div>
|
<div className="dashboard-info">
|
||||||
|
<img src="splash.svg" alt="connect your wallet" className="dashboard-splash"/>
|
||||||
|
{LABELS.DASHBOARD_INFO}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
{connected &&
|
{connected &&
|
||||||
userDeposits.length === 0 &&
|
userDeposits.length === 0 &&
|
||||||
userObligations.length === 0 && (
|
userObligations.length === 0 && (
|
||||||
<div className="dashboard-info">{LABELS.NO_LOANS_NO_DEPOSITS}</div>
|
<div className="dashboard-info">
|
||||||
|
<img src="splash.svg" alt="connect your wallet" className="dashboard-splash"/>
|
||||||
|
{LABELS.NO_LOANS_NO_DEPOSITS}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
{userDeposits.length > 0 && (
|
<Row gutter={[16, { xs: 8, sm: 16, md: 16, lg: 16 }]} >
|
||||||
<div className="dashboard-left">
|
{userDeposits.length >0 && (
|
||||||
<span>{LABELS.DASHBOARD_TITLE_DEPOSITS}</span>
|
<Col md={24} xl={12} span={24}>
|
||||||
<div className="dashboard-item dashboard-header">
|
<DashboardDeposits />
|
||||||
<div>{LABELS.TABLE_TITLE_ASSET}</div>
|
</Col>
|
||||||
<div>{LABELS.TABLE_TITLE_DEPOSIT_BALANCE}</div>
|
)}
|
||||||
<div>{LABELS.TABLE_TITLE_APY}</div>
|
{userObligations.length >0 && (
|
||||||
<div>{LABELS.TABLE_TITLE_ACTION}</div>
|
<Col md={24} xl={12} span={24}>
|
||||||
</div>
|
<DashboardObligations />
|
||||||
{userDeposits.map((deposit) => (
|
</Col>
|
||||||
<DepositItem reserve={deposit.reserve} account={deposit.account} />
|
)}
|
||||||
))}
|
</Row>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{userObligations.length > 0 && (
|
|
||||||
<div className="dashboard-right">
|
|
||||||
<span>{LABELS.DASHBOARD_TITLE_LOANS}</span>
|
|
||||||
<div className="dashboard-item dashboard-header">
|
|
||||||
<div>{LABELS.TABLE_TITLE_ASSET}</div>
|
|
||||||
<div>{LABELS.TABLE_TITLE_YOUR_LOAN_BALANCE}</div>
|
|
||||||
<div>{LABELS.TABLE_TITLE_COLLATERAL_BALANCE}</div>
|
|
||||||
<div>{LABELS.TABLE_TITLE_APY}</div>
|
|
||||||
<div>{LABELS.TABLE_TITLE_LTV}</div>
|
|
||||||
<div>{LABELS.TABLE_TITLE_ACTION}</div>
|
|
||||||
</div>
|
|
||||||
{userObligations.map((item) => {
|
|
||||||
return <ObligationItem obligation={item.obligation} />;
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import React from "react";
|
||||||
|
import { LABELS } from "../../../constants";
|
||||||
|
import { useUserObligations } from "./../../../hooks";
|
||||||
|
import { ObligationItem } from "./item";
|
||||||
|
|
||||||
|
export const DashboardObligations = () => {
|
||||||
|
const { userObligations } = useUserObligations();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span>{LABELS.DASHBOARD_TITLE_LOANS}</span>
|
||||||
|
<div className="dashboard-item dashboard-header">
|
||||||
|
<div>{LABELS.TABLE_TITLE_ASSET}</div>
|
||||||
|
<div>{LABELS.TABLE_TITLE_YOUR_LOAN_BALANCE}</div>
|
||||||
|
<div>{LABELS.TABLE_TITLE_COLLATERAL_BALANCE}</div>
|
||||||
|
<div>{LABELS.TABLE_TITLE_APY}</div>
|
||||||
|
<div>{LABELS.TABLE_TITLE_LTV}</div>
|
||||||
|
<div>{LABELS.TABLE_TITLE_ACTION}</div>
|
||||||
|
</div>
|
||||||
|
{userObligations.map((item) => {
|
||||||
|
return <ObligationItem obligation={item.obligation} />;
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,20 +1,20 @@
|
||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
import { EnrichedLendingObligation, useTokenName } from "../../hooks";
|
import { EnrichedLendingObligation, useTokenName } from "../../../hooks";
|
||||||
import {
|
import {
|
||||||
calculateBorrowAPY,
|
calculateBorrowAPY,
|
||||||
collateralToLiquidity,
|
collateralToLiquidity,
|
||||||
LendingReserve,
|
LendingReserve,
|
||||||
} from "../../models/lending";
|
} from "../../../models/lending";
|
||||||
import { TokenIcon } from "../../components/TokenIcon";
|
import { TokenIcon } from "../../../components/TokenIcon";
|
||||||
import {
|
import {
|
||||||
wadToLamports,
|
wadToLamports,
|
||||||
formatNumber,
|
formatNumber,
|
||||||
fromLamports,
|
fromLamports,
|
||||||
formatPct,
|
formatPct,
|
||||||
} from "../../utils/utils";
|
} from "../../../utils/utils";
|
||||||
import { Button, Card } from "antd";
|
import { Button, Card } from "antd";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { cache, ParsedAccount, useMint } from "../../contexts/accounts";
|
import { cache, ParsedAccount, useMint } from "../../../contexts/accounts";
|
||||||
|
|
||||||
export const ObligationItem = (props: {
|
export const ObligationItem = (props: {
|
||||||
obligation: EnrichedLendingObligation;
|
obligation: EnrichedLendingObligation;
|
|
@ -41,25 +41,23 @@
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-right {
|
& > :first-child {
|
||||||
flex: 50%;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-left {
|
|
||||||
flex: 50%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-info {
|
.dashboard-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 700px) {
|
.dashboard-splash {
|
||||||
.dashboard-right, .dashboard-left {
|
max-width: 400px;
|
||||||
flex: 100%;
|
width: 100%;
|
||||||
|
align-self: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,23 +3,45 @@ import { Card, Col, Row, Statistic } from "antd";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { LABELS } from "../../constants";
|
import { LABELS } from "../../constants";
|
||||||
import { cache, ParsedAccount } from "../../contexts/accounts";
|
import { cache, ParsedAccount } from "../../contexts/accounts";
|
||||||
|
import { useConnectionConfig } from "../../contexts/connection";
|
||||||
import { useMarkets } from "../../contexts/market";
|
import { useMarkets } from "../../contexts/market";
|
||||||
import { useLendingReserves } from "../../hooks";
|
import { useLendingReserves } from "../../hooks";
|
||||||
import { reserveMarketCap } from "../../models";
|
import { reserveMarketCap } from "../../models";
|
||||||
import { fromLamports, wadToLamports } from "../../utils/utils";
|
import { fromLamports, getTokenName, wadToLamports } from "../../utils/utils";
|
||||||
import { LendingReserveItem } from "./item";
|
import { LendingReserveItem } from "./item";
|
||||||
|
import { BarChartStatistic } from "./../../components/BarChartStatistic";
|
||||||
import "./itemStyle.less";
|
import "./itemStyle.less";
|
||||||
|
|
||||||
|
interface Totals {
|
||||||
|
marketSize: number;
|
||||||
|
borrowed: number;
|
||||||
|
lentOutPct: number;
|
||||||
|
items: {
|
||||||
|
marketSize: number;
|
||||||
|
borrowed: number;
|
||||||
|
name: string;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
export const HomeView = () => {
|
export const HomeView = () => {
|
||||||
const { reserveAccounts } = useLendingReserves();
|
const { reserveAccounts } = useLendingReserves();
|
||||||
const [totalMarketSize, setTotalMarketSize] = useState(0);
|
|
||||||
const [totalBorrowed, setTotalBorrowed] = useState(0);
|
|
||||||
const { marketEmitter, midPriceInUSD } = useMarkets();
|
const { marketEmitter, midPriceInUSD } = useMarkets();
|
||||||
|
const { tokenMap } = useConnectionConfig();
|
||||||
|
const [totals, setTotals] = useState<Totals>({
|
||||||
|
marketSize: 0,
|
||||||
|
borrowed: 0,
|
||||||
|
lentOutPct: 0,
|
||||||
|
items: [],
|
||||||
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const refreshTotal = () => {
|
const refreshTotal = () => {
|
||||||
let totalSize = 0;
|
let newTotals: Totals = {
|
||||||
let borrowed = 0;
|
marketSize: 0,
|
||||||
|
borrowed: 0,
|
||||||
|
lentOutPct: 0,
|
||||||
|
items: [],
|
||||||
|
};
|
||||||
|
|
||||||
reserveAccounts.forEach((item) => {
|
reserveAccounts.forEach((item) => {
|
||||||
const marketCapLamports = reserveMarketCap(item.info);
|
const marketCapLamports = reserveMarketCap(item.info);
|
||||||
|
@ -33,22 +55,28 @@ export const HomeView = () => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const marketCap =
|
let leaf = {
|
||||||
fromLamports(marketCapLamports, liquidityMint?.info) *
|
marketSize: fromLamports(marketCapLamports, liquidityMint?.info) *
|
||||||
midPriceInUSD(liquidityMint?.pubkey.toBase58());
|
midPriceInUSD(liquidityMint?.pubkey.toBase58()),
|
||||||
|
borrowed: fromLamports(
|
||||||
totalSize = totalSize + marketCap;
|
|
||||||
|
|
||||||
borrowed =
|
|
||||||
borrowed +
|
|
||||||
fromLamports(
|
|
||||||
wadToLamports(item.info?.borrowedLiquidityWad).toNumber(),
|
wadToLamports(item.info?.borrowedLiquidityWad).toNumber(),
|
||||||
liquidityMint.info
|
liquidityMint.info
|
||||||
);
|
),
|
||||||
|
name: getTokenName(tokenMap, item.info.liquidityMint.toBase58())
|
||||||
|
}
|
||||||
|
|
||||||
|
newTotals.items.push(leaf);
|
||||||
|
|
||||||
|
newTotals.marketSize = newTotals.marketSize + leaf.marketSize;
|
||||||
|
newTotals.borrowed = newTotals.borrowed + leaf.borrowed;
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
setTotalMarketSize(totalSize);
|
newTotals.lentOutPct = newTotals.borrowed / newTotals.marketSize;
|
||||||
setTotalBorrowed(borrowed);
|
newTotals.lentOutPct = Number.isFinite(newTotals.lentOutPct) ? newTotals.lentOutPct : 0;
|
||||||
|
newTotals.items = newTotals.items.sort((a, b) => b.marketSize - a.marketSize)
|
||||||
|
|
||||||
|
setTotals(newTotals);
|
||||||
};
|
};
|
||||||
|
|
||||||
const dispose = marketEmitter.onMarket(() => {
|
const dispose = marketEmitter.onMarket(() => {
|
||||||
|
@ -60,32 +88,53 @@ export const HomeView = () => {
|
||||||
return () => {
|
return () => {
|
||||||
dispose();
|
dispose();
|
||||||
};
|
};
|
||||||
}, [marketEmitter, midPriceInUSD, setTotalMarketSize, reserveAccounts]);
|
}, [marketEmitter, midPriceInUSD, setTotals, reserveAccounts, tokenMap]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flexColumn">
|
<div className="flexColumn">
|
||||||
<Row gutter={16} className="home-info-row">
|
<Row
|
||||||
<Col span={12}>
|
gutter={[16, { xs: 8, sm: 16, md: 16, lg: 16 }]}
|
||||||
|
className="home-info-row" >
|
||||||
|
<Col xs={24} xl={5}>
|
||||||
<Card>
|
<Card>
|
||||||
<Statistic
|
<Statistic
|
||||||
title="Current market size"
|
title="Current market size"
|
||||||
value={totalMarketSize}
|
value={totals.marketSize}
|
||||||
precision={2}
|
precision={2}
|
||||||
valueStyle={{ color: "#3f8600" }}
|
valueStyle={{ color: "#3f8600" }}
|
||||||
prefix="$"
|
prefix="$"
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col xs={24} xl={5}>
|
||||||
<Card>
|
<Card>
|
||||||
<Statistic
|
<Statistic
|
||||||
title="Total borrowed"
|
title="Total borrowed"
|
||||||
value={totalBorrowed}
|
value={totals.borrowed}
|
||||||
precision={2}
|
precision={2}
|
||||||
prefix="$"
|
prefix="$"
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
|
<Col xs={24} xl={5}>
|
||||||
|
<Card>
|
||||||
|
<Statistic
|
||||||
|
title="% Lent out"
|
||||||
|
value={totals.lentOutPct * 100}
|
||||||
|
precision={2}
|
||||||
|
suffix="%"
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
<Col xs={24} xl={9}>
|
||||||
|
<Card>
|
||||||
|
<BarChartStatistic
|
||||||
|
title="Market composition"
|
||||||
|
name={(item) => item.name}
|
||||||
|
getPct={(item) => item.marketSize / totals.marketSize}
|
||||||
|
items={totals.items} />
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<div className="home-item home-header">
|
<div className="home-item home-header">
|
||||||
|
|
|
@ -14,12 +14,12 @@ export const LiquidateReserveView = () => {
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
|
|
||||||
const obligation = useEnrichedLendingObligation(id);
|
const obligation = useEnrichedLendingObligation(id);
|
||||||
const reserve = useLendingReserve(obligation?.info.borrowReserve);
|
const repayReserve = useLendingReserve(obligation?.info.borrowReserve);
|
||||||
const collateralReserve = useLendingReserve(
|
const withdrawReserve = useLendingReserve(
|
||||||
obligation?.info.collateralReserve
|
obligation?.info.collateralReserve
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!obligation || !reserve) {
|
if (!obligation || !repayReserve) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,12 +29,12 @@ export const LiquidateReserveView = () => {
|
||||||
<LiquidateInput
|
<LiquidateInput
|
||||||
className="liquidate-reserve-item liquidate-reserve-item-left"
|
className="liquidate-reserve-item liquidate-reserve-item-left"
|
||||||
obligation={obligation}
|
obligation={obligation}
|
||||||
collateralReserve={collateralReserve}
|
withdrawReserve={withdrawReserve}
|
||||||
reserve={reserve}
|
repayReserve={repayReserve}
|
||||||
/>
|
/>
|
||||||
<SideReserveOverview
|
<SideReserveOverview
|
||||||
className="liquidate-reserve-item liquidate-reserve-item-right"
|
className="liquidate-reserve-item liquidate-reserve-item-right"
|
||||||
reserve={reserve}
|
reserve={repayReserve}
|
||||||
mode={SideReserveOverviewMode.Deposit}
|
mode={SideReserveOverviewMode.Deposit}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
38
yarn.lock
38
yarn.lock
|
@ -42,7 +42,7 @@
|
||||||
resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.1.0.tgz#480b025f4b20ef7fe8f47d4a4846e4fee84ea06c"
|
resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.1.0.tgz#480b025f4b20ef7fe8f47d4a4846e4fee84ea06c"
|
||||||
integrity sha512-Fi03PfuUqRs76aI3UWYpP864lkrfPo0hluwGqh7NJdLhvH4iRDc3jbJqZIvRDLHKbXrvAfPPV3+zjUccfFvWOQ==
|
integrity sha512-Fi03PfuUqRs76aI3UWYpP864lkrfPo0hluwGqh7NJdLhvH4iRDc3jbJqZIvRDLHKbXrvAfPPV3+zjUccfFvWOQ==
|
||||||
|
|
||||||
"@ant-design/icons@^4.0.0":
|
"@ant-design/icons@^4.0.0", "@ant-design/icons@^4.3.0":
|
||||||
version "4.3.0"
|
version "4.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-4.3.0.tgz#420e0cd527486c0fe57f81310d681950fc4cfacf"
|
resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-4.3.0.tgz#420e0cd527486c0fe57f81310d681950fc4cfacf"
|
||||||
integrity sha512-UoIbw4oz/L/msbkgqs2nls2KP7XNKScOxVR54wRrWwnXOzJaGNwwSdYjHQz+5ETf8C53YPpzMOnRX99LFCdeIQ==
|
integrity sha512-UoIbw4oz/L/msbkgqs2nls2KP7XNKScOxVR54wRrWwnXOzJaGNwwSdYjHQz+5ETf8C53YPpzMOnRX99LFCdeIQ==
|
||||||
|
@ -66,14 +66,14 @@
|
||||||
insert-css "^2.0.0"
|
insert-css "^2.0.0"
|
||||||
rc-util "^5.0.1"
|
rc-util "^5.0.1"
|
||||||
|
|
||||||
"@ant-design/pro-layout@^6.5.14":
|
"@ant-design/pro-layout@^6.7.0":
|
||||||
version "6.5.15"
|
version "6.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/@ant-design/pro-layout/-/pro-layout-6.5.15.tgz#a5b8fb414ca51deebc8c4214b09699cfc30b60a7"
|
resolved "https://registry.yarnpkg.com/@ant-design/pro-layout/-/pro-layout-6.7.0.tgz#eba8d51bdf588c9a7ee58837a134f0f2bd8c14b3"
|
||||||
integrity sha512-AN9UQGbdK/4RwF0Uf5HkwdXsCRRzC1FeQv8jBKlzcsHEm/FGa9KslIDcjDzOR7q2P9NiysaBsWIGPIM/aRox5A==
|
integrity sha512-IGTtbsiTuKLwyHj4DEq6CfwFdh0YOnz/jGnikDQFMPmUtK/yeFa/rM2M4xmtVycPTzOZAx7A+UARw5VWLNy9cg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ant-design/icons" "^4.0.0"
|
"@ant-design/icons" "^4.0.0"
|
||||||
"@ant-design/pro-provider" "1.2.1"
|
"@ant-design/pro-provider" "1.2.4"
|
||||||
"@ant-design/pro-utils" "1.2.0"
|
"@ant-design/pro-utils" "1.5.1"
|
||||||
"@umijs/route-utils" "^1.0.33"
|
"@umijs/route-utils" "^1.0.33"
|
||||||
classnames "^2.2.6"
|
classnames "^2.2.6"
|
||||||
history "^4.10.1"
|
history "^4.10.1"
|
||||||
|
@ -82,6 +82,7 @@
|
||||||
path-to-regexp "2.4.0"
|
path-to-regexp "2.4.0"
|
||||||
qs "^6.9.0"
|
qs "^6.9.0"
|
||||||
rc-resize-observer "^0.2.1"
|
rc-resize-observer "^0.2.1"
|
||||||
|
rc-util "^5.0.6"
|
||||||
react-copy-to-clipboard "^5.0.1"
|
react-copy-to-clipboard "^5.0.1"
|
||||||
react-responsive "^8.0.1"
|
react-responsive "^8.0.1"
|
||||||
react-router-dom "5.1.2"
|
react-router-dom "5.1.2"
|
||||||
|
@ -90,20 +91,23 @@
|
||||||
use-media-antd-query "^1.0.3"
|
use-media-antd-query "^1.0.3"
|
||||||
warning "^4.0.3"
|
warning "^4.0.3"
|
||||||
|
|
||||||
"@ant-design/pro-provider@1.2.1":
|
"@ant-design/pro-provider@1.2.4":
|
||||||
version "1.2.1"
|
version "1.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/@ant-design/pro-provider/-/pro-provider-1.2.1.tgz#1dadd1e196dac363e002390aeb84a1ce0ed67118"
|
resolved "https://registry.yarnpkg.com/@ant-design/pro-provider/-/pro-provider-1.2.4.tgz#de1dc8fdf9c3e3156e0540a4d5db61c4e6c4e2c1"
|
||||||
integrity sha512-k2kT7zb1QhzJQvCuhfKXUwJJV6KsvyNtxbguDA3zoiK57EEAMRyxuiLMeNk6ejeY/Kak3l9yx2YiANuRRzU6Vg==
|
integrity sha512-tYk97VmhctXY/BAv6K+qVRBddHxmWkSf3QbU6cRhmnb0ofPV26wEX8oWa26MBqlS4QPry0+L4VO8n4iLQ8HaUw==
|
||||||
dependencies:
|
dependencies:
|
||||||
rc-util "^5.0.1"
|
rc-util "^5.0.1"
|
||||||
|
|
||||||
"@ant-design/pro-utils@1.2.0":
|
"@ant-design/pro-utils@1.5.1":
|
||||||
version "1.2.0"
|
version "1.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/@ant-design/pro-utils/-/pro-utils-1.2.0.tgz#2bf2157558095b18224bba4ba0a18081f03a1689"
|
resolved "https://registry.yarnpkg.com/@ant-design/pro-utils/-/pro-utils-1.5.1.tgz#e3cc1ef352cd804d562fcf1049e8d490602b48f4"
|
||||||
integrity sha512-k5Mo1Ma8cTB2aMSXI6VA5YwNO4BxGmUIugUrQJVRcu3OupTle1bYJf7tePUpPyN++Oe5AbSHwXl+c0CtfF01hA==
|
integrity sha512-dM2Q9ex8d28+RuaoFczkCY50KHG5666RqryzxA1IeMNVNTsU8iqflPl3v94+yiYonDTyHECIm4ZKqUlz2H4c/g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ant-design/pro-provider" "1.2.1"
|
"@ant-design/icons" "^4.3.0"
|
||||||
|
"@ant-design/pro-provider" "1.2.4"
|
||||||
|
classnames "^2.2.6"
|
||||||
fast-deep-equal "^3.1.3"
|
fast-deep-equal "^3.1.3"
|
||||||
|
moment "^2.27.0"
|
||||||
rc-util "^5.0.6"
|
rc-util "^5.0.6"
|
||||||
|
|
||||||
"@ant-design/react-slick@~0.27.0":
|
"@ant-design/react-slick@~0.27.0":
|
||||||
|
@ -8042,7 +8046,7 @@ mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.5"
|
minimist "^1.2.5"
|
||||||
|
|
||||||
moment@^2.24.0, moment@^2.25.3:
|
moment@^2.24.0, moment@^2.25.3, moment@^2.27.0:
|
||||||
version "2.29.1"
|
version "2.29.1"
|
||||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
|
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
|
||||||
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
|
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
|
||||||
|
|
Loading…
Reference in New Issue