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

Connect via Safe Apps SDK #83

Merged
merged 7 commits into from
Feb 12, 2024
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ When the simulator iframe opens any page, we inject the build/inject.js script a
The injected script then runs in the context of the Dapp and injects an [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) compatible API at `window.ethereum`.
The injected provider forwards all `request` calls to the parent extension page via `window.postMessage` where they are recorded and executed in a fork of the connected network.

We also subscribe to messages sent via the [safe-apps-sdk](https://github.com/safe-global/safe-apps-sdk).
This enables instant and smart-account optimized connections to Safe-compatible apps.

### Simulating transaction in a fork

When the provider we inject into the Dapp iframe receives a transaction request, we record it and simulate the transaction in a fork of the target network, impersonating the Safe.
Expand Down
1 change: 1 addition & 0 deletions extension/.cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"blockies",
"borderless",
"Bytecode",
"ccip",
"cowswap",
"Delegatecall",
"delegatecalls",
Expand Down
10 changes: 9 additions & 1 deletion extension/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,15 @@
"@typescript-eslint/no-non-null-assertion": "off",
"react/prop-types": "off", // we are not exporting any components so this rule is not super relevant, in tests we don't want to be forced to define prop types
"jsx-a11y/no-onchange": "off", // this rule is deprecated, but somehow still part of jsx-a11y/recommended
"jsx-a11y/label-has-associated-control": "off" // this rule gives false positives when the control lives in the component of a child element
"jsx-a11y/label-has-associated-control": "off", // this rule gives false positives when the control lives in the component of a child element
"@typescript-eslint/no-unused-vars": [
"warn",
{
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_",
"caughtErrorsIgnorePattern": "^_"
}
]
},
"settings": {
"react": {
Expand Down
165 changes: 165 additions & 0 deletions extension/.pnp.cjs

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

5 changes: 3 additions & 2 deletions extension/.vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
"typescript.tsdk": ".yarn/sdks/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit"
},
"jest.jestCommandLine": "yarn test",
"search.exclude": {
"**/.yarn": true,
"**/.pnp.*": true
},
"eslint.nodePath": ".yarn/sdks",
"prettier.prettierPath": ".yarn/sdks/prettier/index.js"
"prettier.prettierPath": ".yarn/sdks/prettier/index.js",
"mdx-preview.preview.useVscodeMarkdownStyles": false
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
4 changes: 3 additions & 1 deletion extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
"@gnosis.pm/zodiac": "^3.4.2",
"@safe-global/api-kit": "^1.3.1",
"@safe-global/protocol-kit": "^1.3.0",
"@safe-global/safe-apps-sdk": "^9.0.0",
"@safe-global/safe-core-sdk-types": "^2.3.0",
"@safe-global/safe-gateway-typescript-sdk": "^3.14.0",
"@shazow/whatsabi": "^0.2.1",
"@testing-library/jest-dom": "^5.16.1",
"@typechain/ethers-v5": "^10.2.1",
Expand Down Expand Up @@ -85,4 +87,4 @@
"typescript-plugin-css-modules": "^3.4.0"
},
"packageManager": "[email protected]"
}
}
19 changes: 1 addition & 18 deletions extension/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { validateAddress } from './utils'
const Routes: React.FC = () => {
const connectionsRouteMatch = useMatchConnectionsRoute()
const pushConnectionsRoute = usePushConnectionsRoute()
const { connection, connected } = useConnection()
const { connection } = useConnection()

const isConnectionsRoute = connectionsRouteMatch.isMatch
const connectionChangeRequired =
Expand All @@ -35,9 +35,6 @@ const Routes: React.FC = () => {
const connectionToEdit =
connections.length === 1 ? connections[0].id : undefined

const waitForWallet =
!isConnectionsRoute && !connectionChangeRequired && !connected

useUpdateLastUsedConnection()

// open connections drawer if a valid connection is not available
Expand All @@ -52,21 +49,7 @@ const Routes: React.FC = () => {
connectionChangeRequired,
])

// open connections drawer if wallet is not connected, but only after a small delay to give the wallet time to connect when initially loading the page
useEffect(() => {
let timeout: number
if (waitForWallet) {
timeout = window.setTimeout(() => {
pushConnectionsRoute()
}, 200)
}
return () => {
window.clearTimeout(timeout)
}
}, [waitForWallet, pushConnectionsRoute])

if (!isConnectionsRoute && connectionChangeRequired) return null
if (!isConnectionsRoute && waitForWallet) return null

return (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ interface Request {
params?: Array<any>
}

export default class BridgeHost {
export default class Eip1193Bridge {
private provider: Eip1193Provider
private connection: Connection
private source: WindowProxy | undefined
Expand All @@ -15,11 +15,11 @@ export default class BridgeHost {
this.connection = connection
}

setProvider(provider: Eip1193Provider) {
setProvider = (provider: Eip1193Provider) => {
this.provider = provider
}

setConnection(connection: Connection) {
setConnection = (connection: Connection) => {
if (connection.avatarAddress !== this.connection.avatarAddress) {
this.emitBridgeEvent('accountsChanged', [[connection.avatarAddress]])
}
Expand Down
Loading
Loading