Skip to content
Merged
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: 9 additions & 0 deletions .erb/configs/webpack.config.main.prod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ const configuration: webpack.Configuration = {
__dirname: false,
__filename: false,
},
module: {
rules: [
// Images
{
test: /\.(svg)$/i,
type: 'asset/resource',
},
],
},
};

export default merge(baseConfig, configuration);
2 changes: 1 addition & 1 deletion .erb/configs/webpack.config.renderer.dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ const configuration: webpack.Configuration = {

new HtmlWebpackPlugin({
filename: path.join('index.html'),
template: path.join(webpackPaths.srcRendererPath, 'index.ejs'),
template: path.join(webpackPaths.srcRendererPath, 'index.html'),
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
Expand Down
2 changes: 1 addition & 1 deletion .erb/configs/webpack.config.renderer.prod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ const configuration: webpack.Configuration = {

new HtmlWebpackPlugin({
filename: 'index.html',
template: path.join(webpackPaths.srcRendererPath, 'index.ejs'),
template: path.join(webpackPaths.srcRendererPath, 'index.html'),
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
Expand Down
35 changes: 35 additions & 0 deletions src/dapps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export type DappType = {
id: string;
label: string;
icon: () => Promise<{ default: string }>;
ens?: string;
ipns?: string;
url?: string;
};

export const DAPPS: DappType[] = [
{
id: 'kwenta',
label: 'Kwenta',
icon: () => import('./kwenta.svg'),
ens: 'kwenta.eth',
ipns: undefined,
url: undefined,
},
{
id: 'staking',
label: 'Staking V2',
icon: () => import('./synthetix.svg'),
ens: 'staking.synthetix.eth',
ipns: 'k2k4r8jvf8qlg4ytq7y3ta749vkjzms0hisd9i92ohk0lsp0yestbhy3',
url: undefined,
},
{
id: 'watcher',
label: 'Perps V2 Watcher',
icon: () => import('./synthetix.svg'),
ens: undefined,
ipns: 'k2k4r8pf9z20v99lm731p2d8vp0lg6w3sics8iot60mvjyret5tzfefl',
url: undefined,
},
];
18 changes: 9 additions & 9 deletions src/renderer/DApps/kwenta.svg → src/kwenta.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 21 additions & 8 deletions src/main/dapps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { mainnet } from 'viem/chains';
import { namehash, normalize } from 'viem/ens';
// @ts-ignore
import * as contentHash from '@ensdomains/content-hash';
import { DappType } from '../dapps';
import { ipfs } from './ipfs';
import { getPid } from './pid';

Expand All @@ -28,9 +29,18 @@ const resolverAbi = [
];

export async function resolveEns(
ens: string
dapp: DappType
): Promise<{ codec: string; hash: string }> {
const name = normalize(ens);
if (dapp.ipns) {
return {
codec: 'ipns-ns',
hash: dapp.ipns,
};
}
if (!dapp.ens) {
throw new Error('Neither ipns nor ens was set, cannot resolve');
}
const name = normalize(dapp.ens);
const resolverAddress = await client.getEnsResolver({ name });
const hash = await client.readContract({
address: resolverAddress,
Expand Down Expand Up @@ -64,31 +74,34 @@ export async function isPinned(qm: string): Promise<boolean> {
}
}

export async function getDappHost(ens: string): Promise<string | undefined> {
export async function getDappHost(dapp: DappType): Promise<string | undefined> {
try {
const { codec, hash } = await resolveEns(ens);
logger.log(ens, 'resolved', codec, hash);
const { codec, hash } = await resolveEns(dapp);
logger.log(dapp.id, 'resolved', codec, hash);
const qm =
codec === 'ipns-ns'
? await resolveQm(hash)
: codec === 'ipfs-ns'
? hash
: undefined;
if (qm !== hash) {
logger.log(dapp.id, 'resolved CID', qm);
}
if (!qm) {
throw new Error(`Codec "${codec}" not supported`);
}
if (await getPid(`pin add --progress ${qm}`)) {
logger.log(ens, 'pinning already in progres...');
logger.log(dapp.id, 'pinning already in progres...');
return undefined;
}
const isDappPinned = await isPinned(qm);
if (!isDappPinned) {
logger.log(ens, 'pinning...', qm);
logger.log(dapp.id, 'pinning...', qm);
await ipfs(`pin add --progress ${qm}`);
}
const bafy = await convertCid(qm);
const url = `${bafy}.ipfs.localhost`;
logger.log(ens, 'local IPFS host:', url);
logger.log(dapp.id, 'local IPFS host:', url);
return url;
} catch (e) {
logger.error(e);
Expand Down
44 changes: 19 additions & 25 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import * as settings from './settings';
import http from 'http';
import { proxy } from './proxy';

import { DAPPS } from '../dapps';

logger.transports.file.level = 'info';

const isDebug =
Expand All @@ -51,15 +53,6 @@ const isDebug =
let tray: Tray | null = null;
let mainWindow: BrowserWindow | null = null;

const dapps: { [key: string]: string | undefined } = {
'kwenta.eth': undefined,
'staking.synthetix.eth': undefined,
};
const localDapps: { [key: string]: string | undefined } = {
'kwenta.eth': 'kwenta',
'staking.synthetix.eth': 'staking',
};

if (process.env.NODE_ENV === 'production') {
const sourceMapSupport = require('source-map-support');
sourceMapSupport.install();
Expand Down Expand Up @@ -210,11 +203,11 @@ function generateMenuItems() {
separator: {
type: 'separator',
},
dapps: Object.entries(localDapps).map(([name, shortcut]) => {
dapps: DAPPS.map((dapp) => {
return {
enabled: Boolean(dapps[name]),
label: name,
click: () => shell.openExternal(`http://${shortcut}.localhost:8888`),
enabled: Boolean(dapp.url),
label: dapp.label,
click: () => shell.openExternal(`http://${dapp.id}.localhost:8888`),
};
}),
quit: {
Expand Down Expand Up @@ -312,31 +305,32 @@ followerDaemon();
const followerCheck = setInterval(followerDaemon, 10_000);
app.on('will-quit', () => clearInterval(followerCheck));

ipcMain.handle('dapp', async (_event, ens: string) =>
dapps[ens] ? `http://${localDapps[ens]}.localhost:8888` : null
);
ipcMain.handle('dapp', async (_event, id: string) => {
const dapp = DAPPS.find((dapp) => dapp.id === id);
return dapp && dapp.url ? `http://${dapp.id}.localhost:8888` : null;
});

async function updateAllDapps() {
Object.keys(dapps).forEach((ens) =>
getDappHost(ens).then((url) => {
DAPPS.forEach((dapp) =>
getDappHost(dapp).then((url) => {
if (url) {
dapps[ens] = url;
dapp.url = url;
updateContextMenu();
}
})
);
}

const dappsUpdater = setInterval(updateAllDapps, 600_000); // 10 minutes
app.on('will-quit', () => clearInterval(dappsUpdater));
waitForIpfs().then(updateAllDapps).catch(logger.error);

http
.createServer((req, res) => {
const shortcut = `${req.headers.host}`.replace('.localhost:8888', '');
const host = Object.keys(localDapps).find(
(key) => localDapps[key] === shortcut
);
if (host && host in dapps && dapps[host]) {
req.headers.host = dapps[host];
const id = `${req.headers.host}`.replace('.localhost:8888', '');
const dapp = DAPPS.find((dapp) => dapp.id === id);
if (dapp && dapp.url) {
req.headers.host = dapp.url;
proxy({ host: '127.0.0.1', port: 8080 }, req, res);
return;
}
Expand Down
40 changes: 19 additions & 21 deletions src/renderer/DApps/Dapps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,35 @@ import {
Stack,
} from '@chakra-ui/react';
import { ExternalLinkIcon } from '@chakra-ui/icons';
import kwentaIcon from './kwenta.svg';
import stakingIcon from './staking.svg';
import { useDapp } from './useDapp';
import { DAPPS, DappType } from '../../dapps';
import { useQuery } from '@tanstack/react-query';

function DappButton({
ens,
label,
icon,
}: {
ens: string;
label: string;
icon: string;
}) {
const { data: url } = useDapp(ens);
function DappButton({ dapp }: { dapp: DappType }) {
const { data: url } = useDapp(dapp.id);
const { data: src } = useQuery({
queryKey: ['icon', dapp.id],
queryFn: async () => {
const { default: icon } = await dapp.icon();
return icon;
},
initialData: () => '',
placeholderData: '',
});
return (
<Button
as={Link}
href={url}
target="_blank"
aria-label={label}
aria-label={dapp.label}
variant="outline"
colorScheme="teal"
leftIcon={<Image src={icon} alt={label} width="1em" />}
leftIcon={<Image src={src} alt={dapp.label} width="1em" />}
rightIcon={url ? <ExternalLinkIcon /> : <Spinner size="xs" />}
isDisabled={!url}
_hover={{ textDecoration: 'none' }}
>
{label}
{dapp.label}
</Button>
);
}
Expand All @@ -48,12 +49,9 @@ export function Dapps() {
Available DApps:
</Heading>
<Stack direction="row" spacing={6} justifyContent="start" mb="2">
<DappButton ens="kwenta.eth" label="Kwenta" icon={kwentaIcon} />
<DappButton
ens="staking.synthetix.eth"
label="Staking V2"
icon={stakingIcon}
/>
{DAPPS.map((dapp) => (
<DappButton key={dapp.id} dapp={dapp} />
))}
</Stack>
</Box>
</Box>
Expand Down
3 changes: 0 additions & 3 deletions src/renderer/DApps/staking.svg

This file was deleted.

6 changes: 3 additions & 3 deletions src/renderer/DApps/useDapp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { useQuery } from '@tanstack/react-query';

const { ipcRenderer } = window?.electron || {};

export function useDapp(ens: string) {
export function useDapp(id: string) {
return useQuery({
queryKey: ['dapp', ens],
queryKey: ['dapp', id],
queryFn: async () => {
const url = await ipcRenderer.invoke('dapp', ens);
const url = await ipcRenderer.invoke('dapp', id);
if (!url) {
return null;
}
Expand Down
File renamed without changes.
3 changes: 3 additions & 0 deletions src/synthetix.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.