feature: calculate `safeTxHash` on client-side

This commit is contained in:
fernandomg 2020-05-27 17:55:41 -03:00
parent a5a99167f4
commit a68a2d32d0
6 changed files with 113 additions and 14 deletions

View File

@ -25,6 +25,7 @@ module.exports = {
'@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-unused-vars': ['error', { ignoreRestSiblings: true }], '@typescript-eslint/no-unused-vars': ['error', { ignoreRestSiblings: true }],
'@typescript-eslint/ban-ts-ignore': 'off',
}, },
settings: { settings: {
react: { react: {

View File

@ -167,10 +167,11 @@
"electron-is-dev": "^1.1.0", "electron-is-dev": "^1.1.0",
"electron-log": "4.1.2", "electron-log": "4.1.2",
"electron-updater": "4.3.1", "electron-updater": "4.3.1",
"eth-sig-util": "^2.5.3",
"ethereum-ens": "0.8.0", "ethereum-ens": "0.8.0",
"express": "^4.17.1", "express": "^4.17.1",
"final-form-calculate": "^1.3.1",
"final-form": "4.19.1", "final-form": "4.19.1",
"final-form-calculate": "^1.3.1",
"history": "4.10.1", "history": "4.10.1",
"immortal-db": "^1.0.2", "immortal-db": "^1.0.2",
"immutable": "^4.0.0-rc.9", "immutable": "^4.0.0-rc.9",
@ -182,9 +183,10 @@
"polished": "3.6.3", "polished": "3.6.3",
"qrcode.react": "1.0.0", "qrcode.react": "1.0.0",
"query-string": "6.12.1", "query-string": "6.12.1",
"react": "16.13.1",
"react-dom": "16.13.1", "react-dom": "16.13.1",
"react-final-form-listeners": "^1.0.2",
"react-final-form": "6.4.0", "react-final-form": "6.4.0",
"react-final-form-listeners": "^1.0.2",
"react-ga": "^2.7.0", "react-ga": "^2.7.0",
"react-hot-loader": "4.12.21", "react-hot-loader": "4.12.21",
"react-qr-reader": "^2.2.1", "react-qr-reader": "^2.2.1",
@ -192,11 +194,10 @@
"react-router-dom": "5.2.0", "react-router-dom": "5.2.0",
"react-scripts": "^3.4.1", "react-scripts": "^3.4.1",
"react-window": "^1.8.5", "react-window": "^1.8.5",
"react": "16.13.1",
"recompose": "^0.30.0", "recompose": "^0.30.0",
"redux": "4.0.5",
"redux-actions": "^2.6.5", "redux-actions": "^2.6.5",
"redux-thunk": "^2.3.0", "redux-thunk": "^2.3.0",
"redux": "4.0.5",
"reselect": "^4.0.0", "reselect": "^4.0.0",
"semver": "7.3.2", "semver": "7.3.2",
"styled-components": "^5.0.1", "styled-components": "^5.0.1",
@ -209,24 +210,24 @@
"@testing-library/user-event": "^7.1.2", "@testing-library/user-event": "^7.1.2",
"@types/jest": "^25.2.1", "@types/jest": "^25.2.1",
"@types/node": "^13.11.0", "@types/node": "^13.11.0",
"@types/react-dom": "^16.9.6",
"@types/react": "^16.9.32", "@types/react": "^16.9.32",
"@types/react-dom": "^16.9.6",
"@typescript-eslint/eslint-plugin": "^2.34.0", "@typescript-eslint/eslint-plugin": "^2.34.0",
"@typescript-eslint/parser": "^2.34.0", "@typescript-eslint/parser": "^2.34.0",
"autoprefixer": "9.7.6", "autoprefixer": "9.7.6",
"cross-env": "^7.0.2", "cross-env": "^7.0.2",
"dotenv-expand": "^5.1.0",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"dotenv-expand": "^5.1.0",
"electron": "7.1.8",
"electron-builder": "22.2.0", "electron-builder": "22.2.0",
"electron-notarize": "^0.2.1", "electron-notarize": "^0.2.1",
"electron": "7.1.8", "eslint": "^6.8.0",
"eslint-config-prettier": "6.11.0", "eslint-config-prettier": "6.11.0",
"eslint-plugin-import": "2.20.2", "eslint-plugin-import": "2.20.2",
"eslint-plugin-jsx-a11y": "^6.2.3", "eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-prettier": "^3.1.2", "eslint-plugin-prettier": "^3.1.2",
"eslint-plugin-react": "^7.18.3", "eslint-plugin-react": "^7.18.3",
"eslint-plugin-sort-destructure-keys": "1.3.4", "eslint-plugin-sort-destructure-keys": "1.3.4",
"eslint": "^6.8.0",
"ethereumjs-abi": "0.6.8", "ethereumjs-abi": "0.6.8",
"husky": "^4.2.2", "husky": "^4.2.2",
"lint-staged": "10.2.2", "lint-staged": "10.2.2",
@ -234,7 +235,7 @@
"prettier": "2.0.5", "prettier": "2.0.5",
"react-app-rewired": "^2.1.6", "react-app-rewired": "^2.1.6",
"truffle": "5.1.23", "truffle": "5.1.23",
"typescript": "~3.7.2" , "typescript": "~3.7.2",
"wait-on": "5.0.0" "wait-on": "5.0.0"
} }
} }

