mirror of https://github.com/certusone/oyster.git
feat: style deposits
This commit is contained in:
parent
56d9d9f673
commit
1c63b1bc39
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "token-swap-ui",
|
"name": "token-lending-ui",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
|
@ -1392,6 +1392,108 @@
|
||||||
"resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz",
|
||||||
"integrity": "sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg=="
|
"integrity": "sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg=="
|
||||||
},
|
},
|
||||||
|
"@formatjs/ecma402-abstract": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-Mv027hcLFjE45K8UJ8PjRpdDGfR0aManEFj1KzoN8zXNveHGEygpZGfFf/FTTMl+QEVSrPAUlyxaCApvmv47AQ==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@formatjs/intl": {
|
||||||
|
"version": "1.4.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-1.4.6.tgz",
|
||||||
|
"integrity": "sha512-NpmOi3T0ADalssZK0JJkS8mfbaiCwbHdlzdsp33cDY6ZMLSkLASwMUirmmEV9Qdh1TEVOixz9bSdy03eo+k7XA==",
|
||||||
|
"requires": {
|
||||||
|
"@formatjs/ecma402-abstract": "1.4.0",
|
||||||
|
"@formatjs/intl-datetimeformat": "2.8.4",
|
||||||
|
"@formatjs/intl-displaynames": "3.4.6",
|
||||||
|
"@formatjs/intl-listformat": "4.3.6",
|
||||||
|
"@formatjs/intl-relativetimeformat": "7.3.6",
|
||||||
|
"fast-memoize": "^2.5.2",
|
||||||
|
"intl-messageformat": "9.3.18",
|
||||||
|
"intl-messageformat-parser": "6.0.16",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@formatjs/intl-datetimeformat": {
|
||||||
|
"version": "2.8.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@formatjs/intl-datetimeformat/-/intl-datetimeformat-2.8.4.tgz",
|
||||||
|
"integrity": "sha512-aCD6VoUvdLiJrAfKfHojbzX/e5inqvO+N9rj9kdOzCMTEdDGJ+Jd9SmXPn2YKb1glp785mYxAPp5vxjgErjq/g==",
|
||||||
|
"requires": {
|
||||||
|
"@formatjs/ecma402-abstract": "1.4.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@formatjs/intl-displaynames": {
|
||||||
|
"version": "3.4.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-3.4.6.tgz",
|
||||||
|
"integrity": "sha512-XWVhTQjcyWweHm2Hi0n92hYILpktbnh8x5Sj5nSBIUNz4Os4uHDj2itJI3xCxl+aDNW8M7VRJ9m0OsnhI8qQrg==",
|
||||||
|
"requires": {
|
||||||
|
"@formatjs/ecma402-abstract": "1.4.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@formatjs/intl-listformat": {
|
||||||
|
"version": "4.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-4.3.6.tgz",
|
||||||
|
"integrity": "sha512-+ePU3/rmgUd8QHcf7EGJAIU12+Y6eMGaDhI3tPw3kKN/vByE9hJAyR8etXTk9fSIV6D7us2tEDM4h0wn8eenig==",
|
||||||
|
"requires": {
|
||||||
|
"@formatjs/ecma402-abstract": "1.4.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@formatjs/intl-relativetimeformat": {
|
||||||
|
"version": "7.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-7.3.6.tgz",
|
||||||
|
"integrity": "sha512-RtmdGG6Lwf99xlGjFiIk6pYe9LQJpTYa7VofV322Q3gmb3tAU6Pgyn7LML4xbG7Pd1F7JBmnVNVwFyofUfyJQA==",
|
||||||
|
"requires": {
|
||||||
|
"@formatjs/ecma402-abstract": "1.4.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@hapi/address": {
|
"@hapi/address": {
|
||||||
"version": "2.1.4",
|
"version": "2.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
|
||||||
|
@ -2217,6 +2319,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz",
|
||||||
"integrity": "sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA=="
|
"integrity": "sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA=="
|
||||||
},
|
},
|
||||||
|
"@types/hoist-non-react-statics": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
|
||||||
|
"requires": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"hoist-non-react-statics": "^3.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/identicon.js": {
|
"@types/identicon.js": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/identicon.js/-/identicon.js-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/identicon.js/-/identicon.js-2.3.0.tgz",
|
||||||
|
@ -6674,6 +6785,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
|
||||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
|
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
|
||||||
},
|
},
|
||||||
|
"fast-memoize": {
|
||||||
|
"version": "2.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz",
|
||||||
|
"integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw=="
|
||||||
|
},
|
||||||
"faye-websocket": {
|
"faye-websocket": {
|
||||||
"version": "0.10.0",
|
"version": "0.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
|
||||||
|
@ -7924,6 +8040,39 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"intl-messageformat": {
|
||||||
|
"version": "9.3.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.3.18.tgz",
|
||||||
|
"integrity": "sha512-OKrLWppdxXtRdRCPjmRZ9Ru7UZkZJDlMl+1Vpb3sCLWK0mFpr129K+gIlIb5zrWoAH3NiYDzekBXPTRWCyHSIA==",
|
||||||
|
"requires": {
|
||||||
|
"fast-memoize": "^2.5.2",
|
||||||
|
"intl-messageformat-parser": "6.0.16",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"intl-messageformat-parser": {
|
||||||
|
"version": "6.0.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-6.0.16.tgz",
|
||||||
|
"integrity": "sha512-Qy3Zz0vF4fhMVuW4BDqUr55LsOl9enM03wuwbP4Yg7v29rYNpf7Z76Whstu6uDLDJokrjbpgDvRcjSDTAhxKJw==",
|
||||||
|
"requires": {
|
||||||
|
"@formatjs/ecma402-abstract": "1.4.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"invariant": {
|
"invariant": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
||||||
|
@ -12619,6 +12768,32 @@
|
||||||
"github-buttons": "^2.8.0"
|
"github-buttons": "^2.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-intl": {
|
||||||
|
"version": "5.10.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.10.2.tgz",
|
||||||
|
"integrity": "sha512-g5sxQXdmXSBo8tqi1I2PBpsAG40dgmHnVGoLdBJPwPNHvJbzc08J1r2IOKomPqHMDDjD47Q/Z1Ls87spw/ES5w==",
|
||||||
|
"requires": {
|
||||||
|
"@formatjs/ecma402-abstract": "1.4.0",
|
||||||
|
"@formatjs/intl": "1.4.6",
|
||||||
|
"@formatjs/intl-displaynames": "3.4.6",
|
||||||
|
"@formatjs/intl-listformat": "4.3.6",
|
||||||
|
"@formatjs/intl-relativetimeformat": "7.3.6",
|
||||||
|
"@types/hoist-non-react-statics": "^3.3.1",
|
||||||
|
"fast-memoize": "^2.5.2",
|
||||||
|
"hoist-non-react-statics": "^3.3.2",
|
||||||
|
"intl-messageformat": "9.3.18",
|
||||||
|
"intl-messageformat-parser": "6.0.16",
|
||||||
|
"shallow-equal": "^1.2.1",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-is": {
|
"react-is": {
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
"react-dom": "^16.13.1",
|
"react-dom": "^16.13.1",
|
||||||
"react-github-btn": "^1.2.0",
|
"react-github-btn": "^1.2.0",
|
||||||
|
"react-intl": "^5.10.2",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-scripts": "3.4.3",
|
"react-scripts": "3.4.3",
|
||||||
"typescript": "^4.0.0"
|
"typescript": "^4.0.0"
|
||||||
|
|
44
src/App.less
44
src/App.less
|
@ -202,6 +202,50 @@ body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-row {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 5px 0px;
|
||||||
|
min-width: 0px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
padding: 0px;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
align-items: center;
|
||||||
|
-webkit-box-pack: justify;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.card-cell {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
box-sizing: border-box;
|
||||||
|
text-align: left;
|
||||||
|
margin: 0px;
|
||||||
|
min-width: 0px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-input {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid grey;
|
||||||
|
padding: 0px 10px;
|
||||||
|
margin: 5px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 600px) {
|
@media only screen and (max-width: 600px) {
|
||||||
.exchange-card {
|
.exchange-card {
|
||||||
width: 360px;
|
width: 360px;
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
import { useLendingReserve, useTokenName, useUserAccounts, useUserBalance } from './../../hooks';
|
||||||
|
import { LendingReserve, LendingReserveParser } from "../../models/lending";
|
||||||
|
import { TokenIcon } from "../../components/TokenIcon";
|
||||||
|
import { formatNumber } from "../../utils/utils";
|
||||||
|
import { Button, Card } from "antd";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { cache, useAccount } from "../../contexts/accounts";
|
||||||
|
import { NumericInput } from "../../components/Input/numeric";
|
||||||
|
import { useConnection } from "../../contexts/connection";
|
||||||
|
import { useWallet } from "../../contexts/wallet";
|
||||||
|
import { deposit } from './../../actions/deposit';
|
||||||
|
import { PublicKey } from "@solana/web3.js";
|
||||||
|
import './style.less';
|
||||||
|
|
||||||
|
export const DepositAdd = (props: { className?: string, reserve: LendingReserve, address: PublicKey }) => {
|
||||||
|
const connection = useConnection();
|
||||||
|
const { wallet } = useWallet();
|
||||||
|
const { id } = useParams<{ id: string }>();
|
||||||
|
const [value, setValue] = useState('');
|
||||||
|
|
||||||
|
const reserve = props.reserve;
|
||||||
|
const address = props.address;
|
||||||
|
|
||||||
|
const name = useTokenName(reserve?.liquidityMint);
|
||||||
|
const { balance: tokenBalance, accounts: fromAccounts } = useUserBalance(reserve?.liquidityMint);
|
||||||
|
// const collateralBalance = useUserBalance(reserve?.collateralMint);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
console.log(`utlization: ${reserve.maxUtilizationRate}`)
|
||||||
|
console.log(`cumulativeBorrowRate: ${reserve.cumulativeBorrowRate.toString()}`)
|
||||||
|
console.log(`cumulativeBorrowRate: ${reserve.cumulativeBorrowRate.toString()}`)
|
||||||
|
console.log(`totalBorrows: ${reserve.totalBorrows.toString()}`)
|
||||||
|
console.log(`totalLiquidity: ${reserve.totalLiquidity.toString()}`)
|
||||||
|
console.log(`lendingMarket: ${reserve.lendingMarket.toBase58()}`);
|
||||||
|
|
||||||
|
const lendingMarket = await cache.get(reserve.lendingMarket);
|
||||||
|
console.log(`lendingMarket quote: ${lendingMarket?.info.quoteMint.toBase58()}`);
|
||||||
|
|
||||||
|
console.log(`liquiditySupply: ${reserve.liquiditySupply.toBase58()}`);
|
||||||
|
console.log(`liquidityMint: ${reserve.liquidityMint.toBase58()}`);
|
||||||
|
console.log(`collateralSupply: ${reserve.collateralSupply.toBase58()}`);
|
||||||
|
console.log(`collateralMint: ${reserve.collateralMint.toBase58()}`);
|
||||||
|
})();
|
||||||
|
}, [reserve])
|
||||||
|
|
||||||
|
const onDeposit = useCallback(() => {
|
||||||
|
deposit(
|
||||||
|
fromAccounts[0],
|
||||||
|
parseFloat(value),
|
||||||
|
reserve,
|
||||||
|
address,
|
||||||
|
connection,
|
||||||
|
wallet);
|
||||||
|
}, [value, reserve, fromAccounts, address]);
|
||||||
|
|
||||||
|
const bodyStyle: React.CSSProperties = {
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
alignItems: 'center'
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Card className={props.className} bodyStyle={bodyStyle}>
|
||||||
|
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-around' }}>
|
||||||
|
<div className="deposit-add-title">
|
||||||
|
How much would you like to deposit?
|
||||||
|
</div>
|
||||||
|
<div className="token-input">
|
||||||
|
<TokenIcon mintAddress={reserve?.liquidityMint} />
|
||||||
|
<NumericInput value={value}
|
||||||
|
onChange={(val: any) => {
|
||||||
|
setValue(val);
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
fontSize: 20,
|
||||||
|
boxShadow: "none",
|
||||||
|
borderColor: "transparent",
|
||||||
|
outline: "transpaernt",
|
||||||
|
}}
|
||||||
|
placeholder="0.00"
|
||||||
|
/>
|
||||||
|
<div>{name}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button type="primary" onClick={onDeposit} disabled={fromAccounts.length === 0}>Deposit</Button>
|
||||||
|
</div>
|
||||||
|
</Card >;
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
.deposit-add-title {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
import { useLendingReserve, useTokenName, useUserAccounts, useUserBalance } from './../../hooks';
|
||||||
|
import { LendingReserve, LendingReserveParser } from "../../models/lending";
|
||||||
|
import { TokenIcon } from "../../components/TokenIcon";
|
||||||
|
import { formatNumber } from "../../utils/utils";
|
||||||
|
import { Button, Card } from "antd";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { cache, useAccount } from "../../contexts/accounts";
|
||||||
|
import { NumericInput } from "../../components/Input/numeric";
|
||||||
|
import { useConnection } from "../../contexts/connection";
|
||||||
|
import { useWallet } from "../../contexts/wallet";
|
||||||
|
import { deposit } from './../../actions/deposit';
|
||||||
|
import './style.less';
|
||||||
|
import { PublicKey } from "@solana/web3.js";
|
||||||
|
|
||||||
|
export const DepositInfoLine = (props: {
|
||||||
|
className?: string,
|
||||||
|
reserve: LendingReserve,
|
||||||
|
address: PublicKey }) => {
|
||||||
|
const name = useTokenName(props.reserve.liquidityMint);
|
||||||
|
const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint);
|
||||||
|
const { balance: collateralBalance } = useUserBalance(props.reserve.collateralMint);
|
||||||
|
|
||||||
|
return <Card className={props.className} bodyStyle={{ display: 'flex', justifyContent: 'space-around', }} >
|
||||||
|
<div className="deposit-info-line-item ">
|
||||||
|
<div>Your balance in Oyster</div>
|
||||||
|
<div>{formatNumber.format(collateralBalance)} {name}</div>
|
||||||
|
</div>
|
||||||
|
<div className="deposit-info-line-item ">
|
||||||
|
<div>Your wallet balance</div>
|
||||||
|
<div>{formatNumber.format(tokenBalance)} {name}</div>
|
||||||
|
</div>
|
||||||
|
<div className="deposit-info-line-item ">
|
||||||
|
<div>Health factor</div>
|
||||||
|
<div>--</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
.deposit-info-line {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deposit-info-line-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
import React, { useMemo } from "react";
|
||||||
|
import { useLendingReserve, useTokenName, useUserAccounts, useUserBalance } from './../../hooks';
|
||||||
|
import { LendingReserve, LendingReserveParser } from "../../models/lending";
|
||||||
|
import { TokenIcon } from "../../components/TokenIcon";
|
||||||
|
import { formatNumber, formatPct, fromLamports } from "../../utils/utils";
|
||||||
|
import { Button, Card, Typography } from "antd";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { useAccount, useMint } from "../../contexts/accounts";
|
||||||
|
import { PublicKey } from "@solana/web3.js";
|
||||||
|
|
||||||
|
const { Text } = Typography;
|
||||||
|
|
||||||
|
export enum SideReserveOverviewMode {
|
||||||
|
Deposit = 'deposit',
|
||||||
|
Borrow = 'borrow'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SideReserveOverview = (props: {
|
||||||
|
className?: string;
|
||||||
|
reserve: LendingReserve,
|
||||||
|
address: PublicKey,
|
||||||
|
mode: SideReserveOverviewMode
|
||||||
|
}) => {
|
||||||
|
const reserve = props.reserve;
|
||||||
|
const mode = props.mode;
|
||||||
|
const name = useTokenName(reserve?.liquidityMint);
|
||||||
|
const liquidityMint = useMint(props.reserve.liquidityMint);
|
||||||
|
|
||||||
|
const totalLiquidity = fromLamports(props.reserve.totalLiquidity.toNumber(), liquidityMint);
|
||||||
|
|
||||||
|
// TODO: calculate
|
||||||
|
const utilizationRate = 0.5802;
|
||||||
|
const depositApy = 0.048;
|
||||||
|
const borrowApy = 0.048;
|
||||||
|
const maxLTV = 0.75;
|
||||||
|
const liquidiationThreshold = 0.8;
|
||||||
|
const liquidiationPenalty = 0.05;
|
||||||
|
|
||||||
|
let extraInfo: JSX.Element | null = null;
|
||||||
|
if (mode === SideReserveOverviewMode.Deposit) {
|
||||||
|
|
||||||
|
extraInfo = <>
|
||||||
|
<div className="card-row">
|
||||||
|
<Text type="secondary" className="card-cell ">
|
||||||
|
Deposit APY:
|
||||||
|
</Text>
|
||||||
|
<div className="card-cell ">
|
||||||
|
{formatPct.format(depositApy)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="card-row">
|
||||||
|
<Text type="secondary" className="card-cell ">
|
||||||
|
Maxiumum LTV:
|
||||||
|
</Text>
|
||||||
|
<div className="card-cell ">
|
||||||
|
{formatPct.format(maxLTV)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="card-row">
|
||||||
|
<Text type="secondary" className="card-cell ">
|
||||||
|
Liquidation threashold:
|
||||||
|
</Text>
|
||||||
|
<div className="card-cell ">
|
||||||
|
{formatPct.format(liquidiationThreshold)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="card-row">
|
||||||
|
<Text type="secondary" className="card-cell ">
|
||||||
|
Liquidation penalty:
|
||||||
|
</Text>
|
||||||
|
<div className="card-cell ">
|
||||||
|
{formatPct.format(liquidiationPenalty)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>;
|
||||||
|
} else if (mode === SideReserveOverviewMode.Borrow) {
|
||||||
|
extraInfo = <>
|
||||||
|
<div className="card-row">
|
||||||
|
<Text type="secondary" className="card-cell ">
|
||||||
|
Borrow APY:
|
||||||
|
</Text>
|
||||||
|
<div className="card-cell ">
|
||||||
|
{formatPct.format(borrowApy)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Card className={props.className} title={
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', fontSize: '1.2rem', justifyContent: 'center' }}>
|
||||||
|
<TokenIcon mintAddress={reserve?.liquidityMint} style={{ width: 30, height: 30 }} /> {name} Reserve Overview
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
|
<div className="card-row">
|
||||||
|
<Text type="secondary" className="card-cell ">
|
||||||
|
Utilization rate:
|
||||||
|
</Text>
|
||||||
|
<div className="card-cell ">
|
||||||
|
{formatPct.format(utilizationRate)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="card-row">
|
||||||
|
<Text type="secondary" className="card-cell ">
|
||||||
|
Available liquidity:
|
||||||
|
</Text>
|
||||||
|
<div className="card-cell ">
|
||||||
|
{formatNumber.format(totalLiquidity)} {name}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{extraInfo}
|
||||||
|
</Card>;
|
||||||
|
}
|
|
@ -38,4 +38,4 @@ export const LendingMarketParser = (pubKey: PublicKey, info: AccountInfo<Buffer>
|
||||||
|
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// create instructions for init, deposit and withdraw
|
// create instructions for init
|
|
@ -0,0 +1 @@
|
||||||
|
export const x = 1;
|
|
@ -24,8 +24,11 @@ export const LendingReserveLayout: typeof BufferLayout.Structure = BufferLayout.
|
||||||
// TODO: replace u32 option with generic quivalent
|
// TODO: replace u32 option with generic quivalent
|
||||||
BufferLayout.u32('dexMarketOption'),
|
BufferLayout.u32('dexMarketOption'),
|
||||||
Layout.publicKey("dexMarket"),
|
Layout.publicKey("dexMarket"),
|
||||||
|
|
||||||
BufferLayout.u8("maxUtilizationRate"),
|
BufferLayout.u8("maxUtilizationRate"),
|
||||||
|
|
||||||
|
BufferLayout.u8("collateralFactor"),
|
||||||
|
|
||||||
Layout.uint128("cumulativeBorrowRate"),
|
Layout.uint128("cumulativeBorrowRate"),
|
||||||
Layout.uint128("totalBorrows"),
|
Layout.uint128("totalBorrows"),
|
||||||
|
|
||||||
|
@ -46,13 +49,13 @@ export interface LendingReserve {
|
||||||
liquidityMint: PublicKey;
|
liquidityMint: PublicKey;
|
||||||
collateralSupply: PublicKey;
|
collateralSupply: PublicKey;
|
||||||
collateralMint: PublicKey;
|
collateralMint: PublicKey;
|
||||||
// TODO: replace u32 option with generic quivalent
|
|
||||||
dexMarketOption: number;
|
dexMarketOption: number;
|
||||||
dexMarket: PublicKey;
|
dexMarket: PublicKey;
|
||||||
dexMarketPrice: BN; // what is precision on the price?
|
dexMarketPrice: BN; // what is precision on the price?
|
||||||
|
|
||||||
maxUtilizationRate: number;
|
maxUtilizationRate: number;
|
||||||
dexMarketPriceUpdatedSlot: BN;
|
// collateralFactor: number;
|
||||||
|
|
||||||
cumulativeBorrowRate: BN;
|
cumulativeBorrowRate: BN;
|
||||||
totalBorrows: BN;
|
totalBorrows: BN;
|
||||||
|
|
|
@ -10,78 +10,38 @@ import { NumericInput } from "../../../components/Input/numeric";
|
||||||
import { useConnection } from "../../../contexts/connection";
|
import { useConnection } from "../../../contexts/connection";
|
||||||
import { useWallet } from "../../../contexts/wallet";
|
import { useWallet } from "../../../contexts/wallet";
|
||||||
import { deposit } from './../../../actions/deposit';
|
import { deposit } from './../../../actions/deposit';
|
||||||
|
import './style.less';
|
||||||
|
|
||||||
|
import { DepositAdd } from './../../../components/DepositAdd';
|
||||||
|
import { DepositInfoLine } from './../../../components/DepositInfoLine';
|
||||||
|
import { SideReserveOverview, SideReserveOverviewMode } from './../../../components/SideReserveOverview';
|
||||||
|
|
||||||
export const DepositAddView = () => {
|
export const DepositAddView = () => {
|
||||||
const connection = useConnection();
|
const connection = useConnection();
|
||||||
const { wallet } = useWallet();
|
const { wallet } = useWallet();
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
const [value, setValue] = useState('');
|
|
||||||
const lendingReserve = useLendingReserve(id);
|
const lendingReserve = useLendingReserve(id);
|
||||||
const reserve = lendingReserve?.info;
|
const reserve = lendingReserve?.info;
|
||||||
|
|
||||||
const name = useTokenName(reserve?.liquidityMint);
|
if (!reserve || !lendingReserve) {
|
||||||
const { balance: tokenBalance, accounts: fromAccounts } = useUserBalance(reserve?.liquidityMint);
|
return null;
|
||||||
// const collateralBalance = useUserBalance(reserve?.collateralMint);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
(async () => {
|
|
||||||
const reserve = lendingReserve?.info;
|
|
||||||
if (!reserve) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`utlization: ${reserve.maxUtilizationRate}`)
|
return <div className="deposit-add">
|
||||||
console.log(`cumulativeBorrowRate: ${reserve.cumulativeBorrowRate.toString()}`)
|
<DepositInfoLine
|
||||||
console.log(`cumulativeBorrowRate: ${reserve.cumulativeBorrowRate.toString()}`)
|
className="deposit-add-item"
|
||||||
console.log(`totalBorrows: ${reserve.totalBorrows.toString()}`)
|
reserve={reserve}
|
||||||
console.log(`totalLiquidity: ${reserve.totalLiquidity.toString()}`)
|
address={lendingReserve.pubkey} />
|
||||||
console.log(`lendingMarket: ${reserve.lendingMarket.toBase58()}`);
|
<div className="deposit-add-container">
|
||||||
|
<DepositAdd
|
||||||
const lendingMarket = await cache.get(reserve.lendingMarket);
|
className="deposit-add-item deposit-add-item-left"
|
||||||
console.log(`lendingMarket quote: ${lendingMarket?.info.quoteMint.toBase58()}`);
|
reserve={reserve}
|
||||||
|
address={lendingReserve.pubkey} />
|
||||||
console.log(`liquiditySupply: ${reserve.liquiditySupply.toBase58()}`);
|
<SideReserveOverview
|
||||||
console.log(`liquidityMint: ${reserve.liquidityMint.toBase58()}`);
|
className="deposit-add-item deposit-add-item-right"
|
||||||
console.log(`collateralSupply: ${reserve.collateralSupply.toBase58()}`);
|
reserve={reserve}
|
||||||
console.log(`collateralMint: ${reserve.collateralMint.toBase58()}`);
|
address={lendingReserve.pubkey}
|
||||||
})();
|
mode={SideReserveOverviewMode.Deposit} />
|
||||||
}, [lendingReserve])
|
|
||||||
|
|
||||||
const onDeposit = useCallback(() => {
|
|
||||||
if (!lendingReserve || !reserve) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
deposit(
|
|
||||||
fromAccounts[0],
|
|
||||||
parseFloat(value),
|
|
||||||
reserve,
|
|
||||||
lendingReserve.pubkey,
|
|
||||||
connection,
|
|
||||||
wallet);
|
|
||||||
}, [value, reserve, fromAccounts, lendingReserve]);
|
|
||||||
|
|
||||||
return <Card title={(
|
|
||||||
<h2 style={{ display: 'flex', alignItems: 'center', width: 400 }}>
|
|
||||||
<TokenIcon mintAddress={reserve?.liquidityMint} style={{ width: 40, height: 40 }} /> Deposit {name}
|
|
||||||
</h2>
|
|
||||||
)}>
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-around' }}>
|
|
||||||
|
|
||||||
<NumericInput value={value}
|
|
||||||
onChange={(val: any) => {
|
|
||||||
setValue(val);
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
fontSize: 20,
|
|
||||||
boxShadow: "none",
|
|
||||||
borderColor: "transparent",
|
|
||||||
outline: "transpaernt",
|
|
||||||
}}
|
|
||||||
placeholder="0.00"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Button type="primary" onClick={onDeposit} disabled={fromAccounts.length === 0}>Deposit</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</Card >;
|
</div>;
|
||||||
}
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
.deposit-add {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deposit-add-item {
|
||||||
|
margin: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deposit-add-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deposit-add-item-left {
|
||||||
|
flex: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deposit-add-item-right {
|
||||||
|
flex: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive layout - makes a one column layout instead of a two-column layout */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.deposit-add-item-right, .deposit-add-item-left {
|
||||||
|
flex: 100%;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue