diff --git a/src/routes/safe/component/Transactions/index.jsx b/src/routes/safe/component/Transactions/index.jsx index 22988209..d72607d1 100644 --- a/src/routes/safe/component/Transactions/index.jsx +++ b/src/routes/safe/component/Transactions/index.jsx @@ -6,7 +6,7 @@ import { sleep } from '~/utils/timer' import { type Safe } from '~/routes/safe/store/model/safe' import actions, { type Actions } from './actions' import selector, { type SelectorProps } from './selector' -import transaction, { storeTransaction, TX_NAME_PARAM, TX_DESTINATION_PARAM, TX_VALUE_PARAM } from './transactions' +import { createTransaction, TX_NAME_PARAM, TX_DESTINATION_PARAM, TX_VALUE_PARAM } from './transactions' import MultisigForm from './MultisigForm' import ReviewTx from './ReviewTx' @@ -37,11 +37,8 @@ class Transactions extends React.Component { const nonce = Date.now() const destination = values[TX_DESTINATION_PARAM] const value = values[TX_VALUE_PARAM] - const tx = await transaction(safe.get('address'), destination, value, nonce, userAddress) - await storeTransaction( - values[TX_NAME_PARAM], nonce, destination, value, userAddress, - safe.get('owners'), tx.tx, safe.get('address'), safe.get('confirmations'), - ) + const name = values[TX_NAME_PARAM] + await createTransaction(safe, name, destination, value, nonce, userAddress) await sleep(1500) this.props.fetchTransactions() this.setState({ done: true }) diff --git a/src/routes/safe/component/Transactions/transactions.js b/src/routes/safe/component/Transactions/transactions.js index 5db5a7cf..bff9ca1d 100644 --- a/src/routes/safe/component/Transactions/transactions.js +++ b/src/routes/safe/component/Transactions/transactions.js @@ -6,22 +6,34 @@ import { type Confirmation, makeConfirmation } from '~/routes/safe/store/model/c import { makeTransaction, type Transaction, type TransactionProps } from '~/routes/safe/store/model/transaction' import { getGnosisSafeContract } from '~/wallets/safeContracts' import { getWeb3 } from '~/wallets/getWeb3' +import { type Safe } from '~/routes/safe/store/model/safe' export const TX_NAME_PARAM = 'txName' export const TX_DESTINATION_PARAM = 'txDestination' export const TX_VALUE_PARAM = 'txValue' -const buildConfirmationsFrom = (owners: List, creator: string): List => { - if (!owners) { - throw new Error('This safe has no owners') +export const EXECUTED_CONFIRMATION_HASH = 'EXECUTED' + +// Exported for testing it, should not use it. Use #transactions fnc. +export const buildConfirmationsFrom = + (owners: List, creator: string, confirmationHash: string): List => { + if (!owners) { + throw new Error('This safe has no owners') + } + + if (!owners.find((owner: Owner) => owner.get('address') === creator)) { + throw new Error('The creator of the tx is not an owner') + } + + return owners.map((owner: Owner) => makeConfirmation({ + owner, + status: owner.get('address') === creator, + hash: owner.get('address') === creator ? confirmationHash : undefined, + })) } - if (!owners.find((owner: Owner) => owner.get('address') === creator)) { - throw new Error('The creator of the tx is not an owner') - } - - return owners.map((owner: Owner) => makeConfirmation({ owner, status: owner.get('address') === creator })) -} +export const buildExecutedConfirmationFrom = (owners: List, creator: string): List => + buildConfirmationsFrom(owners, creator, EXECUTED_CONFIRMATION_HASH) export const storeTransaction = ( name: string, @@ -29,14 +41,12 @@ export const storeTransaction = ( destination: string, value: number, creator: string, - owners: List, + confirmations: List, tx: string, safeAddress: string, safeThreshold: number, ) => { - const confirmations: List = buildConfirmationsFrom(owners, creator) - - const notMinedWhenOneOwnerSafe = owners.count() === 1 && !tx + const notMinedWhenOneOwnerSafe = confirmations.count() === 1 && !tx if (notMinedWhenOneOwnerSafe) { throw new Error('The tx should be mined before storing it in safes with one owner') } @@ -58,15 +68,39 @@ export const storeTransaction = ( localStorage.setItem(TX_KEY, JSON.stringify(safeTransactions)) } -const transactions = async (safeAddress: string, destination: string, value: number, nonce: number, user: string) => { - const web3 = getWeb3() - const GnosisSafe = await getGnosisSafeContract(web3) - const gnosisSafe = GnosisSafe.at(safeAddress) +const hasOneOwner = (safe: Safe) => { + const owners = safe.get('owners') + if (!owners) { + throw new Error('Received a Safe without owners when creating a tx') + } - const valueInWei = web3.toWei(value, 'ether') - const CALL = 0 - - return gnosisSafe.approveTransactionWithParameters(destination, valueInWei, '0x', CALL, nonce, { from: user, gas: '5000000' }) + return owners.count() === 1 } -export default transactions +export const createTransaction = async ( + safe: Safe, + txName: string, + txDestination: string, + txValue: number, + nonce: number, + user: string, +) => { + const web3 = getWeb3() + const GnosisSafe = await getGnosisSafeContract(web3) + const safeAddress = safe.get('address') + const gnosisSafe = GnosisSafe.at(safeAddress) + + const valueInWei = web3.toWei(txValue, 'ether') + const CALL = 0 + + if (hasOneOwner(safe)) { + const txHash = await gnosisSafe.execTransactionIfApproved(txDestination, valueInWei, '0x', CALL, nonce, { from: user, gas: '5000000' }) + const executedConfirmations: List = buildExecutedConfirmationFrom(safe.get('owners'), user) + return storeTransaction(txName, nonce, txDestination, txValue, user, executedConfirmations, txHash, safeAddress, safe.get('confirmations')) + } + + const txConfirmationHash = await gnosisSafe.approveTransactionWithParameters(txDestination, valueInWei, '0x', CALL, nonce, { from: user, gas: '5000000' }) + const confirmations: List = buildConfirmationsFrom(safe.get('owners'), user, txConfirmationHash) + + return storeTransaction(txName, nonce, txDestination, txValue, user, confirmations, '', safeAddress, safe.get('confirmations')) +}