diff --git a/packages/common/src/components/ActionConfirmation/index.tsx b/packages/common/src/components/ActionConfirmation/index.tsx
new file mode 100644
index 0000000..0e8fd50
--- /dev/null
+++ b/packages/common/src/components/ActionConfirmation/index.tsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import { Button } from 'antd';
+import { LABELS } from '../../constants';
+import { Link } from 'react-router-dom';
+import './style.css';
+
+export const ActionConfirmation = (props: {
+ className?: string;
+ onClose: () => void;
+}) => {
+ return (
+
+
Congratulations!
+
Your action has been successfully executed
+
+
+
+
+
+
+ );
+};
diff --git a/packages/common/src/components/ActionConfirmation/style.less b/packages/common/src/components/ActionConfirmation/style.less
new file mode 100644
index 0000000..64d27b2
--- /dev/null
+++ b/packages/common/src/components/ActionConfirmation/style.less
@@ -0,0 +1,5 @@
+.success-icon {
+ background-image: url('data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9JzMwMHB4JyB3aWR0aD0nMzAwcHgnICBmaWxsPSIjNzBjMDQxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGRhdGEtbmFtZT0iTGF5ZXIgMSIgdmlld0JveD0iMCAwIDY0IDY0IiB4PSIwcHgiIHk9IjBweCI+PHRpdGxlPmJ1c2luZXNzIGZpbmFuY2UgdXAgcmlzZSBhcnJvdyBkZW1hbmQ8L3RpdGxlPjxwYXRoIGQ9Ik01LDE0YTEsMSwwLDEsMC0xLTFINEExLDEsMCwwLDAsNSwxNFoiPjwvcGF0aD48cGF0aCBkPSJNNyw1Mkg1NWExLDEsMCwwLDAsMC0ySDUwVjI3YTEsMSwwLDAsMC0yLDBWNTBINDRWMzRhMSwxLDAsMCwwLTIsMFY1MEgzOFYzOWExLDEsMCwwLDAtMiwwVjUwSDMyVjQzYTEsMSwwLDAsMC0yLDB2N0gyNlY0NmExLDEsMCwwLDAtMiwwdjRIMjBWNDdhMSwxLDAsMCwwLTIsMHYzSDE0VjQ4YTEsMSwwLDAsMC0yLDB2Mkg3YTEsMSwwLDAsMS0xLTFWMTdhMSwxLDAsMCwwLTIsMFY0OUEzLDMsMCwwLDAsNyw1MloiPjwvcGF0aD48cGF0aCBkPSJNNTksNTBhMSwxLDAsMCwwLTEsMWgwYTEsMSwwLDEsMCwxLTFaIj48L3BhdGg+PHBhdGggZD0iTTExLDQ0aC4wN2E0OS4wNyw0OS4wNywwLDAsMCwyNS41Mi05LjE5QTQ4LjkxLDQ4LjkxLDAsMCwwLDQ5LjcsMjAuNTlMNTIuMzgsMjIsNTIsMTRsLTYuNzEsNC4zMSwyLjYzLDEuMzZBNDYuODEsNDYuODEsMCwwLDEsMzUuNDEsMzMuMTksNDYuOTQsNDYuOTQsMCwwLDEsMTAuOTMsNDIsMSwxLDAsMCwwLDExLDQ0WiI+PC9wYXRoPjwvc3ZnPg==');
+ width: 280px;
+ height: 280px;
+}
diff --git a/packages/common/src/components/AppBar/index.tsx b/packages/common/src/components/AppBar/index.tsx
new file mode 100644
index 0000000..5330169
--- /dev/null
+++ b/packages/common/src/components/AppBar/index.tsx
@@ -0,0 +1,43 @@
+import React from 'react';
+import { Button, Popover } from 'antd';
+import { CurrentUserBadge } from '../CurrentUserBadge';
+import { SettingOutlined } from '@ant-design/icons';
+import { Settings } from '../Settings';
+import { LABELS } from '../../constants/labels';
+import { ConnectButton } from '..';
+import { useWallet } from '../../contexts/wallet';
+import './style.css';
+export const AppBar = (props: { left?: JSX.Element; right?: JSX.Element }) => {
+ const { connected, wallet } = useWallet();
+
+ const TopBar = (
+
+ {connected ? (
+
+ ) : (
+
+ )}
+
}
+ trigger="click"
+ >
+
}
+ />
+
+ {props.right}
+
+ );
+
+ return TopBar;
+};
diff --git a/packages/common/src/components/AppBar/style.less b/packages/common/src/components/AppBar/style.less
new file mode 100644
index 0000000..377c1cc
--- /dev/null
+++ b/packages/common/src/components/AppBar/style.less
@@ -0,0 +1,58 @@
+.App-Bar {
+ display: grid;
+ grid-template-columns: 1fr 120px;
+ -webkit-box-pack: justify;
+ justify-content: space-between;
+ -webkit-box-align: center;
+ align-items: center;
+ flex-direction: row;
+ width: 100%;
+ top: 0px;
+ position: relative;
+ padding: 1rem;
+ z-index: 2;
+
+ .ant-menu-horizontal {
+ border-bottom-color: transparent;
+ background-color: transparent;
+ line-height: inherit;
+ font-size: 16px;
+ margin: 0 10px;
+
+ .ant-menu-item {
+ margin: 0 10px;
+ color: lightgrey;
+ height: 35px;
+ line-height: 35px;
+ border-width: 0px !important;
+ }
+
+ .ant-menu-item:hover {
+ color: white;
+ border-width: 0px !important;
+ }
+
+ .ant-menu-item-selected {
+ font-weight: bold;
+ }
+ }
+}
+
+.App-Bar-left {
+ box-sizing: border-box;
+ margin: 0px;
+ min-width: 0px;
+ display: flex;
+ padding: 0px;
+ -webkit-box-align: center;
+ align-items: center;
+ width: fit-content;
+}
+
+.App-Bar-right {
+ display: flex;
+ flex-direction: row;
+ -webkit-box-align: center;
+ align-items: center;
+ justify-self: flex-end;
+}
diff --git a/packages/common/src/components/BackButton/index.tsx b/packages/common/src/components/BackButton/index.tsx
new file mode 100644
index 0000000..82ce886
--- /dev/null
+++ b/packages/common/src/components/BackButton/index.tsx
@@ -0,0 +1,13 @@
+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 (
+
+ );
+};
diff --git a/packages/common/src/components/CurrentUserBadge/index.tsx b/packages/common/src/components/CurrentUserBadge/index.tsx
new file mode 100644
index 0000000..41c9617
--- /dev/null
+++ b/packages/common/src/components/CurrentUserBadge/index.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+
+import { Identicon } from '../Identicon';
+import { LAMPORTS_PER_SOL } from '@solana/web3.js';
+import { useWallet } from '../../contexts/wallet';
+import { useNativeAccount } from '../../contexts/accounts';
+import { formatNumber, shortenAddress } from '../../utils';
+
+export const CurrentUserBadge = (props: {}) => {
+ const { wallet } = useWallet();
+ const { account } = useNativeAccount();
+
+ if (!wallet || !wallet.publicKey) {
+ return null;
+ }
+
+ // should use SOL ◎ ?
+
+ return (
+
+
+ {formatNumber.format((account?.lamports || 0) / LAMPORTS_PER_SOL)} SOL
+
+
+ {shortenAddress(`${wallet.publicKey}`)}
+
+
+
+ );
+};
diff --git a/packages/common/src/components/Icons/info.tsx b/packages/common/src/components/Icons/info.tsx
new file mode 100644
index 0000000..4bb0e3a
--- /dev/null
+++ b/packages/common/src/components/Icons/info.tsx
@@ -0,0 +1,20 @@
+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 (
+ {props.text}}
+ >
+
+
+ );
+};
diff --git a/packages/common/src/components/Identicon/index.tsx b/packages/common/src/components/Identicon/index.tsx
new file mode 100644
index 0000000..675b7b7
--- /dev/null
+++ b/packages/common/src/components/Identicon/index.tsx
@@ -0,0 +1,36 @@
+import React, { useEffect, useRef } from 'react';
+
+import Jazzicon from 'jazzicon';
+import bs58 from 'bs58';
+import './style.css';
+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();
+
+ 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 (
+
+ );
+};
diff --git a/packages/common/src/components/Identicon/style.less b/packages/common/src/components/Identicon/style.less
new file mode 100644
index 0000000..4b04246
--- /dev/null
+++ b/packages/common/src/components/Identicon/style.less
@@ -0,0 +1,8 @@
+.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}; */
+}
diff --git a/packages/common/src/components/Settings/index.tsx b/packages/common/src/components/Settings/index.tsx
new file mode 100644
index 0000000..aed2649
--- /dev/null
+++ b/packages/common/src/components/Settings/index.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import { Button, Select } from 'antd';
+import { useWallet } from '../../contexts/wallet';
+import { ENDPOINTS, useConnectionConfig } from '../../contexts/connection';
+
+export const Settings = () => {
+ const { connected, disconnect } = useWallet();
+ const { endpoint, setEndpoint } = useConnectionConfig();
+
+ return (
+ <>
+
+ Network:{' '}
+
+ {connected && (
+
+ )}
+
+ >
+ );
+};
diff --git a/packages/common/src/components/index.tsx b/packages/common/src/components/index.tsx
index 0038ed5..52c503f 100644
--- a/packages/common/src/components/index.tsx
+++ b/packages/common/src/components/index.tsx
@@ -1,2 +1,8 @@
export { ExplorerLink } from './ExplorerLink/index';
export { ConnectButton } from './ConnectButton/index';
+export { CurrentUserBadge } from './CurrentUserBadge/index';
+export { Identicon } from './Identicon/index';
+export { Info } from './Icons/info';
+export { NumericInput } from './Input/numeric';
+export { AppBar } from './AppBar/index';
+export { Settings } from './Settings/index';
diff --git a/packages/common/src/constants/index.ts b/packages/common/src/constants/index.ts
index 3531606..7d6bf06 100644
--- a/packages/common/src/constants/index.ts
+++ b/packages/common/src/constants/index.ts
@@ -1 +1,2 @@
export * from './math';
+export * from './labels';
diff --git a/packages/common/src/constants/labels.ts b/packages/common/src/constants/labels.ts
new file mode 100644
index 0000000..be5c639
--- /dev/null
+++ b/packages/common/src/constants/labels.ts
@@ -0,0 +1,15 @@
+export const LABELS = {
+ CONNECT_LABEL: 'Connect Wallet',
+ AUDIT_WARNING:
+ 'Oyster is an unaudited software project used for internal purposes at the Solana Foundation. This app is not for public use.',
+ FOOTER:
+ 'This page was produced by the Solana Foundation ("SF") for internal educational and inspiration purposes only. SF does not encourage, induce or sanction the deployment, integration or use of Oyster or any similar application (including its code) in violation of applicable laws or regulations and hereby prohibits any such deployment, integration or use. Anyone using this code or a derivation thereof must comply with applicable laws and regulations when releasing related software.',
+ MENU_HOME: 'Home',
+ MENU_DASHBOARD: 'Dashboard',
+ CONNECT_BUTTON: 'Connect',
+ WALLET_TOOLTIP: 'Wallet public key',
+ WALLET_BALANCE: 'Wallet balance',
+ SETTINGS_TOOLTIP: 'Settings',
+ DASHBOARD_ACTION: 'Go to dashboard',
+ GO_BACK_ACTION: 'Go back',
+};
diff --git a/packages/common/src/index.tsx b/packages/common/src/index.tsx
index c08601d..4c23c1d 100644
--- a/packages/common/src/index.tsx
+++ b/packages/common/src/index.tsx
@@ -1,5 +1,5 @@
export * as actions from './actions';
-export * from './components';
+export * as components from './components';
export * as config from './config';
export * as constants from './constants';
export * as hooks from './hooks';
diff --git a/packages/common/src/utils/ids.ts b/packages/common/src/utils/ids.ts
index f3fe709..2d5b89e 100644
--- a/packages/common/src/utils/ids.ts
+++ b/packages/common/src/utils/ids.ts
@@ -18,6 +18,11 @@ let WORMHOLE_BRIDGE: {
wrappedMaster: string;
};
+let TIMELOCK: {
+ programId: PublicKey;
+ programAccountId: PublicKey;
+};
+
let SWAP_PROGRAM_ID: PublicKey;
let SWAP_PROGRAM_LEGACY_IDS: PublicKey[];
let SWAP_PROGRAM_LAYOUT: any;
@@ -34,6 +39,12 @@ export const ENABLE_FEES_INPUT = false;
export const PROGRAM_IDS = [
{
name: 'mainnet-beta',
+ timelock: () => ({
+ programAccountId: new PublicKey(
+ '9gBhDCCKV7KELLFRY8sAJZXqDmvUfmNzFzpB2b4FUVVr',
+ ),
+ programId: new PublicKey('9iAeqqppjn7g1Jn8o2cQCqU5aQVV3h4q9bbWdKRbeC2w'),
+ }),
wormhole: () => ({
pubkey: new PublicKey('WormT3McKhFJ2RkiGpdw9GKvNCrB2aB54gb2uV9MfQC'),
bridge: '0xf92cD566Ea4864356C5491c177A430C222d7e678',
@@ -52,6 +63,12 @@ export const PROGRAM_IDS = [
},
{
name: 'testnet',
+ timelock: () => ({
+ programAccountId: new PublicKey(
+ '9gBhDCCKV7KELLFRY8sAJZXqDmvUfmNzFzpB2b4FUVVr',
+ ),
+ programId: new PublicKey('9iAeqqppjn7g1Jn8o2cQCqU5aQVV3h4q9bbWdKRbeC2w'),
+ }),
wormhole: () => ({
pubkey: new PublicKey('5gQf5AUhAgWYgUCt9ouShm9H7dzzXUsLdssYwe5krKhg'),
bridge: '0x251bBCD91E84098509beaeAfF0B9951859af66D3',
@@ -67,6 +84,12 @@ export const PROGRAM_IDS = [
},
{
name: 'devnet',
+ timelock: () => ({
+ programAccountId: new PublicKey(
+ '9gBhDCCKV7KELLFRY8sAJZXqDmvUfmNzFzpB2b4FUVVr',
+ ),
+ programId: new PublicKey('9iAeqqppjn7g1Jn8o2cQCqU5aQVV3h4q9bbWdKRbeC2w'),
+ }),
wormhole: () => ({
pubkey: new PublicKey('WormT3McKhFJ2RkiGpdw9GKvNCrB2aB54gb2uV9MfQC'),
bridge: '0xf92cD566Ea4864356C5491c177A430C222d7e678',
@@ -82,6 +105,12 @@ export const PROGRAM_IDS = [
},
{
name: 'localnet',
+ timelock: () => ({
+ programAccountId: new PublicKey(
+ '9gBhDCCKV7KELLFRY8sAJZXqDmvUfmNzFzpB2b4FUVVr',
+ ),
+ programId: new PublicKey('9iAeqqppjn7g1Jn8o2cQCqU5aQVV3h4q9bbWdKRbeC2w'),
+ }),
wormhole: () => ({
pubkey: new PublicKey('WormT3McKhFJ2RkiGpdw9GKvNCrB2aB54gb2uV9MfQC'),
bridge: '0xf92cD566Ea4864356C5491c177A430C222d7e678',
@@ -111,6 +140,8 @@ export const setProgramIds = (envName: string) => {
SWAP_PROGRAM_LAYOUT = swap.current.layout;
SWAP_PROGRAM_LEGACY_IDS = swap.legacy;
+ TIMELOCK = instance.timelock();
+
if (envName === 'mainnet-beta') {
LENDING_PROGRAM_ID = new PublicKey(
'LendZqTs7gn5CTSJU1jWKhKuVpjJGom45nnwPb2AMTi',
@@ -125,5 +156,6 @@ export const programIds = () => {
swapLayout: SWAP_PROGRAM_LAYOUT,
lending: LENDING_PROGRAM_ID,
wormhole: WORMHOLE_BRIDGE,
+ timelock: TIMELOCK,
};
};