Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Captain #37

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
PORT = 3000
PORT = 3000
REACT_APP_API_KEY = "AIzaSyDFxijru-JjmRguGZBjTFBvDx8edNDJJRA"
REACT_APP_AUTH_DOMAIN= "dapphub-a9a1b.firebaseapp.com"
REACT_APP_PROJECT_ID= "dapphub-a9a1b"
REACT_APP_STORAGE_BUCKET = "dapphub-a9a1b.appspot.com"
REACT_APP_MESSAGING_SENDER_ID = "1926803898"
REACT_APP_APP_ID = "1:1926803898:web:4e9a55b282574066c66d83"
REACT_APP_MEASUREMENT_ID = "G-R2T3G62GNP"
100 changes: 95 additions & 5 deletions electron/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
import { join } from 'path';

// Packages
import { BrowserWindow, app, ipcMain, IpcMainEvent } from 'electron';
import { BrowserWindow, app, ipcMain, IpcMainEvent, BrowserView } from 'electron';
import isDev from 'electron-is-dev';
import { autoUpdater } from 'electron-updater';

const height = 600;
const width = 800;

const height = 800;
const width = 1000;
let view: BrowserView | null = null;
function createWindow() {
// Create the browser window.
const window = new BrowserWindow({
Expand All @@ -23,9 +24,31 @@ function createWindow() {
}
});

let isBrowserViewVisible = false; // Initial state
autoUpdater.checkForUpdatesAndNotify();

// Initialize BrowserView
ipcMain.on('load-url', (_, url) => {
if (!view) {
view = new BrowserView({
webPreferences: {
preload: join(__dirname, 'preload.js') // Specify preload here for consistency
}
});
window.setBrowserView(view);
}
view.webContents.loadURL(url);
view.setBounds({ x: 0, y: 80, width: window.getBounds().width - 250, height: window.getBounds().height * 0.8 });
view.setAutoResize({ width: true, height: true });
isBrowserViewVisible = true;
});

const port = process.env.PORT || 3000;
const url = isDev ? `http://localhost:${port}` : join(__dirname, '../src/out/index.html');

let Address: string;
let PrivateKey: string;

// and load the index.html of the app.
if (isDev) {
window?.loadURL(url);
Expand All @@ -35,6 +58,36 @@ function createWindow() {
// Open the DevTools.
// window.webContents.openDevTools();

autoUpdater.on('update-available', () => {
// Notify the user that an update is available and will be downloaded
});

autoUpdater.on('update-downloaded', () => {
// Notify the user that the update is downloaded and ready to be installed
autoUpdater.quitAndInstall();
});

// When toggling the view, update this state
ipcMain.on('toggle-view', () => {
if (view && window.getBrowserView() === view) {
window.removeBrowserView(view);
isBrowserViewVisible = false;
} else if (view) {
window.setBrowserView(view);
isBrowserViewVisible = true;
}
// Send an update to all renderer processes about the change
window.webContents.send('update-view-visibility', isBrowserViewVisible);
});

ipcMain.handle('query-view-visibility', () => isBrowserViewVisible);

window.on('resize', () => {
// Update BrowserView bounds on window resize
if (view !== null) {
view.setBounds({ x: 0, y: 80, width: window.getBounds().width - 100, height: window.getBounds().height * 0.8 });
}
});
// For AppBar
ipcMain.on('minimize', () => {
// eslint-disable-next-line no-unused-expressions
Expand All @@ -49,6 +102,43 @@ function createWindow() {
ipcMain.on('close', () => {
window.close();
});

ipcMain.on('send-user-data', (event, { userAddress, privateKey }) => {
// Store userAddress and privateKey securely and make them available to preload script
console.log(event.NONE);
Address = userAddress;
PrivateKey = privateKey;
});

ipcMain.handle('get-user-data', (event) => {
// Optional: Decrypt userData here
console.log(event.NONE);
return { Address, PrivateKey };
});

ipcMain.on('open-url', (event, url) => {
if (window) {
console.log(url);
setTimeout(() => event.sender.send('message', 'hi from electron'), 500);
} else {
console.log('Dapphub is not open. Event called:', event);
}
// console.log(event);
// const urlWindow = new BrowserWindow({
// width: 800,
// height: 600,
// webPreferences: {
// preload: join(__dirname, 'preload.js'),
// nodeIntegration: false
// }
// });
// urlWindow.loadURL(url);
// if (window) {
// window.webContents.loadURL(url);
// } else {
// console.error('The main window is not available.', event);
// }
});
}

// This method will be called when Electron has finished
Expand Down Expand Up @@ -77,5 +167,5 @@ app.on('window-all-closed', () => {
// listen the channel `message` and resend the received message to the renderer process
ipcMain.on('message', (event: IpcMainEvent, message: any) => {
console.log(message);
setTimeout(() => event.sender.send('message', 'hi from electron'), 500);
setTimeout(() => event.sender.send('message', message), 500);
});
226 changes: 225 additions & 1 deletion electron/preload.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
/* eslint-disable no-underscore-dangle */
/* eslint-disable no-redeclare */
/* eslint-disable no-else-return */
/* eslint-disable no-case-declarations */
/* eslint-disable no-fallthrough */
/* eslint-disable @typescript-eslint/ban-types */
import { ipcRenderer, contextBridge } from 'electron';
import axios from 'axios';
import { ethers } from 'ethers';
// import WalletProvider = require('../main/accountProvider.cjs');

declare global {
interface Window {
Expand All @@ -7,6 +16,29 @@ declare global {
}
}

declare global {
interface Window {
electronAPI?: {
sendUrl: (...args: any[]) => Promise<any>;
sendUserData: (...args: any[]) => Promise<any>;
requestUserData: (...args: any[]) => Promise<any>;
};
}
}

declare global {
interface Window {
ethereum?: {
// Define the properties and methods of 'ethereum' you use, for example:
userAddress: string;
privateKey: string;
isMetaMask: boolean;
isConnected: boolean;
autoRefreshOnNetworkChange: boolean;
request: (...args: any[]) => Promise<any>;
};
}
}
const api = {
/**
* Here you can expose functions to the renderer process
Expand Down Expand Up @@ -37,9 +69,201 @@ const api = {
ipcRenderer.on(channel, (_, data) => callback(data));
}
};

// const { contextBridge } = require('electron');
// const WalletProvider = require(`${__dirname}/newWalletProvider.cjs`);

// const walletProvider = new WalletProvider()
// console.log(walletProvider);
// contextBridge.exposeInMainWorld('ethereum', walletProvider);

// // contextBridge.exposeInMainWorld('wallet', {
// // walletProvider: new WalletProvider(),
// // createPopup: (...args) => global.createPopup(...args)
// // });

// In your preload.js
// const { contextBridge } = require('electron');
// const WalletProvider = require(`${__dirname}/walletProvider.cjs`);

// const walletProvider = new WalletProvider();

// Simplified EventEmitter for the renderer context
class SimpleEventEmitter {
private listeners: { [event: string]: Function[] };

constructor() {
this.listeners = {};
}

on(event: string, listener: Function): void {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event].push(listener);
}

emit(event: string, ...args: any[]): void {
if (this.listeners[event]) {
this.listeners[event].forEach((listener) => listener(...args));
}
}

off(event: string, listenerToRemove: Function): void {
if (this.listeners[event]) {
this.listeners[event] = this.listeners[event].filter((listener) => listener !== listenerToRemove);
}
}
}

const ethereumEmitter = new SimpleEventEmitter();

const walletApi = axios.create({
// baseURL: 'http://localhost:8080',
baseURL: 'https://dapphub-account-provider.fly.dev',
withCredentials: true
});

const ethMainnetUrl = 'https://ethereum-rpc.publicnode.com';
const etherprovider = new ethers.JsonRpcProvider(ethMainnetUrl);

// Example of exposing a mock `window.ethereum` object
contextBridge.exposeInMainWorld('ethereum', {
isMetaMask: true,
isConnected: true,
// Method to handle requests from dApps
request: async ({ method, params }: { method: string; params: any[] }) => {
const { Address, PrivateKey } = await ipcRenderer.invoke('get-user-data');
const userAddress = [Address];
const etherswallet = new ethers.Wallet(PrivateKey, etherprovider);
// const walletProvider = new WalletProvider(PrivateKey);
switch (method) {
case 'eth_requestAccounts':
// Return mock accounts
return userAddress;
case 'eth_accounts':
return userAddress; // Return the currently connected accounts
case 'eth_sendTransaction':
const sendTransactionObj = { decryptedPrivateKey: PrivateKey, transaction: params[0] };
walletApi.post('/eth_sendTransaction', sendTransactionObj).then((response) => {
if (response.status === 200) {
return response.data.txReceipt;
} else {
return {
message: 'Internal Error',
code: -32603
};
}
});
break;
case 'personal_sign':
return etherswallet.signMessage(params[0]);

// must pass in the network id before the dapp is loaded
case 'eth_chainId':
return etherprovider._network;
case 'eth_call':
return etherswallet.call(params[0]);
case 'eth_getBalance':
return etherprovider.getBalance(Address);
// case 'eth_signTypedData_v4':
// return walletProvider.signTypedData(params);
case 'wallet_switchEthereumChain':
console.log(params[0]);
const chainId = parseInt(params[0].chainId, 16).toString();
const chainIdObj = { decryptedPrivateKey: PrivateKey, chainId };
await walletApi.post('/eth_switchEthereumChain', chainIdObj).then((response) => {
if (response.status === 200) {
return null;
} else {
return {
message: 'Internal Error',
code: 4902
};
}
});
break;
// case 'wallet_addEthereumChain':
// console.log(params[0]);
// return;
case 'eth_getTransactionCount':
return etherprovider.getTransactionCount(params[0], params[1]);
case 'eth_estimateGas':
return etherprovider.estimateGas(params[0]);
case 'eth_blockNumber':
return etherprovider.getBlockNumber();
case 'eth_getTransactionByHash':
return null;
case 'eth_getTransactionReceipt':
return etherprovider.getTransactionReceipt(params[0]);
default:
throw new Error(`Method ${method} not implemented. with ${params}`);
}
},

// Event subscription method
on: (event: string, listener: Function) => {
ethereumEmitter.on(event, listener);
},
// Method to remove event listeners if needed
removeListener: (event: string, listener: Function) => {
ethereumEmitter.off(event, listener);
},
// Emit chainChanged event for changing networks (mock example)
emitChainChanged: (chainId: any) => {
ethereumEmitter.emit('chainChanged', chainId);
},
// Emit accountsChanged event for account changes (mock example)
emitAccountsChanged: (accounts: any) => {
ethereumEmitter.emit('accountsChanged', accounts);
},
// Add a listener for the chainChanged event
onChainChanged: (listener: Function) => {
ethereumEmitter.on('chainChanged', listener);
},

onConnected: (listener: Function) => {
ethereumEmitter.on('connected', listener);
},

// Optionally, add a method to remove a listener for the chainChanged event
removeChainChangedListener: (listener: Function) => {
ethereumEmitter.off('chainChanged', listener);
}
// // Additional utility methods like `isConnected` can also be added as needed
// isConnected: () => {
// // Mock connected status
// ethereumEmitter.emit('isConnected', true);
// }
});

contextBridge.exposeInMainWorld('electronAPI', {
sendUrl: (url: string) => ipcRenderer.send('open-url', url),
sendUserData: (data: any) => ipcRenderer.send('send-user-data', data),
requestUserData: () => ipcRenderer.invoke('get-user-data'),
createBrowserWindow: (data: any) => ipcRenderer.send('load-url', data),
toggleBrowserView: () => ipcRenderer.send('toggle-view'),
onVisibilityChanged: (callback: any) => ipcRenderer.on('update-view-visibility', callback),
queryViewVisibility: () => ipcRenderer.invoke('query-view-visibility'),
removeVisibilityChangedListener: (callback: any) => ipcRenderer.removeListener('update-view-visibility', callback),

send: (channel: any, data: any) => {
// whitelist channels
let validChannels = ['create-browser-view'];
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data);
}
},
receive: (channel: any, func: any) => {
let validChannels = ['fromMain'];
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`
ipcRenderer.on(channel, (...args) => func(...args));
}
}
});
contextBridge.exposeInMainWorld('Main', api);
/**
* Using the ipcRenderer directly in the browser through the contextBridge ist not really secure.
* I advise using the Main/api way !!
*/
contextBridge.exposeInMainWorld('ipcRenderer', ipcRenderer);
Loading