Skip to content

Commit

Permalink
dedupe providers and fix various issues
Browse files Browse the repository at this point in the history
  • Loading branch information
jfschwarz committed Jan 31, 2024
1 parent 3152e0a commit 5a56047
Show file tree
Hide file tree
Showing 20 changed files with 320 additions and 199 deletions.
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
14 changes: 0 additions & 14 deletions extension/.pnp.cjs

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

2 changes: 1 addition & 1 deletion extension/.vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"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": {
Expand Down
Binary file not shown.
Binary file not shown.
1 change: 0 additions & 1 deletion extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
"devDependencies": {
"@ethersproject/abi": "^5.7.0",
"@ethersproject/abstract-provider": "^5.7.0",
"@ethersproject/experimental": "^5.7.0",
"@ethersproject/providers": "^5.7.2",
"@gnosis.pm/zodiac": "^3.4.2",
"@safe-global/api-kit": "^1.3.1",
Expand Down
11 changes: 5 additions & 6 deletions extension/src/browser/Drawer/Submit.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { providers } from 'ethers'
import React, { useState } from 'react'
import { RiCloseLine, RiExternalLinkLine } from 'react-icons/ri'
import Modal, { Styles } from 'react-modal'
Expand All @@ -16,9 +15,10 @@ import { useSubmitTransactions } from '../ProvideProvider'
import { useDispatch, useNewTransactions } from '../state'

import classes from './style.module.css'
import { getReadOnlyProvider } from '../../providers/readOnlyProvider'

const Submit: React.FC = () => {
const { provider, connection } = useConnection()
const { connection } = useConnection()
const { chainId, pilotAddress, providerType } = connection
const dispatch = useDispatch()

Expand Down Expand Up @@ -61,16 +61,15 @@ const Submit: React.FC = () => {

// wait for transaction to be mined
const realBatchTransactionHash = await waitForMultisigExecution(
provider,
chainId,
batchTransactionHash
)
console.log(
`Transaction batch ${batchTransactionHash} has been executed with transaction hash ${realBatchTransactionHash}`
)
const receipt = await new providers.Web3Provider(
provider
).waitForTransaction(realBatchTransactionHash)
const receipt = await getReadOnlyProvider(chainId).waitForTransaction(
realBatchTransactionHash
)
console.log(
`Transaction ${realBatchTransactionHash} has been mined`,
receipt
Expand Down
8 changes: 5 additions & 3 deletions extension/src/browser/Frame.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useRef } from 'react'
import React, { useLayoutEffect, useRef } from 'react'

import Eip1193Bridge from '../bridge/Eip1193Bridge'
import SafeAppBridge from '../bridge/SafeAppBridge'
Expand All @@ -16,7 +16,10 @@ const BrowserFrame: React.FC<Props> = ({ src }) => {
const eip1193BridgeRef = useRef<Eip1193Bridge | null>(null)
const safeAppBridgeRef = useRef<SafeAppBridge | null>(null)

useEffect(() => {
// We need the message listener to be set up before the iframe content window is loaded.
// Otherwise we might miss the initial handshake message from the Safe SDK.
// Using a layout effect ensures that the listeners are set synchronously after DOM flush.
useLayoutEffect(() => {
if (!provider) return

// establish EIP-1193 bridge
Expand All @@ -40,7 +43,6 @@ const BrowserFrame: React.FC<Props> = ({ src }) => {
safeAppBridgeRef.current?.handleMessage(ev)
}
window.addEventListener('message', handle)
console.log('messages will be handled')

return () => {
window.removeEventListener('message', handle)
Expand Down
148 changes: 84 additions & 64 deletions extension/src/providers/ProvideTenderly.tsx
Original file line number Diff line number Diff line change
@@ -1,94 +1,56 @@
import EventEmitter from 'events'

import { JsonRpcProvider } from '@ethersproject/providers'
import React, { useContext, useEffect, useState } from 'react'
import React, { useContext, useEffect, useMemo } from 'react'

import { useConnection } from '../settings/connectionHooks'
import { Eip1193Provider, JsonRpcRequest } from '../types'
import { useBeforeUnload } from '../utils'
import { initSafeProtocolKit } from '../safe/kits'
import { safeInterface } from '../safe'
import { getEip1193ReadOnlyProvider } from './readOnlyProvider'
import { ChainId } from '../chains'

const TenderlyContext = React.createContext<TenderlyProvider | null>(null)

export const useTenderlyProvider = (): TenderlyProvider | null =>
useContext(TenderlyContext)
export const useTenderlyProvider = (): TenderlyProvider => {
const value = useContext(TenderlyContext)
if (!value) throw new Error('must be wrapped in <ProvideTenderly>')
return value
}

const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'

const ProvideTenderly: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const { provider, chainId, connection } = useConnection()
const {
connection: { chainId, avatarAddress, moduleAddress, pilotAddress },
} = useConnection()

const [tenderlyProvider, setTenderlyProvider] =
useState<TenderlyProvider | null>(null)
const tenderlyProvider = useMemo(() => {
return new TenderlyProvider(chainId)
}, [chainId])

// whenever anything changes in the connection settings, we delete the current fork and start afresh
useEffect(() => {
if (!chainId) return

const tenderlyProvider = new TenderlyProvider(provider, chainId)
setTenderlyProvider(tenderlyProvider)

const canceled = false
async function prepareSafeForSimulation() {
const { avatarAddress, moduleAddress, pilotAddress } = connection
const safe = await initSafeProtocolKit(provider, avatarAddress)

// If we simulate as a Safe owner, we might have to override the owners & threshold of the Safe to allow single signature transactions
if (!moduleAddress) {
const [owners, threshold] = await Promise.all([
safe.getOwners(),
safe.getThreshold(),
])

const pilotIsOwner = owners.some(
(owner) => owner.toLowerCase() === pilotAddress.toLowerCase()
)

if (!pilotIsOwner) {
// the pilot account is not an owner, so we need to make it one and set the threshold to 1 at the same time
await tenderlyProvider.request({
method: 'eth_sendTransaction',
params: [
{
to: avatarAddress,
data: safeInterface.encodeFunctionData(
'addOwnerWithThreshold',
[pilotAddress, 1]
),
from: avatarAddress,
},
],
})
} else if (threshold > 1) {
// doesn't allow to execute with single signature, so we need to override the threshold
await tenderlyProvider.request({
method: 'eth_sendTransaction',
params: [
{
to: avatarAddress,
data: safeInterface.encodeFunctionData('changeThreshold', [1]),
from: avatarAddress,
},
],
})
}
}

if (!canceled) return
}

prepareSafeForSimulation()
prepareSafeForSimulation(
{ chainId, avatarAddress, moduleAddress, pilotAddress },
tenderlyProvider
)

return () => {
tenderlyProvider.deleteFork()
}
}, [provider, chainId, connection])
}, [tenderlyProvider, chainId, avatarAddress, moduleAddress, pilotAddress])

// delete fork when closing browser tab (the effect teardown won't be executed in that case)
useBeforeUnload(() => {
if (tenderlyProvider) tenderlyProvider.deleteFork()
})

if (!tenderlyProvider) return null

return (
<TenderlyContext.Provider value={tenderlyProvider}>
{children}
Expand All @@ -98,6 +60,64 @@ const ProvideTenderly: React.FC<{ children: React.ReactNode }> = ({

export default ProvideTenderly

async function prepareSafeForSimulation(
{
chainId,
avatarAddress,
moduleAddress,
pilotAddress,
}: {
chainId: ChainId
avatarAddress: string
moduleAddress: string
pilotAddress: string
},
tenderlyProvider: TenderlyProvider
) {
const safe = await initSafeProtocolKit(chainId, avatarAddress)

// If we simulate as a Safe owner, we might have to override the owners & threshold of the Safe to allow single signature transactions
if (!moduleAddress) {
const [owners, threshold] = await Promise.all([
safe.getOwners(),
safe.getThreshold(),
])

const pilotIsOwner = owners.some(
(owner) => owner.toLowerCase() === pilotAddress.toLowerCase()
)

if (!pilotIsOwner) {
// the pilot account is not an owner, so we need to make it one and set the threshold to 1 at the same time
await tenderlyProvider.request({
method: 'eth_sendTransaction',
params: [
{
to: avatarAddress,
data: safeInterface.encodeFunctionData('addOwnerWithThreshold', [
pilotAddress,
1,
]),
from: avatarAddress,
},
],
})
} else if (threshold > 1) {
// doesn't allow to execute with single signature, so we need to override the threshold
await tenderlyProvider.request({
method: 'eth_sendTransaction',
params: [
{
to: avatarAddress,
data: safeInterface.encodeFunctionData('changeThreshold', [1]),
from: avatarAddress,
},
],
})
}
}
}

export interface TenderlyTransactionInfo {
id: string
project_id: string
Expand Down Expand Up @@ -157,9 +177,9 @@ export class TenderlyProvider extends EventEmitter {

private tenderlyForkApi: string

constructor(provider: Eip1193Provider, chainId: number) {
constructor(chainId: ChainId) {
super()
this.provider = provider
this.provider = getEip1193ReadOnlyProvider(chainId, ZERO_ADDRESS)
this.chainId = chainId
this.tenderlyForkApi = 'https://fork-api.pilot.gnosisguild.org'
}
Expand Down
5 changes: 1 addition & 4 deletions extension/src/providers/WrappingProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,7 @@ class WrappingProvider extends EventEmitter {

if (!this.connection.moduleAddress) {
// use safeTxGas estimation for direct execution
const safeApiKit = initSafeApiKit(
this.provider,
this.connection.chainId
)
const safeApiKit = initSafeApiKit(this.connection.chainId)
const result = await safeApiKit.estimateSafeTransaction(
this.connection.avatarAddress,
request
Expand Down
Loading

0 comments on commit 5a56047

Please sign in to comment.