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

feat(extension): make extension load companion app #565

Merged
merged 7 commits into from
Jan 17, 2025
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
2 changes: 1 addition & 1 deletion .github/workflows/live-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
timeout-minutes: 60
runs-on: ubuntu-latest
env:
CONNECT_IFRAME_URL: https://connect.pilot.gnosisguild.org
CONNECT_IFRAME_URL: https://app.pilot.gnosisguild.org
if: github.event.deployment_status.state == 'success'
defaults:
run:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/prod-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
name: Build Extension
env:
CONNECT_IFRAME_URL: https://connect.pilot.gnosisguild.org
CONNECT_IFRAME_URL: https://app.pilot.gnosisguild.org
defaults:
run:
working-directory: ./deployables/extension
Expand Down
2 changes: 1 addition & 1 deletion deployables/app/app/components/DebugRouteData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const DebugRouteData = () => {
}

return (
<code>
<code className="overflow-y-auto text-sm">
<pre>{JSON.stringify(parseRouteData(data), undefined, 2)}</pre>
</code>
)
Expand Down
7 changes: 4 additions & 3 deletions deployables/app/app/components/ZodiacMod.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getChainId } from '@zodiac/chains'
import {
decodeRoleKey,
encodeRoleKey,
Expand All @@ -13,7 +14,7 @@ import { useFetcher } from 'react-router'
import {
AccountType,
ConnectionType,
splitPrefixedAddress,
parsePrefixedAddress,
type PrefixedAddress,
} from 'ser-kit'
import { ModSelect, NO_MODULE_OPTION } from './ModSelect'
Expand Down Expand Up @@ -53,7 +54,7 @@ export const ZodiacMod = ({
} = useFetcher<ZodiacModule[]>({
key: 'modules',
})
const [chainId] = splitPrefixedAddress(avatar)
const chainId = getChainId(avatar)
const pilotAddress = waypoints == null ? null : getPilotAddress(waypoints)

useEffect(() => {
Expand All @@ -66,7 +67,7 @@ export const ZodiacMod = ({
}, [chainId, loadDelegates, loadSafes, pilotAddress])

useEffect(() => {
const [, address] = splitPrefixedAddress(avatar)
const address = parsePrefixedAddress(avatar)

loadModules(`/${address}/${chainId}/modules`)
}, [avatar, chainId, loadModules])
Expand Down
150 changes: 73 additions & 77 deletions deployables/app/app/routes/edit-route.$data.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import {
ZodiacMod,
} from '@/components'
import { editRoute, jsonRpcProvider, parseRouteData } from '@/utils'
import { invariantResponse } from '@epic-web/invariant'
import { verifyChainId, ZERO_ADDRESS } from '@zodiac/chains'
import { getChainId, verifyChainId, ZERO_ADDRESS } from '@zodiac/chains'
import {
formData,
getHexString,
Expand Down Expand Up @@ -39,14 +38,14 @@ import {
type Waypoints,
} from '@zodiac/schema'
import { PrimaryButton, TextInput } from '@zodiac/ui'
import classNames from 'classnames'
import { Form, useSubmit } from 'react-router'
import { splitPrefixedAddress } from 'ser-kit'
import type { Route } from './+types/edit-route.$data'

export const loader = ({ params }: Route.LoaderArgs) => {
const route = parseRouteData(params.data)

const [chainId] = splitPrefixedAddress(route.avatar)
const chainId = getChainId(route.avatar)

return {
label: route.label,
Expand Down Expand Up @@ -79,7 +78,7 @@ export const clientAction = async ({
request,
params,
}: Route.ClientActionArgs) => {
const data = await request.formData()
const data = await request.clone().formData()

const intent = getOptionalString(data, 'intent')

Expand Down Expand Up @@ -148,80 +147,82 @@ const EditRoute = ({
const submit = useSubmit()

return (
<main className="mx-auto flex max-w-3xl flex-col gap-4">
<h1 className="my-8 text-3xl font-semibold">Route configuration</h1>

<Form method="POST" className="flex flex-col gap-4">
<TextInput label="Label" name="label" defaultValue={label} />

<ChainSelect
value={chainId}
onChange={(chainId) => {
submit(formData({ intent: Intent.UpdateChain, chainId }), {
method: 'POST',
})
}}
/>

<WalletProvider>
<ConnectWallet
chainId={chainId}
pilotAddress={getPilotAddress(waypoints)}
providerType={providerType}
onConnect={({ account, chainId, providerType }) => {
submit(
formData({
intent: Intent.ConnectWallet,
account,
chainId,
providerType,
}),
{ method: 'POST' },
)
}}
onDisconnect={() => {
submit(formData({ intent: Intent.DisconnectWallet }), {
<div className={classNames('grid', isDev && 'grid-cols-2')}>
<main className="mx-auto flex max-w-3xl flex-col gap-4">
<h1 className="my-8 text-3xl font-semibold">Route configuration</h1>

<Form method="POST" className="flex flex-col gap-4">
<TextInput label="Label" name="label" defaultValue={label} />

<ChainSelect
value={chainId}
onChange={(chainId) => {
submit(formData({ intent: Intent.UpdateChain, chainId }), {
method: 'POST',
})
}}
/>
</WalletProvider>

<AvatarInput
value={avatar}
startingWaypoint={startingWaypoint}
onChange={(avatar) => {
if (avatar != null) {
submit(formData({ intent: Intent.UpdateAvatar, avatar }), {
method: 'POST',
})
} else {
submit(formData({ intent: Intent.RemoveAvatar }), {

<WalletProvider>
<ConnectWallet
chainId={chainId}
pilotAddress={getPilotAddress(waypoints)}
providerType={providerType}
onConnect={({ account, chainId, providerType }) => {
submit(
formData({
intent: Intent.ConnectWallet,
account,
chainId,
providerType,
}),
{ method: 'POST' },
)
}}
onDisconnect={() => {
submit(formData({ intent: Intent.DisconnectWallet }), {
method: 'POST',
})
}}
/>
</WalletProvider>

<AvatarInput
value={avatar}
startingWaypoint={startingWaypoint}
onChange={(avatar) => {
if (avatar != null) {
submit(formData({ intent: Intent.UpdateAvatar, avatar }), {
method: 'POST',
})
} else {
submit(formData({ intent: Intent.RemoveAvatar }), {
method: 'POST',
})
}
}}
/>

<ZodiacMod
avatar={avatar}
waypoints={waypoints}
onSelect={(module) => {
submit(formData({ module: JSON.stringify(module) }), {
method: 'POST',
})
}
}}
/>

<ZodiacMod
avatar={avatar}
waypoints={waypoints}
onSelect={(module) => {
submit(formData({ module: JSON.stringify(module) }), {
method: 'POST',
})
}}
/>

<div className="mt-8 flex justify-end">
<PrimaryButton submit intent={Intent.Save}>
Save
</PrimaryButton>
</div>
</Form>
}}
/>

<div className="mt-8 flex justify-end">
<PrimaryButton submit intent={Intent.Save}>
Save
</PrimaryButton>
</div>
</Form>
</main>

{isDev && <DebugRouteData />}
</main>
</div>
)
}

Expand All @@ -247,12 +248,7 @@ const getPilotAddress = (waypoints?: Waypoints) => {
}

const getMultisend = (route: ExecutionRoute, module: ZodiacModule) => {
const [chainId] = splitPrefixedAddress(route.avatar)

invariantResponse(
chainId != null,
`chainId is required but could not be retrieved from avatar "${route.avatar}"`,
)
const chainId = getChainId(route.avatar)

switch (module.type) {
case SupportedZodiacModuleType.ROLES_V1:
Expand Down
1 change: 1 addition & 0 deletions deployables/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@zodiac/safe": "workspace:*",
"@zodiac/schema": "workspace:*",
"@zodiac/ui": "workspace:*",
"classnames": "^2.5.1",
"connectkit": "^1.8.2",
"ethers": "6.13.5",
"isbot": "^5.1.17",
Expand Down
12 changes: 10 additions & 2 deletions deployables/extension/manifest-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,27 @@ config()
//
// node manifest-util.js ./public/manifest.json

const updateManifest = (templateFileName, outFileName, version) => {
const getIframeUrl = () => {
const iframeUrl = process.env.CONNECT_IFRAME_URL

invariant(iframeUrl != null, 'CONNECT_IFRAME_URL is missing')

if (iframeUrl.endsWith('/')) {
return iframeUrl
}

return `${iframeUrl}/`
}

const updateManifest = (templateFileName, outFileName, version) => {
try {
console.log(chalk.white.bold('Manifest template file:'))
console.log(new URL(templateFileName, import.meta.url).pathname)

const data = fs
.readFileSync(templateFileName)
.toString()
.replaceAll('<CONNECT_IFRAME_URL>', iframeUrl)
.replaceAll('<CONNECT_IFRAME_URL>', getIframeUrl())

const manifest = JSON.parse(data)
manifest['version'] = version.replace('v', '')
Expand Down
2 changes: 1 addition & 1 deletion deployables/extension/src/inject/contentScript/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const alreadyInjected =

if (
!alreadyInjected &&
window.location.origin !== 'https://connect.pilot.gnosisguild.org' &&
window.location.origin !== process.env.CONNECT_IFRAME_URL &&
isValidTab(window.location.href)
) {
document.documentElement.dataset.__zodiacPilotInjected = 'true'
Expand Down
6 changes: 3 additions & 3 deletions deployables/extension/src/manifest.template.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,19 @@
"content_scripts": [
{
"matches": ["<all_urls>"],
"exclude_globs": ["<CONNECT_IFRAME_URL>/", "about:*", "chrome:*"],
"exclude_globs": ["<CONNECT_IFRAME_URL>", "about:*", "chrome:*"],
"run_at": "document_start",
"js": ["build/connect/contentScripts/dApp.js"]
},
{
"matches": ["<CONNECT_IFRAME_URL>/"],
"matches": ["<CONNECT_IFRAME_URL>"],
"run_at": "document_start",
"all_frames": true,
"js": ["build/connect/contentScripts/connectIframe.js"]
},
{
"matches": ["<all_urls>"],
"exclude_globs": ["<CONNECT_IFRAME_URL>/", "about:*", "chrome:*"],
"exclude_globs": ["<CONNECT_IFRAME_URL>", "about:*", "chrome:*"],
"run_at": "document_start",
"js": ["build/monitor/contentScript/main.js"]
}
Expand Down
16 changes: 3 additions & 13 deletions packages/modules/src/removeAvatar.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
import { invariant } from '@epic-web/invariant'
import { ZERO_ADDRESS } from '@zodiac/chains'
import { getChainId, ZERO_ADDRESS } from '@zodiac/chains'
import type { ExecutionRoute } from '@zodiac/schema'
import {
AccountType,
formatPrefixedAddress,
splitPrefixedAddress,
} from 'ser-kit'
import { AccountType, formatPrefixedAddress } from 'ser-kit'

export const removeAvatar = (route: ExecutionRoute): ExecutionRoute => {
const [chainId] = splitPrefixedAddress(route.avatar)

invariant(
chainId,
`Could not retrieve chain ID from avatar "${route.avatar}"`,
)
const chainId = getChainId(route.avatar)

if (route.waypoints == null) {
return { ...route, avatar: formatPrefixedAddress(chainId, ZERO_ADDRESS) }
Expand Down
10 changes: 3 additions & 7 deletions packages/modules/src/updateRolesWaypoint.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { invariant } from '@epic-web/invariant'
import { getChainId } from '@zodiac/chains'
import type { ExecutionRoute, HexAddress, Waypoints } from '@zodiac/schema'
import { AccountType, splitPrefixedAddress } from 'ser-kit'
import { AccountType } from 'ser-kit'
import { createRolesWaypoint } from './createRolesWaypoint'

type RoleUpdatePayload = {
Expand All @@ -13,12 +14,7 @@ export const updateRolesWaypoint = (
route: ExecutionRoute,
{ moduleAddress, multisend, version }: RoleUpdatePayload,
): ExecutionRoute => {
const [chainId] = splitPrefixedAddress(route.avatar)

invariant(
chainId != null,
`chainId is required but could not be retrieved from avatar "${route.avatar}"`,
)
const chainId = getChainId(route.avatar)

invariant(
route.waypoints,
Expand Down
8 changes: 1 addition & 7 deletions packages/test-utils/src/renderFramework.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,7 @@ export async function createRenderFramework<Config extends RouteConfig>(
if (clientAction != null) {
return clientAction({
...actionArgs,
serverAction: action
? () =>
action({
...actionArgs,
request: new Request(actionArgs.request),
})
: undefined,
serverAction: action ? () => action(actionArgs) : undefined,
})
}

Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

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

Loading