feat: query users obligations
This commit is contained in:
parent
e2f8cafade
commit
ab975de0b8
|
@ -2,7 +2,6 @@
|
|||
|
||||
Any content produced by Solana, or developer resources that Solana provides, are for educational and inspiration purposes only. Solana does not encourage, induce or sanction the deployment of any such applications in violation of applicable laws or regulations.
|
||||
|
||||
|
||||
## TODO
|
||||
|
||||
- [] Calculate deposit APY and borrow APY for home page
|
||||
|
@ -14,4 +13,4 @@ Any content produced by Solana, or developer resources that Solana provides, are
|
|||
- [] Add liquidate view
|
||||
- [] Borrow view calculate available to you and borrow APY
|
||||
- [] Facuet add USDC that is using sol airdrop and USDC reserve to give user USDC
|
||||
- [] Borrow - Convert target ccy to collateral on oposite side
|
||||
- [] Borrow - Convert target ccy to collateral on oposite side
|
||||
|
|
|
@ -5,7 +5,11 @@ import {
|
|||
SystemProgram,
|
||||
TransactionInstruction,
|
||||
} from "@solana/web3.js";
|
||||
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID, WRAPPED_SOL_MINT } from "../constants/ids";
|
||||
import {
|
||||
LENDING_PROGRAM_ID,
|
||||
TOKEN_PROGRAM_ID,
|
||||
WRAPPED_SOL_MINT,
|
||||
} from "../constants/ids";
|
||||
import { LendingObligationLayout, TokenAccount } from "../models";
|
||||
import { cache, TokenAccountParser } from "./../contexts/accounts";
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ export const borrow = async (
|
|||
cleanupInstructions = [];
|
||||
|
||||
const [authority] = await PublicKey.findProgramAddress(
|
||||
[depositReserve.lendingMarket.toBuffer()], // which account should be authority
|
||||
[depositReserve.lendingMarket.toBuffer()],
|
||||
LENDING_PROGRAM_ID
|
||||
);
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export { borrow } from "./borrow";
|
||||
export { deposit } from "./deposit";
|
||||
export { repay } from "./repay";
|
||||
export { withdraw } from "./withdraw";
|
||||
export * from "./account";
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
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 {
|
||||
repayInstruction,
|
||||
} from "./../models/lending/repay";
|
||||
import { AccountLayout, Token } from "@solana/spl-token";
|
||||
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../constants/ids";
|
||||
import { findOrCreateAccountByMint } from "./account";
|
||||
import { LendingObligation, TokenAccount } from "../models";
|
||||
|
||||
export const repay = async (
|
||||
from: TokenAccount, // CollateralAccount
|
||||
amountLamports: number, // in collateral token (lamports)
|
||||
|
||||
// which loan to repay
|
||||
obligation: LendingObligation,
|
||||
|
||||
repayReserve: LendingReserve,
|
||||
repayReserveAddress: PublicKey,
|
||||
|
||||
withdrawReserve: LendingReserve,
|
||||
withdrawReserveAddress: PublicKey,
|
||||
|
||||
connection: Connection,
|
||||
wallet: any
|
||||
) => {
|
||||
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.lendingMarket.toBuffer()],
|
||||
LENDING_PROGRAM_ID
|
||||
);
|
||||
|
||||
const fromAccount = from.pubkey;
|
||||
|
||||
// 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.liquidityMint,
|
||||
signers
|
||||
);
|
||||
|
||||
// TODO: add obligation
|
||||
|
||||
// instructions.push(
|
||||
// repayInstruction(
|
||||
// amountLamports,
|
||||
// fromAccount,
|
||||
// toAccount,
|
||||
// reserveAddress,
|
||||
// reserve.collateralMint,
|
||||
// reserve.liquiditySupply,
|
||||
// authority
|
||||
// )
|
||||
// );
|
||||
|
||||
try {
|
||||
let tx = await sendTransaction(
|
||||
connection,
|
||||
wallet,
|
||||
instructions.concat(cleanupInstructions),
|
||||
signers,
|
||||
true
|
||||
);
|
||||
|
||||
notify({
|
||||
message: "Funds repaid.",
|
||||
type: "success",
|
||||
description: `Transaction - ${tx}`,
|
||||
});
|
||||
} catch {
|
||||
// TODO:
|
||||
}
|
||||
};
|
|
@ -39,7 +39,7 @@ export const withdraw = async (
|
|||
);
|
||||
|
||||
const [authority] = await PublicKey.findProgramAddress(
|
||||
[reserve.lendingMarket.toBuffer()], // which account should be authority
|
||||
[reserve.lendingMarket.toBuffer()],
|
||||
LENDING_PROGRAM_ID
|
||||
);
|
||||
|
||||
|
|
|
@ -1,65 +1,17 @@
|
|||
import React, { useCallback, useMemo, useState } from "react";
|
||||
import { useLendingReserves, useTokenName, useUserBalance } from "../../hooks";
|
||||
import { useTokenName, useUserBalance } from "../../hooks";
|
||||
import { LendingReserve, LendingReserveParser } from "../../models";
|
||||
import { TokenIcon } from "../TokenIcon";
|
||||
import { getTokenName } from "../../utils/utils";
|
||||
import { Button, Card, Select } from "antd";
|
||||
import { Button, Card } from "antd";
|
||||
import { cache, ParsedAccount } from "../../contexts/accounts";
|
||||
import { NumericInput } from "../Input/numeric";
|
||||
import { useConnection, useConnectionConfig } from "../../contexts/connection";
|
||||
import { useConnection } from "../../contexts/connection";
|
||||
import { useWallet } from "../../contexts/wallet";
|
||||
import { borrow } from "../../actions";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { CollateralSelector } from "./../CollateralSelector";
|
||||
import "./style.less";
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
const CollateralSelector = (props: {
|
||||
reserve: LendingReserve;
|
||||
mint?: string;
|
||||
onMintChange: (id: string) => void;
|
||||
}) => {
|
||||
const { reserveAccounts } = useLendingReserves();
|
||||
const { tokenMap } = useConnectionConfig();
|
||||
|
||||
return (
|
||||
<Select
|
||||
size="large"
|
||||
showSearch
|
||||
style={{ minWidth: 120 }}
|
||||
placeholder="Collateral"
|
||||
value={props.mint}
|
||||
onChange={(item) => {
|
||||
if (props.onMintChange) {
|
||||
props.onMintChange(item);
|
||||
}
|
||||
}}
|
||||
filterOption={(input, option) =>
|
||||
option?.name?.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
>
|
||||
{reserveAccounts
|
||||
.filter((reserve) => reserve.info !== props.reserve)
|
||||
.map((reserve) => {
|
||||
const mint = reserve.info.liquidityMint.toBase58();
|
||||
const address = reserve.pubkey.toBase58();
|
||||
const name = getTokenName(tokenMap, mint);
|
||||
return (
|
||||
<Option key={address} value={address} name={name} title={address}>
|
||||
<div
|
||||
key={address}
|
||||
style={{ display: "flex", alignItems: "center" }}
|
||||
>
|
||||
<TokenIcon mintAddress={mint} />
|
||||
{name}
|
||||
</div>
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
|
||||
export const BorrowInput = (props: {
|
||||
className?: string;
|
||||
reserve: LendingReserve;
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
import React from "react";
|
||||
import { useLendingReserves } from "../../hooks";
|
||||
import { LendingReserve } from "../../models";
|
||||
import { TokenIcon } from "../TokenIcon";
|
||||
import { getTokenName } from "../../utils/utils";
|
||||
import { Select } from "antd";
|
||||
import { useConnectionConfig } from "../../contexts/connection";
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
export const CollateralSelector = (props: {
|
||||
reserve: LendingReserve;
|
||||
mint?: string;
|
||||
onMintChange: (id: string) => void;
|
||||
}) => {
|
||||
const { reserveAccounts } = useLendingReserves();
|
||||
const { tokenMap } = useConnectionConfig();
|
||||
|
||||
return (
|
||||
<Select
|
||||
size="large"
|
||||
showSearch
|
||||
style={{ minWidth: 120 }}
|
||||
placeholder="Collateral"
|
||||
value={props.mint}
|
||||
onChange={(item) => {
|
||||
if (props.onMintChange) {
|
||||
props.onMintChange(item);
|
||||
}
|
||||
}}
|
||||
filterOption={(input, option) =>
|
||||
option?.name?.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
>
|
||||
{reserveAccounts
|
||||
.filter((reserve) => reserve.info !== props.reserve)
|
||||
.map((reserve) => {
|
||||
const mint = reserve.info.liquidityMint.toBase58();
|
||||
const address = reserve.pubkey.toBase58();
|
||||
const name = getTokenName(tokenMap, mint);
|
||||
return (
|
||||
<Option key={address} value={address} name={name} title={address}>
|
||||
<div
|
||||
key={address}
|
||||
style={{ display: "flex", alignItems: "center" }}
|
||||
>
|
||||
<TokenIcon mintAddress={mint} />
|
||||
{name}
|
||||
</div>
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,128 @@
|
|||
import React, { useCallback, useMemo, useState } from "react";
|
||||
import { useTokenName, useUserBalance } from "../../hooks";
|
||||
import { LendingObligation, LendingReserve, LendingReserveParser } from "../../models";
|
||||
import { TokenIcon } from "../TokenIcon";
|
||||
import { Button, Card } from "antd";
|
||||
import { cache, ParsedAccount } from "../../contexts/accounts";
|
||||
import { NumericInput } from "../Input/numeric";
|
||||
import { useConnection } from "../../contexts/connection";
|
||||
import { useWallet } from "../../contexts/wallet";
|
||||
import { repay } from "../../actions";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { CollateralSelector } from "./../CollateralSelector";
|
||||
import "./style.less";
|
||||
|
||||
export const RepayInput = (props: {
|
||||
className?: string;
|
||||
reserve: LendingReserve;
|
||||
obligation: LendingObligation;
|
||||
address: PublicKey;
|
||||
}) => {
|
||||
const connection = useConnection();
|
||||
const { wallet } = useWallet();
|
||||
const [value, setValue] = useState("");
|
||||
|
||||
const repayReserve = props.reserve;
|
||||
const repayReserveAddress = props.address;
|
||||
const obligation = props.obligation;
|
||||
|
||||
const [collateralReserveMint, setCollateralReserveMint] = useState<string>();
|
||||
|
||||
const collateralReserve = useMemo(() => {
|
||||
const id: string =
|
||||
cache
|
||||
.byParser(LendingReserveParser)
|
||||
.find((acc) => acc === collateralReserveMint) || "";
|
||||
|
||||
return cache.get(id) as ParsedAccount<LendingReserve>;
|
||||
}, [collateralReserveMint]);
|
||||
|
||||
const name = useTokenName(repayReserve?.liquidityMint);
|
||||
const { accounts: fromAccounts } = useUserBalance(
|
||||
collateralReserve?.info.collateralMint
|
||||
);
|
||||
// const collateralBalance = useUserBalance(reserve?.collateralMint);
|
||||
|
||||
const onReoay = useCallback(() => {
|
||||
if (!collateralReserve) {
|
||||
return;
|
||||
}
|
||||
|
||||
repay(
|
||||
fromAccounts[0],
|
||||
parseFloat(value),
|
||||
obligation,
|
||||
repayReserve,
|
||||
repayReserveAddress,
|
||||
collateralReserve.info,
|
||||
collateralReserve.pubkey,
|
||||
connection,
|
||||
wallet
|
||||
);
|
||||
}, [
|
||||
connection,
|
||||
wallet,
|
||||
value,
|
||||
obligation,
|
||||
collateralReserve,
|
||||
repayReserve,
|
||||
fromAccounts,
|
||||
repayReserveAddress,
|
||||
]);
|
||||
|
||||
const bodyStyle: React.CSSProperties = {
|
||||
display: "flex",
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: "100%",
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className={props.className} bodyStyle={bodyStyle}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-around",
|
||||
}}
|
||||
>
|
||||
<div className="repay-input-title">
|
||||
How much would you like to repay?
|
||||
</div>
|
||||
<div className="token-input">
|
||||
<TokenIcon mintAddress={repayReserve?.liquidityMint} />
|
||||
<NumericInput
|
||||
value={value}
|
||||
onChange={(val: any) => {
|
||||
setValue(val);
|
||||
}}
|
||||
autoFocus={true}
|
||||
style={{
|
||||
fontSize: 20,
|
||||
boxShadow: "none",
|
||||
borderColor: "transparent",
|
||||
outline: "transpaernt",
|
||||
}}
|
||||
placeholder="0.00"
|
||||
/>
|
||||
<div>{name}</div>
|
||||
</div>
|
||||
<div className="repay-input-title">Select collateral account?</div>
|
||||
<CollateralSelector
|
||||
reserve={repayReserve}
|
||||
mint={collateralReserveMint}
|
||||
onMintChange={setCollateralReserveMint}
|
||||
/>
|
||||
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={onReoay}
|
||||
disabled={fromAccounts.length === 0}
|
||||
>
|
||||
Repay
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
.borrow-input-title {
|
||||
font-size: 1.05rem;
|
||||
}
|
|
@ -115,7 +115,7 @@ export const UserLendingCard = (props: {
|
|||
<Button>Withdraw</Button>
|
||||
</Link>
|
||||
<Link to={`/repay/${address}`}>
|
||||
<Button disabled={true}>Repay</Button>
|
||||
<Button>Repay</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</Card>
|
||||
|
|
|
@ -7,6 +7,8 @@ import {
|
|||
isLendingMarket,
|
||||
LendingReserveParser,
|
||||
LendingReserve,
|
||||
isLendingObligation,
|
||||
LendingObligationParser,
|
||||
} from "./../models/lending";
|
||||
import {
|
||||
cache,
|
||||
|
@ -51,6 +53,12 @@ export const useLending = () => {
|
|||
item.account,
|
||||
LendingMarketParser
|
||||
);
|
||||
}else if (isLendingObligation(item.account)) {
|
||||
return cache.add(
|
||||
item.pubkey.toBase58(),
|
||||
item.account,
|
||||
LendingObligationParser,
|
||||
);
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
|
|
@ -4,3 +4,5 @@ export * from "./useLendingReserves";
|
|||
export * from "./useTokenName";
|
||||
export * from "./useUserBalance";
|
||||
export * from "./useCollateralBalance";
|
||||
export * from "./useLendingObligations";
|
||||
export * from "./useUserObligations";
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
import { PublicKey } from "@solana/web3.js";
|
||||
import { useEffect, useState } from "react";
|
||||
import { LendingObligation, LendingObligationParser } from "../models/lending";
|
||||
import { cache, ParsedAccount } from "./../contexts/accounts";
|
||||
|
||||
const getLendingObligations = () => {
|
||||
return cache
|
||||
.byParser(LendingObligationParser)
|
||||
.map((id) => cache.get(id))
|
||||
.filter((acc) => acc !== undefined) as any[];
|
||||
};
|
||||
|
||||
export function useLendingObligations() {
|
||||
const [obligations, setObligations] = useState<
|
||||
ParsedAccount<LendingObligation>[]
|
||||
>([]);
|
||||
|
||||
useEffect(() => {
|
||||
setObligations(getLendingObligations());
|
||||
|
||||
const dispose = cache.emitter.onCache((args) => {
|
||||
if (args.parser === LendingObligationParser) {
|
||||
setObligations(getLendingObligations());
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
dispose();
|
||||
};
|
||||
}, [setObligations]);
|
||||
|
||||
return {
|
||||
obligations,
|
||||
};
|
||||
}
|
||||
|
||||
export function useLendingObligation(address: string | PublicKey) {
|
||||
const id = typeof address === "string" ? address : address?.toBase58();
|
||||
const [obligationAccount, setObligationAccount] = useState<
|
||||
ParsedAccount<LendingObligation>
|
||||
>();
|
||||
|
||||
useEffect(() => {
|
||||
setObligationAccount(cache.get(id));
|
||||
|
||||
const dispose = cache.emitter.onCache((args) => {
|
||||
if (args.id === id) {
|
||||
setObligationAccount(cache.get(id));
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
dispose();
|
||||
};
|
||||
}, [id, setObligationAccount]);
|
||||
|
||||
return obligationAccount;
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
import { useMemo } from "react";
|
||||
import { useUserAccounts } from "./useUserAccounts";
|
||||
import { useLendingObligations } from "./useLendingObligations";
|
||||
import { TokenAccount } from "../models";
|
||||
|
||||
export function useUserObligations() {
|
||||
const { userAccounts } = useUserAccounts();
|
||||
const { obligations } = useLendingObligations();
|
||||
|
||||
const accountsByMint = useMemo(() => {
|
||||
return userAccounts.reduce((res, acc) => {
|
||||
const id = acc.info.mint.toBase58();
|
||||
res.set(id, [...(res.get(id) || []), acc]);
|
||||
return res;
|
||||
}, new Map<string, TokenAccount[]>())
|
||||
},
|
||||
[userAccounts]);
|
||||
|
||||
const userObligations = useMemo(() => {
|
||||
if(accountsByMint.size === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return obligations
|
||||
.filter((acc) => accountsByMint.get(acc.info.tokenMint.toBase58()) !== undefined)
|
||||
.map(ob => {
|
||||
return {
|
||||
oblication: ob,
|
||||
userAccounts: [...accountsByMint.get(ob.info.tokenMint.toBase58())],
|
||||
|
||||
// TODO: add total borrowed amount?
|
||||
}
|
||||
});
|
||||
}, [accountsByMint, obligations]);
|
||||
|
||||
|
||||
return {
|
||||
userObligations
|
||||
};
|
||||
}
|
|
@ -1,4 +1,8 @@
|
|||
import { PublicKey, SYSVAR_CLOCK_PUBKEY, TransactionInstruction } from "@solana/web3.js";
|
||||
import {
|
||||
PublicKey,
|
||||
SYSVAR_CLOCK_PUBKEY,
|
||||
TransactionInstruction,
|
||||
} from "@solana/web3.js";
|
||||
import BN from "bn.js";
|
||||
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../../constants/ids";
|
||||
import { LendingInstruction } from "./lending";
|
||||
|
@ -32,7 +36,7 @@ export const liquidateInstruction = (
|
|||
authority: PublicKey,
|
||||
dexMarket: PublicKey,
|
||||
dexOrderBookSide: PublicKey,
|
||||
memory: PublicKey,
|
||||
memory: PublicKey
|
||||
): TransactionInstruction => {
|
||||
const dataLayout = BufferLayout.struct([
|
||||
BufferLayout.u8("instruction"),
|
||||
|
@ -56,7 +60,11 @@ export const liquidateInstruction = (
|
|||
{ pubkey: repayReserveLiquiditySupply, isSigner: false, isWritable: true },
|
||||
|
||||
{ pubkey: withdrawReserve, isSigner: false, isWritable: true },
|
||||
{ pubkey: withdrawReserveCollateralSupply, isSigner: false, isWritable: true },
|
||||
{
|
||||
pubkey: withdrawReserveCollateralSupply,
|
||||
isSigner: false,
|
||||
isWritable: true,
|
||||
},
|
||||
|
||||
{ pubkey: obligation, isSigner: false, isWritable: true },
|
||||
|
||||
|
@ -75,4 +83,4 @@ export const liquidateInstruction = (
|
|||
programId: LENDING_PROGRAM_ID,
|
||||
data,
|
||||
});
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { PublicKey } from "@solana/web3.js";
|
||||
import { AccountInfo, PublicKey } from "@solana/web3.js";
|
||||
import BN from "bn.js";
|
||||
import * as BufferLayout from "buffer-layout";
|
||||
import * as Layout from "./../../utils/layout";
|
||||
|
@ -22,6 +22,10 @@ export const LendingObligationLayout: typeof BufferLayout.Structure = BufferLayo
|
|||
]
|
||||
);
|
||||
|
||||
export const isLendingObligation = (info: AccountInfo<Buffer>) => {
|
||||
return info.data.length === LendingObligationLayout.span;
|
||||
};
|
||||
|
||||
export interface LendingObligation {
|
||||
lastUpdateSlot: BN;
|
||||
collateralAmount: BN;
|
||||
|
@ -31,3 +35,23 @@ export interface LendingObligation {
|
|||
borrowReserve: PublicKey;
|
||||
tokenMint: PublicKey;
|
||||
}
|
||||
|
||||
export const LendingObligationParser = (
|
||||
pubKey: PublicKey,
|
||||
info: AccountInfo<Buffer>
|
||||
) => {
|
||||
const buffer = Buffer.from(info.data);
|
||||
const data = LendingObligationLayout.decode(buffer);
|
||||
|
||||
console.log(data);
|
||||
|
||||
const details = {
|
||||
pubkey: pubKey,
|
||||
account: {
|
||||
...info,
|
||||
},
|
||||
info: data,
|
||||
};
|
||||
|
||||
return details;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
import { PublicKey, SYSVAR_CLOCK_PUBKEY, TransactionInstruction } from "@solana/web3.js";
|
||||
import {
|
||||
PublicKey,
|
||||
SYSVAR_CLOCK_PUBKEY,
|
||||
TransactionInstruction,
|
||||
} from "@solana/web3.js";
|
||||
import BN from "bn.js";
|
||||
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../../constants/ids";
|
||||
import { LendingInstruction } from "./lending";
|
||||
|
@ -55,12 +59,16 @@ export const repayInstruction = (
|
|||
{ pubkey: repayReserveLiquiditySupply, isSigner: false, isWritable: true },
|
||||
|
||||
{ pubkey: withdrawReserve, isSigner: false, isWritable: false },
|
||||
{ pubkey: withdrawReserveCollateralSupply, isSigner: false, isWritable: true },
|
||||
{
|
||||
pubkey: withdrawReserveCollateralSupply,
|
||||
isSigner: false,
|
||||
isWritable: true,
|
||||
},
|
||||
|
||||
{ pubkey: obligation, isSigner: false, isWritable: true },
|
||||
{ pubkey: obligationMint, isSigner: false, isWritable: true },
|
||||
{ pubkey: obligationInput, isSigner: false, isWritable: true },
|
||||
|
||||
|
||||
{ pubkey: authority, isSigner: false, isWritable: false },
|
||||
{ pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
|
||||
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
||||
|
@ -70,4 +78,4 @@ export const repayInstruction = (
|
|||
programId: LENDING_PROGRAM_ID,
|
||||
data,
|
||||
});
|
||||
};
|
||||
};
|
||||
|
|
|
@ -8,15 +8,16 @@ import { LendingProvider } from "./contexts/lending";
|
|||
import { AppLayout } from "./components/Layout";
|
||||
|
||||
import {
|
||||
HomeView,
|
||||
DepositView,
|
||||
DepositReserveView,
|
||||
BorrowView,
|
||||
ReserveView,
|
||||
DashboardView,
|
||||
BorrowReserveView,
|
||||
WithdrawView,
|
||||
BorrowView,
|
||||
DashboardView,
|
||||
DepositReserveView,
|
||||
DepositView,
|
||||
FaucetView,
|
||||
HomeView,
|
||||
RepayReserveView,
|
||||
ReserveView,
|
||||
WithdrawView,
|
||||
} from "./views";
|
||||
|
||||
export function Routes() {
|
||||
|
@ -52,6 +53,10 @@ export function Routes() {
|
|||
path="/borrow/:id"
|
||||
children={<BorrowReserveView />}
|
||||
/>
|
||||
<Route
|
||||
path="/repay/:id"
|
||||
children={<RepayReserveView />}
|
||||
/>
|
||||
<Route exact path="/faucet" children={<FaucetView />} />
|
||||
</Switch>
|
||||
</AppLayout>
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
import React from "react";
|
||||
import { useUserObligations } from "./../../hooks";
|
||||
|
||||
export const DashboardView = () => {
|
||||
const { userObligations } = useUserObligations();
|
||||
|
||||
return (
|
||||
<div className="flexColumn">
|
||||
DASHBOARD: TODO: 1. Add deposits 2. Add obligations
|
||||
|
||||
{userObligations.map(item => {
|
||||
return <div>{item?.oblication.info.borrowAmount.toString()}</div>;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@ export const LendingReserveItem = (props: {
|
|||
props.reserve.totalLiquidity.toNumber(),
|
||||
liquidityMint
|
||||
);
|
||||
|
||||
|
||||
const totalBorrows = props.reserve.totalBorrows.toString();
|
||||
|
||||
console.log(liquidityMint);
|
||||
|
|
|
@ -7,3 +7,4 @@ export { DepositReserveView } from "./depositReserve";
|
|||
export { ReserveView } from "./reserve";
|
||||
export { WithdrawView } from "./withdraw";
|
||||
export { FaucetView } from "./faucet";
|
||||
export { RepayReserveView } from "./repayReserve";
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import React from "react";
|
||||
import { useLendingReserve } from "../../hooks";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import { RepayInput } from "../../components/RepayInput";
|
||||
import {
|
||||
SideReserveOverview,
|
||||
SideReserveOverviewMode,
|
||||
} from "../../components/SideReserveOverview";
|
||||
|
||||
import "./style.less";
|
||||
import { LendingObligation } from "../../models";
|
||||
|
||||
export const RepayReserveView = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const lendingReserve = useLendingReserve(id);
|
||||
const reserve = lendingReserve?.info;
|
||||
|
||||
// TODO: query for lending obligation
|
||||
const ob: LendingObligation = {} as any;
|
||||
|
||||
if (!reserve || !lendingReserve) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="repay-reserve">
|
||||
<div className="repay-reserve-container">
|
||||
<RepayInput
|
||||
className="repay-reserve-item repay-reserve-item-left"
|
||||
reserve={reserve}
|
||||
obligation={ob}
|
||||
address={lendingReserve.pubkey}
|
||||
/>
|
||||
<SideReserveOverview
|
||||
className="repay-reserve-item repay-reserve-item-right"
|
||||
reserve={reserve}
|
||||
address={lendingReserve.pubkey}
|
||||
mode={SideReserveOverviewMode.Borrow}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
.repay-reserve {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.repay-reserve-item {
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
.repay-reserve-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.repay-reserve-item-left {
|
||||
flex: 60%;
|
||||
}
|
||||
|
||||
.repay-reserve-item-right {
|
||||
flex: 30%;
|
||||
}
|
||||
|
||||
/* Responsive layout - makes a one column layout instead of a two-column layout */
|
||||
@media (max-width: 600px) {
|
||||
.repay-reserve-item-right, .repay-reserve-item-left {
|
||||
flex: 100%;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue