mirror of https://github.com/certusone/oyster.git
Merge branch 'main' of https://github.com/solana-labs/oyster into main
This commit is contained in:
commit
2eec1d7ead
17
README.md
17
README.md
|
@ -1,19 +1,30 @@
|
|||
## Setup
|
||||
|
||||
|
||||
Be sure to be running Node v12.16.2 and npm version 6.14.4.
|
||||
|
||||
|
||||
`npm run bootstrap`
|
||||
|
||||
Then run:
|
||||
|
||||
`npm start lending`
|
||||
|
||||
|
||||
You may have to rebuild your package more than one time to secure a
|
||||
running environment.
|
||||
|
||||
## Known Issues
|
||||
|
||||
### Can't find CSS files in common
|
||||
|
||||
Common currently uses a less library to compile down less files into css in both the src directory for the TS server
|
||||
in vscode to pick up and in the dist folder for importers like lending and proposal projects to pick up. If you do not see these files appear when running the `npm start lending` or other commands, and you see missing CSS errors,
|
||||
you likely did not install the packages for common correctly. Try running:
|
||||
|
||||
`lerna exec npm install --scope @oyster/common` to specifically install packages for common.
|
||||
|
||||
Then, test that css transpiling is working:
|
||||
|
||||
`lerna exec npm watch-css-src --scope @oyster/common` and verify css files appear next to their less counterparts in src.
|
||||
|
||||
## ⚠️ Warning
|
||||
|
||||
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.
|
||||
|
|
|
@ -6,3 +6,5 @@ export { Info } from './Icons/info';
|
|||
export { NumericInput } from './Input/numeric';
|
||||
export { AppBar } from './AppBar/index';
|
||||
export { Settings } from './Settings/index';
|
||||
export { ActionConfirmation } from './ActionConfirmation/index';
|
||||
export { BackButton } from './BackButton/index';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
export * as actions from './actions';
|
||||
export * as components from './components';
|
||||
export * from './components'; // Allow direct exports too
|
||||
export * as config from './config';
|
||||
export * as constants from './constants';
|
||||
export * as hooks from './hooks';
|
||||
|
|
|
@ -153,6 +153,7 @@ export const programIds = () => {
|
|||
return {
|
||||
token: TOKEN_PROGRAM_ID,
|
||||
swap: SWAP_PROGRAM_ID,
|
||||
swap_legacy: SWAP_PROGRAM_LEGACY_IDS,
|
||||
swapLayout: SWAP_PROGRAM_LAYOUT,
|
||||
lending: LENDING_PROGRAM_ID,
|
||||
wormhole: WORMHOLE_BRIDGE,
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
import React from "react";
|
||||
import { Button } from "antd";
|
||||
import "./style.less";
|
||||
import { LABELS } from "../../constants";
|
||||
import { Link } from "react-router-dom";
|
||||
import "./style.less";
|
||||
|
||||
export const ActionConfirmation = (props: {
|
||||
className?: string;
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-around",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<h2>Congratulations!</h2>
|
||||
<div>Your action has been successfully executed</div>
|
||||
<div className="success-icon" />
|
||||
<Link to="/dashboard">
|
||||
<Button type="primary">{LABELS.DASHBOARD_ACTION}</Button>
|
||||
</Link>
|
||||
<Button type="text" onClick={props.onClose}>
|
||||
{LABELS.GO_BACK_ACTION}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,5 +0,0 @@
|
|||
.success-icon {
|
||||
background-image: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9JzMwMHB4JyB3aWR0aD0nMzAwcHgnICBmaWxsPSIjNzBjMDQxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGRhdGEtbmFtZT0iTGF5ZXIgMSIgdmlld0JveD0iMCAwIDY0IDY0IiB4PSIwcHgiIHk9IjBweCI+PHRpdGxlPmJ1c2luZXNzIGZpbmFuY2UgdXAgcmlzZSBhcnJvdyBkZW1hbmQ8L3RpdGxlPjxwYXRoIGQ9Ik01LDE0YTEsMSwwLDEsMC0xLTFINEExLDEsMCwwLDAsNSwxNFoiPjwvcGF0aD48cGF0aCBkPSJNNyw1Mkg1NWExLDEsMCwwLDAsMC0ySDUwVjI3YTEsMSwwLDAsMC0yLDBWNTBINDRWMzRhMSwxLDAsMCwwLTIsMFY1MEgzOFYzOWExLDEsMCwwLDAtMiwwVjUwSDMyVjQzYTEsMSwwLDAsMC0yLDB2N0gyNlY0NmExLDEsMCwwLDAtMiwwdjRIMjBWNDdhMSwxLDAsMCwwLTIsMHYzSDE0VjQ4YTEsMSwwLDAsMC0yLDB2Mkg3YTEsMSwwLDAsMS0xLTFWMTdhMSwxLDAsMCwwLTIsMFY0OUEzLDMsMCwwLDAsNyw1MloiPjwvcGF0aD48cGF0aCBkPSJNNTksNTBhMSwxLDAsMCwwLTEsMWgwYTEsMSwwLDEsMCwxLTFaIj48L3BhdGg+PHBhdGggZD0iTTExLDQ0aC4wN2E0OS4wNyw0OS4wNywwLDAsMCwyNS41Mi05LjE5QTQ4LjkxLDQ4LjkxLDAsMCwwLDQ5LjcsMjAuNTlMNTIuMzgsMjIsNTIsMTRsLTYuNzEsNC4zMSwyLjYzLDEuMzZBNDYuODEsNDYuODEsMCwwLDEsMzUuNDEsMzMuMTksNDYuOTQsNDYuOTQsMCwwLDEsMTAuOTMsNDIsMSwxLDAsMCwwLDExLDQ0WiI+PC9wYXRoPjwvc3ZnPg==");
|
||||
width: 280px;
|
||||
height: 280px;
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Button, Popover } from 'antd';
|
||||
import { contexts, ConnectButton } from '@oyster/common';
|
||||
import { CurrentUserBadge } from '../CurrentUserBadge';
|
||||
import { SettingOutlined } from '@ant-design/icons';
|
||||
import { Settings } from '../Settings';
|
||||
import { LABELS } from '../../constants';
|
||||
const { useWallet } = contexts.Wallet;
|
||||
|
||||
export const AppBar = (props: { left?: JSX.Element; right?: JSX.Element }) => {
|
||||
const { connected, wallet } = useWallet();
|
||||
|
||||
const TopBar = (
|
||||
<div className="App-Bar-right">
|
||||
{connected ? (
|
||||
<CurrentUserBadge />
|
||||
) : (
|
||||
<ConnectButton
|
||||
type="text"
|
||||
size="large"
|
||||
allowWalletChange={true}
|
||||
style={{ color: "#2abdd2" }}/>
|
||||
)}
|
||||
<Popover
|
||||
placement="topRight"
|
||||
title={LABELS.SETTINGS_TOOLTIP}
|
||||
content={<Settings />}
|
||||
trigger="click"
|
||||
>
|
||||
<Button
|
||||
shape="circle"
|
||||
size="large"
|
||||
type="text"
|
||||
icon={<SettingOutlined />}
|
||||
/>
|
||||
</Popover>
|
||||
{props.right}
|
||||
</div>
|
||||
);
|
||||
|
||||
return TopBar;
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import React from "react";
|
||||
import { Button } from "antd";
|
||||
import { LABELS } from "../../constants";
|
||||
import { useHistory } from "react-router-dom";
|
||||
|
||||
export const BackButton = () => {
|
||||
const history = useHistory();
|
||||
return (
|
||||
<Button type="text" onClick={history.goBack}>
|
||||
{LABELS.GO_BACK_ACTION}
|
||||
</Button>
|
||||
);
|
||||
};
|
|
@ -11,13 +11,17 @@ import {
|
|||
LendingReserveParser,
|
||||
} from '../../models';
|
||||
import { Card } from 'antd';
|
||||
import { contexts, ParsedAccount, ConnectButton } from '@oyster/common';
|
||||
import {
|
||||
contexts,
|
||||
ParsedAccount,
|
||||
ConnectButton,
|
||||
BackButton,
|
||||
ActionConfirmation,
|
||||
} from '@oyster/common';
|
||||
|
||||
import { borrow } from '../../actions';
|
||||
import './style.less';
|
||||
import { LABELS } from '../../constants';
|
||||
import { ActionConfirmation } from './../ActionConfirmation';
|
||||
import { BackButton } from './../BackButton';
|
||||
import CollateralInput from '../CollateralInput';
|
||||
import { useMidPriceInUSD } from '../../contexts/market';
|
||||
import { RiskSlider } from '../RiskSlider';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { contexts, utils, ParsedAccount } from '@oyster/common';
|
||||
import { contexts, utils, ParsedAccount, NumericInput } from '@oyster/common';
|
||||
import {
|
||||
useLendingReserves,
|
||||
useUserBalance,
|
||||
|
@ -12,7 +12,6 @@ import {
|
|||
} from '../../models';
|
||||
import { Card, Select } from 'antd';
|
||||
import { TokenIcon } from '../TokenIcon';
|
||||
import { NumericInput } from '../Input/numeric';
|
||||
import './style.less';
|
||||
import { TokenDisplay } from '../TokenDisplay';
|
||||
const { getTokenName } = utils;
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
import React from 'react';
|
||||
import { contexts, utils } from '@oyster/common';
|
||||
|
||||
import { Identicon } from '../Identicon';
|
||||
import { LAMPORTS_PER_SOL } from '@solana/web3.js';
|
||||
const { useWallet } = contexts.Wallet;
|
||||
const { useNativeAccount } = contexts.Accounts;
|
||||
const { formatNumber, shortenAddress } = utils;
|
||||
|
||||
export const CurrentUserBadge = (props: {}) => {
|
||||
const { wallet } = useWallet();
|
||||
const { account } = useNativeAccount();
|
||||
|
||||
if (!wallet || !wallet.publicKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// should use SOL ◎ ?
|
||||
|
||||
return (
|
||||
<div className="wallet-wrapper">
|
||||
<span>
|
||||
{formatNumber.format((account?.lamports || 0) / LAMPORTS_PER_SOL)} SOL
|
||||
</span>
|
||||
<div className="wallet-key">
|
||||
{shortenAddress(`${wallet.publicKey}`)}
|
||||
<Identicon
|
||||
address={wallet.publicKey?.toBase58()}
|
||||
style={{ marginLeft: '0.5rem', display: 'flex' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -5,10 +5,9 @@ import { Card, Slider } from 'antd';
|
|||
import { deposit } from '../../actions/deposit';
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
import './style.less';
|
||||
import { ActionConfirmation } from './../ActionConfirmation';
|
||||
import { LABELS, marks } from '../../constants';
|
||||
import CollateralInput from '../CollateralInput';
|
||||
import { contexts, ConnectButton } from '@oyster/common';
|
||||
import { contexts, ConnectButton, ActionConfirmation } from '@oyster/common';
|
||||
const { useWallet } = contexts.Wallet;
|
||||
const { useConnection } = contexts.Connection;
|
||||
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
import { Button, Popover } from "antd";
|
||||
import React from "react";
|
||||
|
||||
import { InfoCircleOutlined } from "@ant-design/icons";
|
||||
|
||||
export const Info = (props: {
|
||||
text: React.ReactElement;
|
||||
style?: React.CSSProperties;
|
||||
}) => {
|
||||
return (
|
||||
<Popover
|
||||
trigger="hover"
|
||||
content={<div style={{ width: 300 }}>{props.text}</div>}
|
||||
>
|
||||
<Button type="text" shape="circle">
|
||||
<InfoCircleOutlined style={props.style} />
|
||||
</Button>
|
||||
</Popover>
|
||||
);
|
||||
};
|
|
@ -1,36 +0,0 @@
|
|||
import React, { useEffect, useRef } from "react";
|
||||
|
||||
import Jazzicon from "jazzicon";
|
||||
import bs58 from "bs58";
|
||||
import "./style.less";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
export const Identicon = (props: {
|
||||
address?: string | PublicKey;
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
}) => {
|
||||
const { style, className } = props;
|
||||
const address =
|
||||
typeof props.address === "string"
|
||||
? props.address
|
||||
: props.address?.toBase58();
|
||||
const ref = useRef<HTMLDivElement>();
|
||||
|
||||
useEffect(() => {
|
||||
if (address && ref.current) {
|
||||
ref.current.innerHTML = "";
|
||||
ref.current.className = className || "";
|
||||
ref.current.appendChild(
|
||||
Jazzicon(
|
||||
style?.width || 16,
|
||||
parseInt(bs58.decode(address).toString("hex").slice(5, 15), 16)
|
||||
)
|
||||
);
|
||||
}
|
||||
}, [address, style, className]);
|
||||
|
||||
return (
|
||||
<div className="identicon-wrapper" ref={ref as any} style={props.style} />
|
||||
);
|
||||
};
|
|
@ -1,8 +0,0 @@
|
|||
.identicon-wrapper {
|
||||
display: flex;
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
border-radius: 1.125rem;
|
||||
margin: 0.2rem 0.2rem 0.2rem 0.1rem;
|
||||
/* background-color: ${({ theme }) => theme.bg4}; */
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
import React from "react";
|
||||
import { Input } from "antd";
|
||||
|
||||
export class NumericInput extends React.Component<any, any> {
|
||||
onChange = (e: any) => {
|
||||
const { value } = e.target;
|
||||
const reg = /^-?\d*(\.\d*)?$/;
|
||||
if (reg.test(value) || value === "" || value === "-") {
|
||||
this.props.onChange(value);
|
||||
}
|
||||
};
|
||||
|
||||
// '.' at the end or only '-' in the input box.
|
||||
onBlur = () => {
|
||||
const { value, onBlur, onChange } = this.props;
|
||||
let valueTemp = value;
|
||||
if (value === undefined || value === null) return;
|
||||
if (
|
||||
value.charAt &&
|
||||
(value.charAt(value.length - 1) === "." || value === "-")
|
||||
) {
|
||||
valueTemp = value.slice(0, -1);
|
||||
}
|
||||
if (value.startsWith && (value.startsWith(".") || value.startsWith("-."))) {
|
||||
valueTemp = valueTemp.replace(".", "0.");
|
||||
}
|
||||
if (valueTemp.replace) onChange?.(valueTemp.replace(/0*(\d+)/, "$1"));
|
||||
if (onBlur) {
|
||||
onBlur();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Input
|
||||
{...this.props}
|
||||
onChange={this.onChange}
|
||||
onBlur={this.onBlur}
|
||||
maxLength={25}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -14,12 +14,11 @@ import {
|
|||
} from '@ant-design/icons';
|
||||
|
||||
import BasicLayout from '@ant-design/pro-layout';
|
||||
import { AppBar } from './../AppBar';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
|
||||
import { LABELS } from '../../constants';
|
||||
import config from './../../../package.json';
|
||||
import { contexts } from '@oyster/common';
|
||||
import { contexts, AppBar } from '@oyster/common';
|
||||
|
||||
const { useConnectionConfig } = contexts.Connection;
|
||||
|
||||
|
|
|
@ -3,7 +3,13 @@ import Card from 'antd/lib/card';
|
|||
import React, { useCallback, useEffect } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { LABELS, marks } from '../../constants';
|
||||
import { ParsedAccount, contexts, utils, ConnectButton } from '@oyster/common';
|
||||
import {
|
||||
ParsedAccount,
|
||||
contexts,
|
||||
utils,
|
||||
ConnectButton,
|
||||
ActionConfirmation,
|
||||
} from '@oyster/common';
|
||||
import {
|
||||
EnrichedLendingObligation,
|
||||
InputType,
|
||||
|
@ -11,7 +17,6 @@ import {
|
|||
useUserBalance,
|
||||
} from '../../hooks';
|
||||
import { LendingReserve } from '../../models';
|
||||
import { ActionConfirmation } from '../ActionConfirmation';
|
||||
import { liquidate } from '../../actions';
|
||||
import './style.less';
|
||||
import CollateralInput from '../CollateralInput';
|
||||
|
|
|
@ -7,11 +7,17 @@ import {
|
|||
} from '../../hooks';
|
||||
import { LendingReserve } from '../../models';
|
||||
import { Card, Slider } from 'antd';
|
||||
import { ParsedAccount, contexts, utils, hooks, ConnectButton } from '@oyster/common';
|
||||
import {
|
||||
ParsedAccount,
|
||||
contexts,
|
||||
utils,
|
||||
hooks,
|
||||
ConnectButton,
|
||||
ActionConfirmation,
|
||||
} from '@oyster/common';
|
||||
import { repay } from '../../actions';
|
||||
import './style.less';
|
||||
import { LABELS, marks } from '../../constants';
|
||||
import { ActionConfirmation } from './../ActionConfirmation';
|
||||
import CollateralInput from '../CollateralInput';
|
||||
import { useMidPriceInUSD } from '../../contexts/market';
|
||||
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Button, Select } from 'antd';
|
||||
import { contexts } from '@oyster/common';
|
||||
|
||||
const { useWallet, WALLET_PROVIDERS } = contexts.Wallet;
|
||||
const { ENDPOINTS, useConnectionConfig } = contexts.Connection;
|
||||
|
||||
export const Settings = () => {
|
||||
const { connected, disconnect } = useWallet();
|
||||
const { endpoint, setEndpoint } = useConnectionConfig();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div style={{ display: "grid" }}>
|
||||
Network:{" "}
|
||||
<Select
|
||||
onSelect={setEndpoint}
|
||||
value={endpoint}
|
||||
style={{ marginBottom: 20 }}
|
||||
>
|
||||
{ENDPOINTS.map(({ name, endpoint }) => (
|
||||
<Select.Option value={endpoint} key={endpoint}>
|
||||
{name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
{connected && <Button type="primary" onClick={disconnect}>Disconnect</Button>}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,6 +1,5 @@
|
|||
import { Identicon } from '../Identicon';
|
||||
import React from 'react';
|
||||
import { utils, contexts } from '@oyster/common';
|
||||
import { utils, contexts, Identicon } from '@oyster/common';
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
const { getTokenIcon } = utils;
|
||||
const { useConnectionConfig } = contexts.Connection;
|
||||
|
|
|
@ -11,9 +11,8 @@ import { withdraw } from '../../actions';
|
|||
import { PublicKey } from '@solana/web3.js';
|
||||
import './style.less';
|
||||
import { LABELS, marks } from '../../constants';
|
||||
import { ActionConfirmation } from './../ActionConfirmation';
|
||||
import CollateralInput from '../CollateralInput';
|
||||
import { contexts, ConnectButton } from '@oyster/common';
|
||||
import { contexts, ConnectButton, ActionConfirmation } from '@oyster/common';
|
||||
const { useConnection } = contexts.Connection;
|
||||
const { useWallet } = contexts.Wallet;
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { Button, Card } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { ActionConfirmation } from '../../../components/ActionConfirmation';
|
||||
import { LABELS } from '../../../constants';
|
||||
import { contexts, ParsedAccount } from '@oyster/common';
|
||||
import { contexts, ParsedAccount, components } from '@oyster/common';
|
||||
import {
|
||||
LendingReserve,
|
||||
LendingReserveParser,
|
||||
|
@ -14,6 +13,7 @@ import { usePoolAndTradeInfoFrom } from './utils';
|
|||
import { UserDeposit } from '../../../hooks';
|
||||
import { ArrowDownOutlined } from '@ant-design/icons';
|
||||
|
||||
const { ActionConfirmation } = components;
|
||||
const { cache } = contexts.Accounts;
|
||||
const { useWallet } = contexts.Wallet;
|
||||
|
||||
|
|
Loading…
Reference in New Issue