Merge pull request #45 from gnosis/feature/WA-438-include-withdraw-movements-as-transactions

WA-438 - Feature include withdraw movements as transactions
This commit is contained in:
Adolfo Panizo 2018-06-26 12:01:27 +02:00 committed by GitHub
commit af222fd011
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 74 additions and 34 deletions

View File

@ -55,7 +55,7 @@ class GnoSafe extends React.PureComponent<SafeProps, State> {
onWithdraw = () => {
const { safe } = this.props
this.setState({ component: <Withdraw safeAddress={safe.get('address')} dailyLimit={safe.get('dailyLimit')} /> })
this.setState({ component: <Withdraw safe={safe} dailyLimit={safe.get('dailyLimit')} /> })
}
onAddTx = () => {

View File

@ -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,
}

View File

@ -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<Props, State> {
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<Props, State> {
}
}
export default connect(selector)(Withdraw)
export default connect(selector, actions)(Withdraw)

View File

@ -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<void> => {
const withdraw = async (values: Object, safe: Safe, userAccount: string): Promise<void> => {
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<Confirmation> = 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

View File

@ -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')

View File

@ -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<GlobalState>) => {
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)
}

View File

@ -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)

View File

@ -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)
})
})

View File

@ -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<any, any>,
@ -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<any, any>,
name: string,
address: string,
funds: number,
) => {
await checkBalanceOf(address, funds)
checkMinedTx(Transaction, name)
}