Display default multisig and format

This commit is contained in:
armaniferrante 2021-04-22 22:05:29 -07:00
parent 0a2228fb3f
commit 20b1e10c3c
No known key found for this signature in database
GPG Key ID: 58BEF301E91F7828
19 changed files with 733 additions and 726 deletions

View File

@ -57,6 +57,7 @@
]
},
"devDependencies": {
"gh-pages": "^3.1.0"
"gh-pages": "^3.1.0",
"prettier": "^2.2.1"
}
}

View File

@ -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();

View File

@ -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: {},
});
@ -29,18 +30,18 @@ function App() {
<MuiThemeProvider theme={theme}>
<CssBaseline />
<SnackbarProvider maxSnack={5} autoHideDuration={8000}>
<WalletProvider>
<HashRouter basename={'/'}>
<Layout>
<Route exact path="/" component={MultisigPage} />
<Route
exact
path="/:address"
component={MultisigInstancePage}
/>
</Layout>
</HashRouter>
</WalletProvider>
<WalletProvider>
<HashRouter basename={"/"}>
<Layout>
<Route exact path="/" component={MultisigPage} />
<Route
exact
path="/:address"
component={MultisigInstancePage}
/>
</Layout>
</HashRouter>
</WalletProvider>
</SnackbarProvider>
</MuiThemeProvider>
</Provider>
@ -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;

View File

@ -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>
);

View File

@ -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 history = useHistory();
const [multisigAddress, setMultisigAddress] = useState("");
const disabled = !isValidPubkey(multisigAddress);
const searchFn = () => {
history.push(`/${multisigAddress}`);
@ -35,74 +31,76 @@ 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" />
<div
style={{
marginLeft: '16px',
marginRight: '16px',
borderRadius: '25px',
display: 'flex',
flex: 1,
backgroundColor: 'rgb(245 245 245)',
}}
>
<input
style={{
flex: 1,
background: 'none',
padding: '16px',
border: 'none',
outlineWidth: 0,
color: 'inherit',
}}
placeholder="Search a multisig address..."
value={multisigAddress}
onChange={e => setMultisigAddress(e.target.value as string)}
onKeyPress={e => {
if (e.key === 'Enter') {
searchFn();
}
}}
/>
<IconButton disabled={disabled} onClick={searchFn}>
<SearchIcon />
</IconButton>
</div>
</div>
<BarButton
label="Lockup"
href="https://stake.projectserum.com/#/lockup"
/>
<div
style={{
marginLeft: "16px",
marginRight: "16px",
borderRadius: "25px",
display: "flex",
flex: 1,
backgroundColor: "rgb(245 245 245)",
}}
>
<input
style={{
flex: 1,
background: "none",
padding: "16px",
border: "none",
outlineWidth: 0,
color: "inherit",
}}
placeholder="Search a multisig address..."
value={multisigAddress}
onChange={(e) => setMultisigAddress(e.target.value as string)}
onKeyPress={(e) => {
if (e.key === "Enter") {
searchFn();
}
}}
/>
<IconButton disabled={disabled} onClick={searchFn}>
<SearchIcon />
</IconButton>
</div>
</div>
<div
style={{
display: 'flex',
display: "flex",
}}
>
<NetworkSelector />
{!wallet.publicKey ? (
<WalletConnectButton
style={{
display: wallet.publicKey ? 'none' : '',
}}
/>
) : (
<UserSelector />
)}
{!wallet.publicKey ? (
<WalletConnectButton
style={{
display: wallet.publicKey ? "none" : "",
}}
/>
) : (
<UserSelector />
)}
</div>
</div>
</Toolbar>
@ -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);

View File

@ -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>

View File

@ -1,72 +1,75 @@
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',
}}>
<NewMultisigButton />
</div>
</Container>
<Container fixed maxWidth="md">
<div
style={{
position: "fixed",
bottom: "75px",
right: "75px",
display: "flex",
flexDirection: "row-reverse",
}}
>
<NewMultisigButton />
</div>
</Container>
{multisig && <MultisigInstance multisig={multisig} />}
</div>
);
@ -75,16 +78,17 @@ 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',
}}
onClick={() => setOpen(true)}>
<AddIcon />
style={{
border: "solid 1pt #ccc",
width: "60px",
height: "60px",
borderRadius: "30px",
}}
onClick={() => setOpen(true)}
>
<AddIcon />
</IconButton>
<NewMultisigDialog open={open} onClose={() => setOpen(false)} />
</div>
@ -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,
});
}

View File

@ -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",
});
}
}

View File

@ -4,22 +4,22 @@ 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
// @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>
);

View File

@ -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: "createTransaction",
accounts: [
{
"name": "multisig",
"isMut": false,
"isSigner": false
name: "multisig",
isMut: false,
isSigner: false,
},
{
"name": "transaction",
"isMut": true,
"isSigner": false
name: "transaction",
isMut: true,
isSigner: false,
},
{
"name": "proposer",
"isMut": false,
"isSigner": true
name: "proposer",
isMut: false,
isSigner: true,
},
{
"name": "rent",
"isMut": false,
"isSigner": false
}
name: "rent",
isMut: false,
isSigner: false,
},
],
"args": [
args: [
{
"name": "pid",
"type": "publicKey"
name: "pid",
type: "publicKey",
},
{
"name": "accs",
"type": {
"vec": {
"defined": "TransactionAccount"
}
}
name: "accs",
type: {
vec: {
defined: "TransactionAccount",
},
},
},
{
"name": "data",
"type": "bytes"
}
]
name: "data",
type: "bytes",
},
],
},
{
"name": "approve",
"accounts": [
name: "approve",
accounts: [
{
"name": "multisig",
"isMut": false,
"isSigner": false
name: "multisig",
isMut: false,
isSigner: false,
},
{
"name": "transaction",
"isMut": true,
"isSigner": false
name: "transaction",
isMut: true,
isSigner: false,
},
{
"name": "owner",
"isMut": false,
"isSigner": true
}
name: "owner",
isMut: false,
isSigner: true,
},
],
"args": []
args: [],
},
{
"name": "setOwners",
"accounts": [
name: "setOwners",
accounts: [
{
"name": "multisig",
"isMut": true,
"isSigner": false
name: "multisig",
isMut: true,
isSigner: false,
},
{
"name": "multisigSigner",
"isMut": false,
"isSigner": true
}
name: "multisigSigner",
isMut: false,
isSigner: true,
},
],
"args": [
args: [
{
"name": "owners",
"type": {
"vec": "publicKey"
}
}
]
name: "owners",
type: {
vec: "publicKey",
},
},
],
},
{
"name": "changeThreshold",
"accounts": [
name: "changeThreshold",
accounts: [
{
"name": "multisig",
"isMut": true,
"isSigner": false
name: "multisig",
isMut: true,
isSigner: false,
},
{
"name": "multisigSigner",
"isMut": false,
"isSigner": true
}
name: "multisigSigner",
isMut: false,
isSigner: true,
},
],
"args": [
args: [
{
"name": "threshold",
"type": "u64"
}
]
name: "threshold",
type: "u64",
},
],
},
{
"name": "executeTransaction",
"accounts": [
name: "executeTransaction",
accounts: [
{
"name": "multisig",
"isMut": false,
"isSigner": false
name: "multisig",
isMut: false,
isSigner: false,
},
{
"name": "multisigSigner",
"isMut": false,
"isSigner": false
name: "multisigSigner",
isMut: false,
isSigner: false,
},
{
"name": "transaction",
"isMut": true,
"isSigner": false
}
name: "transaction",
isMut: true,
isSigner: false,
},
],
"args": []
}
args: [],
},
],
"accounts": [
accounts: [
{
"name": "Multisig",
"type": {
"kind": "struct",
"fields": [
name: "Multisig",
type: {
kind: "struct",
fields: [
{
"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": "ownerSetSeqno",
"type": "u32"
}
]
}
name: "ownerSetSeqno",
type: "u32",
},
],
},
},
{
"name": "Transaction",
"type": {
"kind": "struct",
"fields": [
name: "Transaction",
type: {
kind: "struct",
fields: [
{
"name": "multisig",
"type": "publicKey"
name: "multisig",
type: "publicKey",
},
{
"name": "programId",
"type": "publicKey"
name: "programId",
type: "publicKey",
},
{
"name": "accounts",
"type": {
"vec": {
"defined": "TransactionAccount"
}
}
name: "accounts",
type: {
vec: {
defined: "TransactionAccount",
},
},
},
{
"name": "data",
"type": "bytes"
name: "data",
type: "bytes",
},
{
"name": "signers",
"type": {
"vec": "bool"
}
name: "signers",
type: {
vec: "bool",
},
},
{
"name": "didExecute",
"type": "bool"
name: "didExecute",
type: "bool",
},
{
"name": "ownerSetSeqno",
"type": "u32"
}
]
}
}
name: "ownerSetSeqno",
type: "u32",
},
],
},
},
],
"types": [
types: [
{
"name": "TransactionAccount",
"type": {
"kind": "struct",
"fields": [
name: "TransactionAccount",
type: {
kind: "struct",
fields: [
{
"name": "pubkey",
"type": "publicKey"
name: "pubkey",
type: "publicKey",
},
{
"name": "isSigner",
"type": "bool"
name: "isSigner",
type: "bool",
},
{
"name": "isWritable",
"type": "bool"
}
]
}
}
name: "isWritable",
type: "bool",
},
],
},
},
],
"errors": [
errors: [
{
"code": 100,
"name": "InvalidOwner",
"msg": "The given owner is not part of this multisig."
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: 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: 102,
name: "TransactionAlreadySigned",
msg: "Cannot delete a transaction that has been signed by an owner.",
},
{
"code": 103,
"name": "Overflow",
"msg": "Overflow when adding."
code: 103,
name: "Overflow",
msg: "Overflow when adding.",
},
{
"code": 104,
"name": "UnableToDelete",
"msg": "Cannot delete a transaction the owner did not create."
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: 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."
}
]
code: 106,
name: "InvalidThreshold",
msg: "Threshold must be less than or equal to the number of owners.",
},
],
};
export default idl;

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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";

View File

@ -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);

View File

@ -1,4 +1,4 @@
import { PublicKey } from '@solana/web3.js';
import { PublicKey } from "@solana/web3.js";
export type Action = {
type: ActionType;
@ -11,12 +11,12 @@ export enum ActionType {
CommonWalletDidConnect,
CommonWalletDidDisconnect,
CommonWalletSetProvider,
CommonSetNetwork,
CommonSetNetwork,
}
export default function reducer(
state: State = initialState,
action: Action,
action: Action
): State {
let newState = {
common: { ...state.common },
@ -36,9 +36,9 @@ export default function reducer(
newState.common.network = action.item.network;
}
return newState;
default:
return newState;
}
default:
return newState;
}
}
export type State = {
@ -48,46 +48,48 @@ export type State = {
export type CommonState = {
walletProvider?: string;
isWalletConnected: boolean;
network: Network;
network: Network;
};
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;
};

View File

@ -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();

View File

@ -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"