Skip to content
This repository was archived by the owner on Nov 10, 2023. It is now read-only.
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
29 changes: 8 additions & 21 deletions src/logic/safe/store/actions/createTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ interface CreateTransactionArgs {

type CreateTransactionAction = ThunkAction<Promise<void>, AppReduxState, undefined, AnyAction>
type ConfirmEventHandler = (safeTxHash: string) => void
type RejectEventHandler = () => void
type ErrorEventHandler = () => void

const createTransaction = (
{
Expand All @@ -126,7 +126,7 @@ const createTransaction = (
origin = null,
}: CreateTransactionArgs,
onUserConfirm?: ConfirmEventHandler,
onUserReject?: RejectEventHandler,
onError?: ErrorEventHandler,
): CreateTransactionAction => async (dispatch: Dispatch, getState: () => AppReduxState): Promise<void> => {
const state = getState()

Expand Down Expand Up @@ -172,27 +172,27 @@ const createTransaction = (
sender: from,
sigs,
}
const safeTxHash = generateSafeTxHash(safeAddress, txArgs)

try {
// Here we're checking that safe contract version is greater or equal 1.1.1, but
// theoretically EIP712 should also work for 1.0.0 contracts
const canTryOffchainSigning =
!isExecution && !smartContractWallet && semverSatisfies(safeVersion, SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES)
if (canTryOffchainSigning) {
const signature = await tryOffchainSigning({ ...txArgs, safeAddress }, hardwareWallet)
const signature = await tryOffchainSigning(safeTxHash, { ...txArgs, safeAddress }, hardwareWallet)

if (signature) {
dispatch(closeSnackbarAction({ key: beforeExecutionKey }))

await saveTxToHistory({ ...txArgs, signature, origin })
dispatch(enqueueSnackbar(notificationsQueue.afterExecution.moreConfirmationsNeeded))

dispatch(fetchTransactions(safeAddress))

await saveTxToHistory({ ...txArgs, signature, origin })
onUserConfirm?.(safeTxHash)
return
}
}

const safeTxHash = generateSafeTxHash(safeAddress, txArgs)
const tx = isExecution
? await getExecutionTransaction(txArgs)
: await getApprovalTransaction(safeInstance, safeTxHash)
Expand Down Expand Up @@ -245,20 +245,7 @@ const createTransaction = (
removeTxFromStore(mockedTx, safeAddress, dispatch, state)
console.error('Tx error: ', error)

// Different wallets return different error messages in this case. This is an assumption that if
// error message includes "user" word, the tx was rejected by user

let errorIncludesUserWord = false
if (typeof error === 'string') {
errorIncludesUserWord = (error as string).includes('User') || (error as string).includes('user')
}
if (error.message) {
errorIncludesUserWord = error.message.includes('User') || error.message.includes('user')
}

if (errorIncludesUserWord) {
onUserReject?.()
}
onError?.()
})
.then(async (receipt) => {
if (pendingExecutionKey) {
Expand Down
2 changes: 1 addition & 1 deletion src/logic/safe/store/actions/processTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const processTransaction = ({ approveAndExecute, notifiedTransaction, safeAddres
const canTryOffchainSigning =
!isExecution && !smartContractWallet && semverSatisfies(safeVersion, SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES)
if (canTryOffchainSigning) {
const signature = await tryOffchainSigning({ ...txArgs, safeAddress }, hardwareWallet)
const signature = await tryOffchainSigning(tx.safeTxHash, { ...txArgs, safeAddress }, hardwareWallet)

if (signature) {
dispatch(closeSnackbarAction(beforeExecutionKey))
Expand Down
2 changes: 1 addition & 1 deletion src/logic/safe/store/middleware/notificationsMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ const notificationsMiddleware = (store) => (next) => async (action) => {
}
case ADD_INCOMING_TRANSACTIONS: {
action.payload.forEach((incomingTransactions, safeAddress) => {
const { latestIncomingTxBlock } = state.safes.get('safes').get(safeAddress)
const { latestIncomingTxBlock } = state.safes.get('safes').get(safeAddress, {})
const viewedSafes = state.currentSession['viewedSafes']
const recurringUser = viewedSafes?.includes(safeAddress)

Expand Down
10 changes: 6 additions & 4 deletions src/logic/safe/transactions/offchainSigner/EIP712Signer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions'
import { AbstractProvider } from 'web3-core'
import { getWeb3 } from 'src/logic/wallets/getWeb3'
import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions'

const EIP712_NOT_SUPPORTED_ERROR_MSG = "EIP712 is not supported by user's wallet"

Expand Down Expand Up @@ -59,7 +60,7 @@ const generateTypedDataFrom = async ({
}

export const getEIP712Signer = (version?: string) => async (txArgs) => {
const web3: any = getWeb3()
const web3 = getWeb3()
const typedData = await generateTypedDataFrom(txArgs)

let method = 'eth_signTypedData_v3'
Expand All @@ -80,13 +81,14 @@ export const getEIP712Signer = (version?: string) => async (txArgs) => {
}

return new Promise((resolve, reject) => {
web3.currentProvider.sendAsync(signedTypedData, (err, signature) => {
const provider = web3.currentProvider as AbstractProvider
provider.sendAsync(signedTypedData, (err, signature) => {
if (err) {
reject(err)
return
}

if (signature.result == null) {
if (signature?.result == null) {
reject(new Error(EIP712_NOT_SUPPORTED_ERROR_MSG))
return
}
Expand Down
27 changes: 7 additions & 20 deletions src/logic/safe/transactions/offchainSigner/ethSigner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,21 @@ import { AbstractProvider } from 'web3-core/types'

const ETH_SIGN_NOT_SUPPORTED_ERROR_MSG = 'ETH_SIGN_NOT_SUPPORTED'

export const ethSigner = async ({
baseGas,
data,
gasPrice,
gasToken,
nonce,
operation,
refundReceiver,
safeInstance,
safeTxGas,
sender,
to,
valueInWei,
}): Promise<string> => {
type EthSignerArgs = {
safeTxHash: string
sender: string
}

export const ethSigner = async ({ safeTxHash, sender }: EthSignerArgs): Promise<string> => {
const web3 = await getWeb3()
const txHash = await safeInstance.methods
.getTransactionHash(to, valueInWei, data, operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, nonce)
.call({
from: sender,
})

return new Promise(function (resolve, reject) {
const provider = web3.currentProvider as AbstractProvider
provider.sendAsync(
{
jsonrpc: '2.0',
method: 'eth_sign',
params: [sender, txHash],
params: [sender, safeTxHash],
id: new Date().getTime(),
},
async function (err, signature) {
Expand Down
6 changes: 3 additions & 3 deletions src/logic/safe/transactions/offchainSigner/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ethSigner } from './ethSigner'
const SIGNERS = {
EIP712_V3: getEIP712Signer('v3'),
EIP712_V4: getEIP712Signer('v4'),
EIP712: getEIP712Signer() as any,
EIP712: getEIP712Signer(),
ETH_SIGN: ethSigner,
}

Expand All @@ -18,13 +18,13 @@ const getSignersByWallet = (isHW) =>

export const SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES = '>=1.1.1'

export const tryOffchainSigning = async (txArgs, isHW) => {
export const tryOffchainSigning = async (safeTxHash: string, txArgs, isHW: boolean): Promise<string> => {
let signature

const signerByWallet = getSignersByWallet(isHW)
for (const signingFunc of signerByWallet) {
try {
signature = await signingFunc(txArgs)
signature = await signingFunc({ ...txArgs, safeTxHash })

break
} catch (err) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,8 @@ type OwnProps = {
safeAddress: string
safeName: string
ethBalance: string
onCancel: () => void
onUserConfirm: (safeTxHash: string) => void
onUserTxReject: () => void
onTxReject: () => void
onClose: () => void
}

Expand All @@ -82,16 +81,20 @@ const ConfirmTransactionModal = ({
safeAddress,
ethBalance,
safeName,
onCancel,
onUserConfirm,
onClose,
onUserTxReject,
onTxReject,
}: OwnProps): React.ReactElement | null => {
const dispatch = useDispatch()
if (!isOpen) {
return null
}

const handleTxRejection = () => {
onTxReject()
onClose()
}

const handleUserConfirmation = (safeTxHash: string): void => {
onUserConfirm(safeTxHash)
onClose()
Expand All @@ -113,10 +116,9 @@ const ConfirmTransactionModal = ({
navigateToTransactionsTab: false,
},
handleUserConfirmation,
onUserTxReject,
handleTxRejection,
),
)
onClose()
}

const areTxsMalformed = txs.some((t) => !isTxValid(t))
Expand Down Expand Up @@ -165,13 +167,13 @@ const ConfirmTransactionModal = ({
footer={
<ModalFooterConfirmation
cancelText="Cancel"
handleCancel={onCancel}
handleCancel={handleTxRejection}
handleOk={confirmTransactions}
okDisabled={areTxsMalformed}
okText="Submit"
/>
}
onClose={onClose}
onClose={handleTxRejection}
/>
)
}
Expand Down
12 changes: 10 additions & 2 deletions src/routes/safe/components/Apps/hooks/useIframeMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from '@gnosis.pm/safe-apps-sdk'
import { useDispatch, useSelector } from 'react-redux'
import { useEffect, useCallback, MutableRefObject } from 'react'
import { getTxServiceHost } from 'src/config/'
import {
safeEthBalanceSelector,
safeNameSelector,
Expand Down Expand Up @@ -85,16 +86,23 @@ const useIframeMessageHandler = (
}

case SDK_MESSAGES.SAFE_APP_SDK_INITIALIZED: {
const message = {
const safeInfoMessage = {
messageId: INTERFACE_MESSAGES.ON_SAFE_INFO,
data: {
safeAddress: safeAddress as string,
network: network.toLowerCase() as LowercaseNetworks,
ethBalance: ethBalance as string,
},
}
const envInfoMessage = {
messageId: INTERFACE_MESSAGES.ENV_INFO,
data: {
txServiceUrl: getTxServiceHost(),
},
}

sendMessageToIframe(message)
sendMessageToIframe(safeInfoMessage)
sendMessageToIframe(envInfoMessage)
break
}
default: {
Expand Down
5 changes: 2 additions & 3 deletions src/routes/safe/components/Apps/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ const Apps = (): React.ReactElement => {
)
}

const onUserTxReject = () => {
const onTxReject = () => {
sendMessageToIframe(
{ messageId: INTERFACE_MESSAGES.TRANSACTION_REJECTED, data: {} },
confirmTransactionModal.requestId,
Expand Down Expand Up @@ -212,10 +212,9 @@ const Apps = (): React.ReactElement => {
ethBalance={ethBalance as string}
safeName={safeName as string}
txs={confirmTransactionModal.txs}
onCancel={closeConfirmationModal}
onClose={closeConfirmationModal}
onUserConfirm={onUserTxConfirm}
onUserTxReject={onUserTxReject}
onTxReject={onTxReject}
/>
</>
)
Expand Down