View File

@ -6,9 +6,15 @@ import semverSatisfies from 'semver/functions/satisfies'
import { onboardUser } from 'src/components/ConnectButton' import { onboardUser } from 'src/components/ConnectButton'
import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts' import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts'
import { getNotificationsFromTxType, showSnackbar } from 'src/logic/notifications' import { getNotificationsFromTxType, showSnackbar } from 'src/logic/notifications'
import { CALL, getApprovalTransaction, getExecutionTransaction, saveTxToHistory } from 'src/logic/safe/transactions' import {
CALL,
getApprovalTransaction,
getExecutionTransaction,
SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES,
saveTxToHistory,
tryOffchainSigning,
} from 'src/logic/safe/transactions'
import { estimateSafeTxGas } from 'src/logic/safe/transactions/gasNew' import { estimateSafeTxGas } from 'src/logic/safe/transactions/gasNew'
import { SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES, tryOffchainSigning } from 'src/logic/safe/transactions/offchainSigner'
import { getCurrentSafeVersion } from 'src/logic/safe/utils/safeVersion' import { getCurrentSafeVersion } from 'src/logic/safe/utils/safeVersion'
import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses' import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses'
import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions' import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions'
@ -18,7 +24,10 @@ import { addOrUpdateCancellationTransactions } from 'src/routes/safe/store/actio
import { addOrUpdateTransactions } from 'src/routes/safe/store/actions/transactions/addOrUpdateTransactions' import { addOrUpdateTransactions } from 'src/routes/safe/store/actions/transactions/addOrUpdateTransactions'
import { removeCancellationTransaction } from 'src/routes/safe/store/actions/transactions/removeCancellationTransaction' import { removeCancellationTransaction } from 'src/routes/safe/store/actions/transactions/removeCancellationTransaction'
import { removeTransaction } from 'src/routes/safe/store/actions/transactions/removeTransaction' import { removeTransaction } from 'src/routes/safe/store/actions/transactions/removeTransaction'
import { mockTransaction } from 'src/routes/safe/store/actions/transactions/utils/transactionHelpers' import {
generateSafeTxHash,
mockTransaction,
} from 'src/routes/safe/store/actions/transactions/utils/transactionHelpers'
import { getLastTx, getNewTxNonce, shouldExecuteTransaction } from 'src/routes/safe/store/actions/utils' import { getLastTx, getNewTxNonce, shouldExecuteTransaction } from 'src/routes/safe/store/actions/utils'
import { getErrorMessage } from 'src/test/utils/ethereumErrors' import { getErrorMessage } from 'src/test/utils/ethereumErrors'
import { makeConfirmation } from '../models/confirmation' import { makeConfirmation } from '../models/confirmation'
@ -151,6 +160,7 @@ const createTransaction = ({
...txArgs, ...txArgs,
confirmations: [], // this is used to determine if a tx is pending or not. See `calculateTransactionStatus` helper confirmations: [], // this is used to determine if a tx is pending or not. See `calculateTransactionStatus` helper
value: txArgs.valueInWei, value: txArgs.valueInWei,
safeTxHash: generateSafeTxHash(safeAddress, txArgs),
} }
const mockedTx = await mockTransaction(txToMock, safeAddress, state) const mockedTx = await mockTransaction(txToMock, safeAddress, state)

