Merge branch 'development' of github.com:gnosis/safe-react into 536-notifications-status-labels-sync
This commit is contained in:
commit
9d95198a20
|
@ -0,0 +1,55 @@
|
|||
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, windows-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') }}
|
|
@ -1,5 +1,4 @@
|
|||
// @flow
|
||||
import ClickAwayListener from '@material-ui/core/ClickAwayListener'
|
||||
import Drawer from '@material-ui/core/Drawer'
|
||||
import SearchIcon from '@material-ui/icons/Search'
|
||||
import { List } from 'immutable'
|
||||
|
@ -90,51 +89,49 @@ const Sidebar = ({ children, currentSafe, defaultSafe, safes, setDefaultSafeActi
|
|||
|
||||
return (
|
||||
<SidebarContext.Provider value={{ isOpen, toggleSidebar }}>
|
||||
<ClickAwayListener onClickAway={toggleSidebar}>
|
||||
<Drawer
|
||||
classes={{ paper: classes.sidebarPaper }}
|
||||
className={classes.sidebar}
|
||||
ModalProps={{ onBackdropClick: toggleSidebar }}
|
||||
onKeyDown={handleEsc}
|
||||
open={isOpen}
|
||||
>
|
||||
<Row align="center" className={classes.topComponents}>
|
||||
<Row align="center" className={classes.searchWrapper}>
|
||||
<SearchIcon className={classes.searchIcon} />
|
||||
<SearchBar
|
||||
classes={searchClasses}
|
||||
onCancelSearch={handleFilterCancel}
|
||||
onChange={handleFilterChange}
|
||||
placeholder="Search by name or address"
|
||||
searchIcon={<div />}
|
||||
value={filter}
|
||||
/>
|
||||
</Row>
|
||||
<Divider className={classes.divider} />
|
||||
<Spacer className={classes.spacer} />
|
||||
<Button
|
||||
className={classes.addSafeBtn}
|
||||
color="primary"
|
||||
component={Link}
|
||||
onClick={toggleSidebar}
|
||||
size="small"
|
||||
to={WELCOME_ADDRESS}
|
||||
variant="contained"
|
||||
>
|
||||
+ Add Safe
|
||||
</Button>
|
||||
<Spacer />
|
||||
<Drawer
|
||||
classes={{ paper: classes.sidebarPaper }}
|
||||
className={classes.sidebar}
|
||||
ModalProps={{ onBackdropClick: toggleSidebar }}
|
||||
onKeyDown={handleEsc}
|
||||
open={isOpen}
|
||||
>
|
||||
<Row align="center" className={classes.topComponents}>
|
||||
<Row align="center" className={classes.searchWrapper}>
|
||||
<SearchIcon className={classes.searchIcon} />
|
||||
<SearchBar
|
||||
classes={searchClasses}
|
||||
onCancelSearch={handleFilterCancel}
|
||||
onChange={handleFilterChange}
|
||||
placeholder="Search by name or address"
|
||||
searchIcon={<div />}
|
||||
value={filter}
|
||||
/>
|
||||
</Row>
|
||||
<Hairline />
|
||||
<SafeList
|
||||
currentSafe={currentSafe}
|
||||
defaultSafe={defaultSafe}
|
||||
onSafeClick={toggleSidebar}
|
||||
safes={filteredSafes}
|
||||
setDefaultSafe={setDefaultSafeAction}
|
||||
/>
|
||||
</Drawer>
|
||||
</ClickAwayListener>
|
||||
<Divider className={classes.divider} />
|
||||
<Spacer className={classes.spacer} />
|
||||
<Button
|
||||
className={classes.addSafeBtn}
|
||||
color="primary"
|
||||
component={Link}
|
||||
onClick={toggleSidebar}
|
||||
size="small"
|
||||
to={WELCOME_ADDRESS}
|
||||
variant="contained"
|
||||
>
|
||||
+ Add Safe
|
||||
</Button>
|
||||
<Spacer />
|
||||
</Row>
|
||||
<Hairline />
|
||||
<SafeList
|
||||
currentSafe={currentSafe}
|
||||
defaultSafe={defaultSafe}
|
||||
onSafeClick={toggleSidebar}
|
||||
safes={filteredSafes}
|
||||
setDefaultSafe={setDefaultSafeAction}
|
||||
/>
|
||||
</Drawer>
|
||||
{children}
|
||||
</SidebarContext.Provider>
|
||||
)
|
||||
|
|
|
@ -83,7 +83,6 @@ export const estimateGasForDeployingSafe = async (
|
|||
numConfirmations: number,
|
||||
userAccount: string,
|
||||
) => {
|
||||
console.log(proxyFactoryMaster)
|
||||
const gnosisSafeData = await safeMaster.contract.methods
|
||||
.setup(safeAccounts, numConfirmations, ZERO_ADDRESS, '0x', DEFAULT_FALLBACK_HANDLER_ADDRESS, ZERO_ADDRESS, 0, ZERO_ADDRESS)
|
||||
.encodeABI()
|
||||
|
|
|
@ -1,92 +1,5 @@
|
|||
// @flow
|
||||
import { BigNumber } from 'bignumber.js'
|
||||
|
||||
import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts'
|
||||
import { EMPTY_DATA } from '~/logic/wallets/ethTransactions'
|
||||
import { getWeb3 } from '~/logic/wallets/getWeb3'
|
||||
|
||||
const estimateDataGasCosts = (data) => {
|
||||
const reducer = (accumulator, currentValue) => {
|
||||
if (currentValue === EMPTY_DATA) {
|
||||
return accumulator + 0
|
||||
}
|
||||
|
||||
if (currentValue === '00') {
|
||||
return accumulator + 4
|
||||
}
|
||||
|
||||
return accumulator + 68
|
||||
}
|
||||
|
||||
return data.match(/.{2}/g).reduce(reducer, 0)
|
||||
}
|
||||
|
||||
// https://docs.gnosis.io/safe/docs/docs4/#safe-transaction-data-gas-estimation
|
||||
// https://github.com/gnosis/safe-contracts/blob/a97c6fd24f79c0b159ddd25a10a2ebd3ea2ef926/test/utils/execution.js
|
||||
export const estimateDataGas = (
|
||||
safe: any,
|
||||
to: string,
|
||||
valueInWei: number,
|
||||
from: string,
|
||||
data: string,
|
||||
operation: number,
|
||||
txGasEstimate: number,
|
||||
gasToken: number,
|
||||
nonce: number,
|
||||
signatureCount: number,
|
||||
refundReceiver: number,
|
||||
) => {
|
||||
// numbers < 256 are 192 -> 31 * 4 + 68
|
||||
// numbers < 65k are 256 -> 30 * 4 + 2 * 68
|
||||
// For signature array length and dataGasEstimate we already calculated
|
||||
// the 0 bytes so we just add 64 for each non-zero byte
|
||||
const gasPrice = 0 // no need to get refund when we submit txs to metamask
|
||||
const signatureCost = signatureCount * (68 + 2176 + 2176 + 6000) // array count (3 -> r, s, v) * signature count
|
||||
|
||||
// https://docs.gnosis.io/safe/docs/docs5/#pre-validated-signatures
|
||||
const sigs = `0x000000000000000000000000${from.replace(
|
||||
'0x',
|
||||
'',
|
||||
)}000000000000000000000000000000000000000000000000000000000000000001`
|
||||
const payload = safe.contract.methods
|
||||
.execTransaction(to, valueInWei, data, operation, txGasEstimate, 0, gasPrice, gasToken, refundReceiver, sigs)
|
||||
.encodeABI()
|
||||
|
||||
// eslint-disable-next-line
|
||||
const dataGasEstimate = estimateDataGasCosts(payload) + signatureCost + (nonce > 0 ? 5000 : 20000) + 1500 // 1500 -> hash generation costs
|
||||
|
||||
return dataGasEstimate + 32000 // Add aditional gas costs (e.g. base tx costs, transfer costs)
|
||||
}
|
||||
|
||||
export const generateTxGasEstimateFrom = async (
|
||||
safe: any,
|
||||
safeAddress: string,
|
||||
data: string,
|
||||
to: string,
|
||||
valueInWei: number,
|
||||
operation: number,
|
||||
) => {
|
||||
try {
|
||||
let safeInstance = safe
|
||||
if (!safeInstance) {
|
||||
safeInstance = await getGnosisSafeInstanceAt(safeAddress)
|
||||
}
|
||||
|
||||
const estimateData = safeInstance.contract.methods.requiredTxGas(to, valueInWei, data, operation).encodeABI()
|
||||
const estimateResponse = await getWeb3().eth.call({
|
||||
to: safeAddress,
|
||||
from: safeAddress,
|
||||
data: estimateData,
|
||||
})
|
||||
const txGasEstimate = new BigNumber(estimateResponse.substring(138), 16)
|
||||
|
||||
// Add 10k else we will fail in case of nested calls
|
||||
return txGasEstimate.toNumber() + 10000
|
||||
} catch (error) {
|
||||
console.error('Error calculating tx gas estimation', error)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
export const calculateTxFee = async (
|
||||
safe: any,
|
||||
|
|
|
@ -1,14 +1,32 @@
|
|||
// @flow
|
||||
import GnosisSafeSol from '@gnosis.pm/safe-contracts/build/contracts/GnosisSafe.json'
|
||||
import { BigNumber } from 'bignumber.js'
|
||||
|
||||
import { CALL } from '.'
|
||||
|
||||
import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts'
|
||||
import { generateSignaturesFromTxConfirmations } from '~/logic/safe/safeTxSigner'
|
||||
import { ZERO_ADDRESS } from '~/logic/wallets/ethAddresses'
|
||||
import { calculateGasOf, calculateGasPrice } from '~/logic/wallets/ethTransactions'
|
||||
import { EMPTY_DATA, calculateGasOf, calculateGasPrice } from '~/logic/wallets/ethTransactions'
|
||||
import { getAccountFrom, getWeb3 } from '~/logic/wallets/getWeb3'
|
||||
import { type Transaction } from '~/routes/safe/store/models/transaction'
|
||||
|
||||
const estimateDataGasCosts = (data) => {
|
||||
const reducer = (accumulator, currentValue) => {
|
||||
if (currentValue === EMPTY_DATA) {
|
||||
return accumulator + 0
|
||||
}
|
||||
|
||||
if (currentValue === '00') {
|
||||
return accumulator + 4
|
||||
}
|
||||
|
||||
return accumulator + 16
|
||||
}
|
||||
|
||||
return data.match(/.{2}/g).reduce(reducer, 0)
|
||||
}
|
||||
|
||||
export const estimateTxGasCosts = async (
|
||||
safeAddress: string,
|
||||
to: string,
|
||||
|
@ -58,3 +76,70 @@ export const estimateTxGasCosts = async (
|
|||
return 10000
|
||||
}
|
||||
}
|
||||
|
||||
export const estimateSafeTxGas = async (
|
||||
safe: any,
|
||||
safeAddress: string,
|
||||
data: string,
|
||||
to: string,
|
||||
valueInWei: number | string,
|
||||
operation: number,
|
||||
) => {
|
||||
try {
|
||||
let safeInstance = safe
|
||||
if (!safeInstance) {
|
||||
safeInstance = await getGnosisSafeInstanceAt(safeAddress)
|
||||
}
|
||||
|
||||
const web3 = await getWeb3()
|
||||
const estimateData = safeInstance.contract.methods.requiredTxGas(to, valueInWei, data, operation).encodeABI()
|
||||
const estimateResponse = await web3.eth.call({
|
||||
to: safeAddress,
|
||||
from: safeAddress,
|
||||
data: estimateData,
|
||||
})
|
||||
const txGasEstimation = new BigNumber(estimateResponse.substring(138), 16).toNumber() + 10000
|
||||
|
||||
// 21000 - additional gas costs (e.g. base tx costs, transfer costs)
|
||||
const dataGasEstimation = estimateDataGasCosts(estimateData) + 21000
|
||||
|
||||
const additionalGasBatches = [10000, 20000, 40000, 80000, 160000, 320000, 640000, 1280000, 2560000, 5120000]
|
||||
|
||||
const batch = new web3.BatchRequest()
|
||||
const estimationRequests = additionalGasBatches.map(
|
||||
(additionalGas) =>
|
||||
new Promise((resolve) => {
|
||||
const request = web3.eth.call.request(
|
||||
{
|
||||
to: safe.address,
|
||||
from: safe.address,
|
||||
data: estimateData,
|
||||
gasPrice: 0,
|
||||
gasLimit: txGasEstimation + dataGasEstimation + additionalGas,
|
||||
},
|
||||
(error, res) => {
|
||||
resolve({
|
||||
success: error || res === '0x' ? false : true,
|
||||
estimation: txGasEstimation + additionalGas,
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
batch.add(request)
|
||||
}),
|
||||
)
|
||||
batch.execute()
|
||||
|
||||
const estimationResponses = await Promise.all(estimationRequests)
|
||||
const firstSuccessfulRequest = estimationResponses.find((res) => res.success)
|
||||
|
||||
if (firstSuccessfulRequest) {
|
||||
return firstSuccessfulRequest.estimation
|
||||
}
|
||||
|
||||
return 0
|
||||
} catch (error) {
|
||||
console.error('Error calculating tx gas estimation', error)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
getExecutionTransaction,
|
||||
saveTxToHistory,
|
||||
} from '~/logic/safe/transactions'
|
||||
import { estimateSafeTxGas } from '~/logic/safe/transactions/gasNew'
|
||||
import { SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES, tryOffchainSigning } from '~/logic/safe/transactions/offchainSigner'
|
||||
import { getCurrentSafeVersion } from '~/logic/safe/utils/safeVersion'
|
||||
import { ZERO_ADDRESS } from '~/logic/wallets/ethAddresses'
|
||||
|
@ -66,6 +67,7 @@ const createTransaction = ({
|
|||
const nonce = await getNewTxNonce(txNonce, lastTx, safeInstance)
|
||||
const isExecution = await shouldExecuteTransaction(safeInstance, nonce, lastTx)
|
||||
const safeVersion = await getCurrentSafeVersion(safeInstance)
|
||||
const safeTxGas = await estimateSafeTxGas(safeInstance, safeAddress, txData, to, valueInWei, operation)
|
||||
|
||||
// https://docs.gnosis.io/safe/docs/docs5/#pre-validated-signatures
|
||||
const sigs = `0x000000000000000000000000${from.replace(
|
||||
|
@ -86,7 +88,7 @@ const createTransaction = ({
|
|||
data: txData,
|
||||
operation,
|
||||
nonce,
|
||||
safeTxGas: 0,
|
||||
safeTxGas,
|
||||
baseGas: 0,
|
||||
gasPrice: 0,
|
||||
gasToken: ZERO_ADDRESS,
|
||||
|
|
|
@ -14935,9 +14935,9 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.13
|
|||
path-parse "^1.0.6"
|
||||
|
||||
resolve@^1.1.7, resolve@^1.4.0:
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.16.1.tgz#49fac5d8bacf1fd53f200fa51247ae736175832c"
|
||||
integrity sha512-rmAglCSqWWMrrBv/XM6sW0NuRFiKViw/W4d9EbC4pt+49H8JwHy+mcGmALTEg504AUDcLTvb1T2q3E9AnmY+ig==
|
||||
version "1.16.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.16.0.tgz#063dc704fa3413e13ac1d0d1756a7cbfe95dd1a7"
|
||||
integrity sha512-LarL/PIKJvc09k1jaeT4kQb/8/7P+qV4qSnN2K80AES+OHdfZELAKVOBjxsvtToT/uLOfFbvYvKfZmV8cee7nA==
|
||||
dependencies:
|
||||
path-parse "^1.0.6"
|
||||
|
||||
|
|
Loading…
Reference in New Issue