Skip to content

Commit

Permalink
Merge pull request #90 from gnosisguild/launch-unconnected
Browse files Browse the repository at this point in the history
Launch with unconnected Pilot wallet
  • Loading branch information
jfschwarz authored Mar 5, 2024
2 parents 9d9d6fc + 828b795 commit 9c0d999
Show file tree
Hide file tree
Showing 20 changed files with 245 additions and 120 deletions.
4 changes: 1 addition & 3 deletions extension/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ const Routes: React.FC = () => {
const { connection } = useConnection()

const isConnectionsRoute = connectionsRouteMatch.isMatch
const connectionChangeRequired =
!validateAddress(connection.avatarAddress) ||
!validateAddress(connection.pilotAddress)
const connectionChangeRequired = !validateAddress(connection.avatarAddress)

const [connections] = useConnections()
const connectionToEdit =
Expand Down
2 changes: 1 addition & 1 deletion extension/src/bridge/SafeAppBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export default class SafeAppBridge {

this.connection = connection
const href = await requestIframeHref()
const currentOrigin = href ? new URL(href).host : undefined
const currentOrigin = href && new URL(href).origin

const isConnected =
this.connectedOrigin && currentOrigin === this.connectedOrigin
Expand Down
83 changes: 52 additions & 31 deletions extension/src/browser/Drawer/RolePermissionCheck.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,54 @@ import { encodeSingle, TransactionInput } from 'react-multisend'

import { Flex, Tag } from '../../components'
import { useApplicableTranslation } from '../../transactionTranslations'
import { JsonRpcError } from '../../types'
import { Connection, JsonRpcError, TransactionData } from '../../types'
import { decodeRolesV1Error } from '../../utils'
import { decodeRolesV2Error, isPermissionsError } from '../../utils/decodeError'
import { useWrappingProvider } from '../ProvideProvider'

import CopyToClipboard from './CopyToClipboard'
import { Translate } from './Translate'
import classes from './style.module.css'
import { useConnection } from '../../connections'
import { KnownContracts } from '@gnosis.pm/zodiac'
import { wrapRequest } from '../../providers/WrappingProvider'
import { useTenderlyProvider } from '../../providers'
import { TenderlyProvider } from '../../providers/ProvideTenderly'

const simulateRolesTransaction = async (
encodedTransaction: TransactionData,
connection: Connection,
tenderlyProvider: TenderlyProvider
) => {
const wrappedTransaction = wrapRequest(encodedTransaction, connection, false)

// TODO enable this once we can query role members from ser
// if (!wrappedTransaction.from && connection.roleId) {
// // If pilotAddress is not yet determined, we will use a random member of the specified role
// wrappedTransaction.from = await getRoleMember(connection)
// }

try {
await tenderlyProvider.request({
method: 'eth_estimateGas',
params: [wrappedTransaction],
})
} catch (e) {
const decodedError =
connection.moduleType === KnownContracts.ROLES_V1
? decodeRolesV1Error(e as JsonRpcError)
: decodeRolesV2Error(e as JsonRpcError)

if (!decodedError) {
console.error('Unexpected error', e)
}

return decodedError && isPermissionsError(decodedError.signature)
? decodedError.signature
: false
}

return false
}

const RolePermissionCheck: React.FC<{
transaction: TransactionInput
Expand All @@ -23,45 +61,28 @@ const RolePermissionCheck: React.FC<{
mini?: boolean
}> = ({ transaction, isDelegateCall, index, mini = false }) => {
const [error, setError] = useState<string | undefined | false>(undefined)
const wrappingProvider = useWrappingProvider()
const {
connection: { moduleType },
} = useConnection()
const { connection } = useConnection()
const tenderlyProvider = useTenderlyProvider()

const encodedTransaction = encodeSingle(transaction)

const translationAvailable = !!useApplicableTranslation(encodedTransaction)

useEffect(() => {
let canceled = false
wrappingProvider
.request({
method: 'eth_estimateGas',
params: [encodedTransaction],
})
.then(() => {
if (!canceled) setError(false)
})
.catch((e: JsonRpcError) => {
const decodedError =
moduleType === KnownContracts.ROLES_V1
? decodeRolesV1Error(e)
: decodeRolesV2Error(e)
if (!canceled && decodedError) {
setError(
isPermissionsError(decodedError.signature)
? decodedError.signature
: false
)
}
if (!decodedError || !isPermissionsError(decodedError.signature)) {
throw e
}
})

simulateRolesTransaction(
encodedTransaction,
connection,
tenderlyProvider
).then((error) => {
if (!canceled) setError(error)
})

return () => {
canceled = true
}
}, [wrappingProvider, moduleType, encodedTransaction])
}, [encodedTransaction, connection, tenderlyProvider])

if (error === undefined) return null

Expand Down
48 changes: 40 additions & 8 deletions extension/src/browser/Drawer/Submit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { toast } from 'react-toastify'

import { Button, IconButton } from '../../components'
import toastClasses from '../../components/Toast/Toast.module.css'
import { ChainId, EXPLORER_URL, CHAIN_PREFIX } from '../../chains'
import { ChainId, EXPLORER_URL, CHAIN_PREFIX, CHAIN_NAME } from '../../chains'
import { waitForMultisigExecution } from '../../providers'
// import { shallExecuteDirectly } from '../../safe/sendTransaction'
import { useConnection } from '../../connections'
Expand All @@ -20,11 +20,13 @@ import { useDispatch, useNewTransactions } from '../state'

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

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

const transactions = useNewTransactions()
const submitTransactions = useSubmitTransactions()
Expand All @@ -41,7 +43,24 @@ const Submit: React.FC = () => {
// }
// }, [provider, connection])

const connectWallet = () => {
pushConnectionsRoute(connection.id)
}

const submit = async () => {
if (!connected) {
if (!connect) throw new Error('invariant violation')

const success = await connect()
if (!success) {
const chainName = CHAIN_NAME[chainId] || `#${chainId}`
toast.error(
`Switch your wallet to ${chainName} to submit the transactions`
)
return
}
}

if (!submitTransactions) throw new Error('invariant violation')
setSignaturePending(true)
let batchTransactionHash: string
Expand Down Expand Up @@ -104,12 +123,25 @@ const Submit: React.FC = () => {

return (
<>
<Button
onClick={submit}
disabled={!submitTransactions || transactions.length === 0}
>
Submit
</Button>
{(connected || !!connect) && (
<Button
onClick={submit}
disabled={!submitTransactions || transactions.length === 0}
>
Submit
</Button>
)}

{!connected && !connect && (
<Button
onClick={connectWallet}
disabled={!submitTransactions || transactions.length === 0}
secondary
>
Connect wallet to submit
</Button>
)}

{signaturePending && (
<AwaitingSignatureModal
isOpen={signaturePending}
Expand Down
6 changes: 4 additions & 2 deletions extension/src/browser/Drawer/Transaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,10 @@ export const Transaction: React.FC<Props> = ({
const { connection } = useConnection()
const elementRef = useScrollIntoView(scrollIntoView)
const showRoles =
connection.moduleType === KnownContracts.ROLES_V1 ||
connection.moduleType === KnownContracts.ROLES_V2
(connection.moduleType === KnownContracts.ROLES_V1 ||
connection.moduleType === KnownContracts.ROLES_V2) &&
!!connection.roleId &&
!!connection.pilotAddress // TODO remove this check once we can query role members via ser to get a fallback

return (
<Box ref={elementRef} p={2} className={classes.container}>
Expand Down
15 changes: 12 additions & 3 deletions extension/src/browser/Drawer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { RiFileCopy2Line, RiRefreshLine } from 'react-icons/ri'
import { encodeMulti, encodeSingle } from 'react-multisend'
import { toast } from 'react-toastify'

import { BlockButton, Box, Drawer, Flex, IconButton } from '../../components'
import { BlockButton, Button, Drawer, Flex, IconButton } from '../../components'
import { ForkProvider } from '../../providers'
import { wrapRequest } from '../../providers/WrappingProvider'
import { useConnection } from '../../connections'
Expand Down Expand Up @@ -166,9 +166,18 @@ const TransactionsDrawer: React.FC = () => {
</p>
)}
</Flex>
<Box p={2} bg>
<Flex justifyContent="space-between" gap={2}>
{!connection.pilotAddress && (
<Button
secondary
onClick={copyTransactionData}
disabled={newTransactions.length === 0}
>
Copy transaction data
</Button>
)}
<Submit />
</Box>
</Flex>
</Flex>
</Drawer>
)
Expand Down
4 changes: 4 additions & 0 deletions extension/src/browser/Drawer/style.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,10 @@ button.link:hover {
left: 0;
}

.secondaryButton {
background: none;
}

@-webkit-keyframes pulse {
to {
box-shadow: 0 0 0 8px rgba(232, 76, 61, 0);
Expand Down
19 changes: 5 additions & 14 deletions extension/src/browser/ProvideProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,6 @@ interface Props {
const ProviderContext = createContext<Eip1193Provider | null>(null)
export const useProvider = () => useContext(ProviderContext)

const WrappingProviderContext = createContext<WrappingProvider | null>(null)
export const useWrappingProvider = () => {
const value = useContext(WrappingProviderContext)
if (!value) throw new Error('must be wrapped in <ProvideProvider>')
return value
}

const SubmitTransactionsContext = createContext<(() => Promise<string>) | null>(
null
)
Expand Down Expand Up @@ -144,13 +137,11 @@ const ProvideProvider: React.FC<Props> = ({ simulate, children }) => {
<ProviderContext.Provider
value={simulate ? forkProvider : wrappingProvider}
>
<WrappingProviderContext.Provider value={wrappingProvider}>
<SubmitTransactionsContext.Provider
value={simulate ? submitTransactions : null}
>
{children}
</SubmitTransactionsContext.Provider>
</WrappingProviderContext.Provider>
<SubmitTransactionsContext.Provider
value={simulate ? submitTransactions : null}
>
{children}
</SubmitTransactionsContext.Provider>
</ProviderContext.Provider>
)
}
Expand Down
5 changes: 4 additions & 1 deletion extension/src/components/Address/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,12 @@ const Address: React.FC<Props> = ({

return (
<Box rounded className={cn(classes.container, className)}>
<div className={classes.address}>{displayAddress}</div>
<div className={classes.address}>
{checksumAddress ? displayAddress : 'No connection'}
</div>
<Box rounded className={classes.blockieContainer}>
{address && <Blockie address={address} className={classes.blockies} />}
{!address && <div className={classes.noAddress} />}
</Box>
{copyToClipboard && (
<IconButton
Expand Down
23 changes: 23 additions & 0 deletions extension/src/components/Address/style.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
.address {
font-family: 'Roboto Mono', monospace;
font-size: 14px;
white-space: nowrap;
}

.link {
Expand All @@ -26,3 +27,25 @@
width: auto;
aspect-ratio: 1;
}

.noAddress {
width: 100%;
height: 100%;
position: relative;
}

.noAddress:after,
.noAddress:before {
content: '';
height: calc(100% + var(--spacing-1) * 2);
width: 2px;
position: absolute;
top: calc(var(--spacing-1) * -1);
left: calc(50% - 1px);
background-color: var(--border-color);
transform: rotate(45deg);
}

.noAddress:before {
transform: rotate(-45deg);
}
19 changes: 14 additions & 5 deletions extension/src/components/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,22 @@ import React from 'react'

import classes from './style.module.css'

const Button: React.FC<
React.DetailedHTMLProps<
interface ButtonProps
extends React.DetailedHTMLProps<
React.ButtonHTMLAttributes<HTMLButtonElement>,
HTMLButtonElement
>
> = ({ className, ...rest }) => (
<button className={cn(classes.button, className)} {...rest} />
> {
secondary?: boolean
}
const Button: React.FC<ButtonProps> = ({
className,
secondary = false,
...rest
}) => (
<button
className={cn(classes.button, className, secondary && classes.secondary)}
{...rest}
/>
)

export default Button
4 changes: 4 additions & 0 deletions extension/src/components/Button/style.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@
height: 16px;
margin-right: 10px;
}

.secondary {
background: none;
}
Loading

0 comments on commit 9c0d999

Please sign in to comment.