diff --git a/src/routes/safe/component/Safe/index.jsx b/src/routes/safe/component/Safe/index.jsx index e1b3be06..6ab1fe5d 100644 --- a/src/routes/safe/component/Safe/index.jsx +++ b/src/routes/safe/component/Safe/index.jsx @@ -55,7 +55,7 @@ class GnoSafe extends React.PureComponent { onWithdraw = () => { const { safe } = this.props - this.setState({ component: }) + this.setState({ component: }) } onAddTx = () => { diff --git a/src/routes/safe/component/Withdraw/actions.js b/src/routes/safe/component/Withdraw/actions.js new file mode 100644 index 00000000..32f51f38 --- /dev/null +++ b/src/routes/safe/component/Withdraw/actions.js @@ -0,0 +1,12 @@ +// @flow +import fetchTransactions from '~/routes/safe/store/actions/fetchTransactions' + +type FetchTransactions = typeof fetchTransactions + +export type Actions = { + fetchTransactions: FetchTransactions, +} + +export default { + fetchTransactions, +} diff --git a/src/routes/safe/component/Withdraw/index.jsx b/src/routes/safe/component/Withdraw/index.jsx index 850d3718..9368cadd 100644 --- a/src/routes/safe/component/Withdraw/index.jsx +++ b/src/routes/safe/component/Withdraw/index.jsx @@ -3,17 +3,19 @@ import * as React from 'react' import { connect } from 'react-redux' import Stepper from '~/components/Stepper' import { type DailyLimit } from '~/routes/safe/store/model/dailyLimit' +import { type Safe } from '~/routes/safe/store/model/safe' import selector, { type SelectorProps } from './selector' import withdraw from './withdraw' import WithdrawForm from './WithdrawForm' import Review from './Review' +import actions, { type Actions } from './actions' const getSteps = () => [ 'Fill Withdraw Form', 'Review Withdraw', ] -type Props = SelectorProps & { - safeAddress: string, +type Props = SelectorProps & Actions & { + safe: Safe, dailyLimit: DailyLimit, } @@ -30,8 +32,10 @@ class Withdraw extends React.Component { onWithdraw = async (values: Object) => { try { - const { safeAddress, userAddress } = this.props - await withdraw(values, safeAddress, userAddress) + const { safe, userAddress, fetchTransactions } = this.props + await withdraw(values, safe, userAddress) + fetchTransactions() + this.setState({ done: true }) } catch (error) { this.setState({ done: false }) @@ -71,5 +75,5 @@ class Withdraw extends React.Component { } } -export default connect(selector)(Withdraw) +export default connect(selector, actions)(Withdraw) diff --git a/src/routes/safe/component/Withdraw/withdraw.js b/src/routes/safe/component/Withdraw/withdraw.js index eda37787..80486497 100644 --- a/src/routes/safe/component/Withdraw/withdraw.js +++ b/src/routes/safe/component/Withdraw/withdraw.js @@ -3,6 +3,8 @@ import { getWeb3 } from '~/wallets/getWeb3' import { getGnosisSafeContract, getCreateDailyLimitExtensionContract } from '~/wallets/safeContracts' import { type DailyLimitProps } from '~/routes/safe/store/model/dailyLimit' import { checkReceiptStatus, calculateGasOf, calculateGasPrice } from '~/wallets/ethTransactions' +import { type Safe } from '~/routes/safe/store/model/safe' +import { buildExecutedConfirmationFrom, storeTransaction } from '~/routes/safe/component/AddTransaction/createTransactions' export const LIMIT_POSITION = 0 export const SPENT_TODAY_POS = 1 @@ -49,12 +51,14 @@ export const getEditDailyLimitData = async (safeAddress: string, token: string, return dailyLimitModule.contract.changeDailyLimit.getData(token, dailyLimitInWei) } -const withdraw = async (values: Object, safeAddress: string, userAccount: string): Promise => { +const withdraw = async (values: Object, safe: Safe, userAccount: string): Promise => { const web3 = getWeb3() + const safeAddress = safe.get('address') const dailyLimitModule = await getDailyLimitModuleFrom(safeAddress) const destination = values[DESTINATION_PARAM] - const value = web3.toWei(values[VALUE_PARAM], 'ether') + const valueInEth = values[VALUE_PARAM] + const value = web3.toWei(valueInEth, 'ether') const dailyLimitData = dailyLimitModule.contract.executeDailyLimit.getData(0, destination, value) const gas = await calculateGasOf(dailyLimitData, userAccount, dailyLimitModule.address) @@ -62,6 +66,11 @@ const withdraw = async (values: Object, safeAddress: string, userAccount: string const txHash = await dailyLimitModule.executeDailyLimit(0, destination, value, { from: userAccount, gas, gasPrice }) checkReceiptStatus(txHash.tx) + + const nonce = Date.now() + const executedConfirmations: List = buildExecutedConfirmationFrom(safe.get('owners'), userAccount) + + return storeTransaction(`Withdraw movement of ${valueInEth}`, nonce, destination, valueInEth, userAccount, executedConfirmations, txHash.tx, safeAddress, safe.get('threshold'), '0x') } export default withdraw diff --git a/src/routes/safe/component/Withdraw/withdraw.test.js b/src/routes/safe/component/Withdraw/withdraw.test.js index f6c56308..c6ca86e0 100644 --- a/src/routes/safe/component/Withdraw/withdraw.test.js +++ b/src/routes/safe/component/Withdraw/withdraw.test.js @@ -2,6 +2,8 @@ import { aNewStore } from '~/store' import { addEtherTo } from '~/test/utils/etherMovements' import { aDeployedSafe, executeWithdrawOn } from '~/routes/safe/store/test/builder/deployedSafe.builder' +import { buildMathPropsFrom } from '~/test/utils/buildReactRouterProps' +import { safeSelector } from '~/routes/safe/store/selectors/index' describe('Safe Blockchain Test', () => { let store @@ -17,8 +19,10 @@ describe('Safe Blockchain Test', () => { const value = 0.15 // WHEN - await executeWithdrawOn(safeAddress, value) - await executeWithdrawOn(safeAddress, value) + const match: Match = buildMathPropsFrom(safeAddress) + const safe = safeSelector(store.getState(), { match }) + await executeWithdrawOn(safe, value) + await executeWithdrawOn(safe, value) // THEN expect(executeWithdrawOn(safeAddress, value)).rejects.toThrow('VM Exception while processing transaction: revert') diff --git a/src/routes/safe/store/test/builder/deployedSafe.builder.js b/src/routes/safe/store/test/builder/deployedSafe.builder.js index 922f99e4..bd8253d1 100644 --- a/src/routes/safe/store/test/builder/deployedSafe.builder.js +++ b/src/routes/safe/store/test/builder/deployedSafe.builder.js @@ -13,6 +13,7 @@ import addProvider from '~/wallets/store/actions/addProvider' import { makeProvider } from '~/wallets/store/model/provider' import withdraw, { DESTINATION_PARAM, VALUE_PARAM } from '~/routes/safe/component/Withdraw/withdraw' import { promisify } from '~/utils/promisify' +import { type Safe } from '~/routes/safe/store/model/safe' export const renderSafe = async (localStore: Store) => { const provider = await getProviderInfo() @@ -94,7 +95,7 @@ export const aDeployedSafe = async ( return deployedSafe.logs[1].args.proxy } -export const executeWithdrawOn = async (safeAddress: string, value: number) => { +export const executeWithdrawOn = async (safe: Safe, value: number) => { const providerInfo = await getProviderInfo() const userAddress = providerInfo.account @@ -103,5 +104,5 @@ export const executeWithdrawOn = async (safeAddress: string, value: number) => { [VALUE_PARAM]: `${value}`, } - return withdraw(values, safeAddress, userAddress) + return withdraw(values, safe, userAddress) } diff --git a/src/routes/safe/test/Safe.withdrawn.test.js b/src/routes/safe/test/Safe.withdraw.test.js similarity index 93% rename from src/routes/safe/test/Safe.withdrawn.test.js rename to src/routes/safe/test/Safe.withdraw.test.js index cc681aa3..d708a83e 100644 --- a/src/routes/safe/test/Safe.withdrawn.test.js +++ b/src/routes/safe/test/Safe.withdraw.test.js @@ -18,6 +18,8 @@ import { getDailyLimitFrom } from '~/routes/safe/component/Withdraw/withdraw' import { type DailyLimitProps } from '~/routes/safe/store/model/dailyLimit' import { ADD_MULTISIG_BUTTON_TEXT } from '~/routes/safe/component/Safe/MultisigTx' import { WITHDRAW_INDEX, MOVE_FUNDS_INDEX } from '~/test/builder/safe.dom.utils' +import { buildMathPropsFrom } from '~/test/utils/buildReactRouterProps' +import { safeSelector } from '~/routes/safe/store/selectors/index' describe('React DOM TESTS > Withdraw funds from safe', () => { let SafeDom @@ -80,10 +82,10 @@ describe('React DOM TESTS > Withdraw funds from safe', () => { // add funds to safe await addEtherTo(address, '0.1') - // GIVEN in beforeEach - // WHEN - await executeWithdrawOn(address, 0.01) - await executeWithdrawOn(address, 0.01) + const match: Match = buildMathPropsFrom(address) + const safe = safeSelector(store.getState(), { match }) + await executeWithdrawOn(safe, 0.01) + await executeWithdrawOn(safe, 0.01) const ethAddress = 0 const dailyLimit: DailyLimitProps = await getDailyLimitFrom(address, ethAddress) diff --git a/src/test/safe.dom.test.js b/src/test/safe.dom.test.js index dc015f7f..eb1fda3b 100644 --- a/src/test/safe.dom.test.js +++ b/src/test/safe.dom.test.js @@ -33,9 +33,9 @@ describe('DOM > Feature > SAFE MULTISIG TX 1 Owner 1 Threshold', () => { const transactions = TestUtils.scryRenderedComponentsWithType(SafeDom, Transaction) checkMinedMoveFundsTx(transactions[0], 'Move funds') - await checkMinedWithdrawTx(address, '0.08') // 0.1 - 0.01 tx - 0.01 withdraw - checkMinedAddOwnerTx(transactions[1], 'Add Owner Adol Metamask 2') - checkMinedThresholdTx(transactions[2], 'Change Safe\'s threshold') + await checkMinedWithdrawTx(transactions[1], 'Withdraw movement of 0.01', address, '0.08') // 0.1 - 0.01 tx - 0.01 withdraw + checkMinedAddOwnerTx(transactions[2], 'Add Owner Adol Metamask 2') + checkMinedThresholdTx(transactions[3], 'Change Safe\'s threshold') }) it('mines withdraw process correctly all multisig txs in a 2 owner & 2 threshold safe', async () => { @@ -55,10 +55,10 @@ describe('DOM > Feature > SAFE MULTISIG TX 1 Owner 1 Threshold', () => { const transactions = TestUtils.scryRenderedComponentsWithType(SafeDom, Transaction) const statusses = ['Adol Metamask 2 [Not confirmed]', 'Adolfo 1 Eth Account [Confirmed]'] - await checkPendingMoveFundsTx(transactions[3], 2, 'Buy batteries', statusses) - await checkPendingAddOwnerTx(transactions[4], 2, 'Add Owner Adol Metamask 3', statusses) + await checkPendingMoveFundsTx(transactions[4], 2, 'Buy batteries', statusses) + await checkPendingAddOwnerTx(transactions[5], 2, 'Add Owner Adol Metamask 3', statusses) // checkMinedThresholdTx(transactions[4], 'Add Owner Adol Metamask 3') - await checkMinedWithdrawTx(address, '0.07') + await checkMinedWithdrawTx(transactions[6], 'Withdraw movement of 0.01', address, '0.07') }) it('approves and executes pending transactions', async () => { @@ -68,17 +68,17 @@ describe('DOM > Feature > SAFE MULTISIG TX 1 Owner 1 Threshold', () => { } = domSafe let transactions = TestUtils.scryRenderedComponentsWithType(SafeDom, Transaction) - expect(transactions.length).toBe(5) + expect(transactions.length).toBe(7) // WHEN... processing pending TXs - await processTransaction(address, transactions[3].props.transaction, 1, accounts[1]) await processTransaction(address, transactions[4].props.transaction, 1, accounts[1]) + await processTransaction(address, transactions[5].props.transaction, 1, accounts[1]) await refreshTransactions(store) // THEN - checkMinedMoveFundsTx(transactions[3], 'Buy batteries') + checkMinedMoveFundsTx(transactions[4], 'Buy batteries') await checkBalanceOf(address, '0.06') - checkMinedAddOwnerTx(transactions[4], 'Add Owner Adol Metamask 3') + checkMinedAddOwnerTx(transactions[5], 'Add Owner Adol Metamask 3') await checkThresholdOf(address, 3) // WHEN... reducing threshold @@ -87,22 +87,22 @@ describe('DOM > Feature > SAFE MULTISIG TX 1 Owner 1 Threshold', () => { // THEN await listTxsClickingOn(safeButtons[LIST_TXS_INDEX]) transactions = TestUtils.scryRenderedComponentsWithType(SafeDom, Transaction) - expect(transactions.length).toBe(6) + expect(transactions.length).toBe(8) let statusses = ['Adol Metamask 3 [Not confirmed]', 'Adol Metamask 2 [Not confirmed]', 'Adolfo 1 Eth Account [Confirmed]'] - await checkPendingRemoveOwnerTx(transactions[5], 3, 'Remove Owner Adol Metamask 3', statusses) + await checkPendingRemoveOwnerTx(transactions[7], 3, 'Remove Owner Adol Metamask 3', statusses) - await processTransaction(address, transactions[5].props.transaction, 1, accounts[1]) + await processTransaction(address, transactions[7].props.transaction, 1, accounts[1]) await refreshTransactions(store) transactions = TestUtils.scryRenderedComponentsWithType(SafeDom, Transaction) statusses = ['Adol Metamask 3 [Not confirmed]', 'Adol Metamask 2 [Confirmed]', 'Adolfo 1 Eth Account [Confirmed]'] - await checkPendingRemoveOwnerTx(transactions[5], 2, 'Remove Owner Adol Metamask 3', statusses) + await checkPendingRemoveOwnerTx(transactions[7], 2, 'Remove Owner Adol Metamask 3', statusses) await checkThresholdOf(address, 3) - await processTransaction(address, transactions[5].props.transaction, 2, accounts[2]) + await processTransaction(address, transactions[7].props.transaction, 2, accounts[2]) await refreshTransactions(store) await checkThresholdOf(address, 2) transactions = TestUtils.scryRenderedComponentsWithType(SafeDom, Transaction) - await checkMinedRemoveOwnerTx(transactions[5], 'Remove Owner') + await checkMinedRemoveOwnerTx(transactions[7], 'Remove Owner') // WHEN... changing threshold await sendChangeThresholdForm(SafeDom, safeButtons[EDIT_THRESHOLD_INDEX], '1') @@ -110,7 +110,7 @@ describe('DOM > Feature > SAFE MULTISIG TX 1 Owner 1 Threshold', () => { // THEN transactions = TestUtils.scryRenderedComponentsWithType(SafeDom, Transaction) - await processTransaction(address, transactions[6].props.transaction, 1, accounts[1]) + await processTransaction(address, transactions[8].props.transaction, 1, accounts[1]) await checkThresholdOf(address, 1) }) }) diff --git a/src/test/utils/transactions/withdraw.helper.js b/src/test/utils/transactions/withdraw.helper.js index 108cd3fa..8a7c33e6 100644 --- a/src/test/utils/transactions/withdraw.helper.js +++ b/src/test/utils/transactions/withdraw.helper.js @@ -2,6 +2,7 @@ import TestUtils from 'react-dom/test-utils' import { sleep } from '~/utils/timer' import { checkBalanceOf } from '~/test/utils/etherMovements' +import { checkMinedTx } from '~/test/builder/safe.dom.utils' export const sendWithdrawForm = ( SafeDom: React$Component, @@ -31,6 +32,13 @@ export const sendWithdrawForm = ( return sleep(2500) } -export const checkMinedWithdrawTx = async (address: string, funds: number) => { +export const checkMinedWithdrawTx = async ( + Transaction: React$Component, + name: string, + address: string, + funds: number, +) => { await checkBalanceOf(address, funds) + + checkMinedTx(Transaction, name) }