Merge branch 'development' of github.com:gnosis/safe-react into feature/wallet-dashboard
This commit is contained in:
commit
041e97c19c
|
@ -28,4 +28,5 @@ REACT_APP_APP_VERSION=$npm_package_version
|
|||
REACT_APP_INFURA_TOKEN=
|
||||
|
||||
# For Apps
|
||||
REACT_APP_GNOSIS_APPS_URL=http://localhost:3002
|
||||
REACT_APP_GNOSIS_APPS_URL=https://safe-apps.staging.gnosisdev.com
|
||||
REACT_APP_APPS_DISABLED=false
|
||||
|
|
|
@ -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]
|
||||
|
||||
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,11 +1,11 @@
|
|||
name: Build/release
|
||||
name: Build/Release Desktop app
|
||||
|
||||
# this will help you specify where to run
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
# this will run on the specified branch
|
||||
- feature/desktop-app
|
||||
- master
|
||||
|
||||
env:
|
||||
REACT_APP_BLOCKNATIVE_KEY: ${{ secrets.REACT_APP_BLOCKNATIVE_KEY }}
|
||||
|
@ -13,25 +13,42 @@ env:
|
|||
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 }}
|
||||
REACT_APP_GNOSIS_APPS_URL: ${{ secrets.REACT_APP_GNOSIS_APPS_URL }}
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
max-parallel: 15
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||
os: [macos-latest, windows-latest, ubuntu-latest]
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Patch node gyp on windows to support Visual Studio 2019
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
shell: powershell
|
||||
run: |
|
||||
yarn global add --production windows-build-tools --vs2015 --msvs_version=2015
|
||||
|
||||
- name: Install node-gyp
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
shell: powershell
|
||||
run: |
|
||||
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
|
||||
with:
|
||||
node-version: 10.16
|
||||
- run: yarn install --network-concurrency 1
|
||||
|
||||
- name: Build/release Electron app
|
||||
- name: Build/Release Desktop App
|
||||
env:
|
||||
# macOS notarization API key
|
||||
APPLEID: ${{ secrets.APPLE_ID }}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
node_modules/
|
||||
build_webpack/
|
||||
.DS_Store
|
||||
build/
|
||||
./build
|
||||
yarn-error.log
|
||||
.env*
|
||||
.idea/
|
||||
dist
|
||||
electron-builder.yml
|
||||
.yalc/
|
||||
yalc.lock
|
|
@ -16,9 +16,11 @@ matrix:
|
|||
- env:
|
||||
- REACT_APP_NETWORK='mainnet'
|
||||
- STAGING_BUCKET_NAME=${STAGING_MAINNET_BUCKET_NAME}
|
||||
- REACT_APP_GNOSIS_APPS_URL=${REACT_APP_GNOSIS_APPS_URL_PROD}
|
||||
if: (branch = master AND NOT type = pull_request) OR tag IS present
|
||||
- env:
|
||||
- REACT_APP_NETWORK='rinkeby'
|
||||
- REACT_APP_GNOSIS_APPS_URL=${REACT_APP_GNOSIS_APPS_URL_STAGING}
|
||||
before_install:
|
||||
# Needed to deploy pull request and releases
|
||||
- sudo apt-get update
|
||||
|
|
|
@ -52,9 +52,11 @@ function ensureSlash(path, needsSlash) {
|
|||
// single-page apps that may serve index.html for nested URLs like /todos/42.
|
||||
// We can't use a relative path in HTML because we don't want to load something
|
||||
// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
|
||||
const buildDesktop = process.env.BUILD_FOR_DESKTOP
|
||||
|
||||
const homepagePath = require(paths.appPackageJson).homepage
|
||||
// var homepagePathname = homepagePath ? url.parse(homepagePath).pathname : '/';
|
||||
const homepagePathname = "/app/"
|
||||
const homepagePathname = buildDesktop === 'true' ? "./" : "/app/"
|
||||
// Webpack uses `publicPath` to determine where the app is being served from.
|
||||
// It requires a trailing slash, or the file assets will get an incorrect path.
|
||||
const publicPath = ensureSlash(homepagePathname, true)
|
||||
|
|
126
package.json
126
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "safe-react",
|
||||
"version": "1.9.5",
|
||||
"version": "2.0.0",
|
||||
"description": "Allowing crypto users manage funds in a safer way",
|
||||
"homepage": "https://github.com/gnosis/safe-react#readme",
|
||||
"bugs": {
|
||||
|
@ -8,16 +8,24 @@
|
|||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/gnosis/safe-react"
|
||||
"url": "https://github.com/gnosis/safe-react.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "Gnosis Team",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
"author": {
|
||||
"name": "Gnosis Team",
|
||||
"email": "safe@gnosis.io"
|
||||
},
|
||||
"main": "public/electron.js",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"scripts": {
|
||||
"build": "REACT_APP_APP_VERSION=$npm_package_version node scripts/build.js",
|
||||
"build-mainnet": "REACT_APP_NETWORK=mainnet yarn build",
|
||||
"build": "cross-env REACT_APP_APP_VERSION=$npm_package_version node scripts/build.js",
|
||||
"electron-build": "electron-builder --mac --windows --linux",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"release": "electron-builder --mac --linux --windows -p always",
|
||||
"electron-dev": "concurrently \"BROWSER=none yarn start\" \"wait-on http://localhost:3000 && electron .\"",
|
||||
"preelectron-pack": "yarn build",
|
||||
"build-mainnet": "cross-env REACT_APP_NETWORK=mainnet yarn build",
|
||||
"build-desktop": "cross-env BUILD_FOR_DESKTOP=true yarn build-mainnet",
|
||||
"flow": "flow",
|
||||
"format:staged": "lint-staged",
|
||||
"lint:check": "eslint './src/**/*.{js,jsx}'",
|
||||
|
@ -40,10 +48,97 @@
|
|||
"prettier --write"
|
||||
]
|
||||
},
|
||||
"productName": "Safe Electron",
|
||||
"build": {
|
||||
"appId": "io.gnosis.safe.macos",
|
||||
"afterSign": "scripts/notarize.js",
|
||||
"productName": "Safe Electron",
|
||||
"asar": true,
|
||||
"publish": [
|
||||
{
|
||||
"provider": "github",
|
||||
"owner": "gnosis",
|
||||
"repo": "safe-react"
|
||||
}
|
||||
],
|
||||
"dmg": {
|
||||
"sign": false,
|
||||
"contents": [
|
||||
{
|
||||
"x": 110,
|
||||
"y": 150
|
||||
},
|
||||
{
|
||||
"x": 240,
|
||||
"y": 150,
|
||||
"type": "link",
|
||||
"path": "/Applications"
|
||||
}
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"**/*",
|
||||
"!src${/*}",
|
||||
"!config${/*}",
|
||||
"!contracts${/*}",
|
||||
"!migrations${/*}",
|
||||
"!flow-typed${/*}",
|
||||
"!apps${/*}",
|
||||
"!build${/*}",
|
||||
"!out${/*}",
|
||||
"!.editorconfig",
|
||||
"!.gitignore",
|
||||
"!README.md",
|
||||
"!yarn-error.log",
|
||||
"!yarn.lock"
|
||||
],
|
||||
"directories": {
|
||||
"buildResources": "public/build"
|
||||
},
|
||||
"mac": {
|
||||
"category": "public.app-category.productivity",
|
||||
"hardenedRuntime": true,
|
||||
"entitlements": "public/build/entitlements.mac.plist",
|
||||
"gatekeeperAssess": false,
|
||||
"entitlementsInherit": "public/build/entitlements.mac.plist",
|
||||
"target": [
|
||||
"dmg",
|
||||
"zip"
|
||||
],
|
||||
"publish": [
|
||||
{
|
||||
"provider": "github",
|
||||
"owner": "gnosis",
|
||||
"repo": "safe-react"
|
||||
}
|
||||
]
|
||||
},
|
||||
"nsis": {
|
||||
"deleteAppDataOnUninstall": true
|
||||
},
|
||||
"linux": {
|
||||
"target": [
|
||||
"AppImage",
|
||||
"deb",
|
||||
"zip"
|
||||
],
|
||||
"icon": "./public/build/safe.png"
|
||||
},
|
||||
"win": {
|
||||
"target": [
|
||||
"nsis"
|
||||
],
|
||||
"icon": "public/build/icon.ico"
|
||||
}
|
||||
},
|
||||
"resolutions": {
|
||||
"node-gyp": "^5.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@gnosis.pm/safe-contracts": "1.1.1-dev.1",
|
||||
"@gnosis.pm/safe-contracts": "1.1.1-dev.2",
|
||||
"@gnosis.pm/util-contracts": "2.0.6",
|
||||
"@gnosis.pm/safe-react-components": "https://github.com/gnosis/safe-react-components.git#71e6fed",
|
||||
"@gnosis.pm/safe-react-components": "https://github.com/gnosis/safe-react-components.git#a057248",
|
||||
"@ledgerhq/hw-transport-node-hid": "5.12.0",
|
||||
"@material-ui/core": "4.9.10",
|
||||
"@material-ui/icons": "4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.39",
|
||||
|
@ -55,18 +150,26 @@
|
|||
"bignumber.js": "9.0.0",
|
||||
"bnc-onboard": "1.7.6",
|
||||
"connected-react-router": "6.8.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"currency-flags": "^2.1.1",
|
||||
"date-fns": "2.12.0",
|
||||
"dotenv": "^8.2.0",
|
||||
"electron-is-dev": "^1.1.0",
|
||||
"electron-log": "^4.1.1",
|
||||
"electron-updater": "4.2.0",
|
||||
"ethereum-ens": "0.8.0",
|
||||
"express": "^4.17.1",
|
||||
"final-form": "4.19.1",
|
||||
"history": "4.10.1",
|
||||
"immortal-db": "^1.0.2",
|
||||
"immutable": "^4.0.0-rc.9",
|
||||
"install": "^0.13.0",
|
||||
"js-cookie": "^2.2.1",
|
||||
"lint-staged": "10.1.3",
|
||||
"material-ui-search-bar": "^1.0.0-beta.13",
|
||||
"notistack": "https://github.com/gnosis/notistack.git#v0.9.4",
|
||||
"npm": "^6.14.4",
|
||||
"open": "^7.0.3",
|
||||
"optimize-css-assets-webpack-plugin": "5.0.3",
|
||||
"polished": "3.5.1",
|
||||
"qrcode.react": "1.0.0",
|
||||
|
@ -89,6 +192,7 @@
|
|||
"reselect": "^4.0.0",
|
||||
"semver": "7.3.2",
|
||||
"styled-components": "^5.0.1",
|
||||
"wait-on": "^4.0.1",
|
||||
"web3": "1.2.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -127,9 +231,13 @@
|
|||
"babel-plugin-transform-es3-property-literals": "^6.22.0",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"classnames": "^2.2.6",
|
||||
"concurrently": "4.1.2",
|
||||
"css-loader": "3.5.2",
|
||||
"detect-port": "^1.3.0",
|
||||
"dotenv-expand": "^5.1.0",
|
||||
"electron": "7.1.8",
|
||||
"electron-builder": "22.2.0",
|
||||
"electron-notarize": "^0.2.1",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-prettier": "6.10.1",
|
||||
"eslint-plugin-flowtype": "4.7.0",
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
const os = require('os');
|
||||
const fetch = require('node-fetch');
|
||||
const { dialog, app } = require('electron');
|
||||
const log = require('electron-log');
|
||||
const isDev = require("electron-is-dev");
|
||||
const { autoUpdater } = require("electron-updater");
|
||||
|
||||
// This logging setup is not required for auto-updates to work,
|
||||
// but it sure makes debugging easier :)
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
autoUpdater.autoDownload = false
|
||||
autoUpdater.logger = log;
|
||||
autoUpdater.logger.transports.file.level = 'info';
|
||||
log.info('App starting...');
|
||||
|
||||
let initialized = false;
|
||||
let downloadProgress = 0;
|
||||
|
||||
function init(mainWindow) {
|
||||
|
||||
if(initialized || isDev) return;
|
||||
|
||||
initialized = true;
|
||||
|
||||
autoUpdater.on('error', (error) => {
|
||||
dialog.showErrorBox('Error: ', error == null ? "unknown" : (error.stack || error).toString());
|
||||
});
|
||||
|
||||
autoUpdater.on('update-available', () => {
|
||||
dialog.showMessageBox({
|
||||
type: 'info',
|
||||
title: 'Found Updates',
|
||||
message: 'There is a newer version of this app available. Do you want to update now?',
|
||||
buttons: ['Yes', 'Remind me later'],
|
||||
cancelId:1,
|
||||
}).then(result => {
|
||||
if(result.response === 0){
|
||||
autoUpdater.downloadUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName) => {
|
||||
autoUpdater.logger.info("Update Downloaded...");
|
||||
dialog.showMessageBox({
|
||||
title: 'Install Updates',
|
||||
message: process.platform === 'win32' ? releaseNotes : releaseName,
|
||||
detail: 'A new version has been downloaded. Restart the application to apply the updates.',
|
||||
buttons: ['Restart', 'Cancel'],
|
||||
cancelId:1,
|
||||
}).then(result => {
|
||||
if(result.response === 0){
|
||||
autoUpdater.quitAndInstall();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
autoUpdater.on("download-progress", (d) => {
|
||||
downloadProgress = d.percent;
|
||||
autoUpdater.logger.info(downloadProgress);
|
||||
});
|
||||
|
||||
autoUpdater.checkForUpdates();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init,
|
||||
};
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 83 KiB |
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.camera</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.microphone</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 8.6 KiB |
|
@ -0,0 +1,162 @@
|
|||
const electron = require("electron");
|
||||
const express = require('express');
|
||||
const open = require('open');
|
||||
const log = require('electron-log');
|
||||
const fs = require('fs');
|
||||
const dialog = electron.dialog;
|
||||
const Menu = electron.Menu;
|
||||
const https = require('https');
|
||||
const autoUpdater = require('./auto-updater');
|
||||
|
||||
const url = require('url');
|
||||
const app = electron.app;
|
||||
const session = electron.session;
|
||||
const BrowserWindow = electron.BrowserWindow;
|
||||
|
||||
const path = require("path");
|
||||
const isDev = require("electron-is-dev");
|
||||
|
||||
const options = {
|
||||
key: fs.readFileSync(path.join(__dirname, './ssl/server.key')),
|
||||
cert: fs.readFileSync(path.join(__dirname, './ssl/server.crt')),
|
||||
ca: fs.readFileSync(path.join(__dirname, './ssl/rootCA.crt'))
|
||||
};
|
||||
|
||||
const PORT = 5000;
|
||||
|
||||
const createServer = () => {
|
||||
const app = express();
|
||||
const staticRoute = path.join(__dirname, '../build_webpack');
|
||||
app.use(express.static(staticRoute));
|
||||
https.createServer(options, app).listen(PORT);
|
||||
}
|
||||
|
||||
|
||||
let mainWindow;
|
||||
|
||||
function getOpenedWindow(url,options) {
|
||||
let display = electron.screen.getPrimaryDisplay();
|
||||
let width = display.bounds.width;
|
||||
let height = display.bounds.height;
|
||||
|
||||
// filter all requests to trezor-bridge and change origin to make it work
|
||||
const filter = {
|
||||
urls: ['http://127.0.0.1:21325/*']
|
||||
};
|
||||
|
||||
options.webPreferences.affinity = 'main-window';
|
||||
|
||||
if(url.includes('about:blank')){
|
||||
/*
|
||||
session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
|
||||
details.requestHeaders['Origin'] = 'https://electron.trezor.io';
|
||||
callback({cancel: false, requestHeaders: details.requestHeaders});
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
if(url.includes('wallet.portis') || url.includes('about:blank') || url.includes('app.tor.us')){
|
||||
const win = new BrowserWindow({
|
||||
width:300,
|
||||
height:700,
|
||||
x: width - 1300,
|
||||
parent:mainWindow,
|
||||
y: height - 200,
|
||||
webContents: options.webContents, // use existing webContents if provided
|
||||
fullscreen: false,
|
||||
show: false,
|
||||
});
|
||||
|
||||
win.once('ready-to-show', () => win.show());
|
||||
|
||||
if(!options.webPreferences){
|
||||
win.loadURL(url);
|
||||
}
|
||||
return win
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
function createWindow() {
|
||||
mainWindow = new BrowserWindow({
|
||||
show: false,
|
||||
width: 1024,
|
||||
height: 768,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, '../scripts/preload.js'),
|
||||
allowRunningInsecureContent: true,
|
||||
nativeWindowOpen: true, // need to be set in order to display modal
|
||||
},
|
||||
icon: path.join(__dirname, './build/safe.png'),
|
||||
});
|
||||
|
||||
mainWindow.once('ready-to-show', () => {
|
||||
mainWindow.show();
|
||||
});
|
||||
|
||||
mainWindow.loadURL(
|
||||
isDev
|
||||
? "http://localhost:3000"
|
||||
: `https://localhost:${PORT}`
|
||||
)
|
||||
|
||||
if (isDev) {
|
||||
// Open the DevTools.
|
||||
mainWindow.webContents.openDevTools();
|
||||
//BrowserWindow.addDevToolsExtension('<location to your react chrome extension>');
|
||||
}
|
||||
|
||||
mainWindow.setMenu(null);
|
||||
mainWindow.setMenuBarVisibility(false);
|
||||
|
||||
mainWindow.webContents.on('new-window', function(event, url, frameName, disposition, options){
|
||||
event.preventDefault();
|
||||
const win = getOpenedWindow(url,options);
|
||||
if(win){
|
||||
win.once('ready-to-show', () => win.show());
|
||||
|
||||
if(!options.webPreferences){
|
||||
win.loadURL(url);
|
||||
}
|
||||
|
||||
event.newGuest = win
|
||||
} else open(url);
|
||||
});
|
||||
|
||||
mainWindow.webContents.on('did-finish-load', () => {
|
||||
autoUpdater.init(mainWindow);
|
||||
});
|
||||
|
||||
mainWindow.webContents.on('crashed', () => {
|
||||
log.info('App Crashed');
|
||||
mainWindow.reload();
|
||||
});
|
||||
|
||||
mainWindow.on("closed", () => (mainWindow = null));
|
||||
}
|
||||
|
||||
app.userAgentFallback = process.platform ==='win32' ?
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.100 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/7.1.7 Safari/537.36';
|
||||
|
||||
app.commandLine.appendSwitch('ignore-certificate-errors');
|
||||
app.on("ready", () =>{
|
||||
// Hide the menu
|
||||
//Menu.setApplicationMenu(null);
|
||||
if(!isDev) createServer();
|
||||
createWindow();
|
||||
});
|
||||
|
||||
app.on("window-all-closed", () => {
|
||||
if (process.platform !== "darwin") {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
app.on("activate", () => {
|
||||
if (mainWindow === null) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDTzCCAjcCFA3n/7f/k+b9g/7W6zodg+u2qC3BMA0GCSqGSIb3DQEBCwUAMGQx
|
||||
CzAJBgNVBAYTAkRFMRAwDgYDVQQIDAdHZXJtYW55MREwDwYDVQQHDAhXYWxsZG9y
|
||||
ZjEPMA0GA1UECgwGU0FQIFNFMQ4wDAYDVQQLDAVUb29sczEPMA0GA1UEAwwGcm9v
|
||||
dENBMB4XDTIwMDMyNzIxMTkxNFoXDTIxMDgwOTIxMTkxNFowZDELMAkGA1UEBhMC
|
||||
REUxEDAOBgNVBAgMB0dlcm1hbnkxETAPBgNVBAcMCFdhbGxkb3JmMQ8wDQYDVQQK
|
||||
DAZTQVAgU0UxDjAMBgNVBAsMBVRvb2xzMQ8wDQYDVQQDDAZjbGllbnQwggEiMA0G
|
||||
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCZ7hsC/0FOfOUUyefqhFvmiYJoqMSi
|
||||
8/vvjJKn+TCXhlfX/BLxBtJN8BlFNDux+qPlPhElbCg0bldeBGkZNgD7Jt6Fjpkd
|
||||
SqqoDIcfl+oxAks76Qi5hh2FKHSOp3BmHgVuur+cbzOd8J+NsskGYay32mAHLrq8
|
||||
ixPLUtkOO9W2PSKm9KQEwOdYV9R/dStvZDA5dEVEDGv3MIBgRVzyu8gGwMfjzci1
|
||||
wgwU5Eb2r2b7Vs19nAoLQwelBf4bL5Z5b2KjfW1HPhmtM1eBaf+3bMscnemAgY8I
|
||||
0ZHMS0XjORLvSBKZ73Q1K9lv6dc45fQA6g3KnVvFSB0nfqbhw7vuDEXrAgMBAAEw
|
||||
DQYJKoZIhvcNAQELBQADggEBAAzKry8DXN6tlIE5ZRp9z/MdT8bOSwNQM9H/E1Rn
|
||||
50fP5C3m5IZioYdsfQtDvEC2bHHIYyWvqL6AAWVOzA8Pvnw1J32Sq3Tz5EwH0B5p
|
||||
wRVxB2GEe7WqSQV88fd2l35/5vcpoe5A444n6qb8ZaqzdBYXgyUPyVAbzcySKEm/
|
||||
b1HuV8dhlOWZcwgGAdgf/yBhu8WN1Mau6zTAFK2osKUQM2TeXCDKX6tDAHryD6jA
|
||||
MP/med+RSLJyyL5OYBl1P/gqSstH0HnpkpeYslaZpXncT2V2PHTwXOs2ywOESil6
|
||||
yEi9KcsPe87hJ5aMJ0iw/A8AkDBnSzvx2LoYtgLxWl/+4xw=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,17 @@
|
|||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIICqTCCAZECAQAwZDELMAkGA1UEBhMCREUxEDAOBgNVBAgMB0dlcm1hbnkxETAP
|
||||
BgNVBAcMCFdhbGxkb3JmMQ8wDQYDVQQKDAZTQVAgU0UxDjAMBgNVBAsMBVRvb2xz
|
||||
MQ8wDQYDVQQDDAZjbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||
AQCZ7hsC/0FOfOUUyefqhFvmiYJoqMSi8/vvjJKn+TCXhlfX/BLxBtJN8BlFNDux
|
||||
+qPlPhElbCg0bldeBGkZNgD7Jt6FjpkdSqqoDIcfl+oxAks76Qi5hh2FKHSOp3Bm
|
||||
HgVuur+cbzOd8J+NsskGYay32mAHLrq8ixPLUtkOO9W2PSKm9KQEwOdYV9R/dStv
|
||||
ZDA5dEVEDGv3MIBgRVzyu8gGwMfjzci1wgwU5Eb2r2b7Vs19nAoLQwelBf4bL5Z5
|
||||
b2KjfW1HPhmtM1eBaf+3bMscnemAgY8I0ZHMS0XjORLvSBKZ73Q1K9lv6dc45fQA
|
||||
6g3KnVvFSB0nfqbhw7vuDEXrAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAMQNR
|
||||
9TrgG1Re1z2nmxQ2lWPfpdx5wTFc2SibJhMGTahEhAW6XJEtYpTPE+S2LEIybr9k
|
||||
Ya+Pg/Q6pE2MjCDMOSkmfXVcyu/Fw+Ek1anNQ1IDS68vVA3lUNpXYHPffZOTdlj6
|
||||
15n7GMUdSISLk8jZOLGli26PLQimSzHeLUjHwFaS6fs5dXrASdDEAq+GfjXw+R83
|
||||
Lh6ENb9ojdtnHhEspWsiuyJVT4GgV2U1q9m+ljZJe8fBgQP3exxVZXgnAeogpy4r
|
||||
+pTjJIRuaxr3xnd/oYQDCaKpXptEuHQ5caQzFmmUM94sAZkQJQn5VVjSCfDv2ZHm
|
||||
Rp+QdXH8iMjiaxKUaQ==
|
||||
-----END CERTIFICATE REQUEST-----
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAme4bAv9BTnzlFMnn6oRb5omCaKjEovP774ySp/kwl4ZX1/wS
|
||||
8QbSTfAZRTQ7sfqj5T4RJWwoNG5XXgRpGTYA+ybehY6ZHUqqqAyHH5fqMQJLO+kI
|
||||
uYYdhSh0jqdwZh4Fbrq/nG8znfCfjbLJBmGst9pgBy66vIsTy1LZDjvVtj0ipvSk
|
||||
BMDnWFfUf3Urb2QwOXRFRAxr9zCAYEVc8rvIBsDH483ItcIMFORG9q9m+1bNfZwK
|
||||
C0MHpQX+Gy+WeW9io31tRz4ZrTNXgWn/t2zLHJ3pgIGPCNGRzEtF4zkS70gSme90
|
||||
NSvZb+nXOOX0AOoNyp1bxUgdJ36m4cO77gxF6wIDAQABAoIBAQCEQ+Vv8Ncz0vug
|
||||
nlEp4St6b2Pf/ExiXNV5I8gMj4FiYexvSUkZVCw3Df0YyuYUa3KTE372MfZl/v4R
|
||||
aibIo++53s9L4ZjNY5A6L/GXgxiXngn5Y6a8i3IoLffhcByTkm5GlC77A05OAymG
|
||||
Pz6eviUEIZ9r7IpGYhbTGdAqe92J5a72yaGK7+xzA/srX1a8C1qVqsoVBT/js99m
|
||||
kNGZDbpjNLZhXAaze51+Z4ehwVDjJXHgxiCRvoZprxo49DTe/xKef5k1p8rEcv78
|
||||
b56B5fYXOnIa2VavEJmEcuaX7uEAW2LT5/hRck/1ekhUBkhJ1BjzgYXqrbX9BiMO
|
||||
wiSjn9pRAoGBAMtg9ewPESMLdKvxAQ9O6DEoRlN1KqHjwkoDZXM9Hw1dDWtxDsuC
|
||||
MwvlzQEj0EVF1N5FedQVuaFVB6Bcq2tIcqvtYOBsflkF01Fe6CSgrqwmWps2tw4p
|
||||
3TbFPNtXlQwUN0CcOzlCimo5dWoa5GT5VAEjXJBy1qmlZunbPlYJBf+pAoGBAMHB
|
||||
2mdK5SKjvduEm+65W4VyhY1S40a8sGI9LcbCnMN1sZcOII0I/1CVsvJMZDj6XVbA
|
||||
/dT76OkrL8uk20oYImNpfMSejiBUB4nLbhcI665jVUd9mG+H0v+wT9L6Nh9cvA8U
|
||||
QNHHPGkzBFOqzXk59p8cFWoIIY5xzcPiydh7jyVzAoGBALwPn7e1wvnt6Ofphjpa
|
||||
k7iI7mbT7CUgz5LTCyeBeEpKJlOYir7CWWOCDowkSr0TsqAKDHqB0FIPp2qw5k3h
|
||||
AzBZ44ACst6s1Vfj87OS5ZIIMTZfZOvy6DxyLDEDDq7JrsHO9bCgCA/rq9f+n/2C
|
||||
1BvtT/W+SyM58C0E7+Jsm6BpAoGBAJrP1T3q7aH7ytr23dCkcafylRsSO15trVER
|
||||
KN5C6RaTl03mj8OgiL9OnShUOU/9W07R7P6cOMD1LL89/aj6F5/uzS4csdrKySsk
|
||||
S9ZD3mv8GkuA4qdakxCRQ3aDTXNJmUlDGXeEBZTYmoBvXLWbxp3ixolt7cHu1EXL
|
||||
kxNRxlJZAoGAO/Qzs10/joY5R7jadZdclVfleuD0Y5HmpEr2WgXvVwzB3QJqHd2i
|
||||
lovuNtDpUal5ncDzDKdHacoED5gYncDIT6Quair3VfHsm3LlQWiZT86bQUWRBD+J
|
||||
z/4ppzGkzePS4Rf01Cjb7RwlQ7uDCaVyPjvCbAVaPBYQVr3FvaYLJbM=
|
||||
-----END RSA PRIVATE KEY-----
|
Binary file not shown.
|
@ -0,0 +1,22 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDqTCCApGgAwIBAgIUdULIRijfukG4cY7OZiPC02tL2WYwDQYJKoZIhvcNAQEL
|
||||
BQAwZDELMAkGA1UEBhMCREUxEDAOBgNVBAgMB0dlcm1hbnkxETAPBgNVBAcMCFdh
|
||||
bGxkb3JmMQ8wDQYDVQQKDAZTQVAgU0UxDjAMBgNVBAsMBVRvb2xzMQ8wDQYDVQQD
|
||||
DAZyb290Q0EwHhcNMjAwMzI3MjExOTE0WhcNMjMwMTE1MjExOTE0WjBkMQswCQYD
|
||||
VQQGEwJERTEQMA4GA1UECAwHR2VybWFueTERMA8GA1UEBwwIV2FsbGRvcmYxDzAN
|
||||
BgNVBAoMBlNBUCBTRTEOMAwGA1UECwwFVG9vbHMxDzANBgNVBAMMBnJvb3RDQTCC
|
||||
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMgq1Lz8VFNo41q0zCrZPnXM
|
||||
im10M9JYUGFl5s3xRP/70scuc7EpUhQPrQy6mIUEHes7uD6kRWwViM4JtpvebH52
|
||||
T+caNNk6iTcYbXC1wENRJF/A64OHeVT1RJQxN0KqVQWMYoZLFS+R1JuXVnGj0J1B
|
||||
jRBwJOL4MC/mYD90k/ik6r05OH8hATk+5DzzGpPK2pawpD6nU8q/X7C1XdRNZS05
|
||||
7jv6Of8aonkKl6k26+zCWgHXitOagWB2sOTBH7moQEwJSWeLR5CTr/5//FSP5TT8
|
||||
aR0RO1y0X/RwIif/bobBsnPZnjvpHgb83a+5ZbZt7PRz6hrvyoQofrhh2yNSogkC
|
||||
AwEAAaNTMFEwHQYDVR0OBBYEFImeXkZLHA+SYuyLyBsR5cWgSL6GMB8GA1UdIwQY
|
||||
MBaAFImeXkZLHA+SYuyLyBsR5cWgSL6GMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
|
||||
hvcNAQELBQADggEBAICitWFs3JzAH42GA/45FBYjfKqXorQp22rzQ2nAXFw9nPWC
|
||||
FXNIv6EUWW4SsV5AnEnOGqpC+14/sXTiSWJnqgVk8ZzeOw8is/52cigGSno7wgcX
|
||||
9me72WZxlehYsf0gdx7vZAnyrFSfJ2Q/N6EAJ1LSZe92xB4A58O7dqfNPqgtZrU2
|
||||
QufA81rGqr7LiWZGzPXTX8jLTV8JuXTs/yiDawSpoInasofTJMom5zdAjYoZJrcW
|
||||
m+gz4yEshWzPl6qbVGvUWYdeWQ1KI9EZXUnxPzswPjqutGlE31QGcJDXvfBTeQS+
|
||||
c0XmLDf22h43UaNzYRdWc3IcPLned3qNlBPI3qY=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAyCrUvPxUU2jjWrTMKtk+dcyKbXQz0lhQYWXmzfFE//vSxy5z
|
||||
sSlSFA+tDLqYhQQd6zu4PqRFbBWIzgm2m95sfnZP5xo02TqJNxhtcLXAQ1EkX8Dr
|
||||
g4d5VPVElDE3QqpVBYxihksVL5HUm5dWcaPQnUGNEHAk4vgwL+ZgP3ST+KTqvTk4
|
||||
fyEBOT7kPPMak8ralrCkPqdTyr9fsLVd1E1lLTnuO/o5/xqieQqXqTbr7MJaAdeK
|
||||
05qBYHaw5MEfuahATAlJZ4tHkJOv/n/8VI/lNPxpHRE7XLRf9HAiJ/9uhsGyc9me
|
||||
O+keBvzdr7lltm3s9HPqGu/KhCh+uGHbI1KiCQIDAQABAoIBAAVr240uzF3h9l5r
|
||||
jSgP8DgijRE/13N3/t1UdDbZtQO131STtoBy3Q08C3TPzPe1T9YiLPBNZK/zuVvT
|
||||
OYXpNUkLjUh2Fj5X7oV2fOhTk9x+4xxQzlAmqCub5PqahqOgl2LqFXULrYw3R+na
|
||||
5HV5eVn4/4gVcXpQPIXfmHW/g451ZL444jcn1TQYnE0CxlUastc5kaltCR53TWY0
|
||||
5UNGbbhnbDm5d04uYBFo+a+5MmYSVJ5KDf/BrUBMKcjPetnv0jQfoBao+oka2uie
|
||||
42dqtRWJG2JiJXXG8sSCiLHW9qPgjp8wVgb79AkMpIjdlabQvaI5q/7pSVyf1x2h
|
||||
bLhVI4ECgYEA58RINaEZkSYhwhUnvY1agBGHajmAAz8krgyPRGj/HzYmLbwUgZI7
|
||||
OIuB6uiWgK3VtP49oVMA5szrkMW4V5pKgL9piSzCOIKtRImblOFQcqKxCt0MrSzx
|
||||
D1kwJ1YIpFueZwZ9/ngZttKhIRRscNkJUSazeFi2gDrKjKXoms4LyLECgYEA3Ri5
|
||||
kMeF3yNwJmSAmupPemeuExR8L00YC5B93WWUzNcJ2pVJhSHHAPZ4OB4Z+7iLoCpM
|
||||
/5iWku+kySg6AugH6fWlDoC5mTXLTa11M+rwVv1INO5PSWbxPoMe7DMTtbYkhUxW
|
||||
HMMXg1P9eS4a1fFiPHnEvT+ovKuOJ4MJIzwOxNkCgYAiw1gpYx6YnOWXXOD3F4qp
|
||||
hveOwU0oL4Jq8MtUcYNCxTZ4yasxvCNR2esEtxpL9scFPNU1q2OJOtdigaWiziu6
|
||||
n/tObf47x64Bh7pkXF9asnhnrrxGBWWq7a/BVrA5JtzdiyW+03jX6UPt2EhjrMou
|
||||
9+UXegb1uNvEuOXowlsWIQKBgEcvx8eaxjqzIU/rOhEm8hIaQcz44ockTmKi0jOv
|
||||
mjpd3llXicVou7dOpCSFZJ5MrAAUvpfpHEoRCMCPyCXZaXrl0ZAa2CdBT8Uh3UOr
|
||||
GFkZ0d7g//xFPdV/yDwKsgTmsVmN24gFNJPfPhR/SLqrrpKELlk0nvKoVevY399N
|
||||
Xf/5AoGBAMTYf04U30iMgjwrXCQAebCKA641DshyxplYiTZYddhGUigUslxrrSA9
|
||||
stdRoYmwlmXwwtGEyu/064sZiOQap8+MtL/mUrdpHzZfy+xK883d6UnYe/iTONgc
|
||||
j7kBt543WxzQSRW/1l58xgZWltJlCE5EZCKXA7fX+IoIQZQnYRI4
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1 @@
|
|||
0DE7FFB7FF93E6FD83FED6EB3A1D83EBB6A82DC1
|
|
@ -0,0 +1,20 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDUjCCAjoCFA3n/7f/k+b9g/7W6zodg+u2qC3AMA0GCSqGSIb3DQEBCwUAMGQx
|
||||
CzAJBgNVBAYTAkRFMRAwDgYDVQQIDAdHZXJtYW55MREwDwYDVQQHDAhXYWxsZG9y
|
||||
ZjEPMA0GA1UECgwGU0FQIFNFMQ4wDAYDVQQLDAVUb29sczEPMA0GA1UEAwwGcm9v
|
||||
dENBMB4XDTIwMDMyNzIxMTkxNFoXDTIxMDgwOTIxMTkxNFowZzELMAkGA1UEBhMC
|
||||
REUxEDAOBgNVBAgMB0dlcm1hbnkxETAPBgNVBAcMCFdhbGxkb3JmMQ8wDQYDVQQK
|
||||
DAZTQVAgU0UxDjAMBgNVBAsMBVRvb2xzMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEi
|
||||
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx+njjit6UtTPP8IImkVc5UAYc
|
||||
3XKXVpCjWSqMSL3xj1O9zWPz4Ke+0mxH4mh6Ko0yO6+eBmzozSJUO1sU/Iz8v5T8
|
||||
ZqqXANkF5v/zBjVMCPb6jiA9hLmJBpzGRB07fiuf17fI0lQ9HpuNNjsmm5x1fWBO
|
||||
/D/KEM9218Bu9XkSAplIGg86xuvpdPpYLrxNbx9xWWlcRB7IRUIGfNbRFHWC0ryW
|
||||
5kLzVSHhK3EYfAvak6mdIJ4iXySWuY4qaUE9/Iijud1JTuq9lKZS4qWdg7NmAGWH
|
||||
bau2cSYWZeFc9ACAVNcE+YNLwzXyGIXCLgAtQ0vJCPj3Yf/lF9vvc2mQ114dAgMB
|
||||
AAEwDQYJKoZIhvcNAQELBQADggEBAL/jf+OeGeXiX2f0ot3kYEe5XKflQb++8eop
|
||||
iXbm6nqD9syWW6mpON1tZQ9EmIpT4dnh+D2+OFqM1QpF7zNZXRzIOrAfjKayq0yd
|
||||
taA6zDdDUVPWAzHZz4R70UiMSXJFIDtKhWm7wEEjr72OgYC3nlYrvffhSS3pRrBF
|
||||
kXRKpuuE9Yt60ciKeFssozS/wuflQ6fcDawTpwtzYU7z5p5B4KL1TmB6ZTXLfmD3
|
||||
aotONmHOKqNUKdvgNfH9+09S3/bNsbSsA5epWjR9rm/PidRyk4x1UZEc3FAoSkGq
|
||||
4r8TBc1LXsMk6TxUTRzEbtxCsoAllpPivi+cyGUNf+iF/FIWD+U=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,17 @@
|
|||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIICrDCCAZQCAQAwZzELMAkGA1UEBhMCREUxEDAOBgNVBAgMB0dlcm1hbnkxETAP
|
||||
BgNVBAcMCFdhbGxkb3JmMQ8wDQYDVQQKDAZTQVAgU0UxDjAMBgNVBAsMBVRvb2xz
|
||||
MRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
|
||||
AoIBAQCx+njjit6UtTPP8IImkVc5UAYc3XKXVpCjWSqMSL3xj1O9zWPz4Ke+0mxH
|
||||
4mh6Ko0yO6+eBmzozSJUO1sU/Iz8v5T8ZqqXANkF5v/zBjVMCPb6jiA9hLmJBpzG
|
||||
RB07fiuf17fI0lQ9HpuNNjsmm5x1fWBO/D/KEM9218Bu9XkSAplIGg86xuvpdPpY
|
||||
LrxNbx9xWWlcRB7IRUIGfNbRFHWC0ryW5kLzVSHhK3EYfAvak6mdIJ4iXySWuY4q
|
||||
aUE9/Iijud1JTuq9lKZS4qWdg7NmAGWHbau2cSYWZeFc9ACAVNcE+YNLwzXyGIXC
|
||||
LgAtQ0vJCPj3Yf/lF9vvc2mQ114dAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEA
|
||||
I3455yEdVYmyv+9aDGNuUAPEKvnubLZOuEC6IweCT88f9cwQlvTNSkgQ8ylJ40oQ
|
||||
D/akfBMQEc11NjVoRE5jFPabLMr0wC/KWL5RhXAwu82pC7l64jd8xLhHWXE1cY7h
|
||||
i7pGBawcvnuqlkwPnd6OSh4c1MdHMsefBb8RKvDJ4I6iWfu1ZKBWWBknnTKwEB/y
|
||||
K/jCq81z3xwlNx8r5MT43thzYMRxRIXX63Le28OurRWJNCxuyrQUh7dqGhbfXvOC
|
||||
VCvFZphRc8bB9h45wvblPAgcVVDcqpKUGHvqUd456wQJ0JIer0VXhrAMIdzBiCzl
|
||||
eqBFgozJ6u2jBWxvfrnHWQ==
|
||||
-----END CERTIFICATE REQUEST-----
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAsfp444relLUzz/CCJpFXOVAGHN1yl1aQo1kqjEi98Y9Tvc1j
|
||||
8+CnvtJsR+JoeiqNMjuvngZs6M0iVDtbFPyM/L+U/GaqlwDZBeb/8wY1TAj2+o4g
|
||||
PYS5iQacxkQdO34rn9e3yNJUPR6bjTY7JpucdX1gTvw/yhDPdtfAbvV5EgKZSBoP
|
||||
Osbr6XT6WC68TW8fcVlpXEQeyEVCBnzW0RR1gtK8luZC81Uh4StxGHwL2pOpnSCe
|
||||
Il8klrmOKmlBPfyIo7ndSU7qvZSmUuKlnYOzZgBlh22rtnEmFmXhXPQAgFTXBPmD
|
||||
S8M18hiFwi4ALUNLyQj492H/5Rfb73NpkNdeHQIDAQABAoIBAD8+OebhWeaN3TNu
|
||||
y1DZJJ2BCisHpciRQiRJcw0WbCiCPcecTIBEvFbafw+sLGP86t+GxgjpT5oKCsDT
|
||||
trHmbFMD4PUvpj6yVmv6gcjh096I8Ppntp0lpKhEaUEqwxh45RePmAcMdlKhpbSw
|
||||
KKS3dwlo2+g1SpWLE62vTPog0DzNh8WtFePAqt5SzA07fpuZSuhobchCHQpZS4Kr
|
||||
Pv8mJj/uWSa3ysVqJ6FaqeIqMTNP5S5QxJw/OOLR+0Wzi8m2ET0Be16H7NO7EgXr
|
||||
6xjY3GcI292oXylSFMSZkKuRf3fX08AP8hKsP7A7qjTY/S4gQ1Tte9B0s2Uq7LJx
|
||||
liXmoAECgYEA3n4cfmCFZwe1NjpksKe8kLm871oq5x7UB3OsbIKPYvdp2/G3eour
|
||||
175qe5oMJgJe28Jqgu7EE9zDQcGMSpV7yapbeGcIg5h7b2a1Un3TbvJIMBLBrbR6
|
||||
fGwW+gL0REeLrl7vMgrUTXO1/MbiMvoa48fUea+PLW48qQ53qGh2TR0CgYEAzMgt
|
||||
oY1pBTNim+0pgjQTwAi+y0Jirc6pAfunrf72onBy5Bt0arBsYMlKjZS6yaR78aqh
|
||||
JNHymXRHwQZK9oMx6tLR8jT+E9LJH7Hk2Vv5M6f8xZwF/f0zc67mSEuj+mHc0X++
|
||||
qd7lYmudyAfWM0+A3DUVAXUyMnoKZzvCO9fFhQECgYEAg5Vj9p3Q56EYW8znFc7t
|
||||
503h3lCeRPfnf8y6caY5dNdMJQbscy49YCe+RAFUI/qM7T0qzuq0zeZnF/GGremA
|
||||
P0FgPXH6CBHbFoRQwkumCtyBMuU05C1zrzgh0pSCsAr8IhEFN7xN2MyRGcDpsCpY
|
||||
UtQw5hKdA8pJV9Y1kETPikUCgYA64z2r/Vw78KDksfiDxrH/QQSMstRposobFeEM
|
||||
Ogt2fturGPILVBx2YKwdtq1YGwLBZg3c5rrawgN4UHTyGpwaKPHSssZ1sOHBSYjD
|
||||
sJ0i66XWtZ1LgqpvE9aI56eJ8uZrIE8VzlEsUkIXKZnBO5WUvXcC6k67ETk4ooii
|
||||
aNQWAQKBgQDOlUkeRQjVhm9fW8HrKdqUf4+1Zge4wwOi6q4fO4EdaSFu9KGnUOmS
|
||||
crvagTexI5MPHuq7LuK4MsWrNlbxJ/axjazDDLqeaWaWOtJvO+L274yWQtRQ4/DY
|
||||
tcTDKTbGln/v+x8eWW9OhUy/ADUFPkweXoemnI5iIdjymkZFGB/XlQ==
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1,22 @@
|
|||
#!/bin/bash
|
||||
|
||||
# From https://github.com/thojansen/client-certificates/blob/master/ssl/setup.sh
|
||||
# create rootCA certificate
|
||||
openssl genrsa -out rootCA.key 2048
|
||||
openssl req -x509 -new -nodes -key rootCA.key -days 1024 -out rootCA.crt -subj "/C=DE/ST=Germany/L=Walldorf/O=SAP SE/OU=Tools/CN=rootCA"
|
||||
|
||||
# create server key and certificate
|
||||
openssl genrsa -out server.key 2048
|
||||
openssl req -new -key server.key -out server.csr -subj "/C=DE/ST=Germany/L=Walldorf/O=SAP SE/OU=Tools/CN=localhost"
|
||||
openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server.crt -days 500
|
||||
|
||||
# create client key and certificate
|
||||
openssl genrsa -out client.key 2048
|
||||
openssl req -new -key client.key -out client.csr -subj "/C=DE/ST=Germany/L=Walldorf/O=SAP SE/OU=Tools/CN=client"
|
||||
openssl x509 -req -in client.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out client.crt -days 500
|
||||
|
||||
# generate client.p12 file which can be easily imported to OS.
|
||||
openssl pkcs12 -export -inkey client.key -in client.crt -name client -out client.p12
|
||||
|
||||
# generate a non-encrypt pem file with key and crt files, from p12 files
|
||||
#openssl pkcs12 -in client.p12 -out client.pem -nodes -clcerts
|
|
@ -0,0 +1,41 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { notarize } = require("electron-notarize");
|
||||
const envConfig = require('dotenv').config();
|
||||
|
||||
Object.entries(envConfig.parsed || {}).forEach(([key, value]) => {
|
||||
process.env[key] = value;
|
||||
});
|
||||
|
||||
module.exports = async function (params) {
|
||||
console.log(process.env);
|
||||
|
||||
// Only notarize the app on Mac OS only.
|
||||
if (process.platform !== "darwin") {
|
||||
return;
|
||||
}
|
||||
// Same appId in electron-builder.
|
||||
let appId = "io.gnosis.safe.macos";
|
||||
let appPath = path.join(
|
||||
params.appOutDir,
|
||||
`${params.packager.appInfo.productFilename}.app`
|
||||
);
|
||||
if (!fs.existsSync(appPath)) {
|
||||
throw new Error(`Cannot find application at: ${appPath}`);
|
||||
}
|
||||
|
||||
console.log(`Notarizing ${appId} found at ${appPath}`);
|
||||
|
||||
try {
|
||||
await notarize({
|
||||
appBundleId: appId,
|
||||
appPath: appPath,
|
||||
appleId: process.env.APPLEID,
|
||||
appleIdPassword: process.env.APPLEIDPASS,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
console.log(`Done notarizing ${appId}`);
|
||||
};
|
|
@ -0,0 +1,18 @@
|
|||
// All of the Node.js APIs are available in the preload process.
|
||||
// It has the same sandbox as a Chrome extension.
|
||||
|
||||
const TransportNodeHid = require("@ledgerhq/hw-transport-node-hid").default;
|
||||
window.TransportNodeHid = TransportNodeHid;
|
||||
|
||||
window.isDesktop = true;
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
const replaceText = (selector, text) => {
|
||||
const element = document.getElementById(selector)
|
||||
if (element) element.innerText = text
|
||||
}
|
||||
|
||||
for (const type of ['chrome', 'node', 'electron']) {
|
||||
replaceText(`${type}-version`, process.versions[type])
|
||||
}
|
||||
})
|
|
@ -4,7 +4,7 @@ import styled from 'styled-components'
|
|||
export const Wrapper = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: 245px auto;
|
||||
grid-template-rows: 500px;
|
||||
grid-template-rows: 514px;
|
||||
min-height: 525px;
|
||||
|
||||
.background {
|
||||
|
@ -33,6 +33,7 @@ export const Menu = styled.div.attrs(() => ({ className: 'background' }))`
|
|||
export const Content = styled.div.attrs(() => ({ className: 'background' }))`
|
||||
grid-column: 2;
|
||||
border-top-right-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
background-color: white;
|
||||
`
|
||||
|
||||
|
|
|
@ -63,36 +63,6 @@ const AddressInput = ({
|
|||
}
|
||||
}}
|
||||
</OnChange>
|
||||
{/* onBlur - didn't work because of the complex validation
|
||||
(if you submit before it gets the address, breaks everything) */}
|
||||
{/* <Field
|
||||
name={name}
|
||||
subscription={{ active: true, value: true }}
|
||||
render={({ meta, input }) => {
|
||||
const [prevActive, setPrevActive] = useState<boolean>(!!meta.active)
|
||||
|
||||
useEffect(() => {
|
||||
async function setAddressFromENS() {
|
||||
if (isValidEnsName(input.value)) {
|
||||
try {
|
||||
const resolverAddr = await getAddressFromENS(input.value)
|
||||
fieldMutator(resolverAddr)
|
||||
} catch (err) {
|
||||
console.error('Error when trying to fetch address for ENS name: ', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prevActive && !meta.active) {
|
||||
setAddressFromENS()
|
||||
} else if (prevActive !== meta.active) {
|
||||
setPrevActive(meta.active)
|
||||
}
|
||||
}, [meta.active])
|
||||
|
||||
return null
|
||||
}}
|
||||
/> */}
|
||||
</>
|
||||
)
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import { Field } from 'react-final-form'
|
|||
|
||||
type Props = {
|
||||
validate: () => void,
|
||||
debounce: number,
|
||||
debounce?: number,
|
||||
}
|
||||
|
||||
const DebounceValidationField = ({ debounce = 1000, validate, ...rest }: Props) => {
|
||||
|
|
|
@ -88,12 +88,8 @@ export const uniqueAddress = (addresses: string[] | List<string>) =>
|
|||
return addressAlreadyExists ? ADDRESS_REPEATED_ERROR : undefined
|
||||
})
|
||||
|
||||
export const composeValidators = (...validators: Function[]): FieldValidator => (value: Field, values, meta) => {
|
||||
if (!meta.modified) {
|
||||
return
|
||||
}
|
||||
return validators.reduce((error, validator) => error || validator(value), undefined)
|
||||
}
|
||||
export const composeValidators = (...validators: Function[]): FieldValidator => (value: Field) =>
|
||||
validators.reduce((error, validator) => error || validator(value), undefined)
|
||||
|
||||
export const inLimit = (limit: number, base: number, baseText: string, symbol: string = 'ETH') => (value: string) => {
|
||||
const amount = Number(value)
|
||||
|
|
|
@ -118,8 +118,15 @@ export const estimateSafeTxGas = async (
|
|||
gasLimit: txGasEstimation + dataGasEstimation + additionalGas,
|
||||
},
|
||||
(error, res) => {
|
||||
// res.data check is for OpenEthereum/Parity revert messages format
|
||||
const isOpenEthereumRevertMsg = res && typeof res.data === 'string'
|
||||
|
||||
const isEstimationSuccessful =
|
||||
!error &&
|
||||
((typeof res === 'string' && res !== '0x') || (isOpenEthereumRevertMsg && res.data.slice(9) !== '0x'))
|
||||
|
||||
resolve({
|
||||
success: error || res === '0x' ? false : true,
|
||||
success: isEstimationSuccessful,
|
||||
estimation: txGasEstimation + additionalGas,
|
||||
})
|
||||
},
|
||||
|
|
|
@ -9,7 +9,7 @@ import { type Token, makeToken } from '~/logic/tokens/store/model/token'
|
|||
|
||||
export const TOKEN_REDUCER_ID = 'tokens'
|
||||
|
||||
export type State = Map<string, Map<string, Token>>
|
||||
export type State = Map<string, Token>
|
||||
|
||||
export default handleActions<State, *>(
|
||||
{
|
||||
|
|
|
@ -18,7 +18,7 @@ export const formatAmount = (number: string | number) => {
|
|||
let numberFloat = parseFloat(number)
|
||||
|
||||
if (numberFloat === 0) {
|
||||
numberFloat = '0.000'
|
||||
numberFloat = '0'
|
||||
} else if (numberFloat < 0.001) {
|
||||
numberFloat = '< 0.001'
|
||||
} else if (numberFloat < 1000) {
|
||||
|
|
|
@ -8,7 +8,7 @@ import OpenPaper from '~/components/Stepper/OpenPaper'
|
|||
import AddressInput from '~/components/forms/AddressInput'
|
||||
import Field from '~/components/forms/Field'
|
||||
import TextField from '~/components/forms/TextField'
|
||||
import { composeValidators, mustBeEthereumAddress, noErrorsOn, required } from '~/components/forms/validator'
|
||||
import { mustBeEthereumAddress, noErrorsOn, required } from '~/components/forms/validator'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Paragraph from '~/components/layout/Paragraph'
|
||||
import { SAFE_MASTER_COPY_ADDRESS_V10, getSafeMasterContract, validateProxy } from '~/logic/contracts/safeContracts'
|
||||
|
@ -120,7 +120,6 @@ const Details = ({ classes, errors, form }: Props) => (
|
|||
placeholder="Safe Address*"
|
||||
text="Safe Address"
|
||||
type="text"
|
||||
validate={composeValidators(required, mustBeEthereumAddress)}
|
||||
/>
|
||||
</Block>
|
||||
<Block margin="sm">
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// @flow
|
||||
import { ButtonLink, Checkbox, ManageListModal, Text, TextField } from '@gnosis.pm/safe-react-components'
|
||||
import type { FieldValidator } from 'final-form'
|
||||
import React, { useState } from 'react'
|
||||
import { FormSpy } from 'react-final-form'
|
||||
import styled from 'styled-components'
|
||||
|
@ -9,7 +10,7 @@ import { getAppInfoFromUrl } from './utils'
|
|||
import Field from '~/components/forms/Field'
|
||||
import DebounceValidationField from '~/components/forms/Field/DebounceValidationField'
|
||||
import GnoForm from '~/components/forms/GnoForm'
|
||||
import { composeValidators, required } from '~/components/forms/validator'
|
||||
import { required } from '~/components/forms/validator'
|
||||
import Img from '~/components/layout/Img'
|
||||
import appsIconSvg from '~/routes/safe/components/Transactions/TxsTable/TxType/assets/appsIcon.svg'
|
||||
|
||||
|
@ -55,6 +56,13 @@ const urlValidator = (value: string) => {
|
|||
: 'Please, provide a valid url'
|
||||
}
|
||||
|
||||
const composeValidatorsApps = (...validators: Function[]): FieldValidator => (value: Field, values, meta) => {
|
||||
if (!meta.modified) {
|
||||
return
|
||||
}
|
||||
return validators.reduce((error, validator) => error || validator(value), undefined)
|
||||
}
|
||||
|
||||
const ManageApps = ({ appList, onAppAdded, onAppToggle }: Props) => {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
|
||||
|
@ -120,7 +128,12 @@ const ManageApps = ({ appList, onAppAdded, onAppToggle }: Props) => {
|
|||
name="appUrl"
|
||||
placeholder="App URL"
|
||||
type="text"
|
||||
validate={composeValidators(customRequiredValidator, urlValidator, uniqueAppValidator, safeAppValidator)}
|
||||
validate={composeValidatorsApps(
|
||||
customRequiredValidator,
|
||||
urlValidator,
|
||||
uniqueAppValidator,
|
||||
safeAppValidator,
|
||||
)}
|
||||
/>
|
||||
|
||||
<AppInfo>
|
||||
|
|
|
@ -26,6 +26,8 @@ const APPS_STORAGE_KEY = 'APPS_STORAGE_KEY'
|
|||
const APPS_LEGAL_DISCLAIMER_STORAGE_KEY = 'APPS_LEGAL_DISCLAIMER_STORAGE_KEY'
|
||||
|
||||
const StyledIframe = styled.iframe`
|
||||
padding: 24px;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: ${(props) => (props.shouldDisplay ? 'block' : 'none')};
|
||||
|
@ -36,7 +38,6 @@ const Centered = styled.div`
|
|||
justify-content: center;
|
||||
flex-direction: column;
|
||||
`
|
||||
|
||||
const operations = {
|
||||
SEND_TRANSACTIONS: 'SEND_TRANSACTIONS',
|
||||
ON_SAFE_INFO: 'ON_SAFE_INFO',
|
||||
|
@ -151,7 +152,7 @@ function Apps({ closeModal, closeSnackbar, enqueueSnackbar, openModal }: Props)
|
|||
<a href="https://gnosis-safe.io/terms" rel="noopener noreferrer" target="_blank">
|
||||
Terms
|
||||
</a>{' '}
|
||||
and this Disclaimer, and agree to be bound by .
|
||||
and this Disclaimer, and agree to be bound by them.
|
||||
</Text>
|
||||
</>
|
||||
}
|
||||
|
@ -299,9 +300,8 @@ function Apps({ closeModal, closeSnackbar, enqueueSnackbar, openModal }: Props)
|
|||
const currentApp = list[index]
|
||||
|
||||
const appInfo = await getAppInfoFromUrl(currentApp.url)
|
||||
|
||||
if (appInfo.error) {
|
||||
throw Error()
|
||||
throw Error(`There was a problem trying to load app ${currentApp.url}`)
|
||||
}
|
||||
|
||||
appInfo.disabled = currentApp.disabled === undefined ? false : currentApp.disabled
|
||||
|
@ -348,6 +348,10 @@ function Apps({ closeModal, closeSnackbar, enqueueSnackbar, openModal }: Props)
|
|||
return <Loader />
|
||||
}
|
||||
|
||||
if (loading || !appList.length) {
|
||||
return <Loader />
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Menu>
|
||||
|
|
|
@ -3,10 +3,15 @@ import axios from 'axios'
|
|||
|
||||
import appsIconSvg from '~/routes/safe/components/Transactions/TxsTable/TxType/assets/appsIcon.svg'
|
||||
|
||||
export const GNOSIS_APPS_URL = 'https://gnosis-apps.netlify.app'
|
||||
|
||||
const appsUrl = process.env.REACT_APP_GNOSIS_APPS_URL ? process.env.REACT_APP_GNOSIS_APPS_URL : GNOSIS_APPS_URL
|
||||
export const staticAppsList = [{ url: `${appsUrl}/compound`, disabled: false }]
|
||||
const gnosisAppsUrl = process.env.REACT_APP_GNOSIS_APPS_URL
|
||||
export const staticAppsList = [
|
||||
{ url: `${gnosisAppsUrl}/compound`, disabled: false },
|
||||
{ url: `${gnosisAppsUrl}/aave`, disabled: false },
|
||||
{ url: `${gnosisAppsUrl}/pool-together`, disabled: false },
|
||||
{ url: `${gnosisAppsUrl}/open-zeppelin`, disabled: false },
|
||||
{ url: `${gnosisAppsUrl}/request`, disabled: false },
|
||||
{ url: `${gnosisAppsUrl}/synthetix`, disabled: false },
|
||||
]
|
||||
|
||||
export const getAppInfoFromOrigin = (origin: string) => {
|
||||
try {
|
||||
|
@ -18,13 +23,18 @@ export const getAppInfoFromOrigin = (origin: string) => {
|
|||
}
|
||||
|
||||
export const getAppInfoFromUrl = async (appUrl: string) => {
|
||||
let res = { id: undefined, url: appUrl, name: 'unknown', iconUrl: appsIconSvg, error: true }
|
||||
|
||||
if (!appUrl) {
|
||||
return res
|
||||
}
|
||||
|
||||
let cleanedUpAppUrl = appUrl.trim()
|
||||
if (cleanedUpAppUrl.substr(-1) === '/') {
|
||||
cleanedUpAppUrl = cleanedUpAppUrl.substr(0, cleanedUpAppUrl.length - 1)
|
||||
res.url = cleanedUpAppUrl
|
||||
}
|
||||
|
||||
let res = { id: undefined, url: cleanedUpAppUrl, name: 'unknown', iconUrl: appsIconSvg, error: true }
|
||||
|
||||
try {
|
||||
const appInfo = await axios.get(`${cleanedUpAppUrl}/manifest.json`)
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import Img from '~/components/layout/Img'
|
|||
import Paragraph from '~/components/layout/Paragraph'
|
||||
import Row from '~/components/layout/Row'
|
||||
import { type Token, type TokenProps } from '~/logic/tokens/store/model/token'
|
||||
import { getWeb3 } from '~/logic/wallets/getWeb3'
|
||||
import TokenPlaceholder from '~/routes/safe/components/Balances/assets/token_placeholder.svg'
|
||||
|
||||
export const ADD_CUSTOM_TOKEN_ADDRESS_INPUT_TEST_ID = 'add-custom-token-address-input'
|
||||
|
@ -64,8 +65,9 @@ const AddCustomToken = (props: Props) => {
|
|||
const [formValues, setFormValues] = useState(INITIAL_FORM_STATE)
|
||||
|
||||
const handleSubmit = (values) => {
|
||||
const address = getWeb3().utils.toChecksumAddress(values.address)
|
||||
const token = {
|
||||
address: values.address,
|
||||
address,
|
||||
decimals: values.decimals,
|
||||
symbol: values.symbol,
|
||||
name: values.symbol,
|
||||
|
|
|
@ -100,7 +100,7 @@ const TabsComponent = (props: Props) => {
|
|||
label={labelTransactions}
|
||||
value={`${match.url}/transactions`}
|
||||
/>
|
||||
{process.env.REACT_APP_ENV !== 'production' && (
|
||||
{!process.env.REACT_APP_APPS_DISABLED && (
|
||||
<Tab
|
||||
classes={{
|
||||
selected: classes.tabWrapperSelected,
|
||||
|
|
|
@ -91,7 +91,7 @@ const Layout = (props: Props) => {
|
|||
<Switch>
|
||||
<Route exact path={`${match.path}/balances/:assetType?`} render={() => wrapInSuspense(<Balances />, null)} />
|
||||
<Route exact path={`${match.path}/transactions`} render={() => wrapInSuspense(<TxsTable />, null)} />
|
||||
{process.env.REACT_APP_ENV !== 'production' && (
|
||||
{!process.env.REACT_APP_APPS_DISABLED && (
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/apps`}
|
||||
|
|
|
@ -49,7 +49,7 @@ const IncomingTxDescription = ({ tx }: Props) => {
|
|||
const txFromName = getNameFromAddressBook(tx.from)
|
||||
return (
|
||||
<Block className={classes.txDataContainer}>
|
||||
<TransferDescription from={tx.from} txFromName={txFromName} value={getIncomingTxAmount(tx)} />
|
||||
<TransferDescription from={tx.from} txFromName={txFromName} value={getIncomingTxAmount(tx, false)} />
|
||||
</Block>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -222,7 +222,7 @@ const TxDescription = ({ classes, tx }: Props) => {
|
|||
removedOwner,
|
||||
upgradeTx,
|
||||
} = getTxData(tx)
|
||||
const amount = getTxAmount(tx)
|
||||
const amount = getTxAmount(tx, false)
|
||||
return (
|
||||
<Block className={classes.txDataContainer}>
|
||||
{modifySettingsTx && action && (
|
||||
|
|
|
@ -41,8 +41,9 @@ const ExpandedTx = ({ cancelTx, tx }: Props) => {
|
|||
const [openModal, setOpenModal] = useState<OpenModal>(null)
|
||||
const openApproveModal = () => setOpenModal('approveTx')
|
||||
const closeModal = () => setOpenModal(null)
|
||||
const thresholdReached = !INCOMING_TX_TYPES.includes(tx.type) && threshold <= tx.confirmations.size
|
||||
const canExecute = !INCOMING_TX_TYPES.includes(tx.type) && nonce === tx.nonce
|
||||
const isIncomingTx = !!INCOMING_TX_TYPES[tx.type]
|
||||
const thresholdReached = !isIncomingTx && threshold <= tx.confirmations.size
|
||||
const canExecute = !isIncomingTx && nonce === tx.nonce
|
||||
const cancelThresholdReached = !!cancelTx && threshold <= cancelTx.confirmations.size
|
||||
const canExecuteCancel = nonce === tx.nonce
|
||||
|
||||
|
@ -59,22 +60,22 @@ const ExpandedTx = ({ cancelTx, tx }: Props) => {
|
|||
<Block className={classes.expandedTxBlock}>
|
||||
<Row>
|
||||
<Col layout="column" xs={6}>
|
||||
<Block
|
||||
className={cn(classes.txDataContainer, INCOMING_TX_TYPES.includes(tx.type) && classes.incomingTxBlock)}
|
||||
>
|
||||
<Block className={cn(classes.txDataContainer, isIncomingTx && classes.incomingTxBlock)}>
|
||||
<Block align="left" className={classes.txData}>
|
||||
<Bold className={classes.txHash}>Hash:</Bold>
|
||||
{tx.executionTxHash ? <EtherScanLink cut={8} type="tx" value={tx.executionTxHash} /> : 'n/a'}
|
||||
</Block>
|
||||
{!isIncomingTx && (
|
||||
<Paragraph noMargin>
|
||||
<Bold>Nonce: </Bold>
|
||||
<Span>{tx.nonce}</Span>
|
||||
</Paragraph>
|
||||
)}
|
||||
<Paragraph noMargin>
|
||||
<Bold>Fee: </Bold>
|
||||
{tx.fee ? tx.fee : 'n/a'}
|
||||
</Paragraph>
|
||||
{INCOMING_TX_TYPES.includes(tx.type) ? (
|
||||
{isIncomingTx ? (
|
||||
<>
|
||||
<Paragraph noMargin>
|
||||
<Bold>Created: </Bold>
|
||||
|
@ -113,9 +114,9 @@ const ExpandedTx = ({ cancelTx, tx }: Props) => {
|
|||
)}
|
||||
</Block>
|
||||
<Hairline />
|
||||
{INCOMING_TX_TYPES.includes(tx.type) ? <IncomingTxDescription tx={tx} /> : <TxDescription tx={tx} />}
|
||||
{isIncomingTx ? <IncomingTxDescription tx={tx} /> : <TxDescription tx={tx} />}
|
||||
</Col>
|
||||
{!INCOMING_TX_TYPES.includes(tx.type) && (
|
||||
{!isIncomingTx && (
|
||||
<OwnersColumn
|
||||
cancelThresholdReached={cancelThresholdReached}
|
||||
cancelTx={cancelTx}
|
||||
|
|
|
@ -31,26 +31,31 @@ const typeToLabel = {
|
|||
}
|
||||
|
||||
const TxType = ({ origin, txType }: { txType: TransactionType, origin: string | null }) => {
|
||||
const isThirdPartyApp = txType === 'third-party-app'
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [appInfo, setAppInfo] = useState()
|
||||
const [forceCustom, setForceCustom] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const getAppInfo = async () => {
|
||||
const parsedOrigin = getAppInfoFromOrigin(origin)
|
||||
if (!parsedOrigin) {
|
||||
setForceCustom(true)
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
const appInfo = await getAppInfoFromUrl(parsedOrigin.url)
|
||||
setAppInfo(appInfo)
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
if (!isThirdPartyApp) {
|
||||
if (!origin) {
|
||||
return
|
||||
}
|
||||
|
||||
getAppInfo()
|
||||
}, [txType])
|
||||
}, [origin, txType])
|
||||
|
||||
if (!isThirdPartyApp) {
|
||||
if (forceCustom || !origin) {
|
||||
return <IconText iconUrl={typeToIcon[txType]} text={typeToLabel[txType]} />
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import TxType from './TxType'
|
|||
|
||||
import { type Column } from '~/components/Table/TableHead'
|
||||
import { type SortRow, buildOrderFieldFrom } from '~/components/Table/sorting'
|
||||
import { getWeb3 } from '~/logic/wallets/getWeb3'
|
||||
import { formatAmount } from '~/logic/tokens/utils/formatAmount'
|
||||
import { INCOMING_TX_TYPES, type IncomingTransaction } from '~/routes/safe/store/models/incomingTransaction'
|
||||
import { type Transaction } from '~/routes/safe/store/models/transaction'
|
||||
|
||||
|
@ -33,25 +33,40 @@ type TxData = {
|
|||
|
||||
export const formatDate = (date: string): string => format(parseISO(date), 'MMM d, yyyy - HH:mm:ss')
|
||||
|
||||
export const getIncomingTxAmount = (tx: IncomingTransaction) => {
|
||||
const txAmount = tx.value ? `${new BigNumber(tx.value).div(`1e${tx.decimals}`).toFixed()}` : 'n/a'
|
||||
return `${txAmount} ${tx.symbol || 'n/a'}`
|
||||
type TxValues = {
|
||||
value?: string | number,
|
||||
decimals?: string | number,
|
||||
symbol?: string,
|
||||
}
|
||||
|
||||
export const getTxAmount = (tx: Transaction) => {
|
||||
const web3 = getWeb3()
|
||||
const { fromWei, toBN } = web3.utils
|
||||
const NOT_AVAILABLE = 'n/a'
|
||||
|
||||
let txAmount = 'n/a'
|
||||
const getAmountWithSymbol = ({ decimals = 0, symbol = NOT_AVAILABLE, value }: TxValues, formatted = false) => {
|
||||
const nonFormattedValue = BigNumber(value).times(`1e-${decimals}`).toFixed()
|
||||
const finalValue = formatted ? formatAmount(nonFormattedValue).toString() : nonFormattedValue
|
||||
const txAmount = finalValue === 'NaN' ? NOT_AVAILABLE : finalValue
|
||||
|
||||
if (tx.isTokenTransfer && tx.decodedParams) {
|
||||
const tokenDecimals = tx.decimals.toNumber ? tx.decimals.toNumber() : tx.decimals
|
||||
txAmount = `${new BigNumber(tx.decodedParams.value).div(10 ** tokenDecimals).toString()} ${tx.symbol}`
|
||||
} else if (Number(tx.value) > 0) {
|
||||
txAmount = `${fromWei(toBN(tx.value), 'ether')} ${tx.symbol}`
|
||||
return `${txAmount} ${symbol}`
|
||||
}
|
||||
|
||||
return txAmount
|
||||
export const getIncomingTxAmount = (tx: IncomingTransaction, formatted: boolean = true) => {
|
||||
// simple workaround to avoid displaying unexpected values for incoming NFT transfer
|
||||
if (INCOMING_TX_TYPES[tx.type] === INCOMING_TX_TYPES.ERC721_TRANSFER) {
|
||||
return `1 ${tx.symbol}`
|
||||
}
|
||||
|
||||
return getAmountWithSymbol(tx, formatted)
|
||||
}
|
||||
|
||||
export const getTxAmount = (tx: Transaction, formatted: boolean = true) => {
|
||||
const { decimals = 18, decodedParams, isTokenTransfer, symbol } = tx
|
||||
const { value } = isTokenTransfer && decodedParams && decodedParams.value ? decodedParams : tx
|
||||
|
||||
if (!isTokenTransfer && !(Number(value) > 0)) {
|
||||
return NOT_AVAILABLE
|
||||
}
|
||||
|
||||
return getAmountWithSymbol({ decimals, symbol, value }, formatted)
|
||||
}
|
||||
|
||||
export type TransactionRow = SortRow<TxData>
|
||||
|
@ -75,7 +90,7 @@ const getTransactionTableData = (tx: Transaction, cancelTx: ?Transaction): Trans
|
|||
} else if (tx.cancellationTx) {
|
||||
txType = 'cancellation'
|
||||
} else if (tx.customTx) {
|
||||
txType = tx.origin ? 'third-party-app' : 'custom'
|
||||
txType = 'custom'
|
||||
} else if (tx.creationTx) {
|
||||
txType = 'creation'
|
||||
} else if (tx.upgradeTx) {
|
||||
|
@ -101,7 +116,7 @@ export const getTxTableData = (
|
|||
const cancelTxsByNonce = cancelTxs.reduce((acc, tx) => acc.set(tx.nonce, tx), Map())
|
||||
|
||||
return transactions.map((tx) => {
|
||||
if (INCOMING_TX_TYPES.includes(tx.type)) {
|
||||
if (INCOMING_TX_TYPES[tx.type]) {
|
||||
return getIncomingTxTableData(tx)
|
||||
}
|
||||
|
||||
|
|
|
@ -76,12 +76,12 @@ type IncomingTxServiceModel = {
|
|||
|
||||
export const buildTransactionFrom = async (
|
||||
safeAddress: string,
|
||||
tx: TxServiceModel,
|
||||
knownTokens,
|
||||
tx: TxServiceModel,
|
||||
txTokenCode,
|
||||
txTokenDecimals,
|
||||
txTokenSymbol,
|
||||
txTokenName,
|
||||
code,
|
||||
txTokenSymbol,
|
||||
): Promise<Transaction> => {
|
||||
const localSafe = await getLocalSafe(safeAddress)
|
||||
|
||||
|
@ -108,7 +108,7 @@ export const buildTransactionFrom = async (
|
|||
const modifySettingsTx = sameAddress(tx.to, safeAddress) && Number(tx.value) === 0 && !!tx.data
|
||||
const cancellationTx = sameAddress(tx.to, safeAddress) && Number(tx.value) === 0 && !tx.data
|
||||
const isERC721Token =
|
||||
(code && code.includes(SAFE_TRANSFER_FROM_WITHOUT_DATA_HASH)) ||
|
||||
(txTokenCode && txTokenCode.includes(SAFE_TRANSFER_FROM_WITHOUT_DATA_HASH)) ||
|
||||
(isTokenTransfer(tx.data, Number(tx.value)) && !knownTokens.get(tx.to) && txTokenDecimals !== null)
|
||||
let isSendTokenTx = !isERC721Token && isTokenTransfer(tx.data, Number(tx.value))
|
||||
const isMultiSendTx = isMultisendTransaction(tx.data, Number(tx.value))
|
||||
|
@ -118,7 +118,7 @@ export const buildTransactionFrom = async (
|
|||
let refundParams = null
|
||||
if (tx.gasPrice > 0) {
|
||||
const refundSymbol = txTokenSymbol || 'ETH'
|
||||
const decimals = txTokenName || 18
|
||||
const decimals = txTokenDecimals || 18
|
||||
const feeString = (tx.gasPrice * (tx.baseGas + tx.safeTxGas)).toString().padStart(decimals, 0)
|
||||
const whole = feeString.slice(0, feeString.length - decimals) || '0'
|
||||
const fraction = feeString.slice(feeString.length - decimals)
|
||||
|
@ -133,7 +133,8 @@ export const buildTransactionFrom = async (
|
|||
let symbol = txTokenSymbol || 'ETH'
|
||||
let decimals = txTokenDecimals || 18
|
||||
let decodedParams
|
||||
if (isSendTokenTx && (txTokenSymbol === null || txTokenDecimals === null)) {
|
||||
if (isSendTokenTx) {
|
||||
if (txTokenSymbol === null || txTokenDecimals === null) {
|
||||
try {
|
||||
const [tokenSymbol, tokenDecimals] = await Promise.all(
|
||||
generateBatchRequests({
|
||||
|
@ -152,6 +153,7 @@ export const buildTransactionFrom = async (
|
|||
isSendTokenTx = false
|
||||
customTx = true
|
||||
}
|
||||
}
|
||||
|
||||
const params = web3.eth.abi.decodeParameters(['address', 'uint256'], tx.data.slice(10))
|
||||
decodedParams = {
|
||||
|
@ -228,8 +230,8 @@ const addMockSafeCreationTx = (safeAddress): Array<TxServiceModel> => [
|
|||
const batchRequestTxsData = (txs: any[]) => {
|
||||
const web3Batch = new web3.BatchRequest()
|
||||
|
||||
const whenTxsValues = txs.map((tx) => {
|
||||
const methods = ['decimals', { method: 'getCode', type: 'eth', args: [tx.to] }, 'symbol', 'name']
|
||||
const txsTokenInfo = txs.map((tx) => {
|
||||
const methods = [{ method: 'getCode', type: 'eth', args: [tx.to] }, 'decimals', 'name', 'symbol']
|
||||
return generateBatchRequests({
|
||||
abi: ERC20Detailed.abi,
|
||||
address: tx.to,
|
||||
|
@ -241,7 +243,7 @@ const batchRequestTxsData = (txs: any[]) => {
|
|||
|
||||
web3Batch.execute()
|
||||
|
||||
return Promise.all(whenTxsValues)
|
||||
return Promise.all(txsTokenInfo)
|
||||
}
|
||||
|
||||
const batchRequestIncomingTxsData = (txs: IncomingTxServiceModel[]) => {
|
||||
|
@ -339,9 +341,15 @@ export const loadSafeTransactions = async (safeAddress: string, getState: GetSta
|
|||
const txsWithData = await batchRequestTxsData(transactions)
|
||||
// In case that the etags don't match, we parse the new transactions and save them to the cache
|
||||
const txsRecord: Array<RecordInstance<TransactionProps>> = await Promise.all(
|
||||
txsWithData.map(([tx: TxServiceModel, decimals, symbol, name, code]) =>
|
||||
buildTransactionFrom(safeAddress, tx, knownTokens, decimals, symbol, name, code),
|
||||
),
|
||||
txsWithData.map(([tx: TxServiceModel, code, decimals, name, symbol]) => {
|
||||
const knownToken = knownTokens.get(tx.to)
|
||||
|
||||
if (knownToken) {
|
||||
;({ decimals, name, symbol } = knownToken)
|
||||
}
|
||||
|
||||
return buildTransactionFrom(safeAddress, knownTokens, tx, code, decimals, name, symbol)
|
||||
}),
|
||||
)
|
||||
|
||||
const groupedTxs = List(txsRecord).groupBy((tx) => (tx.get('cancellationTx') ? 'cancel' : 'outgoing'))
|
||||
|
|
|
@ -2,7 +2,12 @@
|
|||
import { Record } from 'immutable'
|
||||
import type { RecordFactory, RecordOf } from 'immutable'
|
||||
|
||||
export const INCOMING_TX_TYPES = ['INCOMING', 'ERC721_TRANSFER', 'ERC20_TRANSFER', 'ETHER_TRANSFER']
|
||||
export const INCOMING_TX_TYPES = {
|
||||
INCOMING: 'INCOMING',
|
||||
ERC721_TRANSFER: 'ERC721_TRANSFER',
|
||||
ERC20_TRANSFER: 'ERC20_TRANSFER',
|
||||
ETHER_TRANSFER: 'ETHER_TRANSFER',
|
||||
}
|
||||
|
||||
export type IncomingTransactionProps = {
|
||||
blockNumber: number,
|
||||
|
|
|
@ -7,14 +7,7 @@ import { type Confirmation } from '~/routes/safe/store/models/confirmation'
|
|||
|
||||
export const OUTGOING_TX_TYPE = 'outgoing'
|
||||
|
||||
export type TransactionType =
|
||||
| 'incoming'
|
||||
| 'outgoing'
|
||||
| 'settings'
|
||||
| 'custom'
|
||||
| 'creation'
|
||||
| 'cancellation'
|
||||
| 'third-party-app'
|
||||
export type TransactionType = 'incoming' | 'outgoing' | 'settings' | 'custom' | 'creation' | 'cancellation'
|
||||
|
||||
export type TransactionStatus =
|
||||
| 'awaiting_your_confirmation'
|
||||
|
@ -24,7 +17,6 @@ export type TransactionStatus =
|
|||
| 'cancelled'
|
||||
| 'awaiting_execution'
|
||||
| 'pending'
|
||||
| 'third-party-app'
|
||||
|
||||
export type TransactionProps = {
|
||||
nonce: ?number,
|
||||
|
|
Loading…
Reference in New Issue