View File

@ -18,6 +18,7 @@ import {
TransactionStatusValues, TransactionStatusValues,
TransactionTypes, TransactionTypes,
TransactionTypeValues, TransactionTypeValues,
TxArgs,
} from 'src/routes/safe/store/models/types/transaction' } from 'src/routes/safe/store/models/types/transaction'
import { CANCELLATION_TRANSACTIONS_REDUCER_ID } from 'src/routes/safe/store/reducer/cancellationTransactions' import { CANCELLATION_TRANSACTIONS_REDUCER_ID } from 'src/routes/safe/store/reducer/cancellationTransactions'
import { SAFE_REDUCER_ID } from 'src/routes/safe/store/reducer/safe' import { SAFE_REDUCER_ID } from 'src/routes/safe/store/reducer/safe'
@ -26,6 +27,7 @@ import { store } from 'src/store'
import { safeSelector, safeTransactionsSelector } from 'src/routes/safe/store/selectors' import { safeSelector, safeTransactionsSelector } from 'src/routes/safe/store/selectors'
import { addOrUpdateTransactions } from 'src/routes/safe/store/actions/transactions/addOrUpdateTransactions' import { addOrUpdateTransactions } from 'src/routes/safe/store/actions/transactions/addOrUpdateTransactions'
import { TxServiceModel } from 'src/routes/safe/store/actions/transactions/fetchTransactions/loadOutgoingTransactions' import { TxServiceModel } from 'src/routes/safe/store/actions/transactions/fetchTransactions/loadOutgoingTransactions'
import { TypedDataUtils } from 'eth-sig-util'
export const isEmptyData = (data?: string | null): boolean => { export const isEmptyData = (data?: string | null): boolean => {
return !data || data === EMPTY_DATA return !data || data === EMPTY_DATA
@ -332,3 +334,42 @@ export const updateStoredTransactionsStatus = (dispatch, walletRecord): void =>
) )
} }
} }
export function generateSafeTxHash(safeAddress: string, txArgs: TxArgs): string {
const typedData = {
types: {
EIP712Domain: [{ type: 'address', name: 'verifyingContract' }],
SafeTx: [
{ type: 'address', name: 'to' },
{ type: 'uint256', name: 'value' },
{ type: 'bytes', name: 'data' },
{ type: 'uint8', name: 'operation' },
{ type: 'uint256', name: 'safeTxGas' },
{ type: 'uint256', name: 'baseGas' },
{ type: 'uint256', name: 'gasPrice' },
{ type: 'address', name: 'gasToken' },
{ type: 'address', name: 'refundReceiver' },
{ type: 'uint256', name: 'nonce' },
],
},
domain: {
verifyingContract: safeAddress,
},
primaryType: 'SafeTx',
message: {
to: txArgs.to,
value: txArgs.valueInWei,
data: txArgs.data,
operation: txArgs.operation,
safeTxGas: txArgs.safeTxGas,
baseGas: txArgs.baseGas,
gasPrice: txArgs.gasPrice,
gasToken: txArgs.gasToken,
refundReceiver: txArgs.refundReceiver,
nonce: txArgs.nonce,
},
}
// @ts-ignore
return `0x${TypedDataUtils.sign(typedData).toString('hex')}`
}

View File

@ -75,3 +75,19 @@ export type TransactionProps = {
} }
export type Transaction = import('immutable').RecordOf<TransactionProps> export type Transaction = import('immutable').RecordOf<TransactionProps>
export type TxArgs = {
data: any
baseGas: number
gasToken: string
safeInstance: any
nonce: number
valueInWei: any
safeTxGas: number
refundReceiver: string
sender: any
sigs: string
to: any
operation: any
gasPrice: number
}

View File

@ -3933,7 +3933,7 @@ bn.js@4.11.8:
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.4.0, bn.js@^4.8.0: bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.4.0, bn.js@^4.8.0:
version "4.11.9" version "4.11.9"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828"
integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==
@ -6861,6 +6861,18 @@ eth-sig-util@^1.4.2:
ethereumjs-abi "git+https://github.com/ethereumjs/ethereumjs-abi.git" ethereumjs-abi "git+https://github.com/ethereumjs/ethereumjs-abi.git"
ethereumjs-util "^5.1.1" ethereumjs-util "^5.1.1"
eth-sig-util@^2.5.3:
version "2.5.3"
resolved "https://registry.yarnpkg.com/eth-sig-util/-/eth-sig-util-2.5.3.tgz#6938308b38226e0b3085435474900b03036abcbe"
integrity sha512-KpXbCKmmBUNUTGh9MRKmNkIPietfhzBqqYqysDavLseIiMUGl95k6UcPEkALAZlj41e9E6yioYXc1PC333RKqw==
dependencies:
buffer "^5.2.1"
elliptic "^6.4.0"
ethereumjs-abi "0.6.5"
ethereumjs-util "^5.1.1"
tweetnacl "^1.0.0"
tweetnacl-util "^0.15.0"
eth-tx-summary@^3.1.2: eth-tx-summary@^3.1.2:
version "3.2.4" version "3.2.4"
resolved "https://registry.yarnpkg.com/eth-tx-summary/-/eth-tx-summary-3.2.4.tgz#e10eb95eb57cdfe549bf29f97f1e4f1db679035c" resolved "https://registry.yarnpkg.com/eth-tx-summary/-/eth-tx-summary-3.2.4.tgz#e10eb95eb57cdfe549bf29f97f1e4f1db679035c"
@ -6951,6 +6963,14 @@ ethereum-public-key-to-address@0.0.1:
meow "^5.0.0" meow "^5.0.0"
secp256k1 "^3.7.1" secp256k1 "^3.7.1"
ethereumjs-abi@0.6.5:
version "0.6.5"
resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.5.tgz#5a637ef16ab43473fa72a29ad90871405b3f5241"
integrity sha1-WmN+8Wq0NHP6cqKa2QhxQFs/UkE=
dependencies:
bn.js "^4.10.0"
ethereumjs-util "^4.3.0"
ethereumjs-abi@0.6.8, "ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git": ethereumjs-abi@0.6.8, "ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git":
version "0.6.8" version "0.6.8"
resolved "git+https://github.com/ethereumjs/ethereumjs-abi.git#1cfbb13862f90f0b391d8a699544d5fe4dfb8c7b" resolved "git+https://github.com/ethereumjs/ethereumjs-abi.git#1cfbb13862f90f0b391d8a699544d5fe4dfb8c7b"
@ -7021,7 +7041,7 @@ ethereumjs-tx@^2.1.1, ethereumjs-tx@^2.1.2:
ethereumjs-common "^1.5.0" ethereumjs-common "^1.5.0"
ethereumjs-util "^6.0.0" ethereumjs-util "^6.0.0"
ethereumjs-util@4.5.0: ethereumjs-util@4.5.0, ethereumjs-util@^4.3.0:
version "4.5.0" version "4.5.0"
resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-4.5.0.tgz#3e9428b317eebda3d7260d854fddda954b1f1bc6" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-4.5.0.tgz#3e9428b317eebda3d7260d854fddda954b1f1bc6"
integrity sha1-PpQosxfuvaPXJg2FT93alUsfG8Y= integrity sha1-PpQosxfuvaPXJg2FT93alUsfG8Y=
@ -16004,11 +16024,21 @@ tunnel@^0.0.6:
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
tweetnacl-util@^0.15.0:
version "0.15.1"
resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b"
integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==
tweetnacl@^0.14.3, tweetnacl@~0.14.0: tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5" version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
tweetnacl@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==
type-check@~0.3.2: type-check@~0.3.2:
version "0.3.2" version "0.3.2"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"