Skip to content
This repository has been archived by the owner on Nov 10, 2023. It is now read-only.

V1.6.1 - Bugfixes #432

Merged
merged 8 commits into from
Jan 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "safe-react",
"version": "1.6.0",
"version": "1.6.1",
"description": "Allowing crypto users manage funds in a safer way",
"homepage": "https://github.com/gnosis/safe-react#readme",
"bugs": {
Expand Down Expand Up @@ -76,7 +76,7 @@
"semver": "^7.1.1",
"squarelink": "^1.1.4",
"web3": "1.2.4",
"web3connect": "^1.0.0-beta.23"
"web3connect": "^1.0.0-beta.25"
},
"devDependencies": {
"@babel/cli": "7.7.5",
Expand Down
6 changes: 5 additions & 1 deletion src/components/ConnectButton/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Fortmatic from 'fortmatic'
import Portis from '@portis/web3'
import Squarelink from 'squarelink'
import Button from '~/components/layout/Button'
import { fetchProvider } from '~/logic/wallets/store/actions'
import { fetchProvider, removeProvider } from '~/logic/wallets/store/actions'
import { getNetwork } from '~/config'
import { store } from '~/store'

Expand Down Expand Up @@ -62,6 +62,10 @@ web3Connect.on('connect', (provider: any) => {
}
})

web3Connect.on('disconnect', () => {
store.dispatch(removeProvider())
})

