Skip to content

Commit

Permalink
prototype using solana wallet-adaptor, and use it for sendSol (#134)
Browse files Browse the repository at this point in the history
* use the solana-wallet-adapter to add account to programchanges

Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>

* use the Cryptoworkbench Solana Workbench Sun logo for the localstorage wallet

Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>

* initial ElectronMainWallet POC

Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>

* electron wallet adapter saves the pubkey into the cfg file, and reuses that account

Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>

* transfer SOL works using the electron wallet adapter

Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>

* lint and add error rendering in toast

Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>

* linties

Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>

* linties

Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>

* today's attempt at linties

Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>

* lintie

Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>

* found a place where I could crash the app

Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>

* use a toast to tell the user we're watching a new key

Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>

* linties

Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>

* again, linties that I can't get npm run lint to show me, but GH actions do

Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>

* comment out the wallet adapters that don't work

Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>

* moved the get cfg wallet for the electron wallet into account and simplify

Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>
  • Loading branch information
SvenDowideit authored May 25, 2022
1 parent e176ef0 commit 03d286f
Show file tree
Hide file tree
Showing 15 changed files with 1,048 additions and 119 deletions.
5 changes: 5 additions & 0 deletions .erb/configs/webpack.config.base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ const configuration: webpack.Configuration = {
resolve: {
extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'],
modules: [webpackPaths.srcPath, 'node_modules'],
fallback:
{
'stream': require.resolve('stream-browserify'),
'crypto': require.resolve('crypto-browserify')
}
},

plugins: [
Expand Down
7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,15 @@
"@fortawesome/free-solid-svg-icons": "^6.1.0",
"@fortawesome/react-fontawesome": "^0.1.18",
"@reduxjs/toolkit": "^1.7.2",
"@solana/wallet-adapter-base": "^0.9.5",
"@solana/wallet-adapter-react": "^0.15.4",
"@solana/wallet-adapter-react-ui": "^0.9.6",
"@solana/wallet-adapter-wallets": "^0.15.5",
"amplitude-js": "^8.12.0",
"bip39": "^3.0.4",
"bootstrap": "^5.1.3",
"command-exists": "^1.2.9",
"crypto-browserify": "^3.12.0",
"electron-cfg": "^1.2.7",
"electron-debug": "^3.2.0",
"electron-log": "^4.4.6",
Expand All @@ -265,6 +271,7 @@
"react-toastify": "^9.0.1",
"regenerator-runtime": "^0.13.9",
"shelljs": "^0.8.5",
"stream-browserify": "^3.0.0",
"styled-components": "^5.3.3",
"typescript-lru-cache": "^1.2.3",
"underscore": "^1.13.1",
Expand Down
34 changes: 33 additions & 1 deletion src/main/ipc/accounts.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,34 @@
import cfg from 'electron-cfg';
import promiseIpc from 'electron-promise-ipc';
import type { IpcMainEvent, IpcRendererEvent } from 'electron';
import { IpcMainEvent, IpcRendererEvent } from 'electron';

import * as web3 from '@solana/web3.js';
import * as bip39 from 'bip39';

import { NewKeyPairInfo } from '../../types/types';

import { logger } from '../logger';

async function createNewKeypair(): Promise<NewKeyPairInfo> {
const mnemonic = bip39.generateMnemonic();
const seed = await bip39.mnemonicToSeed(mnemonic);
const newKeypair = web3.Keypair.fromSeed(seed.slice(0, 32));

logger.silly(
`main generated new account${newKeypair.publicKey.toString()} ${JSON.stringify(
newKeypair
)}`
);

const val = {
privatekey: newKeypair.secretKey,
mnemonic,
};
cfg.set(`accounts.${newKeypair.publicKey.toString()}`, val);

return val;
}

declare type IpcEvent = IpcRendererEvent & IpcMainEvent;

// Need to import the file and call a function (from the main process) to get the IPC promise to exist.
Expand All @@ -26,6 +51,13 @@ export function initAccountPromises() {
return cfg.set(`accounts.${key}`, val);
}
);
promiseIpc.on(
'ACCOUNT-CreateNew',
(event: IpcEvent | undefined): Promise<NewKeyPairInfo> => {
logger.silly(`main: called ACCOUNT-CreateNew, ${event}`);
return createNewKeypair();
}
);
}

export default {};
1 change: 1 addition & 0 deletions src/main/ipc/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ declare type IpcEvent = IpcRendererEvent & IpcMainEvent;

// Need to import the file and call a function (from the main process) to get the IPC promise to exist.
export function initConfigPromises() {
logger.info(`Config file at ${cfg.file()}`);
// gets written to .\AppData\Roaming\SolanaWorkbench\electron-cfg.json on windows
promiseIpc.on('CONFIG-GetAll', (event: IpcEvent | undefined) => {
logger.silly('main: called CONFIG-GetAll', event);
Expand Down
7 changes: 7 additions & 0 deletions src/renderer/App.scss
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,13 @@ th,
overflow-y: auto;
}

// Wallet adapter colours so its easier to see our dragonfly logo
// this isn't really right, but i don't like the purple, as it implies Phantom wallet?
// .wallet-adapter-button-trigger {
// background-color: powderblue !important;
// color: steelblue !important;
// }

// EdiText - https://github.com/alioguzhan/react-editext#styling-with-styled-components
div[editext='view-container'],
div[editext='view'],
Expand Down
85 changes: 74 additions & 11 deletions src/renderer/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import PropTypes from 'prop-types';
import isElectron from 'is-electron';
import './App.scss';
import * as sol from '@solana/web3.js';

import { Routes, Route, NavLink, Outlet } from 'react-router-dom';

Expand All @@ -25,19 +26,40 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

import { SizeProp } from '@fortawesome/fontawesome-svg-core';

import { useState } from 'react';
import { FC, useMemo, useState } from 'react';

import {
ConnectionProvider,
WalletProvider,
} from '@solana/wallet-adapter-react';
// import { LedgerWalletAdapter } from '@solana/wallet-adapter-wallets';
import {
WalletModalProvider,
WalletMultiButton,
} from '@solana/wallet-adapter-react-ui';
import { ElectronAppStorageWalletAdapter } from './wallet-adapter/electronAppStorage';

import Account from './nav/Account';
import Anchor from './nav/Anchor';
import Validator from './nav/Validator';
import ValidatorNetworkInfo from './nav/ValidatorNetworkInfo';

import { useAppDispatch } from './hooks';
import { useAppDispatch, useAppSelector } from './hooks';
import {
useConfigState,
setConfigValue,
ConfigKey,
} from './data/Config/configState';
import { useAccountsState } from './data/accounts/accountState';
import ValidatorNetwork from './data/ValidatorNetwork/ValidatorNetwork';
import {
netToURL,
selectValidatorNetworkState,
} from './data/ValidatorNetwork/validatorNetworkState';
import { getElectronStorageWallet } from './data/accounts/account';

// Default styles that can be overridden by your app
require('@solana/wallet-adapter-react-ui/styles.css');

const logger = window.electron.log;

Expand Down Expand Up @@ -178,6 +200,7 @@ function Topbar() {
>
<TopbarNavItems />
</Nav>
<WalletMultiButton />
<Form className="d-flex">
<ValidatorNetwork />
</Form>
Expand Down Expand Up @@ -244,26 +267,66 @@ function AnalyticsBanner() {
);
}

function GlobalContainer() {
// Note: NavLink is not compatible with react-router-dom's NavLink, so just add the styling
export const GlobalContainer: FC = () => {
const dispatch = useAppDispatch();
const config = useConfigState();
const accounts = useAccountsState();
const { net } = useAppSelector(selectValidatorNetworkState);

const wallets = useMemo(() => {
const electronStorageWallet = new ElectronAppStorageWalletAdapter({
accountFn: (): Promise<sol.Keypair> => {
if (!config) {
throw Error(
"Config not loaded, can't get ElectronWallet keypair yet"
);
}

return getElectronStorageWallet(dispatch, config, accounts);
},
});
return [
// Sadly, electron apps don't run browser plugins, so these won't work without lots of pain
// new PhantomWalletAdapter(),
// new SlopeWalletAdapter(),
// new SolflareWalletAdapter({ network }),
// new TorusWalletAdapter(),
// new LedgerWalletAdapter(),
// new SolletWalletAdapter({ network }),
// new SolletExtensionWalletAdapter({ network }),
electronStorageWallet,
// new LocalStorageWalletAdapter({ endpoint }),
];
}, [accounts, config, dispatch]);

if (config.loading || accounts.loading) {
return <>Config Loading ...${accounts.loading}</>;
}
return (
<div className="vh-100">
<Topbar />
<Sidebar />
<Container fluid className="page-content mt-3">
<Outlet />
</Container>
<ConnectionProvider endpoint={netToURL(net)}>
<WalletProvider wallets={wallets} autoConnect>
<WalletModalProvider>
<Topbar />
<Sidebar />
<Container fluid className="page-content mt-3">
<Outlet />
</Container>
</WalletModalProvider>
</WalletProvider>
</ConnectionProvider>
</div>
);
}
};

function App() {
const config = useConfigState();
const accounts = useAccountsState();

Object.assign(console, logger.functions);

if (config.loading) {
return <>Config Loading ...</>;
return <>Config Loading ...${accounts.loading}</>;
}
if (!config.values || !(`${ConfigKey.AnalyticsEnabled}` in config.values)) {
return <AnalyticsBanner />;
Expand Down
16 changes: 9 additions & 7 deletions src/renderer/components/LogView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,15 @@ function LogView() {

return () => {
const sub = logSubscriptions[net];
sub.solConn
.removeOnLogsListener(sub.subscriptionID)
// eslint-disable-next-line promise/always-return
.then(() => {
delete logSubscriptions[net];
})
.catch(logger.info);
if (sub?.solConn) {
sub.solConn
.removeOnLogsListener(sub.subscriptionID)
// eslint-disable-next-line promise/always-return
.then(() => {
delete logSubscriptions[net];
})
.catch(logger.info);
}
};
}, [net, status]);

Expand Down
Loading

0 comments on commit 03d286f

Please sign in to comment.