Display default multisig and format
This commit is contained in:
parent
0a2228fb3f
commit
20b1e10c3c
|
@ -57,6 +57,7 @@
|
|||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"gh-pages": "^3.1.0"
|
||||
"gh-pages": "^3.1.0",
|
||||
"prettier": "^2.2.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import App from './App';
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import App from "./App";
|
||||
|
||||
test('renders learn react link', () => {
|
||||
test("renders learn react link", () => {
|
||||
render(<App />);
|
||||
const linkElement = screen.getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
|
|
45
src/App.tsx
45
src/App.tsx
|
@ -1,26 +1,27 @@
|
|||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { useHistory, useLocation } from 'react-router';
|
||||
import { HashRouter, Route } from 'react-router-dom';
|
||||
import { SnackbarProvider } from 'notistack';
|
||||
import { MuiThemeProvider } from '@material-ui/core/styles';
|
||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
||||
import { unstable_createMuiStrictModeTheme as createMuiTheme } from '@material-ui/core/styles';
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
import { store } from './store';
|
||||
import WalletProvider from './components/WalletProvider';
|
||||
import Layout from './components/Layout';
|
||||
import Multisig from './components/Multisig';
|
||||
import React from "react";
|
||||
import { Provider } from "react-redux";
|
||||
import { useHistory, useLocation } from "react-router";
|
||||
import { HashRouter, Route } from "react-router-dom";
|
||||
import { SnackbarProvider } from "notistack";
|
||||
import { MuiThemeProvider } from "@material-ui/core/styles";
|
||||
import CssBaseline from "@material-ui/core/CssBaseline";
|
||||
import { unstable_createMuiStrictModeTheme as createMuiTheme } from "@material-ui/core/styles";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { store } from "./store";
|
||||
import WalletProvider from "./components/WalletProvider";
|
||||
import Layout from "./components/Layout";
|
||||
import Multisig from "./components/Multisig";
|
||||
import { networks } from "./store/reducer";
|
||||
|
||||
function App() {
|
||||
const theme = createMuiTheme({
|
||||
palette: {
|
||||
background: {
|
||||
default: 'rgb(255,255,255)',
|
||||
default: "rgb(255,255,255)",
|
||||
},
|
||||
},
|
||||
typography: {
|
||||
fontFamily: ['Source Sans Pro', 'sans-serif'].join(','),
|
||||
fontFamily: ["Source Sans Pro", "sans-serif"].join(","),
|
||||
},
|
||||
overrides: {},
|
||||
});
|
||||
|
@ -30,7 +31,7 @@ function App() {
|
|||
<CssBaseline />
|
||||
<SnackbarProvider maxSnack={5} autoHideDuration={8000}>
|
||||
<WalletProvider>
|
||||
<HashRouter basename={'/'}>
|
||||
<HashRouter basename={"/"}>
|
||||
<Layout>
|
||||
<Route exact path="/" component={MultisigPage} />
|
||||
<Route
|
||||
|
@ -48,15 +49,18 @@ function App() {
|
|||
}
|
||||
|
||||
function MultisigPage() {
|
||||
return (
|
||||
<Multisig />
|
||||
);
|
||||
const { hash } = window.location;
|
||||
if (hash) {
|
||||
window.location.href = `/#/${networks.mainnet.multisigUpgradeAuthority!.toString()}`;
|
||||
}
|
||||
const multisig = networks.mainnet.multisigUpgradeAuthority;
|
||||
return <Multisig multisig={multisig} />;
|
||||
}
|
||||
|
||||
export function MultisigInstancePage() {
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
const path = location.pathname.split('/');
|
||||
const path = location.pathname.split("/");
|
||||
if (path.length !== 2) {
|
||||
history.push(`/multisig`);
|
||||
return <></>;
|
||||
|
@ -66,5 +70,4 @@ export function MultisigInstancePage() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
export default App;
|
||||
|
|
|
@ -1,48 +1,46 @@
|
|||
import React from 'react';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import Link from '@material-ui/core/Link';
|
||||
import React from "react";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import Link from "@material-ui/core/Link";
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
zIndex: 99,
|
||||
position: 'fixed',
|
||||
width: '100%',
|
||||
position: "fixed",
|
||||
width: "100%",
|
||||
bottom: 0,
|
||||
textAlign: 'center',
|
||||
height: '30px',
|
||||
backgroundColor: '#fbfbfb',
|
||||
borderTop: 'solid 1pt #ccc',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
paddingLeft: '10px',
|
||||
paddingRight: '10px',
|
||||
textAlign: "center",
|
||||
height: "30px",
|
||||
backgroundColor: "#fbfbfb",
|
||||
borderTop: "solid 1pt #ccc",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
paddingLeft: "10px",
|
||||
paddingRight: "10px",
|
||||
}}
|
||||
>
|
||||
<div style={{ marginLeft: 'auto', marginRight: 'auto', display: 'flex' }}>
|
||||
<div style={{ marginLeft: "auto", marginRight: "auto", display: "flex" }}>
|
||||
<FooterButton
|
||||
href={
|
||||
'https://github.com/project-serum/anchor/blob/master/examples/lockup/docs/staking.md'
|
||||
}
|
||||
label={'Technical Documentation'}
|
||||
href={"https://github.com/project-serum/multisig"}
|
||||
label={"Source"}
|
||||
/>
|
||||
<FooterButton
|
||||
href={'https://github.com/project-serum/awesome-serum'}
|
||||
label={'Developer Resources'}
|
||||
href={"https://github.com/project-serum/awesome-serum"}
|
||||
label={"Developer Resources"}
|
||||
/>
|
||||
<FooterButton
|
||||
href={'https://discord.com/channels/739225212658122886'}
|
||||
label={'Discord'}
|
||||
href={"https://discord.com/channels/739225212658122886"}
|
||||
label={"Discord"}
|
||||
/>
|
||||
<FooterButton href={'https://t.me/ProjectSerum'} label={'Telegram'} />
|
||||
<FooterButton href={"https://t.me/ProjectSerum"} label={"Telegram"} />
|
||||
<FooterButton
|
||||
href={'https://github.com/project-serum'}
|
||||
label={'GitHub'}
|
||||
href={"https://github.com/project-serum"}
|
||||
label={"GitHub"}
|
||||
/>
|
||||
<FooterButton
|
||||
href={'https://solanabeach.io/'}
|
||||
label={'Solana Network'}
|
||||
href={"https://solanabeach.io/"}
|
||||
label={"Solana Network"}
|
||||
isEnd={true}
|
||||
/>
|
||||
</div>
|
||||
|
@ -61,14 +59,14 @@ function FooterButton(props: FooterButtonProps) {
|
|||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'column',
|
||||
marginRight: isEnd ? '' : '15px',
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
flexDirection: "column",
|
||||
marginRight: isEnd ? "" : "15px",
|
||||
}}
|
||||
>
|
||||
<Link href={href} target="_blank" color="inherit">
|
||||
<Typography style={{ fontSize: '14px' }}>{label}</Typography>
|
||||
<Typography style={{ fontSize: "14px" }}>{label}</Typography>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,32 +1,28 @@
|
|||
import React, { useState, useEffect, ReactElement } from 'react';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useSnackbar } from 'notistack';
|
||||
import AppBar from '@material-ui/core/AppBar';
|
||||
import Toolbar from '@material-ui/core/Toolbar';
|
||||
import Select from '@material-ui/core/Select';
|
||||
import Menu from '@material-ui/core/Menu';
|
||||
import Link from '@material-ui/core/Link';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import ExitToAppIcon from '@material-ui/icons/ExitToApp';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import PersonIcon from '@material-ui/icons/Person';
|
||||
import BubbleChartIcon from '@material-ui/icons/BubbleChart';
|
||||
import SearchIcon from '@material-ui/icons/Search';
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
import {
|
||||
networks,
|
||||
State as StoreState,
|
||||
ActionType,
|
||||
} from '../store/reducer';
|
||||
import { useWallet } from './WalletProvider';
|
||||
import React, { useState, useEffect, ReactElement } from "react";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { useSnackbar } from "notistack";
|
||||
import AppBar from "@material-ui/core/AppBar";
|
||||
import Toolbar from "@material-ui/core/Toolbar";
|
||||
import Select from "@material-ui/core/Select";
|
||||
import Menu from "@material-ui/core/Menu";
|
||||
import Link from "@material-ui/core/Link";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import MenuItem from "@material-ui/core/MenuItem";
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
import ExitToAppIcon from "@material-ui/icons/ExitToApp";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import PersonIcon from "@material-ui/icons/Person";
|
||||
import BubbleChartIcon from "@material-ui/icons/BubbleChart";
|
||||
import SearchIcon from "@material-ui/icons/Search";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { networks, State as StoreState, ActionType } from "../store/reducer";
|
||||
import { useWallet } from "./WalletProvider";
|
||||
|
||||
export default function Header() {
|
||||
const { wallet } = useWallet();
|
||||
const history = useHistory();
|
||||
const [multisigAddress, setMultisigAddress] = useState('');
|
||||
const [multisigAddress, setMultisigAddress] = useState("");
|
||||
const disabled = !isValidPubkey(multisigAddress);
|
||||
const searchFn = () => {
|
||||
history.push(`/${multisigAddress}`);
|
||||
|
@ -35,50 +31,52 @@ export default function Header() {
|
|||
<AppBar
|
||||
position="static"
|
||||
style={{
|
||||
background: '#ffffff',
|
||||
color: '#272727',
|
||||
boxShadow: 'none',
|
||||
borderBottom: 'solid 1pt #ccc',
|
||||
background: "#ffffff",
|
||||
color: "#272727",
|
||||
boxShadow: "none",
|
||||
borderBottom: "solid 1pt #ccc",
|
||||
}}
|
||||
>
|
||||
<Toolbar>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', flex: 1 }}>
|
||||
<div style={{ display: "flex", flex: 1 }}>
|
||||
<SerumLogoButton />
|
||||
<BarButton label="Multisig" hrefClient="/" />
|
||||
<BarButton label="Trade" href="https://dex.projectserum.com" />
|
||||
<BarButton label="Swap" href="https://swap.projectserum.com" />
|
||||
<BarButton label="Stake" href="https://stake.projectserum.com" />
|
||||
<BarButton label="Lockup" href="https://stake.projectserum.com/#/lockup" />
|
||||
<BarButton
|
||||
label="Lockup"
|
||||
href="https://stake.projectserum.com/#/lockup"
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
marginLeft: '16px',
|
||||
marginRight: '16px',
|
||||
borderRadius: '25px',
|
||||
display: 'flex',
|
||||
marginLeft: "16px",
|
||||
marginRight: "16px",
|
||||
borderRadius: "25px",
|
||||
display: "flex",
|
||||
flex: 1,
|
||||
backgroundColor: 'rgb(245 245 245)',
|
||||
backgroundColor: "rgb(245 245 245)",
|
||||
}}
|
||||
>
|
||||
<input
|
||||
style={{
|
||||
flex: 1,
|
||||
background: 'none',
|
||||
padding: '16px',
|
||||
border: 'none',
|
||||
background: "none",
|
||||
padding: "16px",
|
||||
border: "none",
|
||||
outlineWidth: 0,
|
||||
color: 'inherit',
|
||||
color: "inherit",
|
||||
}}
|
||||
placeholder="Search a multisig address..."
|
||||
value={multisigAddress}
|
||||
onChange={e => setMultisigAddress(e.target.value as string)}
|
||||
onKeyPress={e => {
|
||||
if (e.key === 'Enter') {
|
||||
onChange={(e) => setMultisigAddress(e.target.value as string)}
|
||||
onKeyPress={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
searchFn();
|
||||
}
|
||||
}}
|
||||
|
@ -90,14 +88,14 @@ export default function Header() {
|
|||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
}}
|
||||
>
|
||||
<NetworkSelector />
|
||||
{!wallet.publicKey ? (
|
||||
<WalletConnectButton
|
||||
style={{
|
||||
display: wallet.publicKey ? 'none' : '',
|
||||
display: wallet.publicKey ? "none" : "",
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
|
@ -113,19 +111,19 @@ export default function Header() {
|
|||
function SerumLogoButton() {
|
||||
const history = useHistory();
|
||||
return (
|
||||
<div style={{ display: 'flex' }} onClick={() => history.push('/')}>
|
||||
<div style={{ display: "flex" }} onClick={() => history.push("/")}>
|
||||
<Button color="inherit">
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
display: 'block',
|
||||
height: '35px',
|
||||
display: "block",
|
||||
height: "35px",
|
||||
}}
|
||||
alt="Logo"
|
||||
src="http://dex.projectserum.com/static/media/logo.49174c73.svg"
|
||||
|
@ -148,26 +146,26 @@ function BarButton(props: BarButtonProps) {
|
|||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
onClick={() => hrefClient && history.push(hrefClient)}
|
||||
>
|
||||
<Link
|
||||
style={{ color: 'inherit', textDecoration: 'none' }}
|
||||
style={{ color: "inherit", textDecoration: "none" }}
|
||||
href={href}
|
||||
target="_blank"
|
||||
>
|
||||
<Button color="inherit">
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'column',
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<Typography style={{ fontSize: '15px' }}>{label}</Typography>
|
||||
<Typography style={{ fontSize: "15px" }}>{label}</Typography>
|
||||
</div>
|
||||
</Button>
|
||||
</Link>
|
||||
|
@ -189,24 +187,24 @@ function NetworkSelector() {
|
|||
return (
|
||||
<div
|
||||
style={{
|
||||
marginRight: '10px',
|
||||
fontSize: '15px',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'column',
|
||||
marginRight: "10px",
|
||||
fontSize: "15px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
color="inherit"
|
||||
onClick={e =>
|
||||
onClick={(e) =>
|
||||
setAnchorEl(
|
||||
// @ts-ignore
|
||||
e.currentTarget,
|
||||
e.currentTarget
|
||||
)
|
||||
}
|
||||
>
|
||||
<BubbleChartIcon />
|
||||
<Typography style={{ marginLeft: '5px', fontSize: '15px' }}>
|
||||
<Typography style={{ marginLeft: "5px", fontSize: "15px" }}>
|
||||
{network.label}
|
||||
</Typography>
|
||||
</Button>
|
||||
|
@ -215,8 +213,8 @@ function NetworkSelector() {
|
|||
open={Boolean(anchorEl)}
|
||||
onClose={handleClose}
|
||||
style={{
|
||||
marginLeft: '12px',
|
||||
color: 'white',
|
||||
marginLeft: "12px",
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
{Object.keys(networks).map((n: string) => (
|
||||
|
@ -249,17 +247,17 @@ function UserSelector() {
|
|||
displayEmpty
|
||||
renderValue={() => {
|
||||
return (
|
||||
<Typography style={{ overflow: 'hidden' }}>
|
||||
<Typography style={{ overflow: "hidden" }}>
|
||||
{wallet.publicKey.toString()}
|
||||
</Typography>
|
||||
);
|
||||
}}
|
||||
style={{
|
||||
marginLeft: '12px',
|
||||
width: '150px',
|
||||
marginLeft: "12px",
|
||||
width: "150px",
|
||||
}}
|
||||
onChange={e => {
|
||||
if (e.target.value === 'disconnect') {
|
||||
onChange={(e) => {
|
||||
if (e.target.value === "disconnect") {
|
||||
wallet.disconnect();
|
||||
}
|
||||
}}
|
||||
|
@ -267,7 +265,7 @@ function UserSelector() {
|
|||
<MenuItem value="disconnect">
|
||||
<IconButton color="inherit">
|
||||
<ExitToAppIcon />
|
||||
<Typography style={{ marginLeft: '15px' }}>Disconnect</Typography>
|
||||
<Typography style={{ marginLeft: "15px" }}>Disconnect</Typography>
|
||||
</IconButton>
|
||||
</MenuItem>
|
||||
</Select>
|
||||
|
@ -279,7 +277,7 @@ type WalletConnectButtonProps = {
|
|||
};
|
||||
|
||||
export function WalletConnectButton(
|
||||
props: WalletConnectButtonProps,
|
||||
props: WalletConnectButtonProps
|
||||
): ReactElement {
|
||||
const { showDisconnect } = useSelector((state: StoreState) => {
|
||||
return {
|
||||
|
@ -292,9 +290,9 @@ export function WalletConnectButton(
|
|||
|
||||
// Wallet connection event listeners.
|
||||
useEffect(() => {
|
||||
wallet.on('disconnect', () => {
|
||||
enqueueSnackbar('Disconnected from wallet', {
|
||||
variant: 'info',
|
||||
wallet.on("disconnect", () => {
|
||||
enqueueSnackbar("Disconnected from wallet", {
|
||||
variant: "info",
|
||||
autoHideDuration: 2500,
|
||||
});
|
||||
dispatch({
|
||||
|
@ -306,7 +304,7 @@ export function WalletConnectButton(
|
|||
item: {},
|
||||
});
|
||||
});
|
||||
wallet.on('connect', async () => {
|
||||
wallet.on("connect", async () => {
|
||||
dispatch({
|
||||
type: ActionType.CommonWalletDidConnect,
|
||||
item: {},
|
||||
|
@ -321,7 +319,7 @@ export function WalletConnectButton(
|
|||
onClick={() => wallet.disconnect()}
|
||||
>
|
||||
<ExitToAppIcon />
|
||||
<Typography style={{ marginLeft: '5px', fontSize: '15px' }}>
|
||||
<Typography style={{ marginLeft: "5px", fontSize: "15px" }}>
|
||||
Disconnect
|
||||
</Typography>
|
||||
</Button>
|
||||
|
@ -332,14 +330,13 @@ export function WalletConnectButton(
|
|||
onClick={() => wallet.connect()}
|
||||
>
|
||||
<PersonIcon />
|
||||
<Typography style={{ marginLeft: '5px', fontSize: '15px' }}>
|
||||
<Typography style={{ marginLeft: "5px", fontSize: "15px" }}>
|
||||
Connect wallet
|
||||
</Typography>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function isValidPubkey(addr: string): boolean {
|
||||
try {
|
||||
new PublicKey(addr);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React, { useState, PropsWithChildren } from 'react';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Header from './Header';
|
||||
import Footer from './Footer';
|
||||
import React, { useState, PropsWithChildren } from "react";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import Header from "./Header";
|
||||
import Footer from "./Footer";
|
||||
|
||||
type Props = {};
|
||||
|
||||
|
@ -11,16 +11,16 @@ export default function Layout(props: PropsWithChildren<Props>) {
|
|||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
minHeight: '100vh',
|
||||
flexDirection: 'column',
|
||||
backgroundColor: 'rgb(251, 251, 251)',
|
||||
display: "flex",
|
||||
minHeight: "100vh",
|
||||
flexDirection: "column",
|
||||
backgroundColor: "rgb(251, 251, 251)",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
width: '100%',
|
||||
position: "fixed",
|
||||
width: "100%",
|
||||
zIndex: 99,
|
||||
}}
|
||||
>
|
||||
|
@ -29,19 +29,19 @@ export default function Layout(props: PropsWithChildren<Props>) {
|
|||
</div>
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
marginTop: '94px',
|
||||
width: "100%",
|
||||
marginTop: "94px",
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
marginBottom: '30px', // Compensates for the fixed position footer.
|
||||
display: "flex",
|
||||
marginBottom: "30px", // Compensates for the fixed position footer.
|
||||
}}
|
||||
>
|
||||
{window.localStorage.getItem('consent') ? (
|
||||
<div style={{ width: '100%' }}>{props.children}</div>
|
||||
{window.localStorage.getItem("consent") ? (
|
||||
<div style={{ width: "100%" }}>{props.children}</div>
|
||||
) : (
|
||||
<RiskDisclosureForm
|
||||
onConsent={() => {
|
||||
window.localStorage.setItem('consent', 'true');
|
||||
window.localStorage.setItem("consent", "true");
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
|
@ -56,21 +56,21 @@ function RiskBar() {
|
|||
return (
|
||||
<div
|
||||
style={{
|
||||
color: '#fff',
|
||||
backgroundColor: 'rgb(39, 39, 39)',
|
||||
height: '30px',
|
||||
textAlign: 'center',
|
||||
color: "#fff",
|
||||
backgroundColor: "rgb(39, 39, 39)",
|
||||
height: "30px",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
flexDirection: "column",
|
||||
height: "100%",
|
||||
}}
|
||||
>
|
||||
<Typography style={{ fontSize: '14px' }}>
|
||||
<Typography style={{ fontSize: "14px" }}>
|
||||
Multisig is unaudited software. Use at your own risk.
|
||||
</Typography>
|
||||
</div>
|
||||
|
@ -82,19 +82,19 @@ function RiskDisclosureForm({ onConsent }: { onConsent: () => void }) {
|
|||
return (
|
||||
<div
|
||||
style={{
|
||||
flex: '1',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'column',
|
||||
textAlign: 'center',
|
||||
flex: "1",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
flexDirection: "column",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<div style={{ width: '100%', display: 'flex' }}>
|
||||
<div style={{ width: "100%", display: "flex" }}>
|
||||
<div
|
||||
style={{ display: 'flex', marginLeft: 'auto', marginRight: 'auto' }}
|
||||
style={{ display: "flex", marginLeft: "auto", marginRight: "auto" }}
|
||||
>
|
||||
<Typography
|
||||
style={{ marginBottom: '16px', maxWidth: '1000px' }}
|
||||
style={{ marginBottom: "16px", maxWidth: "1000px" }}
|
||||
color="textSecondary"
|
||||
variant="h4"
|
||||
>
|
||||
|
@ -106,7 +106,7 @@ function RiskDisclosureForm({ onConsent }: { onConsent: () => void }) {
|
|||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', marginLeft: 'auto', marginRight: 'auto' }}>
|
||||
<div style={{ display: "flex", marginLeft: "auto", marginRight: "auto" }}>
|
||||
<Button variant="contained" color="primary" onClick={onConsent}>
|
||||
I agree
|
||||
</Button>
|
||||
|
|
|
@ -1,69 +1,72 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { useHistory } from 'react-router';
|
||||
import { useSnackbar } from 'notistack';
|
||||
import { encode as encodeBase64 } from 'js-base64';
|
||||
import Container from '@material-ui/core/Container';
|
||||
import AppBar from '@material-ui/core/AppBar';
|
||||
import GavelIcon from '@material-ui/icons/Gavel';
|
||||
import DescriptionIcon from '@material-ui/icons/Description';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import SupervisorAccountIcon from '@material-ui/icons/SupervisorAccount';
|
||||
import CheckIcon from '@material-ui/icons/Check';
|
||||
import ReceiptIcon from '@material-ui/icons/Receipt';
|
||||
import RemoveIcon from '@material-ui/icons/Remove';
|
||||
import Collapse from '@material-ui/core/Collapse';
|
||||
import Toolbar from '@material-ui/core/Toolbar';
|
||||
import InfoIcon from '@material-ui/icons/Info';
|
||||
import Table from '@material-ui/core/Table';
|
||||
import TableHead from '@material-ui/core/TableHead';
|
||||
import TableBody from '@material-ui/core/TableBody';
|
||||
import TableCell from '@material-ui/core/TableCell';
|
||||
import TableRow from '@material-ui/core/TableRow';
|
||||
import BuildIcon from '@material-ui/icons/Build';
|
||||
import Tooltip from '@material-ui/core/Tooltip';
|
||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import Card from '@material-ui/core/Card';
|
||||
import ExpandLess from '@material-ui/icons/ExpandLess';
|
||||
import ExpandMore from '@material-ui/icons/ExpandMore';
|
||||
import CardContent from '@material-ui/core/CardContent';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import DialogContent from '@material-ui/core/DialogContent';
|
||||
import DialogContentText from '@material-ui/core/DialogContentText';
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
import AddIcon from '@material-ui/icons/Add';
|
||||
import List from '@material-ui/core/List';
|
||||
import ListItemIcon from '@material-ui/core/ListItemIcon';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
import CheckCircleIcon from '@material-ui/icons/CheckCircle';
|
||||
import BN from 'bn.js';
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useHistory } from "react-router";
|
||||
import { useSnackbar } from "notistack";
|
||||
import { encode as encodeBase64 } from "js-base64";
|
||||
import Container from "@material-ui/core/Container";
|
||||
import AppBar from "@material-ui/core/AppBar";
|
||||
import GavelIcon from "@material-ui/icons/Gavel";
|
||||
import DescriptionIcon from "@material-ui/icons/Description";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import SupervisorAccountIcon from "@material-ui/icons/SupervisorAccount";
|
||||
import CheckIcon from "@material-ui/icons/Check";
|
||||
import ReceiptIcon from "@material-ui/icons/Receipt";
|
||||
import RemoveIcon from "@material-ui/icons/Remove";
|
||||
import Collapse from "@material-ui/core/Collapse";
|
||||
import Toolbar from "@material-ui/core/Toolbar";
|
||||
import InfoIcon from "@material-ui/icons/Info";
|
||||
import Table from "@material-ui/core/Table";
|
||||
import TableHead from "@material-ui/core/TableHead";
|
||||
import TableBody from "@material-ui/core/TableBody";
|
||||
import TableCell from "@material-ui/core/TableCell";
|
||||
import TableRow from "@material-ui/core/TableRow";
|
||||
import BuildIcon from "@material-ui/icons/Build";
|
||||
import Tooltip from "@material-ui/core/Tooltip";
|
||||
import CircularProgress from "@material-ui/core/CircularProgress";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import Card from "@material-ui/core/Card";
|
||||
import ExpandLess from "@material-ui/icons/ExpandLess";
|
||||
import ExpandMore from "@material-ui/icons/ExpandMore";
|
||||
import CardContent from "@material-ui/core/CardContent";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import DialogContent from "@material-ui/core/DialogContent";
|
||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||
import Dialog from "@material-ui/core/Dialog";
|
||||
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||
import DialogActions from "@material-ui/core/DialogActions";
|
||||
import AddIcon from "@material-ui/icons/Add";
|
||||
import List from "@material-ui/core/List";
|
||||
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
||||
import ListItem from "@material-ui/core/ListItem";
|
||||
import ListItemText from "@material-ui/core/ListItemText";
|
||||
import CheckCircleIcon from "@material-ui/icons/CheckCircle";
|
||||
import BN from "bn.js";
|
||||
import {
|
||||
Account,
|
||||
PublicKey,
|
||||
SYSVAR_RENT_PUBKEY,
|
||||
SYSVAR_CLOCK_PUBKEY,
|
||||
} from '@solana/web3.js';
|
||||
import * as anchor from '@project-serum/anchor';
|
||||
import { useWallet } from './WalletProvider';
|
||||
import { ViewTransactionOnExplorerButton } from './Notification';
|
||||
import * as idl from '../utils/idl';
|
||||
} from "@solana/web3.js";
|
||||
import * as anchor from "@project-serum/anchor";
|
||||
import { useWallet } from "./WalletProvider";
|
||||
import { ViewTransactionOnExplorerButton } from "./Notification";
|
||||
import * as idl from "../utils/idl";
|
||||
import { networks } from "../store/reducer";
|
||||
|
||||
export default function Multisig({ multisig }: { multisig?: PublicKey }) {
|
||||
return (
|
||||
<div>
|
||||
<Container fixed maxWidth="md">
|
||||
<div style={{
|
||||
position: 'fixed',
|
||||
bottom: '75px',
|
||||
right: '75px',
|
||||
display: 'flex',
|
||||
flexDirection: 'row-reverse',
|
||||
}}>
|
||||
<div
|
||||
style={{
|
||||
position: "fixed",
|
||||
bottom: "75px",
|
||||
right: "75px",
|
||||
display: "flex",
|
||||
flexDirection: "row-reverse",
|
||||
}}
|
||||
>
|
||||
<NewMultisigButton />
|
||||
</div>
|
||||
</Container>
|
||||
|
@ -75,15 +78,16 @@ export default function Multisig({ multisig }: { multisig?: PublicKey }) {
|
|||
function NewMultisigButton() {
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<div style={{ display: 'flex', }}>
|
||||
<div style={{ display: "flex" }}>
|
||||
<IconButton
|
||||
style={{
|
||||
border: 'solid 1pt #ccc',
|
||||
width: '60px',
|
||||
height: '60px',
|
||||
borderRadius: '30px',
|
||||
border: "solid 1pt #ccc",
|
||||
width: "60px",
|
||||
height: "60px",
|
||||
borderRadius: "30px",
|
||||
}}
|
||||
onClick={() => setOpen(true)}>
|
||||
onClick={() => setOpen(true)}
|
||||
>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
<NewMultisigDialog open={open} onClose={() => setOpen(false)} />
|
||||
|
@ -97,7 +101,7 @@ export function MultisigInstance({ multisig }: { multisig: PublicKey }) {
|
|||
const [transactions, setTransactions] = useState<any>(null);
|
||||
const [showSignerDialog, setShowSignerDialog] = useState(false);
|
||||
const [showAddTransactionDialog, setShowAddTransactionDialog] = useState(
|
||||
false,
|
||||
false
|
||||
);
|
||||
const [forceRefresh, setForceRefresh] = useState(false);
|
||||
useEffect(() => {
|
||||
|
@ -112,28 +116,28 @@ export function MultisigInstance({ multisig }: { multisig: PublicKey }) {
|
|||
});
|
||||
}, [multisig, multisigClient.account]);
|
||||
useEffect(() => {
|
||||
multisigClient.account.transaction.all(multisig.toBuffer()).then(txs => {
|
||||
multisigClient.account.transaction.all(multisig.toBuffer()).then((txs) => {
|
||||
setTransactions(txs);
|
||||
});
|
||||
}, [multisigClient.account.transaction, multisig, forceRefresh]);
|
||||
useEffect(() => {
|
||||
multisigClient.account.multisig
|
||||
.subscribe(multisig)
|
||||
.on('change', account => {
|
||||
.on("change", (account) => {
|
||||
setMultisigAccount(account);
|
||||
});
|
||||
}, [multisigClient, multisig]);
|
||||
return (
|
||||
<Container fixed maxWidth="md" style={{ marginBottom: '16px' }}>
|
||||
<Container fixed maxWidth="md" style={{ marginBottom: "16px" }}>
|
||||
<div>
|
||||
<Card style={{ marginTop: '24px' }}>
|
||||
<Card style={{ marginTop: "24px" }}>
|
||||
{multisigAccount === undefined ? (
|
||||
<div style={{ padding: '16px' }}>
|
||||
<div style={{ padding: "16px" }}>
|
||||
<CircularProgress
|
||||
style={{
|
||||
display: 'block',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
display: "block",
|
||||
marginLeft: "auto",
|
||||
marginRight: "auto",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -142,8 +146,8 @@ export function MultisigInstance({ multisig }: { multisig: PublicKey }) {
|
|||
<Typography
|
||||
color="textSecondary"
|
||||
style={{
|
||||
padding: '24px',
|
||||
textAlign: 'center',
|
||||
padding: "24px",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
Multisig not found
|
||||
|
@ -156,14 +160,14 @@ export function MultisigInstance({ multisig }: { multisig: PublicKey }) {
|
|||
{multisigAccount && (
|
||||
<Paper>
|
||||
<AppBar
|
||||
style={{ marginTop: '24px' }}
|
||||
style={{ marginTop: "24px" }}
|
||||
position="static"
|
||||
color="default"
|
||||
elevation={1}
|
||||
>
|
||||
<Toolbar>
|
||||
<Typography variant="h6" style={{ flexGrow: 1 }} component="h2">
|
||||
{multisig.toString()} | {multisigAccount.threshold.toString()}{' '}
|
||||
{multisig.toString()} | {multisigAccount.threshold.toString()}{" "}
|
||||
of {multisigAccount.owners.length.toString()} Multisig
|
||||
</Typography>
|
||||
<Tooltip title="Signer" arrow>
|
||||
|
@ -180,12 +184,12 @@ export function MultisigInstance({ multisig }: { multisig: PublicKey }) {
|
|||
</AppBar>
|
||||
<List disablePadding>
|
||||
{transactions === null ? (
|
||||
<div style={{ padding: '16px' }}>
|
||||
<div style={{ padding: "16px" }}>
|
||||
<CircularProgress
|
||||
style={{
|
||||
display: 'block',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
display: "block",
|
||||
marginLeft: "auto",
|
||||
marginRight: "auto",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -238,17 +242,15 @@ export function NewMultisigDialog({
|
|||
const [threshold, setThreshold] = useState(2);
|
||||
// @ts-ignore
|
||||
const zeroAddr = new PublicKey().toString();
|
||||
const [participants, setParticipants] = useState([
|
||||
zeroAddr,
|
||||
]);
|
||||
const [participants, setParticipants] = useState([zeroAddr]);
|
||||
const _onClose = () => {
|
||||
onClose();
|
||||
setThreshold(2);
|
||||
setParticipants([zeroAddr, zeroAddr]);
|
||||
};
|
||||
const createMultisig = async () => {
|
||||
enqueueSnackbar('Creating multisig', {
|
||||
variant: 'info',
|
||||
enqueueSnackbar("Creating multisig", {
|
||||
variant: "info",
|
||||
});
|
||||
const multisig = new Account();
|
||||
// Disc. + threshold + nonce.
|
||||
|
@ -258,9 +260,9 @@ export function NewMultisigDialog({
|
|||
const multisigSize = baseSize + ownerSize;
|
||||
const [, nonce] = await PublicKey.findProgramAddress(
|
||||
[multisig.publicKey.toBuffer()],
|
||||
multisigClient.programId,
|
||||
multisigClient.programId
|
||||
);
|
||||
const owners = participants.map(p => new PublicKey(p));
|
||||
const owners = participants.map((p) => new PublicKey(p));
|
||||
const tx = await multisigClient.rpc.createMultisig(
|
||||
owners,
|
||||
new BN(threshold),
|
||||
|
@ -275,13 +277,13 @@ export function NewMultisigDialog({
|
|||
await multisigClient.account.multisig.createInstruction(
|
||||
multisig,
|
||||
// @ts-ignore
|
||||
multisigSize,
|
||||
multisigSize
|
||||
),
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
enqueueSnackbar(`Multisig created: ${multisig.publicKey.toString()}`, {
|
||||
variant: 'success',
|
||||
variant: "success",
|
||||
action: <ViewTransactionOnExplorerButton signature={tx} />,
|
||||
});
|
||||
_onClose();
|
||||
|
@ -300,7 +302,7 @@ export function NewMultisigDialog({
|
|||
label="Threshold"
|
||||
value={threshold}
|
||||
type="number"
|
||||
onChange={e => setThreshold(parseInt(e.target.value) as number)}
|
||||
onChange={(e) => setThreshold(parseInt(e.target.value) as number)}
|
||||
/>
|
||||
{participants.map((p, idx) => (
|
||||
<TextField
|
||||
|
@ -308,14 +310,14 @@ export function NewMultisigDialog({
|
|||
fullWidth
|
||||
label="Participant"
|
||||
value={p}
|
||||
onChange={e => {
|
||||
onChange={(e) => {
|
||||
const p = [...participants];
|
||||
p[idx] = e.target.value;
|
||||
setParticipants(p);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
const p = [...participants];
|
||||
|
@ -335,10 +337,10 @@ export function NewMultisigDialog({
|
|||
type="submit"
|
||||
color="primary"
|
||||
onClick={() =>
|
||||
createMultisig().catch(err => {
|
||||
const str = err ? err.toString() : '';
|
||||
createMultisig().catch((err) => {
|
||||
const str = err ? err.toString() : "";
|
||||
enqueueSnackbar(`Error creating multisig: ${str}`, {
|
||||
variant: 'error',
|
||||
variant: "error",
|
||||
});
|
||||
})
|
||||
}
|
||||
|
@ -366,30 +368,30 @@ function TxListItem({
|
|||
useEffect(() => {
|
||||
multisigClient.account.transaction
|
||||
.subscribe(tx.publicKey)
|
||||
.on('change', account => {
|
||||
.on("change", (account) => {
|
||||
setTxAccount(account);
|
||||
});
|
||||
}, [multisigClient, multisig, tx.publicKey]);
|
||||
const rows = [
|
||||
{
|
||||
field: 'Program ID',
|
||||
field: "Program ID",
|
||||
value: txAccount.programId.toString(),
|
||||
},
|
||||
{
|
||||
field: 'Did execute',
|
||||
field: "Did execute",
|
||||
value: txAccount.didExecute.toString(),
|
||||
},
|
||||
{
|
||||
field: 'Instruction data',
|
||||
field: "Instruction data",
|
||||
value: (
|
||||
<code
|
||||
style={{
|
||||
wordBreak: 'break-word',
|
||||
width: '370px',
|
||||
background: 'black',
|
||||
color: '#ffffff',
|
||||
float: 'right',
|
||||
textAlign: 'left',
|
||||
wordBreak: "break-word",
|
||||
width: "370px",
|
||||
background: "black",
|
||||
color: "#ffffff",
|
||||
float: "right",
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
{encodeBase64(txAccount.data)}
|
||||
|
@ -397,15 +399,15 @@ function TxListItem({
|
|||
),
|
||||
},
|
||||
{
|
||||
field: 'Multisig',
|
||||
field: "Multisig",
|
||||
value: txAccount.multisig.toString(),
|
||||
},
|
||||
{
|
||||
field: 'Transaction account',
|
||||
field: "Transaction account",
|
||||
value: tx.publicKey.toString(),
|
||||
},
|
||||
{
|
||||
field: 'Owner set seqno',
|
||||
field: "Owner set seqno",
|
||||
value: txAccount.ownerSetSeqno.toString(),
|
||||
},
|
||||
];
|
||||
|
@ -415,11 +417,11 @@ function TxListItem({
|
|||
field: owner.toString(),
|
||||
value: txAccount.signers[idx] ? <CheckIcon /> : <RemoveIcon />,
|
||||
};
|
||||
},
|
||||
}
|
||||
);
|
||||
const approve = async () => {
|
||||
enqueueSnackbar('Approving transaction', {
|
||||
variant: 'info',
|
||||
enqueueSnackbar("Approving transaction", {
|
||||
variant: "info",
|
||||
});
|
||||
await multisigClient.rpc.approve({
|
||||
accounts: {
|
||||
|
@ -428,17 +430,17 @@ function TxListItem({
|
|||
owner: multisigClient.provider.wallet.publicKey,
|
||||
},
|
||||
});
|
||||
enqueueSnackbar('Transaction approved', {
|
||||
variant: 'success',
|
||||
enqueueSnackbar("Transaction approved", {
|
||||
variant: "success",
|
||||
});
|
||||
};
|
||||
const execute = async () => {
|
||||
enqueueSnackbar('Executing transaction', {
|
||||
variant: 'info',
|
||||
enqueueSnackbar("Executing transaction", {
|
||||
variant: "info",
|
||||
});
|
||||
const [multisigSigner] = await PublicKey.findProgramAddress(
|
||||
[multisig.toBuffer()],
|
||||
multisigClient.programId,
|
||||
multisigClient.programId
|
||||
);
|
||||
await multisigClient.rpc.executeTransaction({
|
||||
accounts: {
|
||||
|
@ -459,8 +461,8 @@ function TxListItem({
|
|||
isSigner: false,
|
||||
}),
|
||||
});
|
||||
enqueueSnackbar('Transaction executed', {
|
||||
variant: 'success',
|
||||
enqueueSnackbar("Transaction executed", {
|
||||
variant: "success",
|
||||
});
|
||||
};
|
||||
return (
|
||||
|
@ -469,25 +471,25 @@ function TxListItem({
|
|||
<ListItemIcon>{icon(tx, multisigClient)}</ListItemIcon>
|
||||
{ixLabel(tx, multisigClient)}
|
||||
{txAccount.didExecute && (
|
||||
<CheckCircleIcon style={{ marginRight: '16px' }} />
|
||||
<CheckCircleIcon style={{ marginRight: "16px" }} />
|
||||
)}
|
||||
{open ? <ExpandLess /> : <ExpandMore />}
|
||||
</ListItem>
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
<div style={{ background: '#ececec', padding: '10px' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||
<div style={{ background: "#ececec", padding: "10px" }}>
|
||||
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
||||
<Button
|
||||
style={{ marginRight: '10px' }}
|
||||
style={{ marginRight: "10px" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() =>
|
||||
approve().catch(err => {
|
||||
let errStr = '';
|
||||
approve().catch((err) => {
|
||||
let errStr = "";
|
||||
if (err) {
|
||||
errStr = err.toString();
|
||||
}
|
||||
enqueueSnackbar(`Unable to approve transaction: ${errStr}`, {
|
||||
variant: 'error',
|
||||
variant: "error",
|
||||
});
|
||||
})
|
||||
}
|
||||
|
@ -498,13 +500,13 @@ function TxListItem({
|
|||
variant="contained"
|
||||
color="secondary"
|
||||
onClick={() =>
|
||||
execute().catch(err => {
|
||||
let errStr = '';
|
||||
execute().catch((err) => {
|
||||
let errStr = "";
|
||||
if (err) {
|
||||
errStr = err.toString();
|
||||
}
|
||||
enqueueSnackbar(`Unable to execute transaction: ${errStr}`, {
|
||||
variant: 'error',
|
||||
variant: "error",
|
||||
});
|
||||
})
|
||||
}
|
||||
|
@ -512,7 +514,7 @@ function TxListItem({
|
|||
Execute
|
||||
</Button>
|
||||
</div>
|
||||
<Card style={{ marginTop: '16px' }}>
|
||||
<Card style={{ marginTop: "16px" }}>
|
||||
<CardContent>
|
||||
<Table>
|
||||
<TableHead>
|
||||
|
@ -522,7 +524,7 @@ function TxListItem({
|
|||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{rows.map(r => (
|
||||
{rows.map((r) => (
|
||||
<TableRow>
|
||||
<TableCell>{r.field}</TableCell>
|
||||
<TableCell align="right">{r.value}</TableCell>
|
||||
|
@ -532,7 +534,7 @@ function TxListItem({
|
|||
</Table>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card style={{ marginTop: '16px' }}>
|
||||
<Card style={{ marginTop: "16px" }}>
|
||||
<CardContent>
|
||||
<Table>
|
||||
<TableHead>
|
||||
|
@ -552,10 +554,10 @@ function TxListItem({
|
|||
</TableBody>
|
||||
</Table>
|
||||
{txAccount.ownerSetSeqno !== multisigAccount.ownerSetSeqno && (
|
||||
<div style={{ marginTop: '16px' }}>
|
||||
<div style={{ marginTop: "16px" }}>
|
||||
<Typography
|
||||
color="textSecondary"
|
||||
style={{ textAlign: 'center' }}
|
||||
style={{ textAlign: "center" }}
|
||||
>
|
||||
The owner set has changed since this transaction was created
|
||||
</Typography>
|
||||
|
@ -563,7 +565,7 @@ function TxListItem({
|
|||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card style={{ marginTop: '16px' }}>
|
||||
<Card style={{ marginTop: "16px" }}>
|
||||
<CardContent>
|
||||
<AccountsList accounts={txAccount.accounts} />
|
||||
</CardContent>
|
||||
|
@ -588,8 +590,8 @@ function ixLabel(tx: any, multisigClient: any) {
|
|||
}
|
||||
if (tx.account.programId.equals(multisigClient.programId)) {
|
||||
const setThresholdSighash = multisigClient.coder.sighash(
|
||||
'global',
|
||||
'change_threshold',
|
||||
"global",
|
||||
"change_threshold"
|
||||
);
|
||||
if (setThresholdSighash.equals(tx.account.data.slice(0, 8))) {
|
||||
return (
|
||||
|
@ -600,8 +602,8 @@ function ixLabel(tx: any, multisigClient: any) {
|
|||
);
|
||||
}
|
||||
const setOwnersSighash = multisigClient.coder.sighash(
|
||||
'global',
|
||||
'set_owners',
|
||||
"global",
|
||||
"set_owners"
|
||||
);
|
||||
if (setOwnersSighash.equals(tx.account.data.slice(0, 8))) {
|
||||
return (
|
||||
|
@ -659,8 +661,8 @@ function SignerDialog({
|
|||
useEffect(() => {
|
||||
PublicKey.findProgramAddress(
|
||||
[multisig.toBuffer()],
|
||||
multisigClient.programId,
|
||||
).then(addrNonce => setSigner(addrNonce[0].toString()));
|
||||
multisigClient.programId
|
||||
).then((addrNonce) => setSigner(addrNonce[0].toString()));
|
||||
}, [multisig, multisigClient.programId, setSigner]);
|
||||
return (
|
||||
<Dialog open={open} fullWidth onClose={onClose} maxWidth="md">
|
||||
|
@ -669,7 +671,13 @@ function SignerDialog({
|
|||
Multisig Info
|
||||
</Typography>
|
||||
</DialogTitle>
|
||||
<DialogContent style={{ paddingBottom: '16px' }}>
|
||||
<DialogContent style={{ paddingBottom: "16px" }}>
|
||||
{multisig?.equals(networks.mainnet.multisigUpgradeAuthority!) && (
|
||||
<DialogContentText>
|
||||
This multisig is the upgrade authority for the multisig program
|
||||
itself.
|
||||
</DialogContentText>
|
||||
)}
|
||||
<DialogContentText>
|
||||
<b>Program derived address</b>: {signer}. This is the address one
|
||||
should use as the authority for data governed by the multisig.
|
||||
|
@ -714,7 +722,7 @@ function AddTransactionDialog({
|
|||
New Transaction
|
||||
</Typography>
|
||||
</DialogTitle>
|
||||
<DialogContent style={{ paddingBottom: '16px' }}>
|
||||
<DialogContent style={{ paddingBottom: "16px" }}>
|
||||
<DialogContentText>
|
||||
Create a new transaction to be signed by the multisig. This
|
||||
transaction will not execute until enough owners have signed the
|
||||
|
@ -759,11 +767,11 @@ function ChangeThresholdListItem({
|
|||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<ListItem button onClick={() => setOpen(open => !open)}>
|
||||
<ListItem button onClick={() => setOpen((open) => !open)}>
|
||||
<ListItemIcon>
|
||||
<GavelIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={'Change threshold'} />
|
||||
<ListItemText primary={"Change threshold"} />
|
||||
{open ? <ExpandLess /> : <ExpandMore />}
|
||||
</ListItem>
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
|
@ -791,13 +799,13 @@ function ChangeThresholdListItemDetails({
|
|||
// @ts-ignore
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const changeThreshold = async () => {
|
||||
enqueueSnackbar('Creating change threshold transaction', {
|
||||
variant: 'info',
|
||||
enqueueSnackbar("Creating change threshold transaction", {
|
||||
variant: "info",
|
||||
});
|
||||
const data = changeThresholdData(multisigClient, threshold);
|
||||
const [multisigSigner] = await PublicKey.findProgramAddress(
|
||||
[multisig.toBuffer()],
|
||||
multisigClient.programId,
|
||||
multisigClient.programId
|
||||
);
|
||||
const accounts = [
|
||||
{
|
||||
|
@ -829,13 +837,13 @@ function ChangeThresholdListItemDetails({
|
|||
await multisigClient.account.transaction.createInstruction(
|
||||
transaction,
|
||||
// @ts-ignore
|
||||
txSize,
|
||||
txSize
|
||||
),
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
enqueueSnackbar('Transaction created', {
|
||||
variant: 'success',
|
||||
enqueueSnackbar("Transaction created", {
|
||||
variant: "success",
|
||||
action: <ViewTransactionOnExplorerButton signature={tx} />,
|
||||
});
|
||||
didAddTransaction(transaction.publicKey);
|
||||
|
@ -844,23 +852,23 @@ function ChangeThresholdListItemDetails({
|
|||
return (
|
||||
<div
|
||||
style={{
|
||||
background: '#f1f0f0',
|
||||
paddingLeft: '24px',
|
||||
paddingRight: '24px',
|
||||
background: "#f1f0f0",
|
||||
paddingLeft: "24px",
|
||||
paddingRight: "24px",
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
fullWidth
|
||||
style={{ marginTop: '16px' }}
|
||||
style={{ marginTop: "16px" }}
|
||||
label="Threshold"
|
||||
value={threshold}
|
||||
type="number"
|
||||
onChange={e => {
|
||||
onChange={(e) => {
|
||||
// @ts-ignore
|
||||
setThreshold(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
||||
<Button onClick={() => changeThreshold()}>Change Threshold</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -879,11 +887,11 @@ function MultisigSetOwnersListItem({
|
|||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<ListItem button onClick={() => setOpen(open => !open)}>
|
||||
<ListItem button onClick={() => setOpen((open) => !open)}>
|
||||
<ListItemIcon>
|
||||
<SupervisorAccountIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={'Set owners'} />
|
||||
<ListItemText primary={"Set owners"} />
|
||||
{open ? <ExpandLess /> : <ExpandMore />}
|
||||
</ListItem>
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
|
@ -909,19 +917,17 @@ function SetOwnersListItemDetails({
|
|||
const { multisigClient } = useWallet();
|
||||
// @ts-ignore
|
||||
const zeroAddr = new PublicKey().toString();
|
||||
const [participants, setParticipants] = useState([
|
||||
zeroAddr,
|
||||
]);
|
||||
const [participants, setParticipants] = useState([zeroAddr]);
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const setOwners = async () => {
|
||||
enqueueSnackbar('Creating setOwners transaction', {
|
||||
variant: 'info',
|
||||
enqueueSnackbar("Creating setOwners transaction", {
|
||||
variant: "info",
|
||||
});
|
||||
const owners = participants.map(p => new PublicKey(p));
|
||||
const owners = participants.map((p) => new PublicKey(p));
|
||||
const data = setOwnersData(multisigClient, owners);
|
||||
const [multisigSigner] = await PublicKey.findProgramAddress(
|
||||
[multisig.toBuffer()],
|
||||
multisigClient.programId,
|
||||
multisigClient.programId
|
||||
);
|
||||
const accounts = [
|
||||
{
|
||||
|
@ -953,13 +959,13 @@ function SetOwnersListItemDetails({
|
|||
await multisigClient.account.transaction.createInstruction(
|
||||
transaction,
|
||||
// @ts-ignore
|
||||
txSize,
|
||||
txSize
|
||||
),
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
enqueueSnackbar('Transaction created', {
|
||||
variant: 'success',
|
||||
enqueueSnackbar("Transaction created", {
|
||||
variant: "success",
|
||||
action: <ViewTransactionOnExplorerButton signature={tx} />,
|
||||
});
|
||||
didAddTransaction(transaction.publicKey);
|
||||
|
@ -968,25 +974,25 @@ function SetOwnersListItemDetails({
|
|||
return (
|
||||
<div
|
||||
style={{
|
||||
background: '#f1f0f0',
|
||||
paddingLeft: '24px',
|
||||
paddingRight: '24px',
|
||||
background: "#f1f0f0",
|
||||
paddingLeft: "24px",
|
||||
paddingRight: "24px",
|
||||
}}
|
||||
>
|
||||
{participants.map((p, idx) => (
|
||||
<TextField
|
||||
fullWidth
|
||||
style={{ marginTop: '16px' }}
|
||||
style={{ marginTop: "16px" }}
|
||||
label="Participant"
|
||||
value={p}
|
||||
onChange={e => {
|
||||
onChange={(e) => {
|
||||
const p = [...participants];
|
||||
p[idx] = e.target.value;
|
||||
setParticipants(p);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
const p = [...participants];
|
||||
|
@ -1000,10 +1006,10 @@ function SetOwnersListItemDetails({
|
|||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
marginTop: '16px',
|
||||
paddingBottom: '16px',
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
marginTop: "16px",
|
||||
paddingBottom: "16px",
|
||||
}}
|
||||
>
|
||||
<Button onClick={() => setOwners()}>Set Owners</Button>
|
||||
|
@ -1024,11 +1030,11 @@ function IdlUpgradeListItem({
|
|||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<ListItem button onClick={() => setOpen(open => !open)}>
|
||||
<ListItem button onClick={() => setOpen((open) => !open)}>
|
||||
<ListItemIcon>
|
||||
<DescriptionIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={'Upgrade IDL'} />
|
||||
<ListItemText primary={"Upgrade IDL"} />
|
||||
{open ? <ExpandLess /> : <ExpandMore />}
|
||||
</ListItem>
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
|
@ -1057,15 +1063,15 @@ function UpgradeIdlListItemDetails({
|
|||
const { multisigClient } = useWallet();
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const createTransactionAccount = async () => {
|
||||
enqueueSnackbar('Creating transaction', {
|
||||
variant: 'info',
|
||||
enqueueSnackbar("Creating transaction", {
|
||||
variant: "info",
|
||||
});
|
||||
const programAddr = new PublicKey(programId as string);
|
||||
const bufferAddr = new PublicKey(buffer as string);
|
||||
const idlAddr = await anchor.utils.idlAddress(programAddr);
|
||||
const [multisigSigner] = await PublicKey.findProgramAddress(
|
||||
[multisig.toBuffer()],
|
||||
multisigClient.programId,
|
||||
multisigClient.programId
|
||||
);
|
||||
const data = idl.encodeInstruction({ setBuffer: {} });
|
||||
const accs = [
|
||||
|
@ -1095,13 +1101,13 @@ function UpgradeIdlListItemDetails({
|
|||
await multisigClient.account.transaction.createInstruction(
|
||||
transaction,
|
||||
// @ts-ignore
|
||||
txSize,
|
||||
txSize
|
||||
),
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
enqueueSnackbar('Transaction created', {
|
||||
variant: 'success',
|
||||
enqueueSnackbar("Transaction created", {
|
||||
variant: "success",
|
||||
action: <ViewTransactionOnExplorerButton signature={tx} />,
|
||||
});
|
||||
didAddTransaction(transaction.publicKey);
|
||||
|
@ -1111,31 +1117,31 @@ function UpgradeIdlListItemDetails({
|
|||
return (
|
||||
<div
|
||||
style={{
|
||||
background: '#f1f0f0',
|
||||
paddingLeft: '24px',
|
||||
paddingRight: '24px',
|
||||
background: "#f1f0f0",
|
||||
paddingLeft: "24px",
|
||||
paddingRight: "24px",
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
fullWidth
|
||||
style={{ marginTop: '16px' }}
|
||||
style={{ marginTop: "16px" }}
|
||||
label="Program ID"
|
||||
value={programId}
|
||||
onChange={e => setProgramId(e.target.value as string)}
|
||||
onChange={(e) => setProgramId(e.target.value as string)}
|
||||
/>
|
||||
<TextField
|
||||
style={{ marginTop: '16px' }}
|
||||
style={{ marginTop: "16px" }}
|
||||
fullWidth
|
||||
label="New IDL buffer"
|
||||
value={buffer}
|
||||
onChange={e => setBuffer(e.target.value as string)}
|
||||
onChange={(e) => setBuffer(e.target.value as string)}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
marginTop: '16px',
|
||||
paddingBottom: '16px',
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
marginTop: "16px",
|
||||
paddingBottom: "16px",
|
||||
}}
|
||||
>
|
||||
<Button onClick={() => createTransactionAccount()}>
|
||||
|
@ -1158,11 +1164,11 @@ function ProgramUpdateListItem({
|
|||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<ListItem button onClick={() => setOpen(open => !open)}>
|
||||
<ListItem button onClick={() => setOpen((open) => !open)}>
|
||||
<ListItemIcon>
|
||||
<BuildIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={'Upgrade program'} />
|
||||
<ListItemText primary={"Upgrade program"} />
|
||||
{open ? <ExpandLess /> : <ExpandMore />}
|
||||
</ListItem>
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
|
@ -1177,7 +1183,7 @@ function ProgramUpdateListItem({
|
|||
}
|
||||
|
||||
const BPF_LOADER_UPGRADEABLE_PID = new PublicKey(
|
||||
'BPFLoaderUpgradeab1e11111111111111111111111',
|
||||
"BPFLoaderUpgradeab1e11111111111111111111111"
|
||||
);
|
||||
|
||||
function UpgradeProgramListItemDetails({
|
||||
|
@ -1195,8 +1201,8 @@ function UpgradeProgramListItemDetails({
|
|||
const { multisigClient } = useWallet();
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const createTransactionAccount = async () => {
|
||||
enqueueSnackbar('Creating transaction', {
|
||||
variant: 'info',
|
||||
enqueueSnackbar("Creating transaction", {
|
||||
variant: "info",
|
||||
});
|
||||
const programAddr = new PublicKey(programId as string);
|
||||
const bufferAddr = new PublicKey(buffer as string);
|
||||
|
@ -1205,10 +1211,10 @@ function UpgradeProgramListItemDetails({
|
|||
|
||||
const programAccount = await (async () => {
|
||||
const programAccount = await multisigClient.provider.connection.getAccountInfo(
|
||||
programAddr,
|
||||
programAddr
|
||||
);
|
||||
if (programAccount === null) {
|
||||
throw new Error('Invalid program ID');
|
||||
throw new Error("Invalid program ID");
|
||||
}
|
||||
return {
|
||||
// Hard code deserialization.
|
||||
|
@ -1218,7 +1224,7 @@ function UpgradeProgramListItemDetails({
|
|||
const spill = multisigClient.provider.wallet.publicKey;
|
||||
const [multisigSigner] = await PublicKey.findProgramAddress(
|
||||
[multisig.toBuffer()],
|
||||
multisigClient.programId,
|
||||
multisigClient.programId
|
||||
);
|
||||
const accs = [
|
||||
{
|
||||
|
@ -1251,13 +1257,13 @@ function UpgradeProgramListItemDetails({
|
|||
await multisigClient.account.transaction.createInstruction(
|
||||
transaction,
|
||||
// @ts-ignore
|
||||
txSize,
|
||||
txSize
|
||||
),
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
enqueueSnackbar('Transaction created', {
|
||||
variant: 'success',
|
||||
enqueueSnackbar("Transaction created", {
|
||||
variant: "success",
|
||||
action: <ViewTransactionOnExplorerButton signature={tx} />,
|
||||
});
|
||||
didAddTransaction(transaction.publicKey);
|
||||
|
@ -1267,31 +1273,31 @@ function UpgradeProgramListItemDetails({
|
|||
return (
|
||||
<div
|
||||
style={{
|
||||
background: '#f1f0f0',
|
||||
paddingLeft: '24px',
|
||||
paddingRight: '24px',
|
||||
background: "#f1f0f0",
|
||||
paddingLeft: "24px",
|
||||
paddingRight: "24px",
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
fullWidth
|
||||
style={{ marginTop: '16px' }}
|
||||
style={{ marginTop: "16px" }}
|
||||
label="Program ID"
|
||||
value={programId}
|
||||
onChange={e => setProgramId(e.target.value as string)}
|
||||
onChange={(e) => setProgramId(e.target.value as string)}
|
||||
/>
|
||||
<TextField
|
||||
style={{ marginTop: '16px' }}
|
||||
style={{ marginTop: "16px" }}
|
||||
fullWidth
|
||||
label="New program buffer"
|
||||
value={buffer}
|
||||
onChange={e => setBuffer(e.target.value as string)}
|
||||
onChange={(e) => setBuffer(e.target.value as string)}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
marginTop: '16px',
|
||||
paddingBottom: '16px',
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
marginTop: "16px",
|
||||
paddingBottom: "16px",
|
||||
}}
|
||||
>
|
||||
<Button onClick={() => createTransactionAccount()}>
|
||||
|
@ -1309,15 +1315,15 @@ function icon(tx, multisigClient) {
|
|||
}
|
||||
if (tx.account.programId.equals(multisigClient.programId)) {
|
||||
const setThresholdSighash = multisigClient.coder.sighash(
|
||||
'global',
|
||||
'change_threshold',
|
||||
"global",
|
||||
"change_threshold"
|
||||
);
|
||||
if (setThresholdSighash.equals(tx.account.data.slice(0, 8))) {
|
||||
return <GavelIcon />;
|
||||
}
|
||||
const setOwnersSighash = multisigClient.coder.sighash(
|
||||
'global',
|
||||
'set_owners',
|
||||
"global",
|
||||
"set_owners"
|
||||
);
|
||||
if (setOwnersSighash.equals(tx.account.data.slice(0, 8))) {
|
||||
return <SupervisorAccountIcon />;
|
||||
|
@ -1331,14 +1337,14 @@ function icon(tx, multisigClient) {
|
|||
|
||||
// @ts-ignore
|
||||
function changeThresholdData(multisigClient, threshold) {
|
||||
return multisigClient.coder.instruction.encode('change_threshold', {
|
||||
return multisigClient.coder.instruction.encode("change_threshold", {
|
||||
threshold: new BN(threshold),
|
||||
});
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
function setOwnersData(multisigClient, owners) {
|
||||
return multisigClient.coder.instruction.encode('set_owners', {
|
||||
return multisigClient.coder.instruction.encode("set_owners", {
|
||||
owners,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import { TransactionSignature } from '@solana/web3.js';
|
||||
import { State as StoreState } from '../store/reducer';
|
||||
import React from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import { TransactionSignature } from "@solana/web3.js";
|
||||
import { State as StoreState } from "../store/reducer";
|
||||
|
||||
type Props = {
|
||||
signature: string;
|
||||
|
@ -12,21 +12,21 @@ export async function withTx(
|
|||
snack: any,
|
||||
beforeLabel: string,
|
||||
afterLabel: string,
|
||||
execTx: () => Promise<TransactionSignature>,
|
||||
execTx: () => Promise<TransactionSignature>
|
||||
) {
|
||||
snack.enqueueSnackbar(beforeLabel, {
|
||||
variant: 'info',
|
||||
variant: "info",
|
||||
});
|
||||
try {
|
||||
let tx = await execTx();
|
||||
snack.closeSnackbar();
|
||||
snack.enqueueSnackbar(afterLabel, {
|
||||
variant: 'success',
|
||||
variant: "success",
|
||||
action: <ViewTransactionOnExplorerButton signature={tx} />,
|
||||
});
|
||||
} catch (err) {
|
||||
snack.enqueueSnackbar(`Error: ${err.toString()}`, {
|
||||
variant: 'error',
|
||||
variant: "error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,20 +4,20 @@ import React, {
|
|||
ReactNode,
|
||||
useMemo,
|
||||
useContext,
|
||||
} from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { Connection, ConfirmOptions } from '@solana/web3.js';
|
||||
} from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { Connection, ConfirmOptions } from "@solana/web3.js";
|
||||
// @ts-ignore
|
||||
import Wallet from '@project-serum/sol-wallet-adapter';
|
||||
import { Provider } from '@project-serum/common';
|
||||
import { Program } from '@project-serum/anchor';
|
||||
import { State as StoreState } from '../store/reducer';
|
||||
import MultisigIdl from '../idl';
|
||||
import Wallet from "@project-serum/sol-wallet-adapter";
|
||||
import { Provider } from "@project-serum/common";
|
||||
import { Program } from "@project-serum/anchor";
|
||||
import { State as StoreState } from "../store/reducer";
|
||||
import MultisigIdl from "../idl";
|
||||
|
||||
export function useWallet(): WalletContextValues {
|
||||
const w = useContext(WalletContext);
|
||||
if (!w) {
|
||||
throw new Error('Missing wallet context');
|
||||
throw new Error("Missing wallet context");
|
||||
}
|
||||
// @ts-ignore
|
||||
return w;
|
||||
|
@ -31,7 +31,7 @@ type WalletContextValues = {
|
|||
};
|
||||
|
||||
export default function WalletProvider(
|
||||
props: PropsWithChildren<ReactNode>,
|
||||
props: PropsWithChildren<ReactNode>
|
||||
): ReactElement {
|
||||
const { walletProvider, network } = useSelector((state: StoreState) => {
|
||||
return {
|
||||
|
@ -40,13 +40,10 @@ export default function WalletProvider(
|
|||
};
|
||||
});
|
||||
|
||||
const {
|
||||
wallet,
|
||||
multisigClient,
|
||||
} = useMemo(() => {
|
||||
const { wallet, multisigClient } = useMemo(() => {
|
||||
const opts: ConfirmOptions = {
|
||||
preflightCommitment: 'recent',
|
||||
commitment: 'recent',
|
||||
preflightCommitment: "recent",
|
||||
commitment: "recent",
|
||||
};
|
||||
const connection = new Connection(network.url, opts.preflightCommitment);
|
||||
const wallet = new Wallet(walletProvider, network.url);
|
||||
|
@ -55,7 +52,7 @@ export default function WalletProvider(
|
|||
const multisigClient = new Program(
|
||||
MultisigIdl,
|
||||
network.multisigProgramId,
|
||||
provider,
|
||||
provider
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -65,9 +62,7 @@ export default function WalletProvider(
|
|||
}, [walletProvider, network]);
|
||||
|
||||
return (
|
||||
<WalletContext.Provider
|
||||
value={{ wallet, multisigClient }}
|
||||
>
|
||||
<WalletContext.Provider value={{ wallet, multisigClient }}>
|
||||
{props.children}
|
||||
</WalletContext.Provider>
|
||||
);
|
||||
|
|
464
src/idl/index.ts
464
src/idl/index.ts
|
@ -1,299 +1,299 @@
|
|||
import { Idl } from '@project-serum/anchor';
|
||||
import { Idl } from "@project-serum/anchor";
|
||||
|
||||
const idl: Idl = {
|
||||
"version": "0.0.0",
|
||||
"name": "multisig",
|
||||
"instructions": [
|
||||
version: "0.0.0",
|
||||
name: "multisig",
|
||||
instructions: [
|
||||
{
|
||||
"name": "createMultisig",
|
||||
"accounts": [
|
||||
name: "createMultisig",
|
||||
accounts: [
|
||||
{
|
||||
"name": "multisig",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
name: "multisig",
|
||||
isMut: true,
|
||||
isSigner: false,
|
||||
},
|
||||
{
|
||||
"name": "rent",
|
||||
"isMut": false,
|
||||
"isSigner": false
|
||||
}
|
||||
name: "rent",
|
||||
isMut: false,
|
||||
isSigner: false,
|
||||
},
|
||||
],
|
||||
"args": [
|
||||
args: [
|
||||
{
|
||||
"name": "owners",
|
||||
"type": {
|
||||
"vec": "publicKey"
|
||||
}
|
||||
name: "owners",
|
||||
type: {
|
||||
vec: "publicKey",
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "threshold",
|
||||
"type": "u64"
|
||||
name: "threshold",
|
||||
type: "u64",
|
||||
},
|
||||
{
|
||||
"name": "nonce",
|
||||
"type": "u8"
|
||||
}
|
||||
]
|
||||
name: "nonce",
|
||||
type: "u8",
|
||||
},
|
||||
{
|
||||
"name": "createTransaction",
|
||||
"accounts": [
|
||||
{
|
||||
"name": "multisig",
|
||||
"isMut": false,
|
||||
"isSigner": false
|
||||
},
|
||||
{
|
||||
"name": "transaction",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
},
|
||||
{
|
||||
"name": "proposer",
|
||||
"isMut": false,
|
||||
"isSigner": true
|
||||
},
|
||||
{
|
||||
"name": "rent",
|
||||
"isMut": false,
|
||||
"isSigner": false
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "pid",
|
||||
"type": "publicKey"
|
||||
},
|
||||
{
|
||||
"name": "accs",
|
||||
"type": {
|
||||
"vec": {
|
||||
"defined": "TransactionAccount"
|
||||
}
|
||||
}
|
||||
name: "createTransaction",
|
||||
accounts: [
|
||||
{
|
||||
name: "multisig",
|
||||
isMut: false,
|
||||
isSigner: false,
|
||||
},
|
||||
{
|
||||
"name": "data",
|
||||
"type": "bytes"
|
||||
}
|
||||
]
|
||||
name: "transaction",
|
||||
isMut: true,
|
||||
isSigner: false,
|
||||
},
|
||||
{
|
||||
"name": "approve",
|
||||
"accounts": [
|
||||
{
|
||||
"name": "multisig",
|
||||
"isMut": false,
|
||||
"isSigner": false
|
||||
name: "proposer",
|
||||
isMut: false,
|
||||
isSigner: true,
|
||||
},
|
||||
{
|
||||
"name": "transaction",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
name: "rent",
|
||||
isMut: false,
|
||||
isSigner: false,
|
||||
},
|
||||
{
|
||||
"name": "owner",
|
||||
"isMut": false,
|
||||
"isSigner": true
|
||||
}
|
||||
],
|
||||
"args": []
|
||||
args: [
|
||||
{
|
||||
name: "pid",
|
||||
type: "publicKey",
|
||||
},
|
||||
{
|
||||
"name": "setOwners",
|
||||
"accounts": [
|
||||
{
|
||||
"name": "multisig",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
name: "accs",
|
||||
type: {
|
||||
vec: {
|
||||
defined: "TransactionAccount",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "multisigSigner",
|
||||
"isMut": false,
|
||||
"isSigner": true
|
||||
}
|
||||
name: "data",
|
||||
type: "bytes",
|
||||
},
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "owners",
|
||||
"type": {
|
||||
"vec": "publicKey"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "changeThreshold",
|
||||
"accounts": [
|
||||
name: "approve",
|
||||
accounts: [
|
||||
{
|
||||
"name": "multisig",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
name: "multisig",
|
||||
isMut: false,
|
||||
isSigner: false,
|
||||
},
|
||||
{
|
||||
"name": "multisigSigner",
|
||||
"isMut": false,
|
||||
"isSigner": true
|
||||
}
|
||||
name: "transaction",
|
||||
isMut: true,
|
||||
isSigner: false,
|
||||
},
|
||||
{
|
||||
name: "owner",
|
||||
isMut: false,
|
||||
isSigner: true,
|
||||
},
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "threshold",
|
||||
"type": "u64"
|
||||
}
|
||||
]
|
||||
args: [],
|
||||
},
|
||||
{
|
||||
"name": "executeTransaction",
|
||||
"accounts": [
|
||||
name: "setOwners",
|
||||
accounts: [
|
||||
{
|
||||
"name": "multisig",
|
||||
"isMut": false,
|
||||
"isSigner": false
|
||||
name: "multisig",
|
||||
isMut: true,
|
||||
isSigner: false,
|
||||
},
|
||||
{
|
||||
"name": "multisigSigner",
|
||||
"isMut": false,
|
||||
"isSigner": false
|
||||
name: "multisigSigner",
|
||||
isMut: false,
|
||||
isSigner: true,
|
||||
},
|
||||
{
|
||||
"name": "transaction",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
}
|
||||
],
|
||||
"args": []
|
||||
}
|
||||
args: [
|
||||
{
|
||||
name: "owners",
|
||||
type: {
|
||||
vec: "publicKey",
|
||||
},
|
||||
},
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"name": "Multisig",
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"fields": [
|
||||
{
|
||||
"name": "owners",
|
||||
"type": {
|
||||
"vec": "publicKey"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "threshold",
|
||||
"type": "u64"
|
||||
name: "changeThreshold",
|
||||
accounts: [
|
||||
{
|
||||
name: "multisig",
|
||||
isMut: true,
|
||||
isSigner: false,
|
||||
},
|
||||
{
|
||||
"name": "nonce",
|
||||
"type": "u8"
|
||||
name: "multisigSigner",
|
||||
isMut: false,
|
||||
isSigner: true,
|
||||
},
|
||||
{
|
||||
"name": "ownerSetSeqno",
|
||||
"type": "u32"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Transaction",
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"fields": [
|
||||
{
|
||||
"name": "multisig",
|
||||
"type": "publicKey"
|
||||
},
|
||||
{
|
||||
"name": "programId",
|
||||
"type": "publicKey"
|
||||
},
|
||||
{
|
||||
"name": "accounts",
|
||||
"type": {
|
||||
"vec": {
|
||||
"defined": "TransactionAccount"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "data",
|
||||
"type": "bytes"
|
||||
},
|
||||
{
|
||||
"name": "signers",
|
||||
"type": {
|
||||
"vec": "bool"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "didExecute",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"name": "ownerSetSeqno",
|
||||
"type": "u32"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"types": [
|
||||
args: [
|
||||
{
|
||||
"name": "TransactionAccount",
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"fields": [
|
||||
{
|
||||
"name": "pubkey",
|
||||
"type": "publicKey"
|
||||
name: "threshold",
|
||||
type: "u64",
|
||||
},
|
||||
{
|
||||
"name": "isSigner",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"name": "isWritable",
|
||||
"type": "bool"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"errors": [
|
||||
{
|
||||
"code": 100,
|
||||
"name": "InvalidOwner",
|
||||
"msg": "The given owner is not part of this multisig."
|
||||
},
|
||||
{
|
||||
"code": 101,
|
||||
"name": "NotEnoughSigners",
|
||||
"msg": "Not enough owners signed this transaction."
|
||||
name: "executeTransaction",
|
||||
accounts: [
|
||||
{
|
||||
name: "multisig",
|
||||
isMut: false,
|
||||
isSigner: false,
|
||||
},
|
||||
{
|
||||
"code": 102,
|
||||
"name": "TransactionAlreadySigned",
|
||||
"msg": "Cannot delete a transaction that has been signed by an owner."
|
||||
name: "multisigSigner",
|
||||
isMut: false,
|
||||
isSigner: false,
|
||||
},
|
||||
{
|
||||
"code": 103,
|
||||
"name": "Overflow",
|
||||
"msg": "Overflow when adding."
|
||||
name: "transaction",
|
||||
isMut: true,
|
||||
isSigner: false,
|
||||
},
|
||||
],
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
accounts: [
|
||||
{
|
||||
name: "Multisig",
|
||||
type: {
|
||||
kind: "struct",
|
||||
fields: [
|
||||
{
|
||||
name: "owners",
|
||||
type: {
|
||||
vec: "publicKey",
|
||||
},
|
||||
},
|
||||
{
|
||||
"code": 104,
|
||||
"name": "UnableToDelete",
|
||||
"msg": "Cannot delete a transaction the owner did not create."
|
||||
name: "threshold",
|
||||
type: "u64",
|
||||
},
|
||||
{
|
||||
"code": 105,
|
||||
"name": "AlreadyExecuted",
|
||||
"msg": "The given transaction has already been executed."
|
||||
name: "nonce",
|
||||
type: "u8",
|
||||
},
|
||||
{
|
||||
"code": 106,
|
||||
"name": "InvalidThreshold",
|
||||
"msg": "Threshold must be less than or equal to the number of owners."
|
||||
}
|
||||
]
|
||||
name: "ownerSetSeqno",
|
||||
type: "u32",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Transaction",
|
||||
type: {
|
||||
kind: "struct",
|
||||
fields: [
|
||||
{
|
||||
name: "multisig",
|
||||
type: "publicKey",
|
||||
},
|
||||
{
|
||||
name: "programId",
|
||||
type: "publicKey",
|
||||
},
|
||||
{
|
||||
name: "accounts",
|
||||
type: {
|
||||
vec: {
|
||||
defined: "TransactionAccount",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "data",
|
||||
type: "bytes",
|
||||
},
|
||||
{
|
||||
name: "signers",
|
||||
type: {
|
||||
vec: "bool",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "didExecute",
|
||||
type: "bool",
|
||||
},
|
||||
{
|
||||
name: "ownerSetSeqno",
|
||||
type: "u32",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
types: [
|
||||
{
|
||||
name: "TransactionAccount",
|
||||
type: {
|
||||
kind: "struct",
|
||||
fields: [
|
||||
{
|
||||
name: "pubkey",
|
||||
type: "publicKey",
|
||||
},
|
||||
{
|
||||
name: "isSigner",
|
||||
type: "bool",
|
||||
},
|
||||
{
|
||||
name: "isWritable",
|
||||
type: "bool",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
errors: [
|
||||
{
|
||||
code: 100,
|
||||
name: "InvalidOwner",
|
||||
msg: "The given owner is not part of this multisig.",
|
||||
},
|
||||
{
|
||||
code: 101,
|
||||
name: "NotEnoughSigners",
|
||||
msg: "Not enough owners signed this transaction.",
|
||||
},
|
||||
{
|
||||
code: 102,
|
||||
name: "TransactionAlreadySigned",
|
||||
msg: "Cannot delete a transaction that has been signed by an owner.",
|
||||
},
|
||||
{
|
||||
code: 103,
|
||||
name: "Overflow",
|
||||
msg: "Overflow when adding.",
|
||||
},
|
||||
{
|
||||
code: 104,
|
||||
name: "UnableToDelete",
|
||||
msg: "Cannot delete a transaction the owner did not create.",
|
||||
},
|
||||
{
|
||||
code: 105,
|
||||
name: "AlreadyExecuted",
|
||||
msg: "The given transaction has already been executed.",
|
||||
},
|
||||
{
|
||||
code: 106,
|
||||
name: "InvalidThreshold",
|
||||
msg: "Threshold must be less than or equal to the number of owners.",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default idl;
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
||||
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||
monospace;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
import reportWebVitals from "./reportWebVitals";
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
document.getElementById("root")
|
||||
);
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
Before Width: | Height: | Size: 2.6 KiB |
|
@ -1,8 +1,8 @@
|
|||
import { ReportHandler } from 'web-vitals';
|
||||
import { ReportHandler } from "web-vitals";
|
||||
|
||||
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
||||
import "@testing-library/jest-dom";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { createStore } from 'redux';
|
||||
import reducer from './reducer';
|
||||
import { createStore } from "redux";
|
||||
import reducer from "./reducer";
|
||||
|
||||
export const store = createStore(reducer);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { PublicKey } from '@solana/web3.js';
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
export type Action = {
|
||||
type: ActionType;
|
||||
|
@ -16,7 +16,7 @@ export enum ActionType {
|
|||
|
||||
export default function reducer(
|
||||
state: State = initialState,
|
||||
action: Action,
|
||||
action: Action
|
||||
): State {
|
||||
let newState = {
|
||||
common: { ...state.common },
|
||||
|
@ -54,40 +54,42 @@ export type CommonState = {
|
|||
export const networks: Networks = {
|
||||
mainnet: {
|
||||
// Cluster.
|
||||
label: 'Mainnet Beta',
|
||||
url: 'https://solana-api.projectserum.com',
|
||||
explorerClusterSuffix: '',
|
||||
label: "Mainnet Beta",
|
||||
url: "https://solana-api.projectserum.com",
|
||||
explorerClusterSuffix: "",
|
||||
multisigProgramId: new PublicKey(
|
||||
'A9HAbnCwoD6f2NkZobKFf6buJoN9gUVVvX5PoUnDHS6u',
|
||||
"A9HAbnCwoD6f2NkZobKFf6buJoN9gUVVvX5PoUnDHS6u"
|
||||
),
|
||||
multisigUpgradeAuthority: new PublicKey(
|
||||
"3uztpEgUmvirDBYRXgDamUDZiU5EcgTwArQ2pULtHJPC"
|
||||
),
|
||||
},
|
||||
devnet: {
|
||||
// Cluster.
|
||||
label: 'Devnet',
|
||||
url: 'https://devnet.solana.com',
|
||||
explorerClusterSuffix: 'devnet',
|
||||
label: "Devnet",
|
||||
url: "https://devnet.solana.com",
|
||||
explorerClusterSuffix: "devnet",
|
||||
multisigProgramId: new PublicKey(
|
||||
'F3Uf5F61dmht1xuNNNkk3jnzj82TY56vVjVEhZALRkN',
|
||||
"F3Uf5F61dmht1xuNNNkk3jnzj82TY56vVjVEhZALRkN"
|
||||
),
|
||||
},
|
||||
// Fill in with your local cluster addresses.
|
||||
localhost: {
|
||||
// Cluster.
|
||||
label: 'Localhost',
|
||||
url: 'http://localhost:8899',
|
||||
explorerClusterSuffix: 'localhost',
|
||||
label: "Localhost",
|
||||
url: "http://localhost:8899",
|
||||
explorerClusterSuffix: "localhost",
|
||||
multisigProgramId: new PublicKey(
|
||||
'9z7Pq56To96qbVLzuBcf47Lc7u8uUWZh6k5rhcaTsDjz',
|
||||
"9z7Pq56To96qbVLzuBcf47Lc7u8uUWZh6k5rhcaTsDjz"
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
export const initialState: State = {
|
||||
common: {
|
||||
isWalletConnected: false,
|
||||
walletProvider: 'https://www.sollet.io',
|
||||
network: networks.localhost,
|
||||
walletProvider: "https://www.sollet.io",
|
||||
network: networks.mainnet,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -99,4 +101,5 @@ export type Network = {
|
|||
url: string;
|
||||
explorerClusterSuffix: string;
|
||||
multisigProgramId: PublicKey;
|
||||
multisigUpgradeAuthority?: PublicKey;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { struct, Layout } from 'buffer-layout';
|
||||
import { rustEnum } from '@project-serum/borsh';
|
||||
import { struct, Layout } from "buffer-layout";
|
||||
import { rustEnum } from "@project-serum/borsh";
|
||||
|
||||
// Simplified since we only use the SetBuffer variant.
|
||||
export type IdlInstruction =
|
||||
|
@ -16,11 +16,11 @@ type SetBuffer = {};
|
|||
type SetAuthority = {};
|
||||
|
||||
const IDL_INSTRUCTION_LAYOUT: Layout<IdlInstruction> = rustEnum([
|
||||
struct([], 'create'),
|
||||
struct([], 'createBuffer'),
|
||||
struct([], 'write'),
|
||||
struct([], 'setBuffer'),
|
||||
struct([], 'setAuthority'),
|
||||
struct([], "create"),
|
||||
struct([], "createBuffer"),
|
||||
struct([], "write"),
|
||||
struct([], "setBuffer"),
|
||||
struct([], "setAuthority"),
|
||||
]);
|
||||
|
||||
export function encodeInstruction(i: IdlInstruction): Buffer {
|
||||
|
@ -30,4 +30,4 @@ export function encodeInstruction(i: IdlInstruction): Buffer {
|
|||
}
|
||||
|
||||
// Reverse for little endian.
|
||||
export const IDL_TAG = Buffer.from('0a69e9a778bcf440', 'hex').reverse();
|
||||
export const IDL_TAG = Buffer.from("0a69e9a778bcf440", "hex").reverse();
|
||||
|
|
|
@ -9641,6 +9641,11 @@ prepend-http@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
|
||||
integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=
|
||||
|
||||
prettier@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5"
|
||||
integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==
|
||||
|
||||
pretty-bytes@^5.3.0:
|
||||
version "5.6.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
|
||||
|
|
Loading…
Reference in New Issue