Merge pull request #1796 from gnosis/release/v2.18.0
Backmerge release v2.18.0 to development
This commit is contained in:
commit
87470956fc
|
@ -1,55 +0,0 @@
|
|||
name: Build/release
|
||||
|
||||
# this will help you specify where to run
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
# this will run on the specified branch
|
||||
- feature/desktop-app
|
||||
|
||||
env:
|
||||
REACT_APP_BLOCKNATIVE_KEY: ${{ secrets.REACT_APP_BLOCKNATIVE_KEY }}
|
||||
REACT_APP_FORTMATIC_KEY: ${{ secrets.REACT_APP_FORTMATIC_KEY }}
|
||||
REACT_APP_GOOGLE_ANALYTICS_ID_MAINNET: ${{ secrets.REACT_APP_GOOGLE_ANALYTICS_ID_MAINNET }}
|
||||
REACT_APP_INFURA_TOKEN: ${{ secrets.REACT_APP_INFURA_TOKEN }}
|
||||
REACT_APP_PORTIS_ID: ${{ secrets.REACT_APP_PORTIS_ID }}
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-latest]
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Install Node.js, NPM and Yarn
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 10.16
|
||||
|
||||
- name: Build/release Electron app
|
||||
env:
|
||||
# macOS notarization API key
|
||||
APPLEID: ${{ secrets.APPLE_ID }}
|
||||
APPLEIDPASS: ${{ secrets.APPLE_ID_PASS }}
|
||||
uses: samuelmeuli/action-electron-builder@v1
|
||||
with:
|
||||
#Build scipt
|
||||
build_script_name: build-desktop
|
||||
|
||||
# GitHub token, automatically provided to the action
|
||||
# (No need to define this secret in the repo settings)
|
||||
|
||||
github_token: ${{ secrets.github_token }}
|
||||
|
||||
# macOS code signing certificate
|
||||
mac_certs: ${{ secrets.MAC_CERTS }}
|
||||
mac_certs_password: ${{ secrets.MAC_CERTS_PASSWORD }}
|
||||
|
||||
# If the commit is tagged with a version (e.g. "v1.0.0"),
|
||||
# release the app after building
|
||||
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
|
@ -5,7 +5,6 @@ on:
|
|||
workflow_dispatch
|
||||
|
||||
env:
|
||||
REACT_APP_BLOCKNATIVE_KEY: ${{ secrets.REACT_APP_BLOCKNATIVE_KEY }}
|
||||
REACT_APP_FORTMATIC_KEY: ${{ secrets.REACT_APP_FORTMATIC_KEY }}
|
||||
REACT_APP_GOOGLE_ANALYTICS_ID_MAINNET: ${{ secrets.REACT_APP_GOOGLE_ANALYTICS_ID_MAINNET }}
|
||||
REACT_APP_INFURA_TOKEN: ${{ secrets.REACT_APP_INFURA_TOKEN }}
|
||||
|
@ -21,24 +20,24 @@ jobs:
|
|||
fail-fast: false
|
||||
max-parallel: 15
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest, ubuntu-latest]
|
||||
os: [macos-latest, windows-latest, ubuntu-20.04]
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Add cache for yarn directory
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
# - name: Get yarn cache directory path
|
||||
# id: yarn-cache-dir-path
|
||||
# run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- uses: actions/cache@v2
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
# - uses: actions/cache@v2
|
||||
# id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
# with:
|
||||
# path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
# key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
# restore-keys: |
|
||||
# ${{ runner.os }}-yarn-
|
||||
|
||||
- name: Patch node gyp on windows to support Visual Studio 2019
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
|
@ -52,9 +51,9 @@ jobs:
|
|||
yarn global add node-gyp
|
||||
yarn config set node_gyp "$_\node_modules\node-gyp\bin\node-gyp.js"
|
||||
- name: Install Node.js, NPM and Yarn
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12
|
||||
node-version: 14
|
||||
- run: yarn install --frozen-lockfile --network-concurrency 1
|
||||
- name: Build/Release Desktop App
|
||||
env:
|
||||
|
|
|
@ -161,7 +161,6 @@ export type GasPriceOracle = {
|
|||
- **REACT_APP_GOOGLE_ANALYTICS**: Used for enabling google analytics
|
||||
- **REACT_APP_PORTIS_ID**: Portis ID for enabling it on given network
|
||||
- **REACT_APP_FORTMATIC_KEY**: Formatic yey for given network
|
||||
- **REACT_APP_BLOCKNATIVE_KEY**: Blocknative key for given network
|
||||
|
||||
---
|
||||
## How to add a network
|
||||
|
@ -190,7 +189,6 @@ export enum ETHEREUM_NETWORK {
|
|||
* REACT_APP_GOOGLE_ANALYTICS
|
||||
* REACT_APP_PORTIS_ID
|
||||
* REACT_APP_FORTMATIC_KEY
|
||||
* REACT_APP_BLOCKNATIVE_KEY
|
||||
|
||||
3) Add the **NetworkSettings** in [`src/config/networks`](/src/config/networks) as `<network_name>.ts`:
|
||||
|
||||
|
|
20
package.json
20
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "safe-react",
|
||||
"version": "2.17.0",
|
||||
"version": "2.18.0",
|
||||
"description": "Allowing crypto users manage funds in a safer way",
|
||||
"website": "https://github.com/gnosis/safe-react#readme",
|
||||
"bugs": {
|
||||
|
@ -94,21 +94,23 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"build/",
|
||||
"files": [
|
||||
"build",
|
||||
"patches",
|
||||
"public",
|
||||
"scripts",
|
||||
"dev-app-update.yml",
|
||||
"package.json"
|
||||
],
|
||||
"directories": {
|
||||
"buildResources": "public/build"
|
||||
"buildResources": "public/resources"
|
||||
},
|
||||
"mac": {
|
||||
"category": "public.app-category.productivity",
|
||||
"hardenedRuntime": true,
|
||||
"entitlements": "public/build/entitlements.mac.plist",
|
||||
"entitlements": "public/resources/entitlements.mac.plist",
|
||||
"gatekeeperAssess": false,
|
||||
"entitlementsInherit": "public/build/entitlements.mac.plist",
|
||||
"entitlementsInherit": "public/resources/entitlements.mac.plist",
|
||||
"target": [
|
||||
"dmg",
|
||||
"zip"
|
||||
|
@ -136,7 +138,7 @@
|
|||
"target": [
|
||||
"nsis"
|
||||
],
|
||||
"icon": "public/build/icon.ico"
|
||||
"icon": "public/resources/icon.ico"
|
||||
}
|
||||
},
|
||||
"resolutions": {
|
||||
|
@ -161,7 +163,7 @@
|
|||
"@gnosis.pm/safe-contracts": "1.1.1-dev.2",
|
||||
"@gnosis.pm/safe-react-components": "https://github.com/gnosis/safe-react-components.git#bf3a84486b7353bd25447ddff39c406f6fafecc6",
|
||||
"@gnosis.pm/util-contracts": "2.0.6",
|
||||
"@ledgerhq/hw-transport-node-hid-singleton": "5.36.0",
|
||||
"@ledgerhq/hw-transport-node-hid-singleton": "5.38.0",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.11.0",
|
||||
"@material-ui/lab": "4.0.0-alpha.56",
|
||||
|
@ -249,7 +251,7 @@
|
|||
"cross-env": "^7.0.3",
|
||||
"dotenv": "^8.2.0",
|
||||
"dotenv-expand": "^5.1.0",
|
||||
"electron": "^9.3.5",
|
||||
"electron": "^9.4.0",
|
||||
"electron-builder": "22.9.1",
|
||||
"electron-notarize": "1.0.0",
|
||||
"eslint": "^7.11.0",
|
||||
|
|
|
@ -90,9 +90,10 @@ function createWindow(port = DEFAULT_PORT) {
|
|||
webPreferences: {
|
||||
preload: path.join(__dirname, '../scripts/preload.js'),
|
||||
allowRunningInsecureContent: true,
|
||||
enableRemoteModule: true,
|
||||
nativeWindowOpen: true, // need to be set in order to display modal
|
||||
},
|
||||
icon: electron.nativeImage.createFromPath(path.join(__dirname, '/public/build/safe.png')),
|
||||
icon: electron.nativeImage.createFromPath(path.join(__dirname, '../build/resources/safe.png')),
|
||||
})
|
||||
|
||||
mainWindow.once('ready-to-show', () => {
|
||||
|
@ -141,7 +142,7 @@ process.on('uncaughtException', function (error) {
|
|||
})
|
||||
|
||||
app.userAgentFallback =
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) old-airport-include/1.0.0 Chrome Electron/9.3.1 Safari/537.36'
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) old-airport-include/1.0.0 Chrome Electron/9.4.1 Safari/537.36'
|
||||
|
||||
// We have one non-context-aware module in node_modules/usb. This is used by @ledgerhq/hw-transport-node-hid
|
||||
// This type of modules will be impossible to use after electron 10
|
||||
|
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
@ -3,6 +3,10 @@ import { EstimationStatus } from 'src/logic/hooks/useEstimateTransactionGas'
|
|||
import Paragraph from 'src/components/layout/Paragraph'
|
||||
import { getNetworkInfo } from 'src/config'
|
||||
import { TransactionFailText } from 'src/components/TransactionFailText'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { providerNameSelector } from 'src/logic/wallets/store/selectors'
|
||||
import { sameString } from 'src/utils/strings'
|
||||
import { WALLETS } from 'src/config/networks/network.d'
|
||||
|
||||
type TransactionFailTextProps = {
|
||||
txEstimationExecutionStatus: EstimationStatus
|
||||
|
@ -20,6 +24,8 @@ export const TransactionFees = ({
|
|||
isOffChainSignature,
|
||||
txEstimationExecutionStatus,
|
||||
}: TransactionFailTextProps): React.ReactElement | null => {
|
||||
const providerName = useSelector(providerNameSelector)
|
||||
|
||||
let transactionAction
|
||||
if (isCreation) {
|
||||
transactionAction = 'create'
|
||||
|
@ -29,6 +35,11 @@ export const TransactionFees = ({
|
|||
transactionAction = 'approve'
|
||||
}
|
||||
|
||||
// FIXME this should be removed when estimating with WalletConnect correctly
|
||||
if (!providerName || sameString(providerName, WALLETS.WALLET_CONNECT)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Paragraph>
|
||||
|
|
|
@ -130,7 +130,11 @@ export const estimateGasForDeployingSafe = async (
|
|||
const proxyFactoryData = proxyFactoryMaster.methods
|
||||
.createProxyWithNonce(safeMaster.options.address, gnosisSafeData, safeCreationSalt)
|
||||
.encodeABI()
|
||||
const gas = await calculateGasOf(proxyFactoryData, userAccount, proxyFactoryMaster.options.address)
|
||||
const gas = await calculateGasOf({
|
||||
data: proxyFactoryData,
|
||||
from: userAccount,
|
||||
to: proxyFactoryMaster.options.address,
|
||||
})
|
||||
const gasPrice = await calculateGasPrice()
|
||||
|
||||
return gas * parseInt(gasPrice, 10)
|
||||
|
|
|
@ -21,6 +21,8 @@ import { List } from 'immutable'
|
|||
import { Confirmation } from 'src/logic/safe/store/models/types/confirmation'
|
||||
import { checkIfOffChainSignatureIsPossible } from 'src/logic/safe/safeTxSigner'
|
||||
import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses'
|
||||
import { sameString } from 'src/utils/strings'
|
||||
import { WALLETS } from 'src/config/networks/network.d'
|
||||
|
||||
export enum EstimationStatus {
|
||||
LOADING = 'LOADING',
|
||||
|
@ -28,13 +30,19 @@ export enum EstimationStatus {
|
|||
SUCCESS = 'SUCCESS',
|
||||
}
|
||||
|
||||
const checkIfTxIsExecution = (threshold: number, preApprovingOwner?: string, txConfirmations?: number): boolean =>
|
||||
txConfirmations === threshold || !!preApprovingOwner || threshold === 1
|
||||
const checkIfTxIsExecution = (
|
||||
threshold: number,
|
||||
preApprovingOwner?: string,
|
||||
txConfirmations?: number,
|
||||
txType?: string,
|
||||
): boolean =>
|
||||
txConfirmations === threshold || !!preApprovingOwner || threshold === 1 || sameString(txType, 'spendingLimit')
|
||||
|
||||
const checkIfTxIsApproveAndExecution = (threshold: number, txConfirmations: number): boolean =>
|
||||
txConfirmations + 1 === threshold
|
||||
const checkIfTxIsApproveAndExecution = (threshold: number, txConfirmations: number, txType?: string): boolean =>
|
||||
txConfirmations + 1 === threshold || sameString(txType, 'spendingLimit')
|
||||
|
||||
const checkIfTxIsCreation = (txConfirmations: number): boolean => txConfirmations === 0
|
||||
const checkIfTxIsCreation = (txConfirmations: number, txType?: string): boolean =>
|
||||
txConfirmations === 0 && !sameString(txType, 'spendingLimit')
|
||||
|
||||
type TransactionEstimationProps = {
|
||||
txData: string
|
||||
|
@ -115,6 +123,7 @@ type UseEstimateTransactionGasProps = {
|
|||
preApprovingOwner?: string
|
||||
operation?: number
|
||||
safeTxGas?: number
|
||||
txType?: string
|
||||
}
|
||||
|
||||
type TransactionGasEstimationResult = {
|
||||
|
@ -136,6 +145,7 @@ export const useEstimateTransactionGas = ({
|
|||
preApprovingOwner,
|
||||
operation,
|
||||
safeTxGas,
|
||||
txType,
|
||||
}: UseEstimateTransactionGasProps): TransactionGasEstimationResult => {
|
||||
const [gasEstimation, setGasEstimation] = useState<TransactionGasEstimationResult>({
|
||||
txEstimationExecutionStatus: EstimationStatus.LOADING,
|
||||
|
@ -151,17 +161,21 @@ export const useEstimateTransactionGas = ({
|
|||
const safeAddress = useSelector(safeParamAddressFromStateSelector)
|
||||
const threshold = useSelector(safeThresholdSelector)
|
||||
const safeVersion = useSelector(safeCurrentVersionSelector)
|
||||
const { account: from, smartContractWallet } = useSelector(providerSelector)
|
||||
const { account: from, smartContractWallet, name: providerName } = useSelector(providerSelector)
|
||||
|
||||
useEffect(() => {
|
||||
const estimateGas = async () => {
|
||||
if (!txData.length) {
|
||||
return
|
||||
}
|
||||
// FIXME this should be removed when estimating with WalletConnect correctly
|
||||
if (!providerName || sameString(providerName, WALLETS.WALLET_CONNECT)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const isExecution = checkIfTxIsExecution(Number(threshold), preApprovingOwner, txConfirmations?.size)
|
||||
const isCreation = checkIfTxIsCreation(txConfirmations?.size || 0)
|
||||
const approvalAndExecution = checkIfTxIsApproveAndExecution(Number(threshold), txConfirmations?.size || 0)
|
||||
const isExecution = checkIfTxIsExecution(Number(threshold), preApprovingOwner, txConfirmations?.size, txType)
|
||||
const isCreation = checkIfTxIsCreation(txConfirmations?.size || 0, txType)
|
||||
const approvalAndExecution = checkIfTxIsApproveAndExecution(Number(threshold), txConfirmations?.size || 0, txType)
|
||||
|
||||
try {
|
||||
const isOffChainSignature = checkIfOffChainSignatureIsPossible(isExecution, smartContractWallet, safeVersion)
|
||||
|
@ -235,6 +249,8 @@ export const useEstimateTransactionGas = ({
|
|||
safeVersion,
|
||||
smartContractWallet,
|
||||
safeTxGas,
|
||||
txType,
|
||||
providerName,
|
||||
])
|
||||
|
||||
return gasEstimation
|
||||
|
|
|
@ -32,6 +32,7 @@ interface ProcessTransactionArgs {
|
|||
safeAddress: string
|
||||
tx: Transaction
|
||||
userAddress: string
|
||||
thresholdReached: boolean
|
||||
}
|
||||
|
||||
type ProcessTransactionAction = ThunkAction<Promise<void | string>, AppReduxState, DispatchReturn, AnyAction>
|
||||
|
@ -42,6 +43,7 @@ export const processTransaction = ({
|
|||
safeAddress,
|
||||
tx,
|
||||
userAddress,
|
||||
thresholdReached,
|
||||
}: ProcessTransactionArgs): ProcessTransactionAction => async (
|
||||
dispatch: Dispatch,
|
||||
getState: () => AppReduxState,
|
||||
|
@ -56,7 +58,7 @@ export const processTransaction = ({
|
|||
const isExecution = approveAndExecute || (await shouldExecuteTransaction(safeInstance, nonce, lastTx))
|
||||
const safeVersion = await getCurrentSafeVersion(safeInstance)
|
||||
|
||||
const preApprovingOwner = approveAndExecute ? userAddress : undefined
|
||||
const preApprovingOwner = approveAndExecute && !thresholdReached ? userAddress : undefined
|
||||
let sigs = generateSignaturesFromTxConfirmations(tx.confirmations, preApprovingOwner)
|
||||
|
||||
if (!sigs) {
|
||||
|
|
Binary file not shown.
|
@ -25,6 +25,21 @@ const parseRequiredTxGasResponse = (data: string): number => {
|
|||
return data.match(/.{2}/g)?.reduce(reducer, 0)
|
||||
}
|
||||
|
||||
interface ErrorDataJson extends JSON {
|
||||
originalError?: {
|
||||
data?: string
|
||||
}
|
||||
data?: string
|
||||
}
|
||||
|
||||
const getJSONOrNullFromString = (stringInput: string): ErrorDataJson | null => {
|
||||
try {
|
||||
return JSON.parse(stringInput)
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// Parses the result from the error message (GETH, OpenEthereum/Parity and Nethermind) and returns the data value
|
||||
export const getDataFromNodeErrorMessage = (errorMessage: string): string | undefined => {
|
||||
// Replace illegal characters that often comes within the error string (like <20> for example)
|
||||
|
@ -35,7 +50,13 @@ export const getDataFromNodeErrorMessage = (errorMessage: string): string | unde
|
|||
const [, ...error] = normalizedErrorString.split('\n')
|
||||
|
||||
try {
|
||||
const errorAsJSON = JSON.parse(error.join(''))
|
||||
const errorAsString = error.join('')
|
||||
const errorAsJSON = getJSONOrNullFromString(errorAsString)
|
||||
|
||||
// Trezor wallet returns the error not as an JSON object but directly as string
|
||||
if (!errorAsJSON) {
|
||||
return errorAsString.length ? errorAsString : undefined
|
||||
}
|
||||
|
||||
// For new GETH nodes they will return the data as error in the format:
|
||||
// {
|
||||
|
@ -251,5 +272,9 @@ export const estimateGasForTransactionApproval = async ({
|
|||
from,
|
||||
})
|
||||
const approveTransactionTxData = await safeInstance.methods.approveHash(txHash).encodeABI()
|
||||
return calculateGasOf(approveTransactionTxData, from, safeAddress)
|
||||
return calculateGasOf({
|
||||
data: approveTransactionTxData,
|
||||
from,
|
||||
to: safeAddress,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import {
|
|||
SAFE_MASTER_COPY_ADDRESS,
|
||||
getGnosisSafeInstanceAt,
|
||||
} from 'src/logic/contracts/safeContracts'
|
||||
import { DELEGATE_CALL } from 'src/logic/safe/transactions'
|
||||
import { getWeb3 } from 'src/logic/wallets/getWeb3'
|
||||
import { MultiSend } from 'src/types/contracts/MultiSend.d'
|
||||
|
||||
|
@ -49,7 +48,7 @@ export const getEncodedMultiSendCallData = (txs: MultiSendTx[], web3: Web3): str
|
|||
return encodedMultiSendCallData
|
||||
}
|
||||
|
||||
export const upgradeSafeToLatestVersion = async (safeAddress: string, createTransaction): Promise<void> => {
|
||||
export const getUpgradeSafeTransactionHash = async (safeAddress: string): Promise<string> => {
|
||||
const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
|
||||
const fallbackHandlerTxData = safeInstance.methods.setFallbackHandler(DEFAULT_FALLBACK_HANDLER_ADDRESS).encodeABI()
|
||||
const updateSafeTxData = safeInstance.methods.changeMasterCopy(SAFE_MASTER_COPY_ADDRESS).encodeABI()
|
||||
|
@ -69,17 +68,5 @@ export const upgradeSafeToLatestVersion = async (safeAddress: string, createTran
|
|||
]
|
||||
|
||||
const web3 = getWeb3()
|
||||
const encodeMultiSendCallData = getEncodedMultiSendCallData(txs, web3)
|
||||
createTransaction({
|
||||
safeAddress,
|
||||
to: MULTI_SEND_ADDRESS,
|
||||
valueInWei: 0,
|
||||
txData: encodeMultiSendCallData,
|
||||
notifiedTransaction: 'STANDARD_TX',
|
||||
enqueueSnackbar: () => {},
|
||||
closeSnackbar: () => {},
|
||||
operation: DELEGATE_CALL,
|
||||
})
|
||||
|
||||
return
|
||||
return getEncodedMultiSendCallData(txs, web3)
|
||||
}
|
||||
|
|
|
@ -50,10 +50,16 @@ export const calculateGasPrice = async (): Promise<string> => {
|
|||
}
|
||||
}
|
||||
|
||||
export const calculateGasOf = async (data: string, from: string, to: string): Promise<number> => {
|
||||
export const calculateGasOf = async (txConfig: {
|
||||
to: string
|
||||
from: string
|
||||
data: string
|
||||
gasPrice?: number
|
||||
gas?: number
|
||||
}): Promise<number> => {
|
||||
const web3 = getWeb3()
|
||||
try {
|
||||
const gas = await web3.eth.estimateGas({ data, from, to })
|
||||
const gas = await web3.eth.estimateGas(txConfig)
|
||||
|
||||
return gas * 2
|
||||
} catch (err) {
|
||||
|
|
|
@ -25,6 +25,8 @@ import GasEstimationInfo from './GasEstimationInfo'
|
|||
import { getNetworkInfo } from 'src/config'
|
||||
import { TransactionParams } from './AppFrame'
|
||||
import { EstimationStatus, useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas'
|
||||
import Row from 'src/components/layout/Row'
|
||||
import { TransactionFees } from 'src/components/TransactionsFees'
|
||||
|
||||
const isTxValid = (t: Transaction): boolean => {
|
||||
if (!['string', 'number'].includes(typeof t.value)) {
|
||||
|
@ -67,6 +69,10 @@ const StyledTextBox = styled(TextBox)`
|
|||
max-width: 444px;
|
||||
`
|
||||
|
||||
const Container = styled.div`
|
||||
max-width: 480px;
|
||||
`
|
||||
|
||||
type OwnProps = {
|
||||
isOpen: boolean
|
||||
app: SafeApp
|
||||
|
@ -96,7 +102,14 @@ export const ConfirmTransactionModal = ({
|
|||
}: OwnProps): React.ReactElement | null => {
|
||||
const [estimatedSafeTxGas, setEstimatedSafeTxGas] = useState(0)
|
||||
|
||||
const { gasEstimation, txEstimationExecutionStatus } = useEstimateTransactionGas({
|
||||
const {
|
||||
gasEstimation,
|
||||
isOffChainSignature,
|
||||
isCreation,
|
||||
isExecution,
|
||||
gasCostFormatted,
|
||||
txEstimationExecutionStatus,
|
||||
} = useEstimateTransactionGas({
|
||||
txData: encodeMultiSendCall(txs),
|
||||
txRecipient: MULTI_SEND_ADDRESS,
|
||||
operation: DELEGATE_CALL,
|
||||
|
@ -159,7 +172,7 @@ export const ConfirmTransactionModal = ({
|
|||
</Text>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Container>
|
||||
<AddressInfo ethBalance={ethBalance} safeAddress={safeAddress} safeName={safeName} />
|
||||
<DividerLine withArrow />
|
||||
{txs.map((tx, index) => (
|
||||
|
@ -195,7 +208,16 @@ export const ConfirmTransactionModal = ({
|
|||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
<Row>
|
||||
<TransactionFees
|
||||
gasCostFormatted={gasCostFormatted}
|
||||
isExecution={isExecution}
|
||||
isCreation={isCreation}
|
||||
isOffChainSignature={isOffChainSignature}
|
||||
txEstimationExecutionStatus={txEstimationExecutionStatus}
|
||||
/>
|
||||
</Row>
|
||||
</Container>
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
|
@ -64,7 +64,7 @@ const ReviewCollectible = ({ onClose, onPrev, tx }: Props): React.ReactElement =
|
|||
isCreation,
|
||||
} = useEstimateTransactionGas({
|
||||
txData: data,
|
||||
txRecipient: tx.recipientAddress,
|
||||
txRecipient: tx.assetAddress,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -56,20 +56,6 @@ type ReviewTxProps = {
|
|||
tx: ReviewTxProp
|
||||
}
|
||||
|
||||
const useTxAmount = (tx: ReviewTxProp, isSendingNativeToken: boolean, txToken?: RecordOf<TokenProps>): string => {
|
||||
const [txAmount, setTxAmount] = useState('0')
|
||||
|
||||
// txAmount should be 0 if we send tokens
|
||||
// the real value is encoded in txData and will be used by the contract
|
||||
// if txAmount > 0 it would send ETH from the Safe (and the data will be empty)
|
||||
useEffect(() => {
|
||||
const txAmount = isSendingNativeToken ? toTokenUnit(tx.amount, nativeCoin.decimals) : '0'
|
||||
setTxAmount(txAmount)
|
||||
}, [tx.amount, txToken, isSendingNativeToken])
|
||||
|
||||
return txAmount
|
||||
}
|
||||
|
||||
const useTxData = (
|
||||
isSendingNativeToken: boolean,
|
||||
txAmount: string,
|
||||
|
@ -88,7 +74,8 @@ const useTxData = (
|
|||
if (!isSendingNativeToken) {
|
||||
const StandardToken = await getHumanFriendlyToken()
|
||||
const tokenInstance = await StandardToken.at(txToken.address as string)
|
||||
txData = tokenInstance.contract.methods.transfer(recipientAddress, txAmount).encodeABI()
|
||||
const erc20TransferAmount = toTokenUnit(txAmount, txToken.decimals)
|
||||
txData = tokenInstance.contract.methods.transfer(recipientAddress, erc20TransferAmount).encodeABI()
|
||||
}
|
||||
setData(txData)
|
||||
}
|
||||
|
@ -107,9 +94,8 @@ const ReviewTx = ({ onClose, onPrev, tx }: ReviewTxProps): React.ReactElement =>
|
|||
const txToken = useMemo(() => tokens.find((token) => sameAddress(token.address, tx.token)), [tokens, tx.token])
|
||||
const isSendingNativeToken = sameAddress(txToken?.address, nativeCoin.address)
|
||||
const txRecipient = isSendingNativeToken ? tx.recipientAddress : txToken?.address || ''
|
||||
|
||||
const txAmount = useTxAmount(tx, isSendingNativeToken, txToken)
|
||||
const data = useTxData(isSendingNativeToken, txAmount, tx.recipientAddress, txToken)
|
||||
const txValue = isSendingNativeToken ? toTokenUnit(tx.amount, nativeCoin.decimals) : '0'
|
||||
const data = useTxData(isSendingNativeToken, tx.amount, tx.recipientAddress, txToken)
|
||||
|
||||
const {
|
||||
gasCostFormatted,
|
||||
|
@ -120,6 +106,7 @@ const ReviewTx = ({ onClose, onPrev, tx }: ReviewTxProps): React.ReactElement =>
|
|||
} = useEstimateTransactionGas({
|
||||
txData: data,
|
||||
txRecipient,
|
||||
txType: tx.txType,
|
||||
})
|
||||
|
||||
const submitTx = async () => {
|
||||
|
@ -152,7 +139,7 @@ const ReviewTx = ({ onClose, onPrev, tx }: ReviewTxProps): React.ReactElement =>
|
|||
createTransaction({
|
||||
safeAddress: safeAddress,
|
||||
to: txRecipient as string,
|
||||
valueInWei: txAmount,
|
||||
valueInWei: txValue,
|
||||
txData: data,
|
||||
notifiedTransaction: TX_NOTIFICATION_TYPES.STANDARD_TX,
|
||||
}),
|
||||
|
|
|
@ -19,7 +19,7 @@ import enqueueSnackbar from 'src/logic/notifications/store/actions/enqueueSnackb
|
|||
import { getNotificationsFromTxType, enhanceSnackbarForAction } from 'src/logic/notifications'
|
||||
import { sameAddress } from 'src/logic/wallets/ethAddresses'
|
||||
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
||||
import UpdateSafeModal from 'src/routes/safe/components/Settings/UpdateSafeModal'
|
||||
import { UpdateSafeModal } from 'src/routes/safe/components/Settings/UpdateSafeModal'
|
||||
import { grantedSelector } from 'src/routes/safe/container/selector'
|
||||
import updateSafe from 'src/logic/safe/store/actions/updateSafe'
|
||||
import Link from 'src/components/layout/Link'
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import IconButton from '@material-ui/core/IconButton'
|
||||
import Close from '@material-ui/icons/Close'
|
||||
import { withStyles } from '@material-ui/styles'
|
||||
import React from 'react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { bindActionCreators } from 'redux'
|
||||
|
||||
import { styles } from './style'
|
||||
|
||||
|
@ -13,17 +11,61 @@ import Button from 'src/components/layout/Button'
|
|||
import Hairline from 'src/components/layout/Hairline'
|
||||
import Paragraph from 'src/components/layout/Paragraph'
|
||||
import Row from 'src/components/layout/Row'
|
||||
import { upgradeSafeToLatestVersion } from 'src/logic/safe/utils/upgradeSafe'
|
||||
import { getUpgradeSafeTransactionHash } from 'src/logic/safe/utils/upgradeSafe'
|
||||
import createTransaction from 'src/logic/safe/store/actions/createTransaction'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import { TransactionFees } from 'src/components/TransactionsFees'
|
||||
import { useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas'
|
||||
import { MULTI_SEND_ADDRESS } from 'src/logic/contracts/safeContracts'
|
||||
import { DELEGATE_CALL } from 'src/logic/safe/transactions'
|
||||
import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions'
|
||||
|
||||
const UpdateSafeModal = ({ classes, onClose, safeAddress }) => {
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
type Props = {
|
||||
onClose: () => void
|
||||
safeAddress: string
|
||||
}
|
||||
|
||||
export const UpdateSafeModal = ({ onClose, safeAddress }: Props): React.ReactElement => {
|
||||
const classes = useStyles()
|
||||
const dispatch = useDispatch()
|
||||
const [multiSendCallData, setMultiSendCallData] = useState(EMPTY_DATA)
|
||||
|
||||
useEffect(() => {
|
||||
const calculateUpgradeSafeModal = async () => {
|
||||
const encodeMultiSendCallData = await getUpgradeSafeTransactionHash(safeAddress)
|
||||
setMultiSendCallData(encodeMultiSendCallData)
|
||||
}
|
||||
calculateUpgradeSafeModal()
|
||||
}, [safeAddress])
|
||||
|
||||
const handleSubmit = async () => {
|
||||
// Call the update safe method
|
||||
await upgradeSafeToLatestVersion(safeAddress, bindActionCreators(createTransaction, dispatch))
|
||||
dispatch(
|
||||
createTransaction({
|
||||
safeAddress,
|
||||
to: MULTI_SEND_ADDRESS,
|
||||
valueInWei: '0',
|
||||
txData: multiSendCallData,
|
||||
notifiedTransaction: 'STANDARD_TX',
|
||||
operation: DELEGATE_CALL,
|
||||
}),
|
||||
)
|
||||
onClose()
|
||||
}
|
||||
|
||||
const {
|
||||
gasCostFormatted,
|
||||
txEstimationExecutionStatus,
|
||||
isExecution,
|
||||
isCreation,
|
||||
isOffChainSignature,
|
||||
} = useEstimateTransactionGas({
|
||||
txData: multiSendCallData,
|
||||
txRecipient: safeAddress,
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row align="center" className={classes.heading} grow>
|
||||
|
@ -56,6 +98,15 @@ const UpdateSafeModal = ({ classes, onClose, safeAddress }) => {
|
|||
have to confirm the update in case more than one confirmation is required for this Safe.
|
||||
</Paragraph>
|
||||
</Row>
|
||||
<Row>
|
||||
<TransactionFees
|
||||
gasCostFormatted={gasCostFormatted}
|
||||
isExecution={isExecution}
|
||||
isCreation={isCreation}
|
||||
isOffChainSignature={isOffChainSignature}
|
||||
txEstimationExecutionStatus={txEstimationExecutionStatus}
|
||||
/>
|
||||
</Row>
|
||||
</Block>
|
||||
<Hairline style={{ position: 'absolute', bottom: 85 }} />
|
||||
<Row align="center" className={classes.buttonRow}>
|
||||
|
@ -72,5 +123,3 @@ const UpdateSafeModal = ({ classes, onClose, safeAddress }) => {
|
|||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default withStyles(styles as any)(UpdateSafeModal)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { lg, md, secondaryText, sm } from 'src/theme/variables'
|
||||
import { createStyles } from '@material-ui/core'
|
||||
|
||||
export const styles = () => ({
|
||||
export const styles = createStyles({
|
||||
heading: {
|
||||
padding: `${sm} ${lg}`,
|
||||
justifyContent: 'space-between',
|
||||
|
|
|
@ -105,6 +105,7 @@ export const ApproveTxModal = ({
|
|||
userAddress,
|
||||
notifiedTransaction: TX_NOTIFICATION_TYPES.CONFIRMATION_TX,
|
||||
approveAndExecute: canExecute && approveAndExecute && isTheTxReadyToBeExecuted,
|
||||
thresholdReached,
|
||||
}),
|
||||
)
|
||||
onClose()
|
||||
|
|
|
@ -7,7 +7,6 @@ export const GOOGLE_ANALYTICS_ID = process.env.REACT_APP_GOOGLE_ANALYTICS || ''
|
|||
export const SENTRY_DSN = process.env.REACT_APP_SENTRY_DSN || ''
|
||||
export const PORTIS_ID = process.env.REACT_APP_PORTIS_ID ?? '852b763d-f28b-4463-80cb-846d7ec5806b'
|
||||
export const FORTMATIC_KEY = process.env.REACT_APP_FORTMATIC_KEY ?? 'pk_test_CAD437AA29BE0A40'
|
||||
export const BLOCKNATIVE_KEY = process.env.REACT_APP_BLOCKNATIVE_KEY ?? '7fbb9cee-7e97-4436-8770-8b29a9a8814c'
|
||||
/*
|
||||
* Not being used
|
||||
export const SQUARELINK_ID = {
|
||||
|
|
82
yarn.lock
82
yarn.lock
|
@ -1782,19 +1782,19 @@
|
|||
dependencies:
|
||||
invariant "2"
|
||||
|
||||
"@ledgerhq/devices@^5.36.0":
|
||||
version "5.36.0"
|
||||
resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-5.36.0.tgz#f4493bea44390325fcc7a2a0d03bba69fc1604ec"
|
||||
integrity sha512-EwQwLZcz66a2V07Bad0J+Q67LR2afj2NJChJNcA6/gqvzXrtNtJ37u1co9eLU7S5GGll5JGi7KdBqAD9ZTHaaQ==
|
||||
"@ledgerhq/devices@^5.38.0":
|
||||
version "5.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-5.38.0.tgz#0b805020c4f3ac40d35f1b1af6d64d04256b1e0f"
|
||||
integrity sha512-1RZ+Dh+oVUDMeaPSCeQ56qzgiMHHy481dbkRCDUMRJEzkGqOLI3k34x7XdkVKy1NQdt8G8sYyobP/yixDry5ow==
|
||||
dependencies:
|
||||
"@ledgerhq/errors" "^5.36.0"
|
||||
"@ledgerhq/logs" "^5.36.0"
|
||||
"@ledgerhq/errors" "^5.38.0"
|
||||
"@ledgerhq/logs" "^5.38.0"
|
||||
rxjs "^6.6.3"
|
||||
|
||||
"@ledgerhq/errors@^5.34.0", "@ledgerhq/errors@^5.36.0":
|
||||
version "5.36.0"
|
||||
resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-5.36.0.tgz#9d178b54116ae81b1fbdfa80afcd33981453be4c"
|
||||
integrity sha512-VS6aFzn3IDUmSLaX2kAPg5sQOc5m7IwvswAGvoMc3FCi9/a1dXWwtiYn5rWd1QjDJlTKSfCwmSpvUMp8FKoY2Q==
|
||||
"@ledgerhq/errors@^5.34.0", "@ledgerhq/errors@^5.38.0":
|
||||
version "5.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-5.38.0.tgz#1642b87de47cbabc7b75ca93005a690895920a72"
|
||||
integrity sha512-d4gQzbOLNBoGIwDtEGFNSb0w0aYN10T5Y749e+vqiJoS3dWrB+5BCSQ/U/ALet0wi/UMIyFY/xmgd1gPaPB3Hw==
|
||||
|
||||
"@ledgerhq/hw-app-eth@^5.21.0":
|
||||
version "5.37.0"
|
||||
|
@ -1807,27 +1807,27 @@
|
|||
bignumber.js "^9.0.1"
|
||||
rlp "^2.2.6"
|
||||
|
||||
"@ledgerhq/hw-transport-node-hid-noevents@^5.36.0":
|
||||
version "5.36.0"
|
||||
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-5.36.0.tgz#a25bdf0d10f9eac73148040e5887f40ca46f6a93"
|
||||
integrity sha512-8zwokin0KFJaTzhy7oLVcy4QCNUjMuc49wPLnr3zxLqhO1innMp2crUxjIFNLRGJm/TfDLnlpxTPud6WZoo5zg==
|
||||
"@ledgerhq/hw-transport-node-hid-noevents@^5.38.0":
|
||||
version "5.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-5.38.0.tgz#ffbfb0b997e585fc08b6b220997caaa59e094554"
|
||||
integrity sha512-zuxN3gWfCuN+pbK3BKc8z3VCulKI7zee1N3xhCWua9TiwL3leBTBs25Bvm22fmiLJYhzszccbF673/bpnnIQAA==
|
||||
dependencies:
|
||||
"@ledgerhq/devices" "^5.36.0"
|
||||
"@ledgerhq/errors" "^5.36.0"
|
||||
"@ledgerhq/hw-transport" "^5.36.0"
|
||||
"@ledgerhq/logs" "^5.36.0"
|
||||
"@ledgerhq/devices" "^5.38.0"
|
||||
"@ledgerhq/errors" "^5.38.0"
|
||||
"@ledgerhq/hw-transport" "^5.38.0"
|
||||
"@ledgerhq/logs" "^5.38.0"
|
||||
node-hid "2.1.1"
|
||||
|
||||
"@ledgerhq/hw-transport-node-hid-singleton@5.36.0":
|
||||
version "5.36.0"
|
||||
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid-singleton/-/hw-transport-node-hid-singleton-5.36.0.tgz#99cedb773e571642d7f151c3134c5924147bc0d8"
|
||||
integrity sha512-eKkJGXW0m7PeJY7V/7FDfU4VFLIVSRjxi7PpfJKFYlUrRwx9QndlZT0LD0u3W9dLktgounHhsOuXvuG44uTHYA==
|
||||
"@ledgerhq/hw-transport-node-hid-singleton@5.38.0":
|
||||
version "5.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid-singleton/-/hw-transport-node-hid-singleton-5.38.0.tgz#ea133ac3a7015aaafd5d629388da4d4a2086c761"
|
||||
integrity sha512-FPpr6R2Cs6YKCamprC9vzJa+P7RieyMLQC3cG6EjYg8tmvHPtIxpmWFclNi45jZ05Ve1YvNjutoHVTVn0cOnpg==
|
||||
dependencies:
|
||||
"@ledgerhq/devices" "^5.36.0"
|
||||
"@ledgerhq/errors" "^5.36.0"
|
||||
"@ledgerhq/hw-transport" "^5.36.0"
|
||||
"@ledgerhq/hw-transport-node-hid-noevents" "^5.36.0"
|
||||
"@ledgerhq/logs" "^5.36.0"
|
||||
"@ledgerhq/devices" "^5.38.0"
|
||||
"@ledgerhq/errors" "^5.38.0"
|
||||
"@ledgerhq/hw-transport" "^5.38.0"
|
||||
"@ledgerhq/hw-transport-node-hid-noevents" "^5.38.0"
|
||||
"@ledgerhq/logs" "^5.38.0"
|
||||
lodash "^4.17.20"
|
||||
node-hid "2.1.1"
|
||||
usb-detection "^4.10.0"
|
||||
|
@ -1842,19 +1842,19 @@
|
|||
"@ledgerhq/logs" "^5.30.0"
|
||||
u2f-api "0.2.7"
|
||||
|
||||
"@ledgerhq/hw-transport@^5.34.0", "@ledgerhq/hw-transport@^5.36.0":
|
||||
version "5.36.0"
|
||||
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-5.36.0.tgz#b6e951c411182ece59c7b527f948240d1b0a8a51"
|
||||
integrity sha512-rwLTBUsdGCC3Ka4G99sqGbbyVhkVxXd4eWWeOb8gnuKhrTydZGkkP3JdZSHgWSrVRYTAUmkE1AnUmchbfh361w==
|
||||
"@ledgerhq/hw-transport@^5.34.0", "@ledgerhq/hw-transport@^5.38.0":
|
||||
version "5.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-5.38.0.tgz#b02bea73d77e729d13c367a967df1666f9ef940d"
|
||||
integrity sha512-CAxvHukCcp+RqaEsSltmBw4Lb1yW42fiF/LTYN7JvCkZyLIQhvkndLDVCgp4hpMdtLK4bkf7RJRElqbN0vRoAQ==
|
||||
dependencies:
|
||||
"@ledgerhq/devices" "^5.36.0"
|
||||
"@ledgerhq/errors" "^5.36.0"
|
||||
"@ledgerhq/devices" "^5.38.0"
|
||||
"@ledgerhq/errors" "^5.38.0"
|
||||
events "^3.2.0"
|
||||
|
||||
"@ledgerhq/logs@^5.30.0", "@ledgerhq/logs@^5.36.0":
|
||||
version "5.36.0"
|
||||
resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-5.36.0.tgz#78721347162fb834d90effa1123b363dc46e3a52"
|
||||
integrity sha512-HnD/hByteUL1MsJu1lMinY9bNq8++5mWJ8qHCW9dLC9LbsvWIqwLwmZiGcW0D2tX9p0/C5ESuIpJ9B/d2dReuw==
|
||||
"@ledgerhq/logs@^5.30.0", "@ledgerhq/logs@^5.38.0":
|
||||
version "5.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-5.38.0.tgz#3c5dbd1f62c0bf5580477b218fb57c67b575643a"
|
||||
integrity sha512-i87Yn89Cq2D9Y0KmrEzCm62XHzI2edeOTBENKH6vAyzESGzyF+SBoqtZNwrjJcKup3/9dNn/zHjpicY7ev94Vw==
|
||||
|
||||
"@material-ui/core@^4.11.0":
|
||||
version "4.11.2"
|
||||
|
@ -8156,10 +8156,10 @@ electron-updater@4.3.5:
|
|||
lodash.isequal "^4.5.0"
|
||||
semver "^7.3.2"
|
||||
|
||||
electron@^9.3.5:
|
||||
version "9.3.5"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-9.3.5.tgz#7967146b81e6d9b484773243fd4a4f671a50b884"
|
||||
integrity sha512-EPmDsp7sO0UPtw7nLD1ufse/nBskP+ifXzBgUg9psCUlapkzuwYi6pmLAzKLW/bVjwgyUKwh1OKWILWfOeLGcQ==
|
||||
electron@^9.4.0:
|
||||
version "9.4.1"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-9.4.1.tgz#62a2aae4cd93f1b56d794a47541505a71654177a"
|
||||
integrity sha512-r4CxoVG9Ja7tBtkilWMnBsBGup8G8Z+v7icZmwysHa8/OSr0OrLjrcOF/30BAP7yPE5fz/XTxygnltzW4OTZdw==
|
||||
dependencies:
|
||||
"@electron/get" "^1.0.1"
|
||||
"@types/node" "^12.0.12"
|
||||
|
|
Loading…
Reference in New Issue