Added MathWallet Provider (#110)

* fixed ledger wallet and filtering out sollet tokens

* sanity check for token amounts

* added wallet adapter

* added math wallet to available wallet

* fixed string case

* lint common package
This commit is contained in:
Juan Diego García 2021-05-08 15:48:08 -05:00 committed by GitHub
parent 4b1d3cfdab
commit cf8f124f8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 450 additions and 262 deletions

View File

@ -24,9 +24,10 @@ export const AppLayout = React.memo((props: any) => {
{props.children} {props.children}
</Content> </Content>
<Footer> <Footer>
<div className={'description-text'} style={{ color: '#2F506F' }}> <div
className={'description-text'}
</div> style={{ color: '#2F506F' }}
></div>
</Footer> </Footer>
</Layout> </Layout>
</div> </div>

View File

@ -34,7 +34,9 @@ export const RecentTransactionsTable = (props: {
showUserTransactions?: boolean; showUserTransactions?: boolean;
tokenAccounts: TokenAccount[]; tokenAccounts: TokenAccount[];
}) => { }) => {
const { loading: loadingTransfers, transfers } = useWormholeTransactions(props.tokenAccounts); const { loading: loadingTransfers, transfers } = useWormholeTransactions(
props.tokenAccounts,
);
const { provider } = useEthereum(); const { provider } = useEthereum();
const bridge = useBridge(); const bridge = useBridge();
@ -338,7 +340,6 @@ export const RecentTransactionsTable = (props: {
scrollToFirstRowOnChange: false, scrollToFirstRowOnChange: false,
x: 900, x: 900,
}} }}
dataSource={transfers.sort((a, b) => b.date - a.date)} dataSource={transfers.sort((a, b) => b.date - a.date)}
columns={userColumns} columns={userColumns}
loading={loadingTransfers} loading={loadingTransfers}

View File

@ -36,7 +36,8 @@ export const TokenSelectModal = (props: {
return tokens.filter(token => { return tokens.filter(token => {
return ( return (
(token.tags?.indexOf('longList') || -1) < 0 && (token.tags?.indexOf('longList') || -1) < 0 &&
token.symbol.includes(search.toUpperCase()) (token.symbol.toLowerCase().includes(search.toLowerCase()) ||
token.name.toLowerCase().includes(search.toLowerCase()))
); );
}); });
} }

View File

@ -54,8 +54,6 @@ export const Transfer = () => {
setLastTypedAccount, setLastTypedAccount,
} = useTokenChainPairState(); } = useTokenChainPairState();
const [request, setRequest] = useState<TransferRequest>({ const [request, setRequest] = useState<TransferRequest>({
from: ASSET_CHAIN.Ethereum, from: ASSET_CHAIN.Ethereum,
to: ASSET_CHAIN.Solana, to: ASSET_CHAIN.Solana,
@ -85,8 +83,11 @@ export const Transfer = () => {
}); });
}, [A, B, mintAddress, A.info]); }, [A, B, mintAddress, A.info]);
const tokenAccounts = useMemo(
const tokenAccounts = useMemo(() => userAccounts.filter(u => u.info.mint.toBase58() === request.info?.mint), [request.info?.mint]) () =>
userAccounts.filter(u => u.info.mint.toBase58() === request.info?.mint),
[request.info?.mint],
);
return ( return (
<> <>

View File

@ -157,7 +157,7 @@ const queryWrappedMetaAccounts = async (
if (asset.mint) { if (asset.mint) {
asset.amount = asset.amount =
asset.mint?.info.supply.toNumber() / asset.mint?.info.supply.toNumber() /
Math.pow(10, asset.mint?.info.decimals) || 0; Math.pow(10, asset.mint?.info.decimals || 0) ;
if (!asset.mint) { if (!asset.mint) {
throw new Error('missing mint'); throw new Error('missing mint');
@ -167,7 +167,11 @@ const queryWrappedMetaAccounts = async (
connection.onAccountChange(asset.mint?.pubkey, acc => { connection.onAccountChange(asset.mint?.pubkey, acc => {
cache.add(key, acc); cache.add(key, acc);
asset.mint = cache.get(key); asset.mint = cache.get(key);
asset.amount = asset.mint?.info.supply.toNumber() || 0; if (asset.mint) {
asset.amount =
asset.mint?.info.supply.toNumber() /
Math.pow(10, asset.mint?.info.decimals || 0);
}
setExternalAssets([...assets.values()]); setExternalAssets([...assets.values()]);
}); });

View File

@ -54,7 +54,9 @@ const EXCLUDED_SPL_TOKENS = ['sol', 'srm', ...EXCLUDED_COMMON_TOKENS];
export const filterModalSolTokens = (tokens: TokenInfo[]) => { export const filterModalSolTokens = (tokens: TokenInfo[]) => {
return tokens.filter( return tokens.filter(
token => EXCLUDED_SPL_TOKENS.indexOf(token.symbol.toLowerCase()) < 0, token =>
EXCLUDED_SPL_TOKENS.indexOf(token.symbol.toLowerCase()) < 0 &&
!token.name.includes('(Sollet)'),
); );
}; };
const EXCLUDED_ETH_TOKENS = [...EXCLUDED_COMMON_TOKENS]; const EXCLUDED_ETH_TOKENS = [...EXCLUDED_COMMON_TOKENS];

View File

@ -29,8 +29,12 @@ export const HelpView = () => {
</Row> </Row>
<Row> <Row>
<Col xs={24} sm={12}> <Col xs={24} sm={12}>
<Button className="action-button" <Button
onClick={() => window.open('https://github.com/certusone/wormhole', '_blank')}> className="action-button"
onClick={() =>
window.open('https://github.com/certusone/wormhole', '_blank')
}
>
View the Code View the Code
</Button> </Button>
</Col> </Col>

View File

@ -18,11 +18,9 @@ export const AppBar = (props: {
const TopBar = ( const TopBar = (
<div className="App-Bar-right"> <div className="App-Bar-right">
{props.left} {props.left}
{connected ? {connected ? (
( <CurrentUserBadge />
<CurrentUserBadge /> ) : (
)
: (
<ConnectButton <ConnectButton
type="text" type="text"
size="large" size="large"

View File

@ -1,15 +1,15 @@
import { Button, Dropdown, Menu } from "antd"; import { Button, Dropdown, Menu } from 'antd';
import { ButtonProps } from "antd/lib/button"; import { ButtonProps } from 'antd/lib/button';
import React from "react"; import React from 'react';
import { useWallet } from './../../contexts/wallet'; import { useWallet } from './../../contexts/wallet';
export interface ConnectButtonProps extends ButtonProps, React.RefAttributes<HTMLElement> { export interface ConnectButtonProps
extends ButtonProps,
React.RefAttributes<HTMLElement> {
allowWalletChange?: boolean; allowWalletChange?: boolean;
} }
export const ConnectButton = ( export const ConnectButton = (props: ConnectButtonProps) => {
props: ConnectButtonProps
) => {
const { connected, connect, select, provider } = useWallet(); const { connected, connect, select, provider } = useWallet();
const { onClick, children, disabled, allowWalletChange, ...rest } = props; const { onClick, children, disabled, allowWalletChange, ...rest } = props;
@ -17,25 +17,30 @@ export const ConnectButton = (
const menu = ( const menu = (
<Menu> <Menu>
<Menu.Item key="3" onClick={select}>Change Wallet</Menu.Item> <Menu.Item key="3" onClick={select}>
Change Wallet
</Menu.Item>
</Menu> </Menu>
); );
if(!provider || !allowWalletChange) { if (!provider || !allowWalletChange) {
return <Button return (
{...rest} <Button
onClick={connected ? onClick : connect} {...rest}
disabled={connected && disabled} onClick={connected ? onClick : connect}
> disabled={connected && disabled}
{connected ? props.children : 'Connect'} >
</Button>; {connected ? props.children : 'Connect'}
</Button>
);
} }
return ( return (
<Dropdown.Button <Dropdown.Button
onClick={connected ? onClick : connect} onClick={connected ? onClick : connect}
disabled={connected && disabled} disabled={connected && disabled}
overlay={menu}> overlay={menu}
>
Connect Connect
</Dropdown.Button> </Dropdown.Button>
); );

View File

@ -9,7 +9,11 @@ import './styles.css';
import { Popover } from 'antd'; import { Popover } from 'antd';
import { Settings } from '../Settings'; import { Settings } from '../Settings';
export const CurrentUserBadge = (props: { showBalance?: boolean, showAddress?: boolean, iconSize?: number }) => { export const CurrentUserBadge = (props: {
showBalance?: boolean;
showAddress?: boolean;
iconSize?: number;
}) => {
const { wallet } = useWallet(); const { wallet } = useWallet();
const { account } = useNativeAccount(); const { account } = useNativeAccount();
@ -17,54 +21,59 @@ export const CurrentUserBadge = (props: { showBalance?: boolean, showAddress?: b
return null; return null;
} }
const iconStyle: React.CSSProperties = props.showAddress ? const iconStyle: React.CSSProperties = props.showAddress
{ ? {
marginLeft: '0.5rem', marginLeft: '0.5rem',
display: 'flex', display: 'flex',
width: props.iconSize, width: props.iconSize,
borderRadius: 50, borderRadius: 50,
}
: {
display: 'flex',
width: props.iconSize,
paddingLeft: 0,
borderRadius: 50,
};
} :{ const baseWalletKey: React.CSSProperties = {
display: 'flex', height: props.iconSize,
width: props.iconSize, cursor: 'pointer',
paddingLeft: 0, userSelect: 'none',
borderRadius: 50,
}; };
const walletKeyStyle: React.CSSProperties = props.showAddress
const baseWalletKey: React.CSSProperties = { height: props.iconSize, cursor: 'pointer', userSelect: 'none' }; ? baseWalletKey
const walletKeyStyle: React.CSSProperties = props.showAddress ? : { ...baseWalletKey, paddingLeft: 0 };
baseWalletKey
:{ ...baseWalletKey, paddingLeft: 0 };
let name = props.showAddress ? shortenAddress(`${wallet.publicKey}`) : ''; let name = props.showAddress ? shortenAddress(`${wallet.publicKey}`) : '';
const unknownWallet = wallet as any; const unknownWallet = wallet as any;
if(unknownWallet.name) { if (unknownWallet.name) {
name = unknownWallet.name; name = unknownWallet.name;
} }
let image = <Identicon let image = (
address={wallet.publicKey?.toBase58()} <Identicon address={wallet.publicKey?.toBase58()} style={iconStyle} />
style={iconStyle} );
/>;
if(unknownWallet.image) { if (unknownWallet.image) {
image = <img src={unknownWallet.image} style={iconStyle} />; image = <img src={unknownWallet.image} style={iconStyle} />;
} }
return ( return (
<div className="wallet-wrapper"> <div className="wallet-wrapper">
{props.showBalance && <span> {props.showBalance && (
{formatNumber.format((account?.lamports || 0) / LAMPORTS_PER_SOL)} SOL <span>
</span>} {formatNumber.format((account?.lamports || 0) / LAMPORTS_PER_SOL)} SOL
</span>
)}
<Popover <Popover
placement="topRight" placement="topRight"
title="Settings" title="Settings"
content={<Settings />} content={<Settings />}
trigger="click" trigger="click"
> >
<div className="wallet-key" style={walletKeyStyle}> <div className="wallet-key" style={walletKeyStyle}>
{name && (<span style={{ marginRight: '0.5rem' }}>{name}</span>)} {name && <span style={{ marginRight: '0.5rem' }}>{name}</span>}
{image} {image}
</div> </div>
</Popover> </Popover>

View File

@ -3,7 +3,7 @@ import { Typography } from 'antd';
import { shortenAddress } from '../../utils/utils'; import { shortenAddress } from '../../utils/utils';
export const EtherscanLink = (props: { export const EtherscanLink = (props: {
address: string ; address: string;
type: string; type: string;
code?: boolean; code?: boolean;
style?: React.CSSProperties; style?: React.CSSProperties;

View File

@ -1,7 +1,7 @@
import { Button, Popover } from "antd"; import { Button, Popover } from 'antd';
import React from "react"; import React from 'react';
import { InfoCircleOutlined } from "@ant-design/icons"; import { InfoCircleOutlined } from '@ant-design/icons';
export const Info = (props: { export const Info = (props: {
text: React.ReactElement; text: React.ReactElement;

View File

@ -20,7 +20,6 @@ export const Identicon = (props: {
useEffect(() => { useEffect(() => {
if (address && ref.current) { if (address && ref.current) {
try { try {
ref.current.innerHTML = ''; ref.current.innerHTML = '';
ref.current.className = className || ''; ref.current.className = className || '';
ref.current.appendChild( ref.current.appendChild(
@ -29,9 +28,8 @@ export const Identicon = (props: {
parseInt(bs58.decode(address).toString('hex').slice(5, 15), 16), parseInt(bs58.decode(address).toString('hex').slice(5, 15), 16),
), ),
); );
} catch (err) { } catch (err) {
// TODO // TODO
} }
} }
}, [address, style, className]); }, [address, style, className]);

View File

@ -1,11 +1,11 @@
import React from "react"; import React from 'react';
import { Input } from "antd"; import { Input } from 'antd';
export class NumericInput extends React.Component<any, any> { export class NumericInput extends React.Component<any, any> {
onChange = (e: any) => { onChange = (e: any) => {
const { value } = e.target; const { value } = e.target;
const reg = /^-?\d*(\.\d*)?$/; const reg = /^-?\d*(\.\d*)?$/;
if (reg.test(value) || value === "" || value === "-") { if (reg.test(value) || value === '' || value === '-') {
this.props.onChange(value); this.props.onChange(value);
} }
}; };
@ -17,14 +17,14 @@ export class NumericInput extends React.Component<any, any> {
if (value === undefined || value === null) return; if (value === undefined || value === null) return;
if ( if (
value.charAt && value.charAt &&
(value.charAt(value.length - 1) === "." || value === "-") (value.charAt(value.length - 1) === '.' || value === '-')
) { ) {
valueTemp = value.slice(0, -1); valueTemp = value.slice(0, -1);
} }
if (value.startsWith && (value.startsWith(".") || value.startsWith("-."))) { if (value.startsWith && (value.startsWith('.') || value.startsWith('-.'))) {
valueTemp = valueTemp.replace(".", "0."); valueTemp = valueTemp.replace('.', '0.');
} }
if (valueTemp.replace) onChange?.(valueTemp.replace(/0*(\d+)/, "$1")); if (valueTemp.replace) onChange?.(valueTemp.replace(/0*(\d+)/, '$1'));
if (onBlur) { if (onBlur) {
onBlur(); onBlur();
} }

View File

@ -3,9 +3,7 @@ import { Button, Select } from 'antd';
import { useWallet } from '../../contexts/wallet'; import { useWallet } from '../../contexts/wallet';
import { ENDPOINTS, useConnectionConfig } from '../../contexts/connection'; import { ENDPOINTS, useConnectionConfig } from '../../contexts/connection';
import { shortenAddress } from '../../utils'; import { shortenAddress } from '../../utils';
import { import { CopyOutlined } from '@ant-design/icons';
CopyOutlined
} from '@ant-design/icons';
export const Settings = ({ export const Settings = ({
additionalSettings, additionalSettings,
@ -33,18 +31,28 @@ export const Settings = ({
{connected && ( {connected && (
<> <>
<span>Wallet:</span> <span>Wallet:</span>
{wallet?.publicKey && (<Button {wallet?.publicKey && (
style={{ marginBottom: 5 }} onClick={() => navigator.clipboard.writeText(wallet.publicKey?.toBase58() || '')}> <Button
<CopyOutlined /> style={{ marginBottom: 5 }}
{shortenAddress(wallet?.publicKey.toBase58())} onClick={() =>
</Button>)} navigator.clipboard.writeText(
wallet.publicKey?.toBase58() || '',
)
}
>
<CopyOutlined />
{shortenAddress(wallet?.publicKey.toBase58())}
</Button>
)}
<Button onClick={select} <Button onClick={select} style={{ marginBottom: 5 }}>
style={{ marginBottom: 5 }}>
Change Change
</Button> </Button>
<Button type="primary" onClick={disconnect} <Button
style={{ marginBottom: 5 }}> type="primary"
onClick={disconnect}
style={{ marginBottom: 5 }}
>
Disconnect Disconnect
</Button> </Button>
</> </>

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { PublicKey } from '@solana/web3.js'; import { PublicKey } from '@solana/web3.js';
import {getTokenIcon, KnownTokenMap} from '../../utils'; import { getTokenIcon, KnownTokenMap } from '../../utils';
import { useConnectionConfig } from '../../contexts/connection'; import { useConnectionConfig } from '../../contexts/connection';
import { Identicon } from '../Identicon'; import { Identicon } from '../Identicon';
@ -9,7 +9,7 @@ export const TokenIcon = (props: {
style?: React.CSSProperties; style?: React.CSSProperties;
size?: number; size?: number;
className?: string; className?: string;
tokenMap?: KnownTokenMap, tokenMap?: KnownTokenMap;
}) => { }) => {
let icon: string | undefined = ''; let icon: string | undefined = '';
if (props.tokenMap) { if (props.tokenMap) {

View File

@ -1,4 +1,4 @@
import BN from "bn.js"; import BN from 'bn.js';
export const TEN = new BN(10); export const TEN = new BN(10);
export const HALF_WAD = TEN.pow(new BN(18)); export const HALF_WAD = TEN.pow(new BN(18));

View File

@ -341,7 +341,7 @@ const UseNativeAccount = () => {
return; return;
} }
const account = await connection.getAccountInfo(wallet.publicKey) const account = await connection.getAccountInfo(wallet.publicKey);
updateAccount(account); updateAccount(account);
subId = connection.onAccountChange(wallet.publicKey, updateAccount); subId = connection.onAccountChange(wallet.publicKey, updateAccount);
@ -351,7 +351,7 @@ const UseNativeAccount = () => {
if (subId) { if (subId) {
connection.removeAccountChangeListener(subId); connection.removeAccountChangeListener(subId);
} }
} };
}, [setNativeAccount, wallet, wallet?.publicKey, connection, updateCache]); }, [setNativeAccount, wallet, wallet?.publicKey, connection, updateCache]);
return { nativeAccount }; return { nativeAccount };

View File

@ -1,42 +1,57 @@
import { WalletAdapter } from "@solana/wallet-base"; import { WalletAdapter } from '@solana/wallet-base';
import Wallet from "@project-serum/sol-wallet-adapter"; import Wallet from '@project-serum/sol-wallet-adapter';
import { Button, Modal } from "antd"; import { Button, Modal } from 'antd';
import React, { useCallback, useContext, useEffect, useMemo, useState } from "react"; import React, {
import { notify } from "./../utils/notifications"; useCallback,
import { useConnectionConfig } from "./connection"; useContext,
import { useLocalStorageState } from "../utils/utils"; useEffect,
import { LedgerProvider } from "@solana/wallet-ledger"; useMemo,
import { SolongWalletAdapter } from "../wallet-adapters/solong"; useState,
import { PhantomWalletAdapter } from "../wallet-adapters/phantom"; } from 'react';
import { TorusWalletAdapter } from "../wallet-adapters/torus"; import { notify } from './../utils/notifications';
import { useLocation } from "react-router"; import { useConnectionConfig } from './connection';
import { useLocalStorageState } from '../utils/utils';
import { SolongWalletAdapter } from '../wallet-adapters/solong';
import { PhantomWalletAdapter } from '../wallet-adapters/phantom';
import { TorusWalletAdapter } from '../wallet-adapters/torus';
import { useLocation } from 'react-router';
import { LedgerWalletAdapter } from '@solana/wallet-ledger';
import {MathWalletAdapter} from "../wallet-adapters/mathWallet";
const ASSETS_URL = 'https://raw.githubusercontent.com/solana-labs/oyster/main/assets/wallets/'; const ASSETS_URL =
'https://raw.githubusercontent.com/solana-labs/oyster/main/assets/wallets/';
export const WALLET_PROVIDERS = [ export const WALLET_PROVIDERS = [
{ {
name: "Phantom", name: 'Phantom',
url: "https://www.phantom.app", url: 'https://www.phantom.app',
icon: `https://www.phantom.app/img/logo.png`, icon: `https://www.phantom.app/img/logo.png`,
adapter: PhantomWalletAdapter, adapter: PhantomWalletAdapter,
}, },
LedgerProvider,
{ {
name: "Sollet", name: 'Ledger',
url: "https://www.sollet.io", url: 'https://www.ledger.com',
icon: `${ASSETS_URL}ledger.svg`,
adapter: LedgerWalletAdapter,
},
{
name: 'Sollet',
url: 'https://www.sollet.io',
icon: `${ASSETS_URL}sollet.svg`, icon: `${ASSETS_URL}sollet.svg`,
}, { },
name: "Solong", {
url: "https://solongwallet.com", name: 'Solong',
url: 'https://solongwallet.com',
icon: `${ASSETS_URL}solong.png`, icon: `${ASSETS_URL}solong.png`,
adapter: SolongWalletAdapter, adapter: SolongWalletAdapter,
}, },
// TODO: enable when fully functional // TODO: enable when fully functional
// { {
// name: "MathWallet", name: 'MathWallet',
// url: "https://mathwallet.org", url: 'https://mathwallet.org',
// icon: `${ASSETS_URL}mathwallet.svg`, icon: `${ASSETS_URL}mathwallet.svg`,
// }, adapter: MathWalletAdapter,
},
// { // {
// name: 'Torus', // name: 'Torus',
// url: 'https://tor.us', // url: 'https://tor.us',
@ -53,60 +68,79 @@ export const WALLET_PROVIDERS = [
]; ];
const WalletContext = React.createContext<{ const WalletContext = React.createContext<{
wallet: WalletAdapter | undefined, wallet: WalletAdapter | undefined;
connected: boolean, connected: boolean;
select: () => void, select: () => void;
provider: typeof WALLET_PROVIDERS[number] | undefined, provider: typeof WALLET_PROVIDERS[number] | undefined;
}>({ }>({
wallet: undefined, wallet: undefined,
connected: false, connected: false,
select() { }, select() {},
provider: undefined, provider: undefined,
}); });
export function WalletProvider({ children = null as any }) { export function WalletProvider({ children = null as any }) {
const { endpoint } = useConnectionConfig(); const { endpoint } = useConnectionConfig();
const location = useLocation(); const location = useLocation();
const [autoConnect, setAutoConnect] = useState(location.pathname.indexOf('result=') >= 0 || false); const [autoConnect, setAutoConnect] = useState(
const [providerUrl, setProviderUrl] = useLocalStorageState("walletProvider"); location.pathname.indexOf('result=') >= 0 || false,
);
const [providerUrl, setProviderUrl] = useLocalStorageState('walletProvider');
const provider = useMemo(() => WALLET_PROVIDERS.find(({ url }) => url === providerUrl), [providerUrl]); const provider = useMemo(
() => WALLET_PROVIDERS.find(({ url }) => url === providerUrl),
[providerUrl],
);
const wallet = useMemo(function () { const wallet = useMemo(
if (provider) { function () {
return new (provider.adapter || Wallet)(providerUrl, endpoint) as WalletAdapter; if (provider) {
} try {
}, [provider, providerUrl, endpoint]); return new (provider.adapter || Wallet)(
providerUrl,
endpoint,
) as WalletAdapter;
} catch (e) {
console.log(`Error connecting to wallet ${provider.name}: ${e}`);
return undefined;
}
}
},
[provider, providerUrl, endpoint],
);
const [connected, setConnected] = useState(false); const [connected, setConnected] = useState(false);
useEffect(() => { useEffect(() => {
if (wallet) { if (wallet) {
wallet.on("connect", () => { wallet.on('connect', () => {
if (wallet.publicKey) { if (wallet.publicKey) {
setConnected(true); setConnected(true);
const walletPublicKey = wallet.publicKey.toBase58(); const walletPublicKey = wallet.publicKey.toBase58();
const keyToDisplay = const keyToDisplay =
walletPublicKey.length > 20 walletPublicKey.length > 20
? `${walletPublicKey.substring(0, 7)}.....${walletPublicKey.substring( ? `${walletPublicKey.substring(
walletPublicKey.length - 7, 0,
walletPublicKey.length 7,
)}` )}.....${walletPublicKey.substring(
walletPublicKey.length - 7,
walletPublicKey.length,
)}`
: walletPublicKey; : walletPublicKey;
notify({ notify({
message: "Wallet update", message: 'Wallet update',
description: "Connected to wallet " + keyToDisplay, description: 'Connected to wallet ' + keyToDisplay,
}); });
} }
}); });
wallet.on("disconnect", () => { wallet.on('disconnect', () => {
setConnected(false); setConnected(false);
// setProviderUrl(null) // setProviderUrl(null)
notify({ notify({
message: "Wallet update", message: 'Wallet update',
description: "Disconnected from wallet", description: 'Disconnected from wallet',
}); });
}); });
} }
@ -126,7 +160,7 @@ export function WalletProvider({ children = null as any }) {
setAutoConnect(false); setAutoConnect(false);
} }
return () => { } return () => {};
}, [wallet, autoConnect]); }, [wallet, autoConnect]);
const [isModalVisible, setIsModalVisible] = useState(false); const [isModalVisible, setIsModalVisible] = useState(false);
@ -148,21 +182,22 @@ export function WalletProvider({ children = null as any }) {
title="Select Wallet" title="Select Wallet"
okText="Connect" okText="Connect"
visible={isModalVisible} visible={isModalVisible}
okButtonProps={{ style: { display: "none" } }} okButtonProps={{ style: { display: 'none' } }}
onCancel={close} onCancel={close}
width={400}> width={400}
>
{WALLET_PROVIDERS.map((provider, idx) => { {WALLET_PROVIDERS.map((provider, idx) => {
const onClick = function () { const onClick = function () {
setProviderUrl(provider.url); setProviderUrl(provider.url);
setAutoConnect(true); setAutoConnect(true);
close(); close();
} };
return ( return (
<Button <Button
key={idx} key={idx}
size="large" size="large"
type={providerUrl === provider.url ? "primary" : "ghost"} type={providerUrl === provider.url ? 'primary' : 'ghost'}
onClick={onClick} onClick={onClick}
icon={ icon={
<img <img
@ -170,15 +205,19 @@ export function WalletProvider({ children = null as any }) {
width={20} width={20}
height={20} height={20}
src={provider.icon} src={provider.icon}
style={{ marginRight: 8 }} /> style={{ marginRight: 8 }}
/>
} }
style={{ style={{
display: "block", display: 'block',
width: "100%", width: '100%',
textAlign: "left", textAlign: 'left',
marginBottom: 8, marginBottom: 8,
}}>{provider.name}</Button> }}
) >
{provider.name}
</Button>
);
})} })}
</Modal> </Modal>
</WalletContext.Provider> </WalletContext.Provider>
@ -199,4 +238,4 @@ export const useWallet = () => {
wallet?.disconnect(); wallet?.disconnect();
}, },
}; };
} };

View File

@ -5,7 +5,9 @@ export const useAccountByMint = (mint?: string | PublicKey) => {
const { userAccounts } = useUserAccounts(); const { userAccounts } = useUserAccounts();
const mintAddress = typeof mint === 'string' ? mint : mint?.toBase58(); const mintAddress = typeof mint === 'string' ? mint : mint?.toBase58();
const index = userAccounts.findIndex((acc) => acc.info.mint.toBase58() === mintAddress); const index = userAccounts.findIndex(
acc => acc.info.mint.toBase58() === mintAddress,
);
if (index !== -1) { if (index !== -1) {
return userAccounts[index]; return userAccounts[index];

View File

@ -4,6 +4,7 @@ import { getTokenName } from '../utils/utils';
export function useTokenName(mintAddress?: string | PublicKey) { export function useTokenName(mintAddress?: string | PublicKey) {
const { tokenMap } = useConnectionConfig(); const { tokenMap } = useConnectionConfig();
const address = typeof mintAddress === 'string' ? mintAddress : mintAddress?.toBase58(); const address =
typeof mintAddress === 'string' ? mintAddress : mintAddress?.toBase58();
return getTokenName(tokenMap, address); return getTokenName(tokenMap, address);
} }

View File

@ -14,7 +14,7 @@ const FEE_LAYOUT = BufferLayout.struct(
BufferLayout.nu64('hostFeeNumerator'), BufferLayout.nu64('hostFeeNumerator'),
BufferLayout.nu64('hostFeeDenominator'), BufferLayout.nu64('hostFeeDenominator'),
], ],
'fees' 'fees',
); );
export const TokenSwapLayoutLegacyV0 = BufferLayout.struct([ export const TokenSwapLayoutLegacyV0 = BufferLayout.struct([
@ -27,42 +27,58 @@ export const TokenSwapLayoutLegacyV0 = BufferLayout.struct([
uint64('feesDenominator'), uint64('feesDenominator'),
]); ]);
export const TokenSwapLayoutV1: typeof BufferLayout.Structure = BufferLayout.struct([ export const TokenSwapLayoutV1: typeof BufferLayout.Structure = BufferLayout.struct(
BufferLayout.u8('isInitialized'), [
BufferLayout.u8('nonce'), BufferLayout.u8('isInitialized'),
publicKey('tokenProgramId'), BufferLayout.u8('nonce'),
publicKey('tokenAccountA'), publicKey('tokenProgramId'),
publicKey('tokenAccountB'), publicKey('tokenAccountA'),
publicKey('tokenPool'), publicKey('tokenAccountB'),
publicKey('mintA'), publicKey('tokenPool'),
publicKey('mintB'), publicKey('mintA'),
publicKey('feeAccount'), publicKey('mintB'),
BufferLayout.u8('curveType'), publicKey('feeAccount'),
uint64('tradeFeeNumerator'), BufferLayout.u8('curveType'),
uint64('tradeFeeDenominator'), uint64('tradeFeeNumerator'),
uint64('ownerTradeFeeNumerator'), uint64('tradeFeeDenominator'),
uint64('ownerTradeFeeDenominator'), uint64('ownerTradeFeeNumerator'),
uint64('ownerWithdrawFeeNumerator'), uint64('ownerTradeFeeDenominator'),
uint64('ownerWithdrawFeeDenominator'), uint64('ownerWithdrawFeeNumerator'),
BufferLayout.blob(16, 'padding'), uint64('ownerWithdrawFeeDenominator'),
]); BufferLayout.blob(16, 'padding'),
],
);
const CURVE_NODE = BufferLayout.union(BufferLayout.u8(), BufferLayout.blob(32), 'curve'); const CURVE_NODE = BufferLayout.union(
BufferLayout.u8(),
BufferLayout.blob(32),
'curve',
);
CURVE_NODE.addVariant(0, BufferLayout.struct([]), 'constantProduct'); CURVE_NODE.addVariant(0, BufferLayout.struct([]), 'constantProduct');
CURVE_NODE.addVariant(1, BufferLayout.struct([BufferLayout.nu64('token_b_price')]), 'constantPrice'); CURVE_NODE.addVariant(
1,
BufferLayout.struct([BufferLayout.nu64('token_b_price')]),
'constantPrice',
);
CURVE_NODE.addVariant(2, BufferLayout.struct([]), 'stable'); CURVE_NODE.addVariant(2, BufferLayout.struct([]), 'stable');
CURVE_NODE.addVariant(3, BufferLayout.struct([BufferLayout.nu64('token_b_offset')]), 'offset'); CURVE_NODE.addVariant(
3,
BufferLayout.struct([BufferLayout.nu64('token_b_offset')]),
'offset',
);
export const TokenSwapLayout: typeof BufferLayout.Structure = BufferLayout.struct([ export const TokenSwapLayout: typeof BufferLayout.Structure = BufferLayout.struct(
BufferLayout.u8('isInitialized'), [
BufferLayout.u8('nonce'), BufferLayout.u8('isInitialized'),
publicKey('tokenProgramId'), BufferLayout.u8('nonce'),
publicKey('tokenAccountA'), publicKey('tokenProgramId'),
publicKey('tokenAccountB'), publicKey('tokenAccountA'),
publicKey('tokenPool'), publicKey('tokenAccountB'),
publicKey('mintA'), publicKey('tokenPool'),
publicKey('mintB'), publicKey('mintA'),
publicKey('feeAccount'), publicKey('mintB'),
FEE_LAYOUT, publicKey('feeAccount'),
CURVE_NODE, FEE_LAYOUT,
]); CURVE_NODE,
],
);

View File

@ -1,9 +1,9 @@
declare module "buffer-layout" { declare module 'buffer-layout' {
const bl: any; const bl: any;
export = bl; export = bl;
} }
declare module "jazzicon" { declare module 'jazzicon' {
const jazzicon: any; const jazzicon: any;
export = jazzicon; export = jazzicon;
} }

View File

@ -1,4 +1,4 @@
declare module "@project-serum/sol-wallet-adapter" { declare module '@project-serum/sol-wallet-adapter' {
const adapter: any; const adapter: any;
export = adapter; export = adapter;
} }

View File

@ -1,7 +1,7 @@
import { EventEmitter as Emitter } from "eventemitter3"; import { EventEmitter as Emitter } from 'eventemitter3';
export class CacheUpdateEvent { export class CacheUpdateEvent {
static type = "CacheUpdate"; static type = 'CacheUpdate';
id: string; id: string;
parser: any; parser: any;
isNew: boolean; isNew: boolean;
@ -13,7 +13,7 @@ export class CacheUpdateEvent {
} }
export class CacheDeleteEvent { export class CacheDeleteEvent {
static type = "CacheUpdate"; static type = 'CacheUpdate';
id: string; id: string;
constructor(id: string) { constructor(id: string) {
this.id = id; this.id = id;
@ -21,7 +21,7 @@ export class CacheDeleteEvent {
} }
export class MarketUpdateEvent { export class MarketUpdateEvent {
static type = "MarketUpdate"; static type = 'MarketUpdate';
ids: Set<string>; ids: Set<string>;
constructor(ids: Set<string>) { constructor(ids: Set<string>) {
this.ids = ids; this.ids = ids;
@ -50,7 +50,7 @@ export class EventEmitter {
raiseCacheUpdated(id: string, isNew: boolean, parser: any) { raiseCacheUpdated(id: string, isNew: boolean, parser: any) {
this.emitter.emit( this.emitter.emit(
CacheUpdateEvent.type, CacheUpdateEvent.type,
new CacheUpdateEvent(id, isNew, parser) new CacheUpdateEvent(id, isNew, parser),
); );
} }

View File

@ -1,13 +1,13 @@
import React from "react"; import React from 'react';
import { notification } from "antd"; import { notification } from 'antd';
// import Link from '../components/Link'; // import Link from '../components/Link';
export function notify({ export function notify({
message = "", message = '',
description = undefined as any, description = undefined as any,
txid = "", txid = '',
type = "info", type = 'info',
placement = "bottomLeft", placement = 'bottomLeft',
}) { }) {
if (txid) { if (txid) {
// <Link // <Link
@ -21,13 +21,13 @@ export function notify({
description = <></>; description = <></>;
} }
(notification as any)[type]({ (notification as any)[type]({
message: <span style={{ color: "black" }}>{message}</span>, message: <span style={{ color: 'black' }}>{message}</span>,
description: ( description: (
<span style={{ color: "black", opacity: 0.5 }}>{description}</span> <span style={{ color: 'black', opacity: 0.5 }}>{description}</span>
), ),
placement, placement,
style: { style: {
backgroundColor: "white", backgroundColor: 'white',
}, },
}); });
} }

View File

@ -0,0 +1,92 @@
import EventEmitter from 'eventemitter3';
import { PublicKey, Transaction } from '@solana/web3.js';
import { WalletAdapter } from '@solana/wallet-base';
import { notify } from '../../utils/notifications';
export class MathWalletAdapter extends EventEmitter implements WalletAdapter {
_publicKey: PublicKey | null;
_onProcess: boolean;
_connected: boolean;
constructor() {
super();
this._publicKey = null;
this._onProcess = false;
this._connected = false;
this.connect = this.connect.bind(this);
}
get publicKey() {
return this._publicKey;
}
get connected() {
return this._connected;
}
get autoApprove() {
return false;
}
// eslint-disable-next-line
async signAllTransactions(
transactions: Transaction[],
): Promise<Transaction[]> {
if (!this._provider) {
return transactions;
}
return this._provider.signAllTransactions(transactions);
}
private get _provider() {
if ((window as any)?.solana?.isMathWallet) {
return (window as any).solana;
}
return undefined;
}
signTransaction(transaction: Transaction) {
if (!this._provider) {
return transaction;
}
return this._provider.signTransaction(transaction);
}
connect() {
if (this._onProcess) {
return;
}
if (!this._provider) {
notify({
message: 'MathWallet Error',
description:
'Please install and initialize Math wallet extension from Chrome first',
});
return;
}
this._onProcess = true;
this._provider
.getAccount()
.then((account: any) => {
this._publicKey = new PublicKey(account);
this._connected = true;
this.emit('connect', this._publicKey);
})
.catch(() => {
this.disconnect();
})
.finally(() => {
this._onProcess = false;
});
}
disconnect() {
if (this._publicKey) {
this._publicKey = null;
this._connected = false;
this.emit('disconnect');
}
}
}

View File

@ -1,7 +1,7 @@
import EventEmitter from "eventemitter3"; import EventEmitter from 'eventemitter3';
import {PublicKey, Transaction} from "@solana/web3.js"; import { PublicKey, Transaction } from '@solana/web3.js';
import { WalletAdapter } from "@solana/wallet-base"; import { WalletAdapter } from '@solana/wallet-base';
import { notify } from "../../utils/notifications"; import { notify } from '../../utils/notifications';
export class SolongWalletAdapter extends EventEmitter implements WalletAdapter { export class SolongWalletAdapter extends EventEmitter implements WalletAdapter {
_publicKey: PublicKey | null; _publicKey: PublicKey | null;
@ -32,8 +32,8 @@ export class SolongWalletAdapter extends EventEmitter implements WalletAdapter {
if ((window as any).solong === undefined) { if ((window as any).solong === undefined) {
notify({ notify({
message: "Solong Error", message: 'Solong Error',
description: "Please install solong wallet from Chrome ", description: 'Please install solong wallet from Chrome ',
}); });
return; return;
} }
@ -43,7 +43,7 @@ export class SolongWalletAdapter extends EventEmitter implements WalletAdapter {
.selectAccount() .selectAccount()
.then((account: any) => { .then((account: any) => {
this._publicKey = new PublicKey(account); this._publicKey = new PublicKey(account);
this.emit("connect", this._publicKey); this.emit('connect', this._publicKey);
}) })
.catch(() => { .catch(() => {
this.disconnect(); this.disconnect();
@ -56,7 +56,7 @@ export class SolongWalletAdapter extends EventEmitter implements WalletAdapter {
disconnect() { disconnect() {
if (this._publicKey) { if (this._publicKey) {
this._publicKey = null; this._publicKey = null;
this.emit("disconnect"); this.emit('disconnect');
} }
} }
} }

View File

@ -1,6 +1,6 @@
import EventEmitter from "eventemitter3"; import EventEmitter from 'eventemitter3';
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from '@solana/web3.js';
import { notify } from "../utils/notifications"; import { notify } from '../utils/notifications';
export class SolongAdapter extends EventEmitter { export class SolongAdapter extends EventEmitter {
_publicKey: any; _publicKey: any;
@ -27,8 +27,8 @@ export class SolongAdapter extends EventEmitter {
if ((window as any).solong === undefined) { if ((window as any).solong === undefined) {
notify({ notify({
message: "Solong Error", message: 'Solong Error',
description: "Please install solong wallet from Chrome ", description: 'Please install solong wallet from Chrome ',
}); });
return; return;
} }
@ -38,7 +38,7 @@ export class SolongAdapter extends EventEmitter {
.selectAccount() .selectAccount()
.then((account: any) => { .then((account: any) => {
this._publicKey = new PublicKey(account); this._publicKey = new PublicKey(account);
this.emit("connect", this._publicKey); this.emit('connect', this._publicKey);
}) })
.catch(() => { .catch(() => {
this.disconnect(); this.disconnect();
@ -51,7 +51,7 @@ export class SolongAdapter extends EventEmitter {
disconnect() { disconnect() {
if (this._publicKey) { if (this._publicKey) {
this._publicKey = null; this._publicKey = null;
this.emit("disconnect"); this.emit('disconnect');
} }
} }
} }

View File

@ -1,13 +1,13 @@
import EventEmitter from "eventemitter3" import EventEmitter from 'eventemitter3';
import { Account, PublicKey, Transaction } from "@solana/web3.js" import { Account, PublicKey, Transaction } from '@solana/web3.js';
import { WalletAdapter } from "@solana/wallet-base" import { WalletAdapter } from '@solana/wallet-base';
import OpenLogin from "@toruslabs/openlogin" import OpenLogin from '@toruslabs/openlogin';
import { getED25519Key } from "@toruslabs/openlogin-ed25519" import { getED25519Key } from '@toruslabs/openlogin-ed25519';
const getSolanaPrivateKey = (openloginKey: string)=>{ const getSolanaPrivateKey = (openloginKey: string) => {
const { sk } = getED25519Key(openloginKey) const { sk } = getED25519Key(openloginKey);
return sk return sk;
} };
export class TorusWalletAdapter extends EventEmitter implements WalletAdapter { export class TorusWalletAdapter extends EventEmitter implements WalletAdapter {
_provider: OpenLogin | undefined; _provider: OpenLogin | undefined;
@ -18,19 +18,21 @@ export class TorusWalletAdapter extends EventEmitter implements WalletAdapter {
name: string = ''; name: string = '';
constructor(providerUrl: string, endpoint: string) { constructor(providerUrl: string, endpoint: string) {
super() super();
this.connect = this.connect.bind(this) this.connect = this.connect.bind(this);
this.endpoint = endpoint; this.endpoint = endpoint;
this.providerUrl = providerUrl; this.providerUrl = providerUrl;
} }
async signAllTransactions(transactions: Transaction[]): Promise<Transaction[]> { async signAllTransactions(
if(this.account) { transactions: Transaction[],
): Promise<Transaction[]> {
if (this.account) {
let account = this.account; let account = this.account;
transactions.forEach(t => t.partialSign(account)); transactions.forEach(t => t.partialSign(account));
} }
return transactions return transactions;
} }
get publicKey() { get publicKey() {
@ -38,25 +40,27 @@ export class TorusWalletAdapter extends EventEmitter implements WalletAdapter {
} }
async signTransaction(transaction: Transaction) { async signTransaction(transaction: Transaction) {
if(this.account) { if (this.account) {
transaction.partialSign(this.account) transaction.partialSign(this.account);
} }
return transaction return transaction;
} }
connect = async () => { connect = async () => {
const clientId = process.env.REACT_APP_CLIENT_ID || 'BNxdRWx08cSTPlzMAaShlM62d4f8Tp6racfnCg_gaH0XQ1NfSGo3h5B_IkLtgSnPMhlxsSvhqugWm0x8x-VkUXA'; const clientId =
process.env.REACT_APP_CLIENT_ID ||
'BNxdRWx08cSTPlzMAaShlM62d4f8Tp6racfnCg_gaH0XQ1NfSGo3h5B_IkLtgSnPMhlxsSvhqugWm0x8x-VkUXA';
this._provider = new OpenLogin({ this._provider = new OpenLogin({
clientId, clientId,
network: "testnet", // mainnet, testnet, development network: 'testnet', // mainnet, testnet, development
uxMode: 'popup' uxMode: 'popup',
}); });
try { try {
await this._provider.init(); await this._provider.init();
} catch (ex) { } catch (ex) {
console.error('init failed', ex) console.error('init failed', ex);
} }
console.error(this._provider?.state.store); console.error(this._provider?.state.store);
@ -67,28 +71,30 @@ export class TorusWalletAdapter extends EventEmitter implements WalletAdapter {
this.account = new Account(secretKey); this.account = new Account(secretKey);
} else { } else {
try { try {
const { privKey } = await this._provider.login({ loginProvider: "unselected"} as any); const { privKey } = await this._provider.login({
loginProvider: 'unselected',
} as any);
const secretKey = getSolanaPrivateKey(privKey); const secretKey = getSolanaPrivateKey(privKey);
this.account = new Account(secretKey); this.account = new Account(secretKey);
} catch(ex) { } catch (ex) {
console.error('login failed', ex); console.error('login failed', ex);
} }
} }
this.name = this._provider?.state.store.get('name');; this.name = this._provider?.state.store.get('name');
this.image = this._provider?.state.store.get('profileImage'); this.image = this._provider?.state.store.get('profileImage');
debugger; debugger;
this.emit("connect"); this.emit('connect');
} };
disconnect = async () => { disconnect = async () => {
console.log("Disconnecting...") console.log('Disconnecting...');
if (this._provider) { if (this._provider) {
await this._provider.logout(); await this._provider.logout();
await this._provider._cleanup(); await this._provider._cleanup();
this._provider = undefined; this._provider = undefined;
this.emit("disconnect"); this.emit('disconnect');
} }
} };
} }