Skip to content

Commit

Permalink
Merge pull request #133 from gnosisguild/rabby-competition
Browse files Browse the repository at this point in the history
co-exist with Rabby
  • Loading branch information
jfschwarz authored Aug 19, 2024
2 parents 72063a0 + d9ee77e commit 0c4e374
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 24 deletions.
1 change: 1 addition & 0 deletions extension/src/browser/Drawer/RolePermissionCheck.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const simulateRolesTransaction = async (
) as SerRoute
const plan = await planExecution([encodedTransaction], routeWithInitiator)

// TODO generalize permission checking logic (ser-kit)
if (plan.length > 1) {
throw new Error('Multi-step execution not yet supported')
}
Expand Down
6 changes: 3 additions & 3 deletions extension/src/contentScript.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
function inject(windowName: string, scriptPath: string) {
if (window.name === windowName) {
const node = document.createElement('script')
node.type = 'text/javascript'
node.async = false
node.src = chrome.runtime.getURL(scriptPath)

const parent = document.head || document.documentElement
Expand All @@ -11,9 +13,7 @@ function inject(windowName: string, scriptPath: string) {
}
parent.dataset.zodiacPilotInjected = 'true'
parent.insertBefore(node, parent.children[0])
node.onload = function () {
node.remove()
}
node.remove()
}
}

Expand Down
62 changes: 41 additions & 21 deletions extension/src/injection.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,54 @@
// This script will be injected via contentScripts.ts into the browser iframe running the Dapp.

import InjectedProvider from './bridge/InjectedProvider'
declare let window: Window & { ethereum: InjectedProvider }

if (window.ethereum) {
// There is already a provider injected
const descriptor = Object.getOwnPropertyDescriptor(window, 'ethereum')
if (descriptor?.configurable === false) {
// We got a problem: The provider is not configurable (most probably Rabby)
alert(
'Zodiac Pilot is unable to connect. In Rabby, flip the setting so it is banned and reload the page.'
)
declare let window: Window & {
ethereum: InjectedProvider
rabbyWalletRouter?: {
setDefaultProvider(rabbyAsDefault: boolean): void
addProvider(provider: InjectedProvider): void
}
}

// inject bridged ethereum provider
const injectedProvider = new InjectedProvider()
Object.defineProperties(window, {
ethereum: {
get() {
return injectedProvider
},
set() {
// do nothing

const canSetWindowEthereum =
Object.getOwnPropertyDescriptor(window, 'ethereum')?.configurable !== false

if (canSetWindowEthereum) {
Object.defineProperties(window, {
ethereum: {
get() {
return injectedProvider
},
set() {
// do nothing
},
configurable: false,
},
configurable: false,
},
})
})

console.log('Injected Zodiac Pilot provider')
} else {
// Houston, we have a problem: There is already a provider injected by another extension and it's not configurable

console.log('injected into', document.title)
// If it's Rabby we have a trick to make sure it routes to the Pilot provider
if (window.rabbyWalletRouter) {
console.log(
'Rabby detected, setting Pilot as default provider in Rabby Wallet Router',
window.rabbyWalletRouter
)
window.rabbyWalletRouter.addProvider(injectedProvider)
window.rabbyWalletRouter.setDefaultProvider(false)
// prevent Rabby from setting its own provider as default subsequently
window.rabbyWalletRouter.setDefaultProvider = () => {}
} else {
// If it's not Rabby, we have to alert the user
alert(
'Zodiac Pilot is unable to connect because of another wallet extension. Disable the other extension and reload the page.'
)
}
}

// establish message bridge for location requests
window.addEventListener('message', (ev: MessageEvent) => {
Expand Down
4 changes: 4 additions & 0 deletions extension/src/providers/ForkProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ class ForkProvider extends EventEmitter {
*
* Even for regular calls (operation: 0) this method is useful to simulate the transaction through the Safe contract with the module or owner as sender (if set).
* This is necessary to make sure the simulation succeeds for some edge cases: If a contract calls `.transfer()` on the sender's address this comes only with a 2300 gas stipend, not enough to run a cold Safe's `fallback` function.
* This could alternatively be solved using EIP-2930 access lists, though.
*
* @param metaTx A MetaTransaction object, can be operation: 1 (delegatecall)
*/
Expand Down Expand Up @@ -325,6 +326,9 @@ const readBlockGasLimit = async (provider: Eip1193Provider) => {
return block?.gasLimit || 30_000_000n
}

/**
* Makes sure that the given Safe is ready for simulating transactions, which requires at least one module to be enabled.
*/
async function prepareSafeForSimulation(
{
chainId,
Expand Down

0 comments on commit 0c4e374

Please sign in to comment.