Feature: Send env info message on safe apps sdk initialization (#1349)
This commit is contained in:
parent
59dc1f711c
commit
eebe972bac
|
@ -111,7 +111,7 @@ interface CreateTransactionArgs {
|
||||||
|
|
||||||
type CreateTransactionAction = ThunkAction<Promise<void>, AppReduxState, undefined, AnyAction>
|
type CreateTransactionAction = ThunkAction<Promise<void>, AppReduxState, undefined, AnyAction>
|
||||||
type ConfirmEventHandler = (safeTxHash: string) => void
|
type ConfirmEventHandler = (safeTxHash: string) => void
|
||||||
type RejectEventHandler = () => void
|
type ErrorEventHandler = () => void
|
||||||
|
|
||||||
const createTransaction = (
|
const createTransaction = (
|
||||||
{
|
{
|
||||||
|
@ -126,7 +126,7 @@ const createTransaction = (
|
||||||
origin = null,
|
origin = null,
|
||||||
}: CreateTransactionArgs,
|
}: CreateTransactionArgs,
|
||||||
onUserConfirm?: ConfirmEventHandler,
|
onUserConfirm?: ConfirmEventHandler,
|
||||||
onUserReject?: RejectEventHandler,
|
onError?: ErrorEventHandler,
|
||||||
): CreateTransactionAction => async (dispatch: Dispatch, getState: () => AppReduxState): Promise<void> => {
|
): CreateTransactionAction => async (dispatch: Dispatch, getState: () => AppReduxState): Promise<void> => {
|
||||||
const state = getState()
|
const state = getState()
|
||||||
|
|
||||||
|
@ -172,6 +172,7 @@ const createTransaction = (
|
||||||
sender: from,
|
sender: from,
|
||||||
sigs,
|
sigs,
|
||||||
}
|
}
|
||||||
|
const safeTxHash = generateSafeTxHash(safeAddress, txArgs)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Here we're checking that safe contract version is greater or equal 1.1.1, but
|
// Here we're checking that safe contract version is greater or equal 1.1.1, but
|
||||||
|
@ -179,20 +180,19 @@ const createTransaction = (
|
||||||
const canTryOffchainSigning =
|
const canTryOffchainSigning =
|
||||||
!isExecution && !smartContractWallet && semverSatisfies(safeVersion, SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES)
|
!isExecution && !smartContractWallet && semverSatisfies(safeVersion, SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES)
|
||||||
if (canTryOffchainSigning) {
|
if (canTryOffchainSigning) {
|
||||||
const signature = await tryOffchainSigning({ ...txArgs, safeAddress }, hardwareWallet)
|
const signature = await tryOffchainSigning(safeTxHash, { ...txArgs, safeAddress }, hardwareWallet)
|
||||||
|
|
||||||
if (signature) {
|
if (signature) {
|
||||||
dispatch(closeSnackbarAction({ key: beforeExecutionKey }))
|
dispatch(closeSnackbarAction({ key: beforeExecutionKey }))
|
||||||
|
dispatch(enqueueSnackbar(notificationsQueue.afterExecution.moreConfirmationsNeeded))
|
||||||
|
dispatch(fetchTransactions(safeAddress))
|
||||||
|
|
||||||
await saveTxToHistory({ ...txArgs, signature, origin })
|
await saveTxToHistory({ ...txArgs, signature, origin })
|
||||||
dispatch(enqueueSnackbar(notificationsQueue.afterExecution.moreConfirmationsNeeded))
|
onUserConfirm?.(safeTxHash)
|
||||||
|
|
||||||
dispatch(fetchTransactions(safeAddress))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const safeTxHash = generateSafeTxHash(safeAddress, txArgs)
|
|
||||||
const tx = isExecution
|
const tx = isExecution
|
||||||
? await getExecutionTransaction(txArgs)
|
? await getExecutionTransaction(txArgs)
|
||||||
: await getApprovalTransaction(safeInstance, safeTxHash)
|
: await getApprovalTransaction(safeInstance, safeTxHash)
|
||||||
|
@ -245,20 +245,7 @@ const createTransaction = (
|
||||||
removeTxFromStore(mockedTx, safeAddress, dispatch, state)
|
removeTxFromStore(mockedTx, safeAddress, dispatch, state)
|
||||||
console.error('Tx error: ', error)
|
console.error('Tx error: ', error)
|
||||||
|
|
||||||
// Different wallets return different error messages in this case. This is an assumption that if
|
onError?.()
|
||||||
// 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?.()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.then(async (receipt) => {
|
.then(async (receipt) => {
|
||||||
if (pendingExecutionKey) {
|
if (pendingExecutionKey) {
|
||||||
|
|
|
@ -78,7 +78,7 @@ const processTransaction = ({ approveAndExecute, notifiedTransaction, safeAddres
|
||||||
const canTryOffchainSigning =
|
const canTryOffchainSigning =
|
||||||
!isExecution && !smartContractWallet && semverSatisfies(safeVersion, SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES)
|
!isExecution && !smartContractWallet && semverSatisfies(safeVersion, SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES)
|
||||||
if (canTryOffchainSigning) {
|
if (canTryOffchainSigning) {
|
||||||
const signature = await tryOffchainSigning({ ...txArgs, safeAddress }, hardwareWallet)
|
const signature = await tryOffchainSigning(tx.safeTxHash, { ...txArgs, safeAddress }, hardwareWallet)
|
||||||
|
|
||||||
if (signature) {
|
if (signature) {
|
||||||
dispatch(closeSnackbarAction(beforeExecutionKey))
|
dispatch(closeSnackbarAction(beforeExecutionKey))
|
||||||
|
|
|
@ -103,7 +103,7 @@ const notificationsMiddleware = (store) => (next) => async (action) => {
|
||||||
}
|
}
|
||||||
case ADD_INCOMING_TRANSACTIONS: {
|
case ADD_INCOMING_TRANSACTIONS: {
|
||||||
action.payload.forEach((incomingTransactions, safeAddress) => {
|
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 viewedSafes = state.currentSession['viewedSafes']
|
||||||
const recurringUser = viewedSafes?.includes(safeAddress)
|
const recurringUser = viewedSafes?.includes(safeAddress)
|
||||||
|
|
||||||
|
|
|
@ -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 { 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"
|
const EIP712_NOT_SUPPORTED_ERROR_MSG = "EIP712 is not supported by user's wallet"
|
||||||
|
|
||||||
|
@ -59,7 +60,7 @@ const generateTypedDataFrom = async ({
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getEIP712Signer = (version?: string) => async (txArgs) => {
|
export const getEIP712Signer = (version?: string) => async (txArgs) => {
|
||||||
const web3: any = getWeb3()
|
const web3 = getWeb3()
|
||||||
const typedData = await generateTypedDataFrom(txArgs)
|
const typedData = await generateTypedDataFrom(txArgs)
|
||||||
|
|
||||||
let method = 'eth_signTypedData_v3'
|
let method = 'eth_signTypedData_v3'
|
||||||
|
@ -80,13 +81,14 @@ export const getEIP712Signer = (version?: string) => async (txArgs) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
web3.currentProvider.sendAsync(signedTypedData, (err, signature) => {
|
const provider = web3.currentProvider as AbstractProvider
|
||||||
|
provider.sendAsync(signedTypedData, (err, signature) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err)
|
reject(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (signature.result == null) {
|
if (signature?.result == null) {
|
||||||
reject(new Error(EIP712_NOT_SUPPORTED_ERROR_MSG))
|
reject(new Error(EIP712_NOT_SUPPORTED_ERROR_MSG))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,26 +4,13 @@ import { AbstractProvider } from 'web3-core/types'
|
||||||
|
|
||||||
const ETH_SIGN_NOT_SUPPORTED_ERROR_MSG = 'ETH_SIGN_NOT_SUPPORTED'
|
const ETH_SIGN_NOT_SUPPORTED_ERROR_MSG = 'ETH_SIGN_NOT_SUPPORTED'
|
||||||
|
|
||||||
export const ethSigner = async ({
|
type EthSignerArgs = {
|
||||||
baseGas,
|
safeTxHash: string
|
||||||
data,
|
sender: string
|
||||||
gasPrice,
|
}
|
||||||
gasToken,
|
|
||||||
nonce,
|
export const ethSigner = async ({ safeTxHash, sender }: EthSignerArgs): Promise<string> => {
|
||||||
operation,
|
|
||||||
refundReceiver,
|
|
||||||
safeInstance,
|
|
||||||
safeTxGas,
|
|
||||||
sender,
|
|
||||||
to,
|
|
||||||
valueInWei,
|
|
||||||
}): Promise<string> => {
|
|
||||||
const web3 = await getWeb3()
|
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) {
|
return new Promise(function (resolve, reject) {
|
||||||
const provider = web3.currentProvider as AbstractProvider
|
const provider = web3.currentProvider as AbstractProvider
|
||||||
|
@ -31,7 +18,7 @@ export const ethSigner = async ({
|
||||||
{
|
{
|
||||||
jsonrpc: '2.0',
|
jsonrpc: '2.0',
|
||||||
method: 'eth_sign',
|
method: 'eth_sign',
|
||||||
params: [sender, txHash],
|
params: [sender, safeTxHash],
|
||||||
id: new Date().getTime(),
|
id: new Date().getTime(),
|
||||||
},
|
},
|
||||||
async function (err, signature) {
|
async function (err, signature) {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { ethSigner } from './ethSigner'
|
||||||
const SIGNERS = {
|
const SIGNERS = {
|
||||||
EIP712_V3: getEIP712Signer('v3'),
|
EIP712_V3: getEIP712Signer('v3'),
|
||||||
EIP712_V4: getEIP712Signer('v4'),
|
EIP712_V4: getEIP712Signer('v4'),
|
||||||
EIP712: getEIP712Signer() as any,
|
EIP712: getEIP712Signer(),
|
||||||
ETH_SIGN: ethSigner,
|
ETH_SIGN: ethSigner,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,13 +18,13 @@ const getSignersByWallet = (isHW) =>
|
||||||
|
|
||||||
export const SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES = '>=1.1.1'
|
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
|
let signature
|
||||||
|
|
||||||
const signerByWallet = getSignersByWallet(isHW)
|
const signerByWallet = getSignersByWallet(isHW)
|
||||||
for (const signingFunc of signerByWallet) {
|
for (const signingFunc of signerByWallet) {
|
||||||
try {
|
try {
|
||||||
signature = await signingFunc(txArgs)
|
signature = await signingFunc({ ...txArgs, safeTxHash })
|
||||||
|
|
||||||
break
|
break
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -69,9 +69,8 @@ type OwnProps = {
|
||||||
safeAddress: string
|
safeAddress: string
|
||||||
safeName: string
|
safeName: string
|
||||||
ethBalance: string
|
ethBalance: string
|
||||||
onCancel: () => void
|
|
||||||
onUserConfirm: (safeTxHash: string) => void
|
onUserConfirm: (safeTxHash: string) => void
|
||||||
onUserTxReject: () => void
|
onTxReject: () => void
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,16 +81,20 @@ const ConfirmTransactionModal = ({
|
||||||
safeAddress,
|
safeAddress,
|
||||||
ethBalance,
|
ethBalance,
|
||||||
safeName,
|
safeName,
|
||||||
onCancel,
|
|
||||||
onUserConfirm,
|
onUserConfirm,
|
||||||
onClose,
|
onClose,
|
||||||
onUserTxReject,
|
onTxReject,
|
||||||
}: OwnProps): React.ReactElement | null => {
|
}: OwnProps): React.ReactElement | null => {
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleTxRejection = () => {
|
||||||
|
onTxReject()
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
|
|
||||||
const handleUserConfirmation = (safeTxHash: string): void => {
|
const handleUserConfirmation = (safeTxHash: string): void => {
|
||||||
onUserConfirm(safeTxHash)
|
onUserConfirm(safeTxHash)
|
||||||
onClose()
|
onClose()
|
||||||
|
@ -113,10 +116,9 @@ const ConfirmTransactionModal = ({
|
||||||
navigateToTransactionsTab: false,
|
navigateToTransactionsTab: false,
|
||||||
},
|
},
|
||||||
handleUserConfirmation,
|
handleUserConfirmation,
|
||||||
onUserTxReject,
|
handleTxRejection,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
onClose()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const areTxsMalformed = txs.some((t) => !isTxValid(t))
|
const areTxsMalformed = txs.some((t) => !isTxValid(t))
|
||||||
|
@ -165,13 +167,13 @@ const ConfirmTransactionModal = ({
|
||||||
footer={
|
footer={
|
||||||
<ModalFooterConfirmation
|
<ModalFooterConfirmation
|
||||||
cancelText="Cancel"
|
cancelText="Cancel"
|
||||||
handleCancel={onCancel}
|
handleCancel={handleTxRejection}
|
||||||
handleOk={confirmTransactions}
|
handleOk={confirmTransactions}
|
||||||
okDisabled={areTxsMalformed}
|
okDisabled={areTxsMalformed}
|
||||||
okText="Submit"
|
okText="Submit"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
onClose={onClose}
|
onClose={handleTxRejection}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
} from '@gnosis.pm/safe-apps-sdk'
|
} from '@gnosis.pm/safe-apps-sdk'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
import { useEffect, useCallback, MutableRefObject } from 'react'
|
import { useEffect, useCallback, MutableRefObject } from 'react'
|
||||||
|
import { getTxServiceHost } from 'src/config/'
|
||||||
import {
|
import {
|
||||||
safeEthBalanceSelector,
|
safeEthBalanceSelector,
|
||||||
safeNameSelector,
|
safeNameSelector,
|
||||||
|
@ -85,7 +86,7 @@ const useIframeMessageHandler = (
|
||||||
}
|
}
|
||||||
|
|
||||||
case SDK_MESSAGES.SAFE_APP_SDK_INITIALIZED: {
|
case SDK_MESSAGES.SAFE_APP_SDK_INITIALIZED: {
|
||||||
const message = {
|
const safeInfoMessage = {
|
||||||
messageId: INTERFACE_MESSAGES.ON_SAFE_INFO,
|
messageId: INTERFACE_MESSAGES.ON_SAFE_INFO,
|
||||||
data: {
|
data: {
|
||||||
safeAddress: safeAddress as string,
|
safeAddress: safeAddress as string,
|
||||||
|
@ -93,8 +94,15 @@ const useIframeMessageHandler = (
|
||||||
ethBalance: ethBalance as string,
|
ethBalance: ethBalance as string,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
const envInfoMessage = {
|
||||||
|
messageId: INTERFACE_MESSAGES.ENV_INFO,
|
||||||
|
data: {
|
||||||
|
txServiceUrl: getTxServiceHost(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
sendMessageToIframe(message)
|
sendMessageToIframe(safeInfoMessage)
|
||||||
|
sendMessageToIframe(envInfoMessage)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
|
|
@ -102,7 +102,7 @@ const Apps = (): React.ReactElement => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onUserTxReject = () => {
|
const onTxReject = () => {
|
||||||
sendMessageToIframe(
|
sendMessageToIframe(
|
||||||
{ messageId: INTERFACE_MESSAGES.TRANSACTION_REJECTED, data: {} },
|
{ messageId: INTERFACE_MESSAGES.TRANSACTION_REJECTED, data: {} },
|
||||||
confirmTransactionModal.requestId,
|
confirmTransactionModal.requestId,
|
||||||
|
@ -212,10 +212,9 @@ const Apps = (): React.ReactElement => {
|
||||||
ethBalance={ethBalance as string}
|
ethBalance={ethBalance as string}
|
||||||
safeName={safeName as string}
|
safeName={safeName as string}
|
||||||
txs={confirmTransactionModal.txs}
|
txs={confirmTransactionModal.txs}
|
||||||
onCancel={closeConfirmationModal}
|
|
||||||
onClose={closeConfirmationModal}
|
onClose={closeConfirmationModal}
|
||||||
onUserConfirm={onUserTxConfirm}
|
onUserConfirm={onUserTxConfirm}
|
||||||
onUserTxReject={onUserTxReject}
|
onTxReject={onTxReject}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue