mirror of https://github.com/certusone/oyster.git
feat: reuse obligations
This commit is contained in:
parent
00db4555be
commit
097dc95f5f
10
README.md
10
README.md
|
@ -4,14 +4,14 @@ Any content produced by Solana, or developer resources that Solana provides, are
|
|||
|
||||
## TODO
|
||||
|
||||
- [] Calculate deposit APY and borrow APY for home page
|
||||
- [x] Calculate deposit APY and borrow APY for home page
|
||||
- [] Finish Reserve overview page (chart, and borrows information)
|
||||
- [] Create Dashboard page
|
||||
- [x] Create Dashboard page
|
||||
- [] Deposit view calculate APY and health factor
|
||||
- [x] Format total Borrows
|
||||
- [x] Add repay view
|
||||
- [] Add liquidate view
|
||||
- [] Borrow view calculate available to you and borrow APY
|
||||
- [x] 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 - liquidity token wrapped SOL should be unwrapped ...
|
||||
|
@ -22,3 +22,7 @@ Any content produced by Solana, or developer resources that Solana provides, are
|
|||
- [] Repay from reserve (add selection for obligation/loan)
|
||||
- [] Add support for token names in URL in addition to reserve address
|
||||
|
||||
# TOP for tomorrow
|
||||
|
||||
* subscribe to all dex markets that are used by lending reserves
|
||||
* finish reserve overivew
|
||||
|
|
|
@ -29,6 +29,9 @@ import {
|
|||
import { toLamports } from "../utils/utils";
|
||||
|
||||
export const borrow = async (
|
||||
connection: Connection,
|
||||
wallet: any,
|
||||
|
||||
from: TokenAccount,
|
||||
amount: number,
|
||||
amountType: BorrowAmountType,
|
||||
|
@ -37,10 +40,10 @@ export const borrow = async (
|
|||
|
||||
depositReserve: ParsedAccount<LendingReserve>,
|
||||
|
||||
exsistingObligation: ParsedAccount<LendingObligation> | undefined,
|
||||
exsistingObligation?: ParsedAccount<LendingObligation>,
|
||||
|
||||
obligationAccount?: PublicKey,
|
||||
|
||||
connection: Connection,
|
||||
wallet: any
|
||||
) => {
|
||||
notify({
|
||||
message: "Borrowing funds...",
|
||||
|
@ -56,28 +59,34 @@ export const borrow = async (
|
|||
AccountLayout.span
|
||||
);
|
||||
|
||||
const obligation = createUninitializedObligation(
|
||||
instructions,
|
||||
wallet.publicKey,
|
||||
await connection.getMinimumBalanceForRentExemption(
|
||||
LendingObligationLayout.span
|
||||
),
|
||||
signers
|
||||
);
|
||||
const obligation = exsistingObligation ?
|
||||
exsistingObligation.pubkey :
|
||||
createUninitializedObligation(
|
||||
instructions,
|
||||
wallet.publicKey,
|
||||
await connection.getMinimumBalanceForRentExemption(
|
||||
LendingObligationLayout.span
|
||||
),
|
||||
signers
|
||||
);
|
||||
|
||||
const obligationMint = createUninitializedMint(
|
||||
instructions,
|
||||
wallet.publicKey,
|
||||
await connection.getMinimumBalanceForRentExemption(MintLayout.span),
|
||||
signers
|
||||
);
|
||||
const obligationMint = exsistingObligation ?
|
||||
exsistingObligation.info.tokenMint :
|
||||
createUninitializedMint(
|
||||
instructions,
|
||||
wallet.publicKey,
|
||||
await connection.getMinimumBalanceForRentExemption(MintLayout.span),
|
||||
signers
|
||||
);
|
||||
|
||||
const obligationTokenOutput = createUninitializedAccount(
|
||||
instructions,
|
||||
wallet.publicKey,
|
||||
accountRentExempt,
|
||||
signers
|
||||
);
|
||||
const obligationTokenOutput = obligationAccount ?
|
||||
obligationAccount :
|
||||
createUninitializedAccount(
|
||||
instructions,
|
||||
wallet.publicKey,
|
||||
accountRentExempt,
|
||||
signers
|
||||
);
|
||||
|
||||
let toAccount = await findOrCreateAccountByMint(
|
||||
wallet.publicKey,
|
||||
|
@ -89,23 +98,26 @@ export const borrow = async (
|
|||
signers
|
||||
);
|
||||
|
||||
// create all accounts in one transaction
|
||||
let tx = await sendTransaction(connection, wallet, instructions, [
|
||||
...signers,
|
||||
]);
|
||||
|
||||
notify({
|
||||
message: "Obligation accounts created",
|
||||
description: `Transaction ${tx}`,
|
||||
type: "success",
|
||||
});
|
||||
if (instructions.length > 0) {
|
||||
// create all accounts in one transaction
|
||||
let tx = await sendTransaction(connection, wallet, instructions, [
|
||||
...signers,
|
||||
]);
|
||||
|
||||
notify({
|
||||
message: "Obligation accounts created",
|
||||
description: `Transaction ${tx}`,
|
||||
type: "success",
|
||||
});
|
||||
}
|
||||
|
||||
notify({
|
||||
message: "Adding Liquidity...",
|
||||
description: "Please review transactions to approve.",
|
||||
type: "warn",
|
||||
});
|
||||
|
||||
|
||||
signers = [];
|
||||
instructions = [];
|
||||
cleanupInstructions = [];
|
||||
|
@ -115,11 +127,9 @@ export const borrow = async (
|
|||
LENDING_PROGRAM_ID
|
||||
);
|
||||
|
||||
|
||||
|
||||
let amountLamports: number = 0;
|
||||
let fromLamports: number = 0;
|
||||
if(amountType === BorrowAmountType.LiquidityBorrowAmount) {
|
||||
if (amountType === BorrowAmountType.LiquidityBorrowAmount) {
|
||||
// approve max transfer
|
||||
// TODO: improve contrain by using dex market data
|
||||
const approvedAmount = from.info.amount.toNumber();
|
||||
|
@ -133,7 +143,7 @@ export const borrow = async (
|
|||
)) as ParsedAccount<MintInfo>;
|
||||
|
||||
amountLamports = toLamports(amount, mint?.info);
|
||||
} else if(amountType === BorrowAmountType.CollateralDepositAmount) {
|
||||
} else if (amountType === BorrowAmountType.CollateralDepositAmount) {
|
||||
const mint = (await cache.query(
|
||||
connection,
|
||||
depositReserve.info.collateralMint,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useCallback, useMemo, useState } from "react";
|
||||
import { useTokenName, useUserBalance } from "../../hooks";
|
||||
import { useTokenName, useUserBalance, useUserObligationByReserve } from "../../hooks";
|
||||
import { BorrowAmountType, LendingReserve, LendingReserveParser } from "../../models";
|
||||
import { TokenIcon } from "../TokenIcon";
|
||||
import { Button, Card } from "antd";
|
||||
|
@ -8,7 +8,6 @@ import { NumericInput } from "../Input/numeric";
|
|||
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";
|
||||
import { LABELS } from "../../constants";
|
||||
|
@ -38,7 +37,8 @@ export const BorrowInput = (props: {
|
|||
const { accounts: fromAccounts } = useUserBalance(
|
||||
collateralReserve?.info.collateralMint
|
||||
);
|
||||
// const collateralBalance = useUserBalance(reserve?.collateralMint);
|
||||
|
||||
const { userObligationsByReserve } = useUserObligationByReserve(borrowReserve.pubkey)
|
||||
|
||||
const onBorrow = useCallback(() => {
|
||||
if (!collateralReserve) {
|
||||
|
@ -46,15 +46,23 @@ export const BorrowInput = (props: {
|
|||
}
|
||||
|
||||
borrow(
|
||||
connection,
|
||||
wallet,
|
||||
|
||||
fromAccounts[0],
|
||||
parseFloat(value),
|
||||
// TODO: switch to collateral when user is using slider
|
||||
BorrowAmountType.LiquidityBorrowAmount,
|
||||
BorrowAmountType.LiquidityBorrowAmount,
|
||||
borrowReserve,
|
||||
collateralReserve,
|
||||
undefined,
|
||||
connection,
|
||||
wallet
|
||||
|
||||
userObligationsByReserve.length > 0 ?
|
||||
userObligationsByReserve[0].obligation :
|
||||
undefined,
|
||||
|
||||
userObligationsByReserve.length > 0 ?
|
||||
userObligationsByReserve[0].userAccounts[0].pubkey :
|
||||
undefined
|
||||
);
|
||||
}, [
|
||||
connection,
|
||||
|
@ -63,6 +71,7 @@ export const BorrowInput = (props: {
|
|||
collateralReserve,
|
||||
borrowReserve,
|
||||
fromAccounts,
|
||||
userObligationsByReserve,
|
||||
]);
|
||||
|
||||
const bodyStyle: React.CSSProperties = {
|
||||
|
|
|
@ -5,7 +5,6 @@ import { TokenIcon } from "../../components/TokenIcon";
|
|||
import { formatNumber, formatPct, fromLamports } from "../../utils/utils";
|
||||
import { Card, Typography } from "antd";
|
||||
import { ParsedAccount, useMint } from "../../contexts/accounts";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
const { Text } = Typography;
|
||||
|
|
|
@ -22,7 +22,7 @@ export const LABELS = {
|
|||
BORROW_ACTION: "Borrow",
|
||||
TABLE_TITLE_ASSET: "Asset",
|
||||
TABLE_TITLE_LOAN_BALANCE: "Your loan balance",
|
||||
TABLE_TITLE_DEPOSIT_BALANCE: "Your deposit balane",
|
||||
TABLE_TITLE_DEPOSIT_BALANCE: "Your deposit balance",
|
||||
TABLE_TITLE_APY: "APY",
|
||||
TABLE_TITLE_APR: "APR",
|
||||
TABLE_TITLE_BORROW_APR: "Borrow APR",
|
||||
|
@ -32,7 +32,7 @@ export const LABELS = {
|
|||
TABLE_TITLE_ACTION: "Action",
|
||||
TABLE_TITLE_MAX_BORROW: "Available fro you",
|
||||
DASHBOARD_TITLE_LOANS: "Loans",
|
||||
DASHBOARD_TITLE_DEPOSITS: "Deposts",
|
||||
DASHBOARD_TITLE_DEPOSITS: "Deposits",
|
||||
WITHDRAW_ACTION: "Withdraw",
|
||||
WITHDRAW_QUESTION: "How much would you like to withdraw?",
|
||||
};
|
||||
|
|
|
@ -11,7 +11,7 @@ export function useUserBalance(mint?: PublicKey, account?: PublicKey) {
|
|||
return userAccounts
|
||||
.filter((acc) => mint?.equals(acc.info.mint) && (!account || account.equals(acc.pubkey)))
|
||||
.sort((a, b) => b.info.amount.sub(a.info.amount).toNumber());
|
||||
}, [userAccounts, mint]);
|
||||
}, [userAccounts, mint, account]);
|
||||
|
||||
const balanceLamports = useMemo(() => {
|
||||
return accounts.reduce(
|
||||
|
|
|
@ -74,7 +74,6 @@ export const borrowInstruction = (
|
|||
},
|
||||
data
|
||||
);
|
||||
debugger;
|
||||
|
||||
const keys = [
|
||||
{ pubkey: from, isSigner: false, isWritable: true },
|
||||
|
|
|
@ -2,14 +2,12 @@ import React from "react";
|
|||
import {
|
||||
useCollateralBalance,
|
||||
useTokenName,
|
||||
useUserBalance,
|
||||
} from "../../hooks";
|
||||
import { LendingReserve } from "../../models/lending";
|
||||
import { TokenIcon } from "../../components/TokenIcon";
|
||||
import { formatNumber } from "../../utils/utils";
|
||||
import { Button, Card } from "antd";
|
||||
import { Link } from "react-router-dom";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { TokenAccount } from "../../models";
|
||||
import { ParsedAccount } from "../../contexts/accounts";
|
||||
|
||||
|
@ -17,31 +15,35 @@ export const DepositItem = (props: {
|
|||
reserve: ParsedAccount<LendingReserve>;
|
||||
account: TokenAccount;
|
||||
}) => {
|
||||
const account = props.account.info;
|
||||
|
||||
const mintAddress = props.reserve.info.liquidityMint;
|
||||
const name = useTokenName(mintAddress);
|
||||
const { balance: collateralBalance } = useCollateralBalance(props.reserve.info, props.account.pubkey);
|
||||
|
||||
return (
|
||||
<Link to={`/withdraw/${props.reserve.pubkey.toBase58()}`}>
|
||||
<Card>
|
||||
<div className="deposit-item">
|
||||
<span style={{ display: "flex" }}>
|
||||
<TokenIcon mintAddress={mintAddress} />
|
||||
{name}
|
||||
</span>
|
||||
<div>
|
||||
{formatNumber.format(collateralBalance)} {name}
|
||||
</div>
|
||||
<div>--</div>
|
||||
<div>
|
||||
<Card>
|
||||
<div className="dashboard-item">
|
||||
<span style={{ display: "flex" }}>
|
||||
<TokenIcon mintAddress={mintAddress} />
|
||||
{name}
|
||||
</span>
|
||||
<div>
|
||||
{formatNumber.format(collateralBalance)} {name}
|
||||
</div>
|
||||
<div>--</div>
|
||||
<div style={{ display: "flex", justifyContent: 'flex-end' }}>
|
||||
<Link to={`/deposit/${props.reserve.pubkey.toBase58()}`}>
|
||||
<Button>
|
||||
<span>Deposit</span>
|
||||
</Button>
|
||||
</Link>
|
||||
<Link to={`/withdraw/${props.reserve.pubkey.toBase58()}`}>
|
||||
<Button>
|
||||
<span>Withdraw</span>
|
||||
</Button>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@ import "./style.less";
|
|||
export const DashboardView = () => {
|
||||
const { userObligations } = useUserObligations();
|
||||
const { userDeposits } = useUserDeposits();
|
||||
|
||||
|
||||
return (
|
||||
<div className="dashboard-container">
|
||||
<div className="dashboard-left">
|
||||
|
|
|
@ -26,24 +26,29 @@ export const ObligationItem = (props: {
|
|||
);
|
||||
|
||||
return (
|
||||
<Link to={`/repay/loan/${obligation.pubkey.toBase58()}`}>
|
||||
<Card>
|
||||
<div className="dashboard-item">
|
||||
<span style={{ display: "flex" }}>
|
||||
<TokenIcon mintAddress={borrowReserve?.info.liquidityMint} />
|
||||
{name}
|
||||
</span>
|
||||
<div>
|
||||
{formatNumber.format(borrowAmount)} {name}
|
||||
</div>
|
||||
<div>--</div>
|
||||
<div>
|
||||
<Card>
|
||||
<div className="dashboard-item">
|
||||
<span style={{ display: "flex" }}>
|
||||
<TokenIcon mintAddress={borrowReserve?.info.liquidityMint} />
|
||||
{name}
|
||||
</span>
|
||||
<div>
|
||||
{formatNumber.format(borrowAmount)} {name}
|
||||
</div>
|
||||
<div>--</div>
|
||||
<div style={{ display: "flex", justifyContent: 'flex-end' }}>
|
||||
<Link to={`/borrow/${borrowReserve.pubkey.toBase58()}`}>
|
||||
<Button>
|
||||
<span>Borrow</span>
|
||||
</Button>
|
||||
</Link>
|
||||
<Link to={`/repay/loan/${obligation.pubkey.toBase58()}`}>
|
||||
<Button>
|
||||
<span>Repay</span>
|
||||
</Button>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
& > :first-child {
|
||||
flex: 80px
|
||||
}
|
||||
|
||||
& > :last-child {
|
||||
flex: 200px
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-header {
|
||||
|
@ -26,6 +30,10 @@
|
|||
text-align: left;
|
||||
flex: 80px
|
||||
}
|
||||
|
||||
& > :last-child {
|
||||
flex: 200px
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-container {
|
||||
|
@ -43,7 +51,7 @@
|
|||
flex: 50%;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
@media (max-width: 700px) {
|
||||
.dashboard-right, .dashboard-left {
|
||||
flex: 100%;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue