Refactor create/confirm transactions
This commit is contained in:
parent
560301f981
commit
9259d79f61
|
@ -1,24 +1,14 @@
|
|||
// @flow
|
||||
import GnosisSafeSol from '@gnosis.pm/safe-contracts/build/contracts/GnosisSafe.json'
|
||||
import { getWeb3 } from '~/logic/wallets/getWeb3'
|
||||
import { getStandardTokenContract } from '~/logic/tokens/store/actions/fetchTokens'
|
||||
import { EMPTY_DATA } from '~/logic/wallets/ethTransactions'
|
||||
import { isEther } from '~/logic/tokens/utils/tokenHelpers'
|
||||
import { type Token } from '~/logic/tokens/store/model/token'
|
||||
import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts'
|
||||
import { type Operation, saveTxToHistory } from '~/logic/safe/transactions'
|
||||
import { type NotificationsQueue } from '~/logic/notifications'
|
||||
import { type Operation } from '~/logic/safe/transactions'
|
||||
import { ZERO_ADDRESS } from '~/logic/wallets/ethAddresses'
|
||||
import { getErrorMessage } from '~/test/utils/ethereumErrors'
|
||||
|
||||
export const CALL = 0
|
||||
export const TX_TYPE_EXECUTION = 'execution'
|
||||
export const TX_TYPE_CONFIRMATION = 'confirmation'
|
||||
|
||||
export const approveTransaction = async (
|
||||
notiQueue: NotificationsQueue,
|
||||
enqueueSnackbar: Function,
|
||||
closeSnackbar: Function,
|
||||
export const getApprovalTransaction = async (
|
||||
safeInstance: any,
|
||||
to: string,
|
||||
valueInWei: number | string,
|
||||
|
@ -43,62 +33,19 @@ export const approveTransaction = async (
|
|||
},
|
||||
)
|
||||
|
||||
const beforeExecutionKey = enqueueSnackbar(notiQueue.beforeExecution.message, notiQueue.beforeExecution.options)
|
||||
let pendingExecutionKey
|
||||
try {
|
||||
const web3 = getWeb3()
|
||||
const contract = new web3.eth.Contract(GnosisSafeSol.abi, safeInstance.address)
|
||||
|
||||
const transactionHash = await contract.methods
|
||||
.approveHash(txHash)
|
||||
.send({
|
||||
from: sender,
|
||||
})
|
||||
.once('transactionHash', () => {
|
||||
closeSnackbar(beforeExecutionKey)
|
||||
pendingExecutionKey = enqueueSnackbar(
|
||||
notiQueue.pendingExecution.single.message,
|
||||
notiQueue.pendingExecution.single.options,
|
||||
)
|
||||
})
|
||||
.on('error', (error) => {
|
||||
console.error('Tx error:', error)
|
||||
})
|
||||
.then(async (receipt) => {
|
||||
closeSnackbar(pendingExecutionKey)
|
||||
await saveTxToHistory(
|
||||
safeInstance,
|
||||
to,
|
||||
valueInWei,
|
||||
data,
|
||||
operation,
|
||||
nonce,
|
||||
receipt.transactionHash,
|
||||
sender,
|
||||
TX_TYPE_CONFIRMATION,
|
||||
)
|
||||
enqueueSnackbar(notiQueue.afterExecution.message, notiQueue.afterExecution.options)
|
||||
return receipt.transactionHash
|
||||
})
|
||||
return contract.methods.approveHash(txHash)
|
||||
} catch (err) {
|
||||
console.error(`Error while approving transaction: ${err}`)
|
||||
|
||||
return transactionHash
|
||||
} catch (error) {
|
||||
closeSnackbar(beforeExecutionKey)
|
||||
closeSnackbar(pendingExecutionKey)
|
||||
enqueueSnackbar(notiQueue.afterExecutionError.message, notiQueue.afterExecutionError.options)
|
||||
|
||||
const executeData = safeInstance.contract.methods.approveHash(txHash).encodeABI()
|
||||
const errMsg = await getErrorMessage(safeInstance.address, 0, executeData, sender)
|
||||
console.error(`Error executing the TX: ${errMsg}`)
|
||||
|
||||
throw error
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
export const executeTransaction = async (
|
||||
notiQueue: NotificationsQueue,
|
||||
enqueueSnackbar: Function,
|
||||
closeSnackbar: Function,
|
||||
export const getExecutionTransaction = async (
|
||||
safeInstance: any,
|
||||
to: string,
|
||||
valueInWei: number | string,
|
||||
|
@ -106,95 +53,16 @@ export const executeTransaction = async (
|
|||
operation: Operation,
|
||||
nonce: string | number,
|
||||
sender: string,
|
||||
signatures?: string,
|
||||
sigs: string,
|
||||
) => {
|
||||
let sigs = signatures
|
||||
|
||||
// https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures
|
||||
if (!sigs) {
|
||||
sigs = `0x000000000000000000000000${sender.replace(
|
||||
'0x',
|
||||
'',
|
||||
)}000000000000000000000000000000000000000000000000000000000000000001`
|
||||
}
|
||||
|
||||
const beforeExecutionKey = enqueueSnackbar(notiQueue.beforeExecution.message, notiQueue.beforeExecution.options)
|
||||
let pendingExecutionKey
|
||||
try {
|
||||
const web3 = getWeb3()
|
||||
const contract = new web3.eth.Contract(GnosisSafeSol.abi, safeInstance.address)
|
||||
|
||||
const transactionHash = await contract.methods
|
||||
.execTransaction(to, valueInWei, data, operation, 0, 0, 0, ZERO_ADDRESS, ZERO_ADDRESS, sigs)
|
||||
.send({
|
||||
from: sender,
|
||||
})
|
||||
.once('transactionHash', () => {
|
||||
closeSnackbar(beforeExecutionKey)
|
||||
pendingExecutionKey = enqueueSnackbar(
|
||||
notiQueue.pendingExecution.single.message,
|
||||
notiQueue.pendingExecution.single.options,
|
||||
)
|
||||
})
|
||||
.on('error', (error) => {
|
||||
console.error('Tx error:', error)
|
||||
})
|
||||
.then(async (receipt) => {
|
||||
closeSnackbar(pendingExecutionKey)
|
||||
await saveTxToHistory(
|
||||
safeInstance,
|
||||
to,
|
||||
valueInWei,
|
||||
data,
|
||||
operation,
|
||||
nonce,
|
||||
receipt.transactionHash,
|
||||
sender,
|
||||
TX_TYPE_EXECUTION,
|
||||
)
|
||||
enqueueSnackbar(notiQueue.afterExecution.message, notiQueue.afterExecution.options)
|
||||
return receipt.transactionHash
|
||||
})
|
||||
return contract.methods.execTransaction(to, valueInWei, data, operation, 0, 0, 0, ZERO_ADDRESS, ZERO_ADDRESS, sigs)
|
||||
} catch (err) {
|
||||
console.error(`Error while creating transaction: ${err}`)
|
||||
|
||||
return transactionHash
|
||||
} catch (error) {
|
||||
closeSnackbar(beforeExecutionKey)
|
||||
closeSnackbar(pendingExecutionKey)
|
||||
enqueueSnackbar(notiQueue.afterExecutionError.message, notiQueue.afterExecutionError.options)
|
||||
|
||||
const executeDataUsedSignatures = safeInstance.contract.methods
|
||||
.execTransaction(to, valueInWei, data, operation, 0, 0, 0, ZERO_ADDRESS, ZERO_ADDRESS, sigs)
|
||||
.encodeABI()
|
||||
const errMsg = await getErrorMessage(safeInstance.address, 0, executeDataUsedSignatures, sender)
|
||||
console.error(`Error executing the TX: ${errMsg}`)
|
||||
|
||||
throw error
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
export const createTransaction = async (safeAddress: string, to: string, valueInEth: string, token: Token) => {
|
||||
const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
|
||||
const web3 = getWeb3()
|
||||
const from = web3.currentProvider.selectedAddress
|
||||
const threshold = await safeInstance.getThreshold()
|
||||
const nonce = (await safeInstance.nonce()).toString()
|
||||
const valueInWei = web3.utils.toWei(valueInEth, 'ether')
|
||||
const isExecution = threshold.toNumber() === 1
|
||||
|
||||
let txData = EMPTY_DATA
|
||||
if (!isEther(token.symbol)) {
|
||||
const StandardToken = await getStandardTokenContract()
|
||||
const sendToken = await StandardToken.at(token.address)
|
||||
|
||||
txData = sendToken.contract.transfer(to, valueInWei).encodeABI()
|
||||
}
|
||||
|
||||
let txHash
|
||||
if (isExecution) {
|
||||
txHash = await executeTransaction(safeInstance, to, valueInWei, txData, CALL, nonce, from)
|
||||
} else {
|
||||
// txHash = await approveTransaction(safeAddress, to, valueInWei, txData, CALL, nonce)
|
||||
}
|
||||
|
||||
return txHash
|
||||
}
|
||||
|
|
|
@ -6,9 +6,17 @@ import fetchTransactions from '~/routes/safe/store/actions/fetchTransactions'
|
|||
import { type GlobalState } from '~/store'
|
||||
import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts'
|
||||
import {
|
||||
type NotifiedTransaction, approveTransaction, executeTransaction, CALL,
|
||||
getApprovalTransaction,
|
||||
getExecutionTransaction,
|
||||
CALL,
|
||||
type NotifiedTransaction,
|
||||
TX_TYPE_CONFIRMATION,
|
||||
TX_TYPE_EXECUTION,
|
||||
saveTxToHistory,
|
||||
} from '~/logic/safe/transactions'
|
||||
import { getNofiticationsFromTxType } from '~/logic/notifications'
|
||||
import { getNofiticationsFromTxType, type NotificationsQueue } from '~/logic/notifications'
|
||||
import { getErrorMessage } from '~/test/utils/ethereumErrors'
|
||||
import { ZERO_ADDRESS } from '~/logic/wallets/ethAddresses'
|
||||
|
||||
const createTransaction = (
|
||||
safeAddress: string,
|
||||
|
@ -28,39 +36,78 @@ const createTransaction = (
|
|||
const nonce = (await safeInstance.nonce()).toString()
|
||||
const isExecution = threshold.toNumber() === 1 || shouldExecute
|
||||
|
||||
const notificationsQueue = getNofiticationsFromTxType(notifiedTransaction)
|
||||
// https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures
|
||||
const sigs = `0x000000000000000000000000${from.replace(
|
||||
'0x',
|
||||
'',
|
||||
)}000000000000000000000000000000000000000000000000000000000000000001`
|
||||
|
||||
const notificationsQueue: NotificationsQueue = getNofiticationsFromTxType(notifiedTransaction)
|
||||
const beforeExecutionKey = enqueueSnackbar(
|
||||
notificationsQueue.beforeExecution.message,
|
||||
notificationsQueue.beforeExecution.options,
|
||||
)
|
||||
let pendingExecutionKey
|
||||
|
||||
let txHash
|
||||
let tx
|
||||
try {
|
||||
if (isExecution) {
|
||||
txHash = await executeTransaction(
|
||||
notificationsQueue,
|
||||
enqueueSnackbar,
|
||||
closeSnackbar,
|
||||
safeInstance,
|
||||
to,
|
||||
valueInWei,
|
||||
txData,
|
||||
CALL,
|
||||
nonce,
|
||||
from,
|
||||
)
|
||||
tx = await getExecutionTransaction(safeInstance, to, valueInWei, txData, CALL, nonce, from, sigs)
|
||||
} else {
|
||||
txHash = await approveTransaction(
|
||||
notificationsQueue,
|
||||
enqueueSnackbar,
|
||||
closeSnackbar,
|
||||
safeInstance,
|
||||
to,
|
||||
valueInWei,
|
||||
txData,
|
||||
CALL,
|
||||
nonce,
|
||||
from,
|
||||
)
|
||||
tx = await getApprovalTransaction(safeInstance, to, valueInWei, txData, CALL, nonce, from)
|
||||
}
|
||||
|
||||
const sendParams = { from }
|
||||
// if not set owner management tests will fail on ganache
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
sendParams.gas = '7000000'
|
||||
}
|
||||
|
||||
await tx
|
||||
.send(sendParams)
|
||||
.once('transactionHash', (hash) => {
|
||||
txHash = hash
|
||||
closeSnackbar(beforeExecutionKey)
|
||||
pendingExecutionKey = enqueueSnackbar(
|
||||
(shouldExecute)
|
||||
? notificationsQueue.pendingExecution.single.message
|
||||
: notificationsQueue.pendingExecution.multiple.message,
|
||||
notificationsQueue.pendingExecution.single.options,
|
||||
)
|
||||
})
|
||||
.on('error', (error) => {
|
||||
console.error('Tx error: ', error)
|
||||
})
|
||||
.then(async (receipt) => {
|
||||
closeSnackbar(pendingExecutionKey)
|
||||
await saveTxToHistory(
|
||||
safeInstance,
|
||||
to,
|
||||
valueInWei,
|
||||
txData,
|
||||
CALL,
|
||||
nonce,
|
||||
receipt.transactionHash,
|
||||
from,
|
||||
isExecution ? TX_TYPE_EXECUTION : TX_TYPE_CONFIRMATION,
|
||||
)
|
||||
if (isExecution) {
|
||||
enqueueSnackbar(notificationsQueue.afterExecution.message, notificationsQueue.afterExecution.options)
|
||||
}
|
||||
|
||||
return receipt.transactionHash
|
||||
})
|
||||
} catch (err) {
|
||||
console.error(`Error while creating transaction: ${err}`)
|
||||
closeSnackbar(beforeExecutionKey)
|
||||
closeSnackbar(pendingExecutionKey)
|
||||
enqueueSnackbar(notificationsQueue.afterExecutionError.message, notificationsQueue.afterExecutionError.options)
|
||||
|
||||
const executeDataUsedSignatures = safeInstance.contract.methods
|
||||
.execTransaction(to, valueInWei, txData, CALL, 0, 0, 0, ZERO_ADDRESS, ZERO_ADDRESS, sigs)
|
||||
.encodeABI()
|
||||
const errMsg = await getErrorMessage(safeInstance.address, 0, executeDataUsedSignatures, from)
|
||||
console.error(`Error executing the TX: ${errMsg}`)
|
||||
}
|
||||
|
||||
dispatch(fetchTransactions(safeAddress))
|
||||
|
|
|
@ -5,8 +5,17 @@ import { userAccountSelector } from '~/logic/wallets/store/selectors'
|
|||
import fetchTransactions from '~/routes/safe/store/actions/fetchTransactions'
|
||||
import { type GlobalState } from '~/store'
|
||||
import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts'
|
||||
import { approveTransaction, executeTransaction, CALL } from '~/logic/safe/transactions'
|
||||
import {
|
||||
type NotificationsQueue,
|
||||
getApprovalTransaction,
|
||||
getExecutionTransaction,
|
||||
CALL,
|
||||
saveTxToHistory,
|
||||
TX_TYPE_EXECUTION,
|
||||
TX_TYPE_CONFIRMATION,
|
||||
} from '~/logic/safe/transactions'
|
||||
import { getNofiticationsFromTxType } from '~/logic/notifications'
|
||||
import { getErrorMessage } from '~/test/utils/ethereumErrors'
|
||||
|
||||
// https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures
|
||||
// https://github.com/gnosis/safe-contracts/blob/master/test/gnosisSafeTeamEdition.js#L26
|
||||
|
@ -45,38 +54,85 @@ const processTransaction = (
|
|||
const nonce = (await safeInstance.nonce()).toString()
|
||||
const threshold = (await safeInstance.getThreshold()).toNumber()
|
||||
const shouldExecute = threshold === tx.confirmations.size || approveAndExecute
|
||||
const sigs = generateSignaturesFromTxConfirmations(tx, approveAndExecute && userAddress)
|
||||
|
||||
const notificationsQueue = getNofiticationsFromTxType(notifiedTransaction)
|
||||
let sigs = generateSignaturesFromTxConfirmations(tx, approveAndExecute && userAddress)
|
||||
// https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures
|
||||
if (!sigs) {
|
||||
sigs = `0x000000000000000000000000${from.replace(
|
||||
'0x',
|
||||
'',
|
||||
)}000000000000000000000000000000000000000000000000000000000000000001`
|
||||
}
|
||||
|
||||
const notificationsQueue: NotificationsQueue = getNofiticationsFromTxType(notifiedTransaction)
|
||||
const beforeExecutionKey = enqueueSnackbar(
|
||||
notificationsQueue.beforeExecution.message,
|
||||
notificationsQueue.beforeExecution.options,
|
||||
)
|
||||
let pendingExecutionKey
|
||||
|
||||
let txHash
|
||||
if (shouldExecute) {
|
||||
txHash = await executeTransaction(
|
||||
notificationsQueue,
|
||||
enqueueSnackbar,
|
||||
closeSnackbar,
|
||||
safeInstance,
|
||||
tx.recipient,
|
||||
tx.value,
|
||||
tx.data,
|
||||
CALL,
|
||||
nonce,
|
||||
from,
|
||||
sigs,
|
||||
)
|
||||
} else {
|
||||
txHash = await approveTransaction(
|
||||
notificationsQueue,
|
||||
enqueueSnackbar,
|
||||
closeSnackbar,
|
||||
safeInstance,
|
||||
tx.recipient,
|
||||
tx.value,
|
||||
tx.data,
|
||||
CALL,
|
||||
nonce,
|
||||
from,
|
||||
)
|
||||
let transaction
|
||||
try {
|
||||
if (shouldExecute) {
|
||||
transaction = await getExecutionTransaction(
|
||||
safeInstance,
|
||||
tx.recipient,
|
||||
tx.value,
|
||||
tx.data,
|
||||
CALL,
|
||||
nonce,
|
||||
from,
|
||||
sigs,
|
||||
)
|
||||
} else {
|
||||
transaction = await getApprovalTransaction(safeInstance, tx.recipient, tx.value, tx.data, CALL, nonce, from)
|
||||
}
|
||||
|
||||
const sendParams = { from }
|
||||
// if not set owner management tests will fail on ganache
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
sendParams.gas = '7000000'
|
||||
}
|
||||
|
||||
await transaction
|
||||
.send(sendParams)
|
||||
.once('transactionHash', (hash) => {
|
||||
txHash = hash
|
||||
closeSnackbar(beforeExecutionKey)
|
||||
pendingExecutionKey = enqueueSnackbar(
|
||||
notificationsQueue.pendingExecution.single.message,
|
||||
notificationsQueue.pendingExecution.single.options,
|
||||
)
|
||||
})
|
||||
.on('error', (error) => {
|
||||
console.error('Processing transaction error: ', error)
|
||||
})
|
||||
.then(async (receipt) => {
|
||||
closeSnackbar(pendingExecutionKey)
|
||||
await saveTxToHistory(
|
||||
safeInstance,
|
||||
tx.recipient,
|
||||
tx.value,
|
||||
tx.data,
|
||||
CALL,
|
||||
nonce,
|
||||
receipt.transactionHash,
|
||||
from,
|
||||
shouldExecute ? TX_TYPE_EXECUTION : TX_TYPE_CONFIRMATION,
|
||||
)
|
||||
enqueueSnackbar(notificationsQueue.afterExecution.message, notificationsQueue.afterExecution.options)
|
||||
|
||||
return receipt.transactionHash
|
||||
})
|
||||
} catch (err) {
|
||||
closeSnackbar(beforeExecutionKey)
|
||||
closeSnackbar(pendingExecutionKey)
|
||||
enqueueSnackbar(notificationsQueue.afterExecutionError.message, notificationsQueue.afterExecutionError.options)
|
||||
|
||||
const executeData = safeInstance.contract.methods.approveHash(txHash).encodeABI()
|
||||
const errMsg = await getErrorMessage(safeInstance.address, 0, executeData, from)
|
||||
console.error(`Error executing the TX: ${errMsg}`)
|
||||
}
|
||||
|
||||
dispatch(fetchTransactions(safeAddress))
|
||||
|
|
Loading…
Reference in New Issue