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

WIP: Sidepanel #138

Draft
wants to merge 41 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
ca70b9c
per tab panel toggling
jfschwarz Aug 20, 2024
20f0e41
fix rules error
jfschwarz Aug 20, 2024
a5fd14d
assume case-insensitive header rules
jfschwarz Aug 20, 2024
64e8be5
separate background script concerns
jfschwarz Aug 20, 2024
bec9cb5
use react-router
jfschwarz Aug 20, 2024
5dc9c3f
cleanup
jfschwarz Aug 20, 2024
bb74169
fix public path
jfschwarz Aug 20, 2024
1e2068a
track pilot sessions across windows and tabs
jfschwarz Aug 21, 2024
2f44f46
poc: make injected wallet available to extension
jfschwarz Aug 21, 2024
7813ebe
solidify connect/useInjectedWallet implementation
jfschwarz Aug 22, 2024
e2065df
sketch implementation for new inject provider bridge
jfschwarz Aug 22, 2024
ac4ca2e
panel live reloading
jfschwarz Aug 22, 2024
792ad8d
inject provider & per-window session tracking
jfschwarz Aug 23, 2024
a353fcc
relay to right window
jfschwarz Aug 23, 2024
1aeb704
route to right window using sender + some messaging fixes
jfschwarz Aug 23, 2024
188453c
fix injected provider
jfschwarz Aug 26, 2024
33ee8b6
fix injected provider issues
jfschwarz Aug 26, 2024
cd8d4a7
wip
jfschwarz Aug 29, 2024
50e319e
use new connect site
jfschwarz Aug 29, 2024
dbf9988
inject the provider script only into tracked tabs
jfschwarz Aug 30, 2024
6e83217
reload tab on panel toggle
jfschwarz Sep 1, 2024
a726588
fix init concurrency issue
jfschwarz Sep 1, 2024
742bdb5
fix injection (it was running as a contentScript instead of in the co…
jfschwarz Sep 1, 2024
8e79ea4
migrate routes to extension storage
jfschwarz Sep 2, 2024
958ac32
small inject fixes
jfschwarz Sep 2, 2024
8652d93
fix routes hooks
jfschwarz Sep 5, 2024
f064eb0
connect state monitoring
jfschwarz Sep 5, 2024
786807d
correct eip1193 connect/disconnect events
jfschwarz Sep 5, 2024
0cfd8b2
sketch reload perf optimization
jfschwarz Sep 6, 2024
24f1380
fix some route edit issues
jfschwarz Sep 6, 2024
065599c
fix error from duplicate injections
jfschwarz Sep 6, 2024
0573e8d
make rpc intercept work
jfschwarz Sep 9, 2024
d5a8ab7
fix issue with duplicate requests
jfschwarz Sep 10, 2024
4c59701
make connect wallet work again
jfschwarz Sep 11, 2024
61562d0
only register all background event listeners on onInstalled
jfschwarz Sep 19, 2024
93f455d
undo using onInstalled listeners
jfschwarz Sep 20, 2024
77bd762
prettier log
jfschwarz Sep 20, 2024
9039440
better labels
jfschwarz Sep 20, 2024
b732cf5
always inject connect content script
jfschwarz Sep 30, 2024
c7327d7
keep port connection with connect content script
jfschwarz Sep 30, 2024
8afefca
cleanup and fix connect button
jfschwarz Sep 30, 2024
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
5 changes: 3 additions & 2 deletions connect/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
This is a minimal web app running under https://connect.pilot.gnosisguild.org to allow connecting to the user's wallet extension.
This is an empty web app running under https://connect.pilot.gnosisguild.org to allow connecting to the user's wallet extension.

It communicates with the Pilot extension side panel through `window.postMessage`.
It's sole purpose is having a host for connecting to the wallet.
Since extensions don't have access to injected wallets, Pilot will load this empty site in an iframe injecting a script to use it as a bridge to the wallet.
3 changes: 2 additions & 1 deletion extension/.cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"apikey",
"arbitrum",
"barnbridge",
"bitshift",
"Bitmask",
"bitshift",
"Blockhash",
"blockie",
"blockies",
Expand All @@ -29,6 +29,7 @@
"hexlify",
"honeyswap",
"IERC20",
"Iframe",
"iframes",
"instadapp",
"keyvaluestorage",
Expand Down
72 changes: 67 additions & 5 deletions extension/.pnp.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
65 changes: 65 additions & 0 deletions extension/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
## Things to keep in mind

### Content scripts

Can access the page's DOM, but JavaScript is sandboxed from the page's execution context. They have access to `chrome.` APIs.

There are multiple ways to register content scripts:

1. statically via `content_scripts` in manifest.json
2. dynamically via `chrome.scripting.registerContentScripts()` (supports [same properties](https://developer.chrome.com/docs/extensions/reference/api/scripting#type-RegisteredContentScript) as `content_scripts` in manifest.json)
3. dynamically via `chrome.scripting.executeScript()`

While options 1 and 2 will automatically run the content script once for every page load, option 3 just runs once at the moment `executeScript()` is invoked.
So we have to manually track when a tab loads a new page to run the content script again in the context of the new page.

We have yet to find a way that reliably lets us run `executeScript()` exactly once for every page.
At the moment we use the `chrome.tabs.onUpdated` event which can trigger multiple times per page loaded.
Thus content scripts executed in this way must handle the case that multiple instances of themselves might run in parallel.

### Injected script

Scripts that are inserted from content scripts via script nodes into the page's DOM. As such they run in the page's execution context. They have no access to `chrome.` APIs. Communicate with content scripts via `window.postMessage`.

## src folder overview

### panel

React app running in the sidePanel document. Does not have access to window.ethereum provided by the user's wallet extension.

### connect

Establish a connection to the user's wallet provided by another extension, such as MetaMask. We connect using an extra iframe we inject to every active tab. This allows us to connect to the wallet under origin `connect.pilot.gnosisguild.org`.
The communication between the injected wallet in the iframe and the Eip1193 provider in the app, goes through messages:

```
REQUEST:
panel --runtime.tabs.sendMessage()--> connect/contentScript --iframe.window.postMessage()--> connect/injectedScript

RESPONSE
connect/injectedScript --window.top.postMessage()--> connect/contentScript --runtime.tabs.sendMessage.sendResponse()--> panel
```

### inject

Override the `window.ethereum` injected provider for apps to connect to Pilot. Will only be injected to pages that are loaded while the panel is open.

Wallet requests will be relayed to the panel app through messages:

```
inject/InjectedProvider --window.top.postMessage()--> inject/contentScript --runtime.tabs.sendMessage.sendResponse()--> panel
```

### monitor

Injected only to the top-level window (not child frames windows), this content script is responsible for hinting the user about required page reload after panel toggling.

### background

The background script, handling tracking of tabs with active Pilot sessions and simulations and RPC request intercepting. There is always only a single running instance of the script across all windows.

Communication between the background script and the panel app goes exclusively through a single port.

### components

A collection of React UI components. These components implement pure, self-contained UI controls so they could eventually make up a design system.
28 changes: 21 additions & 7 deletions extension/esbuild.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,24 @@ const stdLibBrowser = require('node-stdlib-browser')

require('dotenv').config()

const SERVE_PORT = 3999

esbuild
.context({
entryPoints: [
'./src/background.ts',
'./src/contentScript.ts',
'./src/introduce.ts',
'./src/launch.ts',
'./src/injection.ts',
'./src/app.tsx',
'./src/background/index.ts',

'./src/connect/contentScript.ts',
'./src/connect/contentScriptIframe.ts',
'./src/connect/injectedScript.ts',

'./src/inject/injectedScript.ts',
'./src/inject/contentScript.ts',

'./src/monitor/injectedScript.ts',
'./src/monitor/contentScript.ts',

'./src/panel/app.tsx',
],
bundle: true,
minify: process.env.NODE_ENV === 'production',
Expand All @@ -24,20 +33,25 @@ esbuild
'.woff': 'file',
'.woff2': 'file',
'.png': 'file',
'.html': 'text',
},
target: ['chrome96'],
outdir: './public/build',
publicPath: '/build',
inject: [require.resolve('node-stdlib-browser/helpers/esbuild/shim')],
define: {
'process.env.NODE_ENV': `"${process.env.NODE_ENV}"`,
'process.env.LIVE_RELOAD':
process.env.NODE_ENV === 'development'
? `"http://127.0.0.1:${SERVE_PORT}/esbuild"`
: 'false',
global: 'window',
},
plugins: [plugin(stdLibBrowser), cssModulesPlugin()],
logLevel: 'info',
})
.then(async (ctx) => {
if (process.env.NODE_ENV === 'development') {
await ctx.serve({ port: SERVE_PORT, servedir: './public' })
await ctx.watch()
} else {
await ctx.rebuild()
Expand Down
7 changes: 5 additions & 2 deletions extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"@testing-library/dom": "^10.3.1",
"@testing-library/jest-dom": "^6.4.6",
"@testing-library/react": "^16.0.0",
"@types/chrome": "^0.0.233",
"@types/chrome": "^0.0.268",
"@types/eslint__js": "^8.42.3",
"@types/events": "^3.0.3",
"@types/react": "^18.2.39",
Expand Down Expand Up @@ -87,5 +87,8 @@
"vitest": "^2.0.2",
"zodiac-roles-deployments": "^2.2.2"
},
"packageManager": "[email protected]"
"packageManager": "[email protected]",
"dependencies": {
"react-router-dom": "^6.26.1"
}
}
41 changes: 31 additions & 10 deletions extension/public/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,27 @@
"128": "zodiac128.png"
},
"action": {
"default_title": "Zodiac Pilot"
"default_title": "Click to open Zodiac Pilot panel"
},
"side_panel": {
"default_path": "sidepanel.html"
},
"externally_connectable": {
"matches": [
"<all_urls>"
]
},
"permissions": [
"sidePanel",
"tabs",
"scripting",
"declarativeNetRequestWithHostAccess",
"declarativeNetRequestFeedback",
"webRequest"
"webRequest",
"storage"
],
"background": {
"service_worker": "build/background.js"
"service_worker": "build/background/index.js"
},
"host_permissions": [
"<all_urls>"
Expand All @@ -29,28 +40,38 @@
"matches": [
"<all_urls>"
],
"exclude_globs": [
"https://connect.pilot.gnosisguild.org/",
"about:*",
"chrome:*"
],
"run_at": "document_start",
"all_frames": true,
"js": [
"build/contentScript.js"
"build/connect/contentScript.js"
]
},
{
"matches": [
"https://pilot.gnosisguild.org/"
"<all_urls>"
],
"exclude_globs": [
"https://connect.pilot.gnosisguild.org/",
"about:*",
"chrome:*"
],
"run_at": "document_start",
"js": [
"build/introduce.js"
"build/monitor/contentScript.js"
]
},
{
"matches": [
"https://pilot.gnosisguild.org/"
"https://connect.pilot.gnosisguild.org/"
],
"run_at": "document_end",
"run_at": "document_start",
"all_frames": true,
"js": [
"build/launch.js"
"build/connect/contentScriptIframe.js"
]
}
],
Expand Down
18 changes: 18 additions & 0 deletions extension/public/sidepanel.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!doctype html>
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/png" href="zodiac32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="zodiac128.png" sizes="128x128" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Simulate dApp interactions and record transactions"
/>
<title>Zodiac Pilot</title>
<link rel="stylesheet" href="/build/panel/app.css" />
<script src="/build/panel/app.js" defer></script>
</head>
<body>
<div id="root"></div>
</body>
Loading
Loading