type Props = {
enqueueSnackbar: Function,
closeSnackbar: Function,
Expand Down
1 change: 1 addition & 0 deletions src/logic/notifications/notificationTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export const NOTIFICATIONS: Notifications = {
variant: SUCCESS,
persist: false,
autoHideDuration: shortDuration,
preventDuplicate: true,
},
},
UNLOCK_WALLET_MSG: {
Expand Down
41 changes: 30 additions & 11 deletions src/logic/safe/safeTxSigner.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,38 @@
// @flow
import { List } from 'immutable'
import { type Confirmation } from '~/routes/safe/store/models/confirmation'

const generateSignatureFrom = (account: string) => `000000000000000000000000${account.replace(
'0x',
'',
)}000000000000000000000000000000000000000000000000000000000000000001`
// 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
export const generateSignaturesFromTxConfirmations = (
confirmations: List<Confirmation>,
preApprovingOwner?: string,
) => {
// The constant parts need to be sorted so that the recovered signers are sorted ascending
// (natural order) by address (not checksummed).
const confirmationsMap = confirmations.reduce((map, obj) => {
map[obj.owner.address.toLowerCase()] = obj // eslint-disable-line no-param-reassign
return map
}, {})

export const buildSignaturesFrom = (ownersWhoHasSigned: List<string>, sender: string) => {
const signatures = ownersWhoHasSigned.push(sender)
const orderedSignatures = signatures.sort() // JS by default sorts in a non case-senstive way
if (preApprovingOwner) {
confirmationsMap[preApprovingOwner.toLowerCase()] = { owner: preApprovingOwner }
}

let sigs = '0x'
orderedSignatures.forEach((owner: string) => {
sigs += generateSignatureFrom(owner)
})

Object.keys(confirmationsMap)
.sort()
.forEach((addr) => {
const conf = confirmationsMap[addr]
if (conf.signature) {
sigs += conf.signature.slice(2)
} else {
// https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures
sigs += `000000000000000000000000${addr.replace(
'0x',
'',
)}000000000000000000000000000000000000000000000000000000000000000001`
}
})
return sigs
}
2 changes: 1 addition & 1 deletion src/logic/safe/transactions/gasNew.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import GnosisSafeSol from '@gnosis.pm/safe-contracts/build/contracts/GnosisSafe.json'
import { type Transaction } from '~/routes/safe/store/models/transaction'
import { getWeb3, getAccountFrom } from '~/logic/wallets/getWeb3'
import { generateSignaturesFromTxConfirmations } from '~/routes/safe/store/actions/processTransaction'
import { generateSignaturesFromTxConfirmations } from '~/logic/safe/safeTxSigner'
import { calculateGasOf, calculateGasPrice } from '~/logic/wallets/ethTransactions'
import { ZERO_ADDRESS } from '~/logic/wallets/ethAddresses'
import { CALL } from '.'
Expand Down
7 changes: 4 additions & 3 deletions src/logic/wallets/store/actions/removeProvider.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
// @flow
import { createAction } from 'redux-actions'
import type { Dispatch as ReduxDispatch } from 'redux'
import { NOTIFICATIONS, showSnackbar } from '~/logic/notifications'
import { NOTIFICATIONS, enhanceSnackbarForAction } from '~/logic/notifications'
import { getWeb3, resetWeb3 } from '~/logic/wallets/getWeb3'
import enqueueSnackbar from '~/logic/notifications/store/actions/enqueueSnackbar'

export const REMOVE_PROVIDER = 'REMOVE_PROVIDER'

const removeProvider = createAction<string, *, *>(REMOVE_PROVIDER)

export default (enqueueSnackbar: Function, closeSnackbar: Function) => (dispatch: ReduxDispatch<*>) => {
showSnackbar(NOTIFICATIONS.WALLET_DISCONNECTED_MSG, enqueueSnackbar, closeSnackbar)
export default () => (dispatch: ReduxDispatch<*>) => {
dispatch(enqueueSnackbar(enhanceSnackbarForAction(NOTIFICATIONS.WALLET_DISCONNECTED_MSG)))

const web3 = getWeb3()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const statusToIcon = {

const statusToLabel = {
success: 'Success',
cancelled: 'Failed',
cancelled: 'Cancelled',
awaiting_your_confirmation: 'Awaiting your confirmation',
awaiting_confirmations: 'Awaiting confirmations',
awaiting_execution: 'Awaiting execution',
Expand Down
102 changes: 74 additions & 28 deletions src/routes/safe/container/selector.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// @flow
import { List, Map } from 'immutable'
import { createSelector, createStructuredSelector, type Selector } from 'reselect'
import {
createSelector,
createStructuredSelector,
type Selector,
} from 'reselect'
import {
safeSelector,
safeActiveTokensSelector,
Expand All @@ -11,16 +15,29 @@ import {
type RouterProps,
type SafeSelectorProps,
} from '~/routes/safe/store/selectors'
import { providerNameSelector, userAccountSelector, networkSelector } from '~/logic/wallets/store/selectors'
import {
providerNameSelector,
userAccountSelector,
networkSelector,
} from '~/logic/wallets/store/selectors'
import { type Safe } from '~/routes/safe/store/models/safe'
import { type GlobalState } from '~/store'
import { isUserOwner } from '~/logic/wallets/ethAddresses'
import { orderedTokenListSelector, tokensSelector } from '~/logic/tokens/store/selectors'
import {
orderedTokenListSelector,
tokensSelector,
} from '~/logic/tokens/store/selectors'
import { type Token } from '~/logic/tokens/store/model/token'
import { type Transaction, type TransactionStatus } from '~/routes/safe/store/models/transaction'
import {
type Transaction,
type TransactionStatus,
} from '~/routes/safe/store/models/transaction'
import { safeParamAddressSelector } from '../store/selectors'
import { getEthAsToken } from '~/logic/tokens/utils/tokenHelpers'
import { currencyValuesListSelector, currentCurrencySelector } from '~/logic/currencyValues/store/selectors'
import {
currencyValuesListSelector,
currentCurrencySelector,
} from '~/logic/currencyValues/store/selectors'
import type { BalanceCurrencyType } from '~/logic/currencyValues/store/model/currencyValues'
import type { IncomingTransaction } from '~/routes/safe/store/models/incomingTransaction'

Expand All @@ -35,10 +52,14 @@ export type SelectorProps = {
safeUrl: string,
currencySelected: string,
currencyValues: BalanceCurrencyType[],
transactions: List<Transaction | IncomingTransaction>,
transactions: List<Transaction | IncomingTransaction>
}

const getTxStatus = (tx: Transaction, userAddress: string, safe: Safe): TransactionStatus => {
const getTxStatus = (
tx: Transaction,
userAddress: string,
safe: Safe,
): TransactionStatus => {
let txStatus
if (tx.executionTxHash) {
txStatus = 'success'
Expand All @@ -51,37 +72,54 @@ const getTxStatus = (tx: Transaction, userAddress: string, safe: Safe): Transact
} else if (!tx.confirmations.size) {
txStatus = 'pending'
} else {
const userConfirmed = tx.confirmations.filter((conf) => conf.owner.address === userAddress).size === 1
const userConfirmed = tx.confirmations.filter((conf) => conf.owner.address === userAddress)
.size === 1
const userIsSafeOwner = safe.owners.filter((owner) => owner.address === userAddress).size === 1
txStatus = !userConfirmed && userIsSafeOwner ? 'awaiting_your_confirmation' : 'awaiting_confirmations'
txStatus = !userConfirmed && userIsSafeOwner
? 'awaiting_your_confirmation'
: 'awaiting_confirmations'
}

return txStatus
}

export const grantedSelector: Selector<GlobalState, RouterProps, boolean> = createSelector(
export const grantedSelector: Selector<
GlobalState,
RouterProps,
boolean
> = createSelector(
userAccountSelector,
safeSelector,
(userAccount: string, safe: Safe | typeof undefined): boolean => isUserOwner(safe, userAccount),
)

const safeEthAsTokenSelector: Selector<GlobalState, RouterProps, ?Token> = createSelector(
safeSelector,
(safe: Safe) => {
if (!safe) {
return undefined
}
const safeEthAsTokenSelector: Selector<
GlobalState,
RouterProps,
?Token
> = createSelector(safeSelector, (safe: Safe) => {
if (!safe) {
return undefined
}

return getEthAsToken(safe.ethBalance)
},
)
return getEthAsToken(safe.ethBalance)
})

const extendedSafeTokensSelector: Selector<GlobalState, RouterProps, List<Token>> = createSelector(
const extendedSafeTokensSelector: Selector<
GlobalState,
RouterProps,
List<Token>
> = createSelector(
safeActiveTokensSelector,
safeBalancesSelector,
tokensSelector,
safeEthAsTokenSelector,
(safeTokens: List<string>, balances: Map<string, string>, tokensList: Map<string, Token>, ethAsToken: Token) => {
(
safeTokens: List<string>,
balances: Map<string, string>,
tokensList: Map<string, Token>,
ethAsToken: Token,
) => {
const extendedTokens = Map().withMutations((map) => {
safeTokens.forEach((tokenAddress: string) => {
const baseToken = tokensList.get(tokenAddress)
Expand All @@ -101,7 +139,11 @@ const extendedSafeTokensSelector: Selector<GlobalState, RouterProps, List<Token>
},
)

const extendedTransactionsSelector: Selector<GlobalState, RouterProps, List<Transaction | IncomingTransaction>> = createSelector(
const extendedTransactionsSelector: Selector<
GlobalState,
RouterProps,
List<Transaction | IncomingTransaction>
> = createSelector(
safeSelector,
userAccountSelector,
safeTransactionsSelector,
Expand All @@ -114,17 +156,21 @@ const extendedTransactionsSelector: Selector<GlobalState, RouterProps, List<Tran
// it means that the transaction was cancelled (Replaced) and shouldn't get executed
let replacementTransaction
if (!tx.isExecuted) {
replacementTransaction = transactions.size > 1 && transactions.findLast(
(transaction) => (
transaction.isExecuted && transaction.nonce && transaction.nonce >= tx.nonce
),
)
replacementTransaction = transactions.size > 1
&& transactions.findLast(
(transaction) => transaction.isExecuted
&& transaction.nonce != null
&& transaction.nonce >= tx.nonce,
)
if (replacementTransaction) {
extendedTx = tx.set('cancelled', true)
}
}

return extendedTx.set('status', getTxStatus(extendedTx, userAddress, safe))
return extendedTx.set(
'status',
getTxStatus(extendedTx, userAddress, safe),
)
})

return List([...extendedTransactions, ...incomingTransactions])
Expand Down
2 changes: 1 addition & 1 deletion src/routes/safe/store/actions/createTransaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const createTransaction = ({
const from = userAccountSelector(state)
const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
const threshold = await safeInstance.getThreshold()
const nonce = txNonce || await getLastPendingTxNonce(safeAddress)
const nonce = typeof txNonce !== 'undefined' ? txNonce : await getLastPendingTxNonce(safeAddress)
const isExecution = threshold.toNumber() === 1 || shouldExecute

// https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures
Expand Down
38 changes: 1 addition & 37 deletions src/routes/safe/store/actions/processTransaction.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// @flow
import type { Dispatch as ReduxDispatch } from 'redux'
import { List } from 'immutable'
import { type Confirmation } from '~/routes/safe/store/models/confirmation'
import { type Transaction } from '~/routes/safe/store/models/transaction'
import { userAccountSelector } from '~/logic/wallets/store/selectors'
import fetchSafe from '~/routes/safe/store/actions/fetchSafe'
Expand All @@ -16,44 +14,10 @@ import {
TX_TYPE_EXECUTION,
TX_TYPE_CONFIRMATION,
} from '~/logic/safe/transactions'
import { generateSignaturesFromTxConfirmations } from '~/logic/safe/safeTxSigner'
import { type NotificationsQueue, getNotificationsFromTxType, showSnackbar } 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
export const generateSignaturesFromTxConfirmations = (
confirmations: List<Confirmation>,
preApprovingOwner?: string,
) => {
// The constant parts need to be sorted so that the recovered signers are sorted ascending
// (natural order) by address (not checksummed).
const confirmationsMap = confirmations.reduce((map, obj) => {
map[obj.owner.address] = obj // eslint-disable-line no-param-reassign
return map
}, {})

if (preApprovingOwner) {
confirmationsMap[preApprovingOwner] = { owner: preApprovingOwner }
}

let sigs = '0x'
Object.keys(confirmationsMap)
.sort()
.forEach((addr) => {
const conf = confirmationsMap[addr]
if (conf.signature) {
sigs += conf.signature.slice(2)
} else {
// https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures
sigs += `000000000000000000000000${addr.replace(
'0x',
'',
)}000000000000000000000000000000000000000000000000000000000000000001`
}
})
return sigs
}

type ProcessTransactionArgs = {
safeAddress: string,
tx: Transaction,
Expand Down
Loading