├── .eslintrc.js ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── images │ ├── ganache_banner.jpg │ └── ganache_screenshot.jpg ├── .gitignore ├── .nvmrc ├── .travis.yml ├── AppXManifest.xml ├── LICENSE ├── README.md ├── appveyor.yml ├── build ├── appx │ ├── LargeTile.scale-100.png │ ├── LargeTile.scale-125.png │ ├── LargeTile.scale-150.png │ ├── LargeTile.scale-200.png │ ├── LargeTile.scale-400.png │ ├── MedTile.scale-100.png │ ├── MedTile.scale-125.png │ ├── MedTile.scale-150.png │ ├── MedTile.scale-200.png │ ├── MedTile.scale-400.png │ ├── SmallTile.scale-100.png │ ├── SmallTile.scale-125.png │ ├── SmallTile.scale-150.png │ ├── SmallTile.scale-200.png │ ├── SmallTile.scale-400.png │ ├── SplashScreen.scale-100.png │ ├── SplashScreen.scale-125.png │ ├── SplashScreen.scale-150.png │ ├── SplashScreen.scale-200.png │ ├── SplashScreen.scale-400.png │ ├── Square150x150Logo.scale-100.png │ ├── Square150x150Logo.scale-125.png │ ├── Square150x150Logo.scale-150.png │ ├── Square150x150Logo.scale-200.png │ ├── Square150x150Logo.scale-400.png │ ├── Square44x44Logo.scale-100.png │ ├── Square44x44Logo.scale-125.png │ ├── Square44x44Logo.scale-150.png │ ├── Square44x44Logo.scale-200.png │ ├── Square44x44Logo.scale-400.png │ ├── Square44x44Logo.targetsize-16.png │ ├── Square44x44Logo.targetsize-16_altform-unplated.png │ ├── Square44x44Logo.targetsize-20.png │ ├── Square44x44Logo.targetsize-20_altform-unplated.png │ ├── Square44x44Logo.targetsize-24.png │ ├── Square44x44Logo.targetsize-24_altform-unplated.png │ ├── Square44x44Logo.targetsize-256.png │ ├── Square44x44Logo.targetsize-256_altform-unplated.png │ ├── Square44x44Logo.targetsize-26.png │ ├── Square44x44Logo.targetsize-26_altform-unplated.png │ ├── Square44x44Logo.targetsize-30.png │ ├── Square44x44Logo.targetsize-30_altform-unplated.png │ ├── Square44x44Logo.targetsize-32.png │ ├── Square44x44Logo.targetsize-32_altform-unplated.png │ ├── Square44x44Logo.targetsize-40.png │ ├── Square44x44Logo.targetsize-40_altform-unplated.png │ ├── Square44x44Logo.targetsize-48.png │ ├── Square44x44Logo.targetsize-48_altform-unplated.png │ ├── Square44x44Logo.targetsize-60.png │ ├── Square44x44Logo.targetsize-60_altform-unplated.png │ ├── Square44x44Logo.targetsize-64.png │ ├── Square44x44Logo.targetsize-64_altform-unplated.png │ ├── Square44x44Logo.targetsize-72.png │ ├── Square44x44Logo.targetsize-72_altform-unplated.png │ ├── Square44x44Logo.targetsize-80.png │ ├── Square44x44Logo.targetsize-80_altform-unplated.png │ ├── Square44x44Logo.targetsize-96.png │ ├── Square44x44Logo.targetsize-96_altform-unplated.png │ ├── StoreLogo.png │ ├── StoreLogo.scale-100.png │ ├── StoreLogo.scale-125.png │ ├── StoreLogo.scale-150.png │ ├── StoreLogo.scale-200.png │ ├── StoreLogo.scale-400.png │ ├── Wide310x150Logo.scale-100.png │ ├── Wide310x150Logo.scale-125.png │ ├── Wide310x150Logo.scale-150.png │ ├── Wide310x150Logo.scale-200.png │ └── Wide310x150Logo.scale-400.png └── dmg │ ├── background.png │ ├── background.tiff │ ├── background@2x.png │ └── entitlements.mac.inherit.plist ├── certs ├── cert.pfx.enc └── osx.p12.enc ├── dev-app-update.yml ├── package-lock.json ├── package.json ├── patches └── webpack+4.46.0.patch ├── scripts └── build │ ├── AppRun │ ├── afterPack.js │ └── afterSignHook.js ├── src ├── common │ ├── README.md │ ├── extras │ │ └── index.js │ ├── redux │ │ ├── appshell │ │ │ ├── actions.js │ │ │ └── reducers.js │ │ ├── auto-update │ │ │ ├── actions.js │ │ │ └── reducers.js │ │ ├── config │ │ │ ├── actions.js │ │ │ └── reducers.js │ │ ├── core │ │ │ ├── actions.js │ │ │ └── reducers.js │ │ ├── logs │ │ │ ├── actions.js │ │ │ └── reducers.js │ │ ├── middleware │ │ │ └── analytics │ │ │ │ ├── engines │ │ │ │ └── GoogleAnalytics.js │ │ │ │ └── index.js │ │ ├── network │ │ │ ├── actions.js │ │ │ └── reducers.js │ │ ├── reducer.js │ │ ├── search │ │ │ └── actions.js │ │ └── workspaces │ │ │ ├── actions.js │ │ │ └── reducers.js │ ├── services │ │ ├── AutoUpdateService.js │ │ └── GoogleAnalyticsService.js │ └── utils │ │ ├── cleanup.js │ │ ├── downloader.js │ │ ├── jsonTheme.js │ │ ├── patch-unzip-stream.js │ │ └── pojofyError.js ├── integrations │ ├── ethereum │ │ ├── common │ │ │ ├── redux │ │ │ │ ├── accounts │ │ │ │ │ ├── actions.js │ │ │ │ │ └── reducers.js │ │ │ │ ├── blocks │ │ │ │ │ ├── actions.js │ │ │ │ │ └── reducers.js │ │ │ │ ├── core │ │ │ │ │ ├── actions.js │ │ │ │ │ └── reducers.js │ │ │ │ ├── events │ │ │ │ │ ├── actions.js │ │ │ │ │ └── reducers.js │ │ │ │ ├── request-cache │ │ │ │ │ ├── actions.js │ │ │ │ │ └── reducers.js │ │ │ │ ├── transactions │ │ │ │ │ ├── actions.js │ │ │ │ │ └── reducers.js │ │ │ │ ├── web3 │ │ │ │ │ ├── actions.js │ │ │ │ │ ├── helpers │ │ │ │ │ │ ├── ReduxWeb3Provider.js │ │ │ │ │ │ └── Web3ActionCreator.js │ │ │ │ │ └── reducers.js │ │ │ │ └── workspaces │ │ │ │ │ ├── actions.js │ │ │ │ │ └── reducers.js │ │ │ └── services │ │ │ │ ├── EthereumChainService.js │ │ │ │ └── TruffleIntegrationService.js │ │ ├── index.js │ │ ├── main │ │ │ └── types │ │ │ │ └── contracts │ │ │ │ └── ContractCache.js │ │ └── renderer │ │ │ ├── components │ │ │ ├── checksum-addresses │ │ │ │ └── ChecksumAddress.js │ │ │ └── formatted-ether-value │ │ │ │ └── FormattedEtherValue.js │ │ │ ├── init │ │ │ └── Events.js │ │ │ └── screens │ │ │ ├── accounts │ │ │ ├── AccountList.js │ │ │ ├── AccountList.scss │ │ │ ├── AccountsScreen.js │ │ │ ├── AccountsScreen.scss │ │ │ ├── KeyModal.js │ │ │ ├── KeyModal.scss │ │ │ ├── MnemonicAndHdPath.js │ │ │ ├── MnemonicAndHdPath.scss │ │ │ ├── MnemonicInfoModal.js │ │ │ └── MnemonicInfoModal.scss │ │ │ ├── blocks │ │ │ ├── BlockCard.js │ │ │ ├── BlockCard.scss │ │ │ ├── BlockList.js │ │ │ ├── BlockList.scss │ │ │ ├── BlocksScreen.js │ │ │ ├── BlocksScreen.scss │ │ │ ├── MiniBlockCard.js │ │ │ └── MiniBlockCard.scss │ │ │ ├── config │ │ │ └── ConfigScreens │ │ │ │ ├── AccountsScreen.js │ │ │ │ ├── AdvancedScreen.js │ │ │ │ ├── ChainScreen.js │ │ │ │ ├── ServerScreen.js │ │ │ │ └── WorkspaceScreen.js │ │ │ ├── contracts │ │ │ ├── ContractCard.js │ │ │ ├── ContractDetails.js │ │ │ ├── ContractDetailsScreen.scss │ │ │ ├── ContractsScreen.js │ │ │ ├── ContractsScreen.scss │ │ │ └── ProjectContracts.js │ │ │ ├── event-details │ │ │ ├── DecodedEventDetails.js │ │ │ ├── DecodedEventDetails.scss │ │ │ ├── EncodedEventDetails.js │ │ │ ├── EncodedEventDetails.scss │ │ │ ├── EventDetailsScreen.js │ │ │ └── EventDetailsScreen.scss │ │ │ ├── events │ │ │ ├── EventItem.js │ │ │ ├── EventItem.scss │ │ │ ├── EventList.js │ │ │ ├── EventList.scss │ │ │ ├── EventsScreen.js │ │ │ ├── EventsScreen.scss │ │ │ └── RecentEvents.js │ │ │ └── transactions │ │ │ ├── DestinationAddress.js │ │ │ ├── MiniTxCard.js │ │ │ ├── MiniTxCard.scss │ │ │ ├── RecentTransactions.js │ │ │ ├── RecentTransactions.scss │ │ │ ├── TransactionTypeBadge.js │ │ │ ├── TransactionTypeBadge.scss │ │ │ ├── TransactionsScreen.js │ │ │ ├── TransactionsScreen.scss │ │ │ ├── TxCard.js │ │ │ ├── TxCard.scss │ │ │ ├── TxList.js │ │ │ └── TxList.scss │ ├── filecoin │ │ ├── common │ │ │ ├── redux │ │ │ │ ├── accounts │ │ │ │ │ ├── actions.js │ │ │ │ │ └── reducers.js │ │ │ │ ├── core │ │ │ │ │ ├── actions.js │ │ │ │ │ └── reducers.js │ │ │ │ ├── deals │ │ │ │ │ ├── actions.js │ │ │ │ │ └── reducers.js │ │ │ │ ├── files │ │ │ │ │ ├── actions.js │ │ │ │ │ └── reducers.js │ │ │ │ ├── lotus │ │ │ │ │ ├── actions.js │ │ │ │ │ ├── helpers │ │ │ │ │ │ ├── LotusActionCreator.js │ │ │ │ │ │ └── ReduxLotusProvider.js │ │ │ │ │ └── reducers.js │ │ │ │ ├── messages │ │ │ │ │ ├── actions.js │ │ │ │ │ └── reducers.js │ │ │ │ ├── prefix.js │ │ │ │ ├── reducer.js │ │ │ │ ├── request-cache │ │ │ │ │ ├── actions.js │ │ │ │ │ └── reducers.js │ │ │ │ └── tipsets │ │ │ │ │ ├── actions.js │ │ │ │ │ └── reducers.js │ │ │ ├── services │ │ │ │ └── FilecoinChainService.js │ │ │ └── utils │ │ │ │ └── cid.js │ │ ├── index.js │ │ └── renderer │ │ │ ├── components │ │ │ └── formatted-fil-value │ │ │ │ └── FormattedFILValue.js │ │ │ └── screens │ │ │ ├── accounts │ │ │ ├── AccountList.js │ │ │ ├── AccountList.scss │ │ │ ├── AccountsBanner.js │ │ │ ├── AccountsBanner.scss │ │ │ ├── AccountsScreen.js │ │ │ ├── AccountsScreen.scss │ │ │ ├── KeyModal.js │ │ │ ├── KeyModal.scss │ │ │ ├── SeedInfoModal.js │ │ │ └── SeedInfoModal.scss │ │ │ ├── config │ │ │ └── ConfigScreens │ │ │ │ ├── AccountsScreen.js │ │ │ │ ├── MinerScreen.js │ │ │ │ ├── ServerScreen.js │ │ │ │ └── WorkspaceScreen.js │ │ │ ├── deals │ │ │ ├── DealList.js │ │ │ ├── DealList.scss │ │ │ ├── DealStatusBadge.js │ │ │ ├── DealStatusBadge.scss │ │ │ ├── DealsScreen.js │ │ │ ├── DealsScreen.scss │ │ │ ├── MiniDealCard.js │ │ │ └── MiniDealCard.scss │ │ │ ├── files │ │ │ ├── FileList.js │ │ │ ├── FileList.scss │ │ │ ├── FilesScreen.js │ │ │ ├── FilesScreen.scss │ │ │ ├── MiniFileCard.js │ │ │ └── MiniFileCard.scss │ │ │ ├── messages │ │ │ ├── MessageCard.js │ │ │ ├── MessageCard.scss │ │ │ ├── MessageList.js │ │ │ ├── MessageList.scss │ │ │ ├── MessageTypeBadge.js │ │ │ ├── MessageTypeBadge.scss │ │ │ ├── MessagesScreen.js │ │ │ ├── MessagesScreen.scss │ │ │ ├── MiniMessageCard.js │ │ │ ├── MiniMessageCard.scss │ │ │ ├── RecentMessages.js │ │ │ └── RecentMessages.scss │ │ │ └── tipsets │ │ │ ├── BlockCard.js │ │ │ ├── BlockCard.scss │ │ │ ├── BlockList.js │ │ │ ├── MiniBlockCard.js │ │ │ ├── MiniBlockCard.scss │ │ │ ├── MiniTipsetCard.js │ │ │ ├── MiniTipsetCard.scss │ │ │ ├── TipsetCard.js │ │ │ ├── TipsetCard.scss │ │ │ ├── TipsetList.js │ │ │ ├── TipsetList.scss │ │ │ ├── TipsetsScreen.js │ │ │ └── TipsetsScreen.scss │ ├── index.js │ └── integrations.js ├── main │ ├── index.js │ ├── init │ │ ├── AutoUpdate.js │ │ └── migration.js │ └── types │ │ ├── json │ │ ├── JsonStorage.js │ │ └── JsonWithKeyPaths.js │ │ ├── settings │ │ ├── GlobalSettings.js │ │ ├── Settings.js │ │ ├── WorkspaceSettings.js │ │ └── flavors │ │ │ ├── ethereum.js │ │ │ └── filecoin.js │ │ └── workspaces │ │ ├── Workspace.js │ │ └── WorkspaceManager.js └── renderer │ ├── App.js │ ├── README.md │ ├── app.global.scss │ ├── components │ ├── file-picker │ │ ├── FilePicker.js │ │ └── FilePicker.scss │ ├── google-analytics │ │ └── GoogleAnalytics.js │ ├── header-bar │ │ ├── HeaderBar.css │ │ └── HeaderBar.js │ ├── logo │ │ └── Logo.js │ ├── modal │ │ ├── ErrorModal.js │ │ ├── Modal.js │ │ ├── Modal.scss │ │ └── ModalDetails.js │ ├── only-if │ │ └── OnlyIf.js │ ├── progress-bar │ │ ├── ProgressBar.js │ │ └── ProgressBar.scss │ ├── spinner │ │ ├── Spinner.css │ │ ├── Spinner.js │ │ ├── SpinnerButton.css │ │ └── SpinnerButton.js │ ├── status-indicator │ │ ├── StatusIndicator.js │ │ └── StatusIndicator.scss │ ├── styled-select │ │ ├── StyledSelect.js │ │ └── StyledSelect.scss │ └── with-empty-state │ │ └── WithEmptyState.js │ ├── css.js │ ├── icons │ ├── account.svg │ ├── blocks.svg │ ├── bug.svg │ ├── chain.svg │ ├── chevron-up-o.svg │ ├── chevron-up.svg │ ├── console.svg │ ├── contract-icon.svg │ ├── contract.svg │ ├── deals-icon.svg │ ├── dots.svg │ ├── download.svg │ ├── eject.svg │ ├── error.svg │ ├── errorant.svg │ ├── events-icon.svg │ ├── file-icon.svg │ ├── force_mine.svg │ ├── ganache_logo.svg │ ├── key.svg │ ├── list.svg │ ├── locked.svg │ ├── logo.svg │ ├── modal-error.svg │ ├── modal-info.svg │ ├── modal-update.svg │ ├── no_transactions.svg │ ├── restart.svg │ ├── revert.svg │ ├── save-icon.svg │ ├── search.svg │ ├── settings.svg │ ├── snapshot.svg │ ├── start.svg │ ├── stop.svg │ ├── transactions.svg │ ├── trash-icon.svg │ ├── unlocked.svg │ ├── warning.svg │ └── welcome-crane.svg │ ├── index.js │ ├── init │ ├── AutoUpdate.js │ ├── Config.js │ ├── Core.js │ ├── ErrorHandler.js │ ├── Logs.js │ ├── Network.js │ ├── Workspaces.js │ ├── index.js │ └── store │ │ ├── createStore.development.js │ │ ├── createStore.js │ │ └── createStore.production.js │ ├── routes.js │ ├── screens │ ├── appshell │ │ ├── AppShell.js │ │ ├── AppShell.scss │ │ ├── BugModal.js │ │ ├── BugModal.scss │ │ ├── TopNavbar.js │ │ └── TopNavbar.scss │ ├── auto-update │ │ ├── UpdateModal.js │ │ ├── UpdateModal.scss │ │ ├── UpdateNotification.js │ │ └── UpdateNotification.scss │ ├── config │ │ ├── ConfigScreen.js │ │ ├── ConfigScreen.scss │ │ └── ConfigScreens │ │ │ └── AboutScreen.js │ ├── first-run │ │ ├── FirstRunScreen.js │ │ └── FirstRunScreen.scss │ ├── helpers │ │ ├── connect.js │ │ └── sanitize.js │ ├── loader │ │ ├── LoaderScreen.js │ │ └── LoaderScreen.scss │ ├── logs │ │ ├── LogContainer.scss │ │ ├── LogContainerLazy.js │ │ ├── LogsLazy.js │ │ ├── LogsScreen.js │ │ ├── LogsScreen.scss │ │ └── Row.js │ ├── not-found │ │ ├── NotFoundScreen.js │ │ └── NotFoundScreen.scss │ ├── startup │ │ ├── HomeScreen.js │ │ └── HomeScreen.scss │ └── title │ │ ├── TitleScreen.js │ │ └── TitleScreen.scss │ └── styles │ ├── buttons.scss │ ├── cards.scss │ ├── clean_colors.scss │ ├── colors.scss │ ├── forms.scss │ ├── logo.scss │ ├── modals.scss │ ├── normalize.scss │ └── panels.scss ├── static ├── README.md ├── fonts │ ├── FiraCode-Regular.ttf │ ├── FiraSans-Bold.ttf │ ├── FiraSans-Regular.ttf │ ├── FiraSans-SemiBold.ttf │ ├── GrandHotel-Regular.ttf │ ├── RobotoCondensed-Bold.ttf │ └── RobotoCondensed-Regular.ttf ├── icons │ ├── mac │ │ └── icon.icns │ ├── png │ │ ├── 1024x1024.png │ │ ├── 128x128.png │ │ ├── 16x16.png │ │ ├── 24x24.png │ │ ├── 256x256.png │ │ ├── 32x32.png │ │ ├── 48x48.png │ │ ├── 512x512.png │ │ ├── 64x64.png │ │ └── 96x96.png │ └── win │ │ └── icon.ico ├── logo.png └── node │ ├── chain │ ├── chain.ethereum.js │ ├── chain.ethereum.v2.js │ ├── chain.filecoin.js │ └── logging.js │ ├── package-lock.json │ ├── package.json │ ├── truffle-integration │ ├── __tests__ │ │ └── projectFsWatcherUtils.test.js │ ├── decode.js │ ├── index.js │ ├── projectDetails.js │ ├── projectFsWatcher.js │ ├── projectFsWatcherUtils.js │ └── projectsWatcher.js │ └── truffle-project-loader │ └── index.js ├── test ├── README.md └── mocha │ ├── project-loader │ ├── project-loader.test.js │ └── truffle-project │ │ ├── myfile.json │ │ └── truffle.js │ └── workspaces │ ├── new-workspace.subtest.js │ ├── test-workspaces │ ├── default │ │ └── Settings │ ├── global │ │ └── Settings │ └── ui │ │ └── workspaces │ │ ├── Test-1 │ │ └── Settings │ │ └── Test-2 │ │ └── Settings │ ├── workspace-manager.subtest.js │ └── workspaces.test.js └── webpack ├── webpack.main.js └── webpack.renderer.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["prettier", "eslint:recommended", "plugin:react/recommended"], 3 | plugins: ["prettier", "react", "jest", "react-hooks"], 4 | rules: { 5 | "no-prototype-builtins": "warn", 6 | "no-console": "warn", 7 | "react/prop-types": 0, 8 | "react-hooks/rules-of-hooks": "error", 9 | "react-hooks/exhaustive-deps": "warn", 10 | }, 11 | parserOptions: { 12 | ecmaVersion: 2020, 13 | sourceType: "module", 14 | }, 15 | env: { 16 | es6: true, 17 | browser: true, 18 | node: true, 19 | mocha: true, 20 | "jest/globals": true, 21 | }, 22 | settings: { 23 | react: { 24 | version: "detect", 25 | }, 26 | }, 27 | parser: "babel-eslint", 28 | }; 29 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Ganache 2 | 3 | Thank you for contributing! Take a moment to review our [**contributing guidelines**](https://github.com/trufflesuite/ganache-ui/blob/master/.github/CONTRIBUTING.md) 4 | to make the process easy and effective for everyone involved. 5 | 6 | **You must open an issue** before embarking on any significant pull request, especially those that 7 | add a new library or change existing tests, otherwise you risk spending a lot of time working 8 | on something that might not end up being merged into the project. 9 | 10 | Before opening a pull request, please ensure: 11 | 12 | - [ ] You have followed our [**contributing guidelines**](https://github.com/trufflesuite/ganache-ui/blob/master/.github/CONTRIBUTING.md) 13 | - [ ] Pull request has tests 14 | - [ ] Code is well-commented, linted and follows project conventions 15 | - [ ] Documentation is updated (if necessary) 16 | - [ ] Description explains the issue/use-case resolved and auto-closes related issues 17 | 18 | Be kind to code reviewers, please try to keep pull requests as small and focused as possible :) 19 | 20 | **IMPORTANT**: By submitting a patch via a Pull Request, you agree to allow the project 21 | owners to license your work under the terms of the [MIT License](https://github.com/trufflesuite/ganache-ui/blob/master/LICENSE.md). 22 | -------------------------------------------------------------------------------- /.github/images/ganache_banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/.github/images/ganache_banner.jpg -------------------------------------------------------------------------------- /.github/images/ganache_screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/.github/images/ganache_screenshot.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | certs/cert.pfx 2 | dist 3 | node_modules 4 | .DS_Store 5 | .vscode 6 | test/mocha/workspaces/test-workspaces/default/ContractCache 7 | test/mocha/workspaces/test-workspaces/workspaces/Test-1/ContractCache 8 | test/mocha/workspaces/test-workspaces/workspaces/Test-2/ContractCache 9 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 14.21.1 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 ConsenSys Software Inc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /build/appx/LargeTile.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/LargeTile.scale-100.png -------------------------------------------------------------------------------- /build/appx/LargeTile.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/LargeTile.scale-125.png -------------------------------------------------------------------------------- /build/appx/LargeTile.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/LargeTile.scale-150.png -------------------------------------------------------------------------------- /build/appx/LargeTile.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/LargeTile.scale-200.png -------------------------------------------------------------------------------- /build/appx/LargeTile.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/LargeTile.scale-400.png -------------------------------------------------------------------------------- /build/appx/MedTile.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/MedTile.scale-100.png -------------------------------------------------------------------------------- /build/appx/MedTile.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/MedTile.scale-125.png -------------------------------------------------------------------------------- /build/appx/MedTile.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/MedTile.scale-150.png -------------------------------------------------------------------------------- /build/appx/MedTile.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/MedTile.scale-200.png -------------------------------------------------------------------------------- /build/appx/MedTile.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/MedTile.scale-400.png -------------------------------------------------------------------------------- /build/appx/SmallTile.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/SmallTile.scale-100.png -------------------------------------------------------------------------------- /build/appx/SmallTile.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/SmallTile.scale-125.png -------------------------------------------------------------------------------- /build/appx/SmallTile.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/SmallTile.scale-150.png -------------------------------------------------------------------------------- /build/appx/SmallTile.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/SmallTile.scale-200.png -------------------------------------------------------------------------------- /build/appx/SmallTile.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/SmallTile.scale-400.png -------------------------------------------------------------------------------- /build/appx/SplashScreen.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/SplashScreen.scale-100.png -------------------------------------------------------------------------------- /build/appx/SplashScreen.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/SplashScreen.scale-125.png -------------------------------------------------------------------------------- /build/appx/SplashScreen.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/SplashScreen.scale-150.png -------------------------------------------------------------------------------- /build/appx/SplashScreen.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/SplashScreen.scale-200.png -------------------------------------------------------------------------------- /build/appx/SplashScreen.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/SplashScreen.scale-400.png -------------------------------------------------------------------------------- /build/appx/Square150x150Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square150x150Logo.scale-100.png -------------------------------------------------------------------------------- /build/appx/Square150x150Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square150x150Logo.scale-125.png -------------------------------------------------------------------------------- /build/appx/Square150x150Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square150x150Logo.scale-150.png -------------------------------------------------------------------------------- /build/appx/Square150x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square150x150Logo.scale-200.png -------------------------------------------------------------------------------- /build/appx/Square150x150Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square150x150Logo.scale-400.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.scale-100.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.scale-125.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.scale-150.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.scale-200.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.scale-400.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-16.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-16_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-16_altform-unplated.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-20.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-20_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-20_altform-unplated.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-24.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-24_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-24_altform-unplated.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-256.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-256_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-256_altform-unplated.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-26.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-26_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-26_altform-unplated.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-30.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-30_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-30_altform-unplated.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-32.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-32_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-32_altform-unplated.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-40.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-40_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-40_altform-unplated.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-48.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-48_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-48_altform-unplated.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-60.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-60_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-60_altform-unplated.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-64.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-64_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-64_altform-unplated.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-72.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-72_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-72_altform-unplated.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-80.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-80_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-80_altform-unplated.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-96.png -------------------------------------------------------------------------------- /build/appx/Square44x44Logo.targetsize-96_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Square44x44Logo.targetsize-96_altform-unplated.png -------------------------------------------------------------------------------- /build/appx/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/StoreLogo.png -------------------------------------------------------------------------------- /build/appx/StoreLogo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/StoreLogo.scale-100.png -------------------------------------------------------------------------------- /build/appx/StoreLogo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/StoreLogo.scale-125.png -------------------------------------------------------------------------------- /build/appx/StoreLogo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/StoreLogo.scale-150.png -------------------------------------------------------------------------------- /build/appx/StoreLogo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/StoreLogo.scale-200.png -------------------------------------------------------------------------------- /build/appx/StoreLogo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/StoreLogo.scale-400.png -------------------------------------------------------------------------------- /build/appx/Wide310x150Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Wide310x150Logo.scale-100.png -------------------------------------------------------------------------------- /build/appx/Wide310x150Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Wide310x150Logo.scale-125.png -------------------------------------------------------------------------------- /build/appx/Wide310x150Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Wide310x150Logo.scale-150.png -------------------------------------------------------------------------------- /build/appx/Wide310x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Wide310x150Logo.scale-200.png -------------------------------------------------------------------------------- /build/appx/Wide310x150Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/appx/Wide310x150Logo.scale-400.png -------------------------------------------------------------------------------- /build/dmg/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/dmg/background.png -------------------------------------------------------------------------------- /build/dmg/background.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/dmg/background.tiff -------------------------------------------------------------------------------- /build/dmg/background@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/build/dmg/background@2x.png -------------------------------------------------------------------------------- /build/dmg/entitlements.mac.inherit.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-jit 6 | 7 | com.apple.security.cs.allow-unsigned-executable-memory 8 | 9 | com.apple.security.cs.allow-dyld-environment-variables 10 | 11 | 12 | -------------------------------------------------------------------------------- /certs/cert.pfx.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/certs/cert.pfx.enc -------------------------------------------------------------------------------- /certs/osx.p12.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/certs/osx.p12.enc -------------------------------------------------------------------------------- /dev-app-update.yml: -------------------------------------------------------------------------------- 1 | owner: trufflesuite 2 | repo: ganache 3 | provider: github 4 | -------------------------------------------------------------------------------- /scripts/build/afterPack.js: -------------------------------------------------------------------------------- 1 | const { join } = require("path"); 2 | const { chmodSync, readFileSync, writeFileSync } = require("fs-extra"); 3 | 4 | const afterPack = async (context) => { 5 | if (context.electronPlatformName !== "linux") { 6 | return; 7 | } 8 | if (context.targets.length !== 1) { 9 | throw new Error("Linux can only target 1 build at a time."); 10 | } 11 | const target = context.targets[0]; 12 | if (target.name !== "appImage") { 13 | throw new Error("I don't know if this will work with other linux targets. You'll need to test before removing this check!"); 14 | } 15 | 16 | const appRunTemplate = readFileSync(join(__dirname, "AppRun"), {encoding: "utf-8"}); 17 | const appRunFinal = appRunTemplate.replace(/{APP_NAME}/g, target.options.executableName); 18 | const appRunPath = join(context.appOutDir, "AppRun") 19 | writeFileSync(appRunPath, appRunFinal, {encoding: "utf-8"}); 20 | // make sure this file is executable 21 | chmodSync(appRunPath, 0o755); 22 | }; 23 | 24 | module.exports = afterPack; 25 | -------------------------------------------------------------------------------- /scripts/build/afterSignHook.js: -------------------------------------------------------------------------------- 1 | // See: https://medium.com/@TwitterArchiveEraser/notarize-electron-apps-7a5f988406db 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const electron_notarize = require('electron-notarize'); 6 | const pkg = require("../../package.json"); 7 | 8 | module.exports = async function (params) { 9 | // Only notarize the app on Mac OS only. 10 | if (process.platform !== 'darwin' || process.env.NOTARIZE !== 'true') { 11 | return; 12 | } 13 | console.log('afterSign hook triggered', params); 14 | 15 | // Same appId in electron-builder. 16 | let appId = pkg.build.appId; 17 | 18 | let appPath = path.join(params.appOutDir, `${params.packager.appInfo.productFilename}.app`); 19 | if (!fs.existsSync(appPath)) { 20 | throw new Error(`Cannot find application at: ${appPath}`); 21 | } 22 | 23 | console.log(`Notarizing ${appId} found at ${appPath}`); 24 | 25 | const interval = setInterval(() => { 26 | console.log("..."); 27 | }, 10000); 28 | 29 | try { 30 | await electron_notarize.notarize({ 31 | appBundleId: appId, 32 | appPath: appPath, 33 | appleId: process.env.appleId, 34 | appleIdPassword: process.env.appleIdPassword 35 | }); 36 | } catch (error) { 37 | console.error(error); 38 | } finally { 39 | clearInterval(interval); 40 | } 41 | 42 | console.log(`Done notarizing ${appId}`); 43 | }; -------------------------------------------------------------------------------- /src/common/README.md: -------------------------------------------------------------------------------- 1 | COMMON SCRIPTS 2 | 3 | This is a convenient place where you can place utility type files that you expect to use between both the main and render processor. Thanks to webpack aliasing, you can easily import files from here using the common alias. 4 | -------------------------------------------------------------------------------- /src/common/extras/index.js: -------------------------------------------------------------------------------- 1 | const Downloader = require("../utils/downloader"); 2 | 3 | module.exports = { 4 | init: (path) => { 5 | const downloader = new Downloader(path); 6 | return { 7 | downloader, 8 | }; 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /src/common/redux/appshell/actions.js: -------------------------------------------------------------------------------- 1 | const prefix = "APPSHELL"; 2 | 3 | export const SET_SCROLL_POSITION = `${prefix}/SET_SCROLL_POSITION`; 4 | export const setScrollPosition = function(scrollPosition) { 5 | return { type: SET_SCROLL_POSITION, scrollPosition }; 6 | }; 7 | -------------------------------------------------------------------------------- /src/common/redux/appshell/reducers.js: -------------------------------------------------------------------------------- 1 | import * as AppShell from "./actions"; 2 | 3 | const initialState = { 4 | scrollPosition: "top", 5 | }; 6 | 7 | export default function(state = initialState, action) { 8 | switch (action.type) { 9 | case AppShell.SET_SCROLL_POSITION: 10 | return Object.assign({}, state, { 11 | scrollPosition: action.scrollPosition, 12 | }); 13 | default: 14 | return state; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/common/redux/config/reducers.js: -------------------------------------------------------------------------------- 1 | import * as Config from "./actions"; 2 | import cloneDeep from "lodash.clonedeep"; 3 | 4 | const initialState = { 5 | validationErrors: {}, // of the format {SETTING_NAME (i.e. hostname would be server.hostname): "error text"} 6 | settings: {}, 7 | startupMode: Config.STARTUP_MODE.NORMAL 8 | }; 9 | 10 | export default function(state = initialState, action) { 11 | let nextState = cloneDeep(state); 12 | switch (action.type) { 13 | case Config.SET_SETTINGS: 14 | // Ignore state; we're overwriting the settings. 15 | nextState.settings.global = cloneDeep(action.global); 16 | nextState.settings.workspace = cloneDeep(action.workspace); 17 | break; 18 | case Config.SET_SETTING_ERROR: 19 | if (typeof nextState.validationErrors === "undefined") { 20 | nextState.validationErrors = {}; 21 | } 22 | nextState.validationErrors[action.key] = action.errorText; 23 | break; 24 | case Config.CLEAR_SETTING_ERROR: 25 | if (action.key in nextState.validationErrors) { 26 | delete nextState.validationErrors[action.key]; 27 | } 28 | break; 29 | case Config.CLEAR_ALL_SETTING_ERRORS: 30 | nextState.validationErrors = {}; 31 | break; 32 | case Config.SET_STARTUP_MODE: 33 | nextState.startupMode = action.mode; 34 | break; 35 | default: 36 | break; 37 | } 38 | 39 | return nextState; 40 | } 41 | -------------------------------------------------------------------------------- /src/common/redux/core/reducers.js: -------------------------------------------------------------------------------- 1 | const Core = require("./actions"); 2 | 3 | const initialState = { 4 | started: false, 5 | systemError: null, 6 | showBugModal: false, 7 | modalError: null, 8 | updateInfo: {} 9 | }; 10 | 11 | export default function (state = initialState, action) { 12 | state = {...initialState, ...state}; 13 | switch (action.type) { 14 | case Core.SET_SERVER_STARTED: 15 | return Object.assign({}, state, { 16 | started: true, 17 | }); 18 | 19 | case Core.SET_SYSTEM_ERROR: 20 | return Object.assign({}, state, { 21 | systemError: action.error, 22 | showBugModal: action.showBugModal, 23 | }); 24 | case Core.SET_PROGRESS: 25 | return Object.assign({}, state, { 26 | progress: action.message, 27 | minDuration: action.minDuration 28 | }); 29 | case Core.SET_MODAL_ERROR: 30 | return Object.assign({}, state, { 31 | modalError: action.error, 32 | }); 33 | 34 | case Core.DISMISS_MODAL_ERROR: 35 | return Object.assign({}, state, { 36 | modalError: null, 37 | }); 38 | 39 | case Core.SET_NEW_VERSION_INFO: 40 | return Object.assign({}, state, { 41 | updateInfo: { 42 | newVersion: action.newVersion, 43 | releaseNotes: action.releaseNotes, 44 | }, 45 | }); 46 | 47 | default: 48 | return state; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/common/redux/logs/actions.js: -------------------------------------------------------------------------------- 1 | const prefix = "LOGS"; 2 | 3 | export const ADD_LOG_LINES = `${prefix}/ADD_LOG_LINES`; 4 | export const CLEAR_LOG_LINES = `${prefix}/CLEAR_LOG_LINES`; 5 | 6 | export const addLogLines = function(lines, context) { 7 | if (Array.isArray(lines) === false) { 8 | lines = [lines]; 9 | } 10 | return { type: ADD_LOG_LINES, lines, context }; 11 | }; 12 | 13 | export const clearLogLines = function(context) { 14 | return { type: CLEAR_LOG_LINES, context }; 15 | }; 16 | -------------------------------------------------------------------------------- /src/common/redux/logs/reducers.js: -------------------------------------------------------------------------------- 1 | import * as Logs from "./actions"; 2 | 3 | const initialState = { 4 | "default": { 5 | lines: [] 6 | } 7 | }; 8 | 9 | export default function(state = initialState, action) { 10 | switch (action.type) { 11 | case Logs.ADD_LOG_LINES: 12 | var time = new Date(); 13 | var newLines = action.lines.map(line => { 14 | return { time: time, line: line }; 15 | }); 16 | 17 | var context = action.context || "default"; 18 | var group = state[context]; 19 | var oldLines = group ? [...group.lines] : []; 20 | if (oldLines.length > 0 && newLines.length > 0) { 21 | const firstLine = newLines.shift(); 22 | oldLines[oldLines.length - 1].line += firstLine.line; 23 | } 24 | return Object.assign({}, state, { 25 | [context]: { 26 | lines: oldLines.concat(newLines) 27 | } 28 | }); 29 | 30 | case Logs.CLEAR_LOG_LINES: 31 | return Object.assign({}, state, { 32 | [action.context]: { 33 | lines: [] 34 | } 35 | }); 36 | 37 | default: 38 | return state; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/common/redux/middleware/analytics/index.js: -------------------------------------------------------------------------------- 1 | import * as GoogleAnalytics from "./engines/GoogleAnalytics"; 2 | 3 | const engines = [GoogleAnalytics]; 4 | 5 | export function processAction({ getState }) { 6 | return next => action => { 7 | engines.forEach(engine => { 8 | engine.process(action, getState()); 9 | }); 10 | 11 | const returnValue = next(action); 12 | 13 | return returnValue; 14 | }; 15 | } 16 | 17 | export function processPage(path, state) { 18 | engines.forEach(engine => { 19 | engine.processPage(path, state); 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /src/common/redux/network/actions.js: -------------------------------------------------------------------------------- 1 | const prefix = "NETWORK"; 2 | 3 | export const SET_INTERFACES = `${prefix}/SET_INTERFACES`; 4 | export function setInterfaces(interfaces) { 5 | return { type: SET_INTERFACES, interfaces }; 6 | } 7 | 8 | export const SET_TOAST = `${prefix}/SET_TOAST`; 9 | export function setToast(message, infinite = false, buttonText = null, toastOnClick = null) { 10 | return { type: SET_TOAST, message, infinite, buttonText, toastOnClick }; 11 | } -------------------------------------------------------------------------------- /src/common/redux/network/reducers.js: -------------------------------------------------------------------------------- 1 | import * as Network from "./actions"; 2 | import cloneDeep from "lodash.clonedeep"; 3 | 4 | const initialState = { 5 | interfaces: {}, 6 | toast: {date:Date.now(), message: null, infinite: false, buttonText: null, toastOnClick: null} 7 | }; 8 | 9 | export default function(state = initialState, action) { 10 | let nextState = cloneDeep(state); 11 | 12 | switch (action.type) { 13 | case Network.SET_INTERFACES: 14 | // Ignore state; we're overwriting the settings. 15 | nextState.interfaces = cloneDeep(action.interfaces); 16 | break; 17 | case Network.SET_TOAST: 18 | // Ignore state; we're overwriting the settings. 19 | nextState.toast = { 20 | message: action.message, 21 | date: Date.now(), 22 | infinite: action.infinite, 23 | buttonText: action.buttonText, 24 | toastOnClick: action.toastOnClick 25 | }; 26 | break; 27 | default: 28 | break; 29 | } 30 | 31 | return nextState; 32 | } 33 | -------------------------------------------------------------------------------- /src/common/utils/cleanup.js: -------------------------------------------------------------------------------- 1 | const noop = () => {}; 2 | 3 | // attach user callback to the process event emitter 4 | const Cleanup = (callback) => { 5 | // if no callback, it will still exit gracefully on Ctrl-C 6 | callback = callback || noop; 7 | 8 | //do something when app is closing 9 | process.on('exit', callback); 10 | 11 | //catches ctrl+c event 12 | process.on('SIGINT', callback); 13 | 14 | // catches "kill pid" (for example: nodemon restart) 15 | process.on('SIGUSR1', callback); 16 | process.on('SIGUSR2', callback); 17 | 18 | //catches uncaught exceptions 19 | process.on('uncaughtException', callback); 20 | }; 21 | 22 | module.exports = Cleanup; 23 | -------------------------------------------------------------------------------- /src/common/utils/jsonTheme.js: -------------------------------------------------------------------------------- 1 | export default { 2 | "scheme": "ganache", 3 | "author": "Ganache", 4 | //transparent main background 5 | "base00": "rgba(0, 0, 0, 0)", 6 | "base01": "rgb(245, 245, 245)", 7 | "base02": "#000", // lines and background color for: NULL, undefined, and brackets 8 | "base03": "rgb(26, 185, 70)", // blue grey -- not used? 9 | "base04": "rgba(0, 0, 0, 0.3)", 10 | "base05": "#aaa", // undefined text 11 | "base06": "#073642", // dark blue -- not sued? 12 | "base07": "#000", // JSON keys 13 | "base08": "#d33682", // pink -- not used? 14 | "base09": "rgb(208, 108, 0)", // string types text (ganache orange) 15 | "base0A": "rgb(208, 108, 0)", // NULL (ganache orange) 16 | "base0B": "#3fe0c5", //aka --truffle-green, for float types 17 | "base0C": "#777", // array indexes and item counts 18 | "base0D": "#000", // arrows 19 | "base0E": "#000", // used for some arrows and bool 20 | "base0F": "#268bd2", // a bright blue -- not used? 21 | "base10": "rgb(26, 185, 70)", // a bright blue -- not used? 22 | }; 23 | -------------------------------------------------------------------------------- /src/common/utils/pojofyError.js: -------------------------------------------------------------------------------- 1 | module.exports = function pojofyError(_error) { 2 | let error; 3 | if (_error instanceof Error) { 4 | // JSON.stringify can't serialize error objects 5 | // so we just convert the Error to an Object here 6 | error = {}; 7 | 8 | Object.getOwnPropertyNames(_error).forEach((key) => { 9 | error[key] = _error[key]; 10 | }); 11 | } else { 12 | error = _error; 13 | } 14 | return error; 15 | } 16 | -------------------------------------------------------------------------------- /src/integrations/ethereum/common/redux/accounts/reducers.js: -------------------------------------------------------------------------------- 1 | import * as Accounts from "./actions"; 2 | 3 | const initialState = { 4 | addresses: [], 5 | balances: {}, 6 | nonces: {}, 7 | }; 8 | 9 | export default function(state = initialState, action) { 10 | let balances; 11 | let nonces; 12 | switch (action.type) { 13 | case Accounts.GET_ACCOUNTS: 14 | var addresses = action.addresses; 15 | balances = Object.assign({}, state.balances); 16 | nonces = Object.assign({}, state.nonces); 17 | 18 | // Set default balance to zero if this is a new account 19 | addresses.forEach(address => { 20 | if (!balances[address]) balances[address] = "0"; 21 | if (!nonces[address]) nonces[address] = 0; 22 | }); 23 | 24 | return Object.assign({}, state, { 25 | addresses, 26 | balances, 27 | nonces, 28 | }); 29 | 30 | case Accounts.GET_ACCOUNT_BALANCE: 31 | balances = Object.assign({}, state.balances, { 32 | [action.address]: action.balance, 33 | }); 34 | return Object.assign({}, state, { 35 | balances, 36 | }); 37 | 38 | case Accounts.GET_ACCOUNT_NONCE: 39 | nonces = Object.assign({}, state.nonces, { 40 | [action.address]: action.nonce, 41 | }); 42 | return Object.assign({}, state, { 43 | nonces, 44 | }); 45 | 46 | default: 47 | return state; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/integrations/ethereum/common/redux/request-cache/actions.js: -------------------------------------------------------------------------------- 1 | const prefix = "CACHE"; 2 | 3 | const createCacheId = function(payload) { 4 | let params = payload.params 5 | .map(param => { 6 | return param.toString(); 7 | }) 8 | .join(","); 9 | return `${payload.method}(${params})`; 10 | }; 11 | 12 | export const CACHE_REQUEST = `${prefix}/CACHE_REQUEST`; 13 | export const cacheRequest = function(payload, response) { 14 | let id = createCacheId(payload); 15 | return { type: CACHE_REQUEST, id, response }; 16 | }; 17 | 18 | export const checkCache = function(payload, getState) { 19 | // Never cache "eth_blockNumber" 20 | if (payload.method == "eth_blockNumber") { 21 | return null; 22 | } 23 | 24 | let cache = getState().requestCache; 25 | let id = createCacheId(payload); 26 | let hit = cache[id]; 27 | 28 | if (!hit) { 29 | return null; 30 | } 31 | 32 | // Tailor the response to the new rpc id 33 | var response = Object.assign({}, hit); 34 | response.id = payload.id; 35 | return response; 36 | }; 37 | -------------------------------------------------------------------------------- /src/integrations/ethereum/common/redux/request-cache/reducers.js: -------------------------------------------------------------------------------- 1 | import * as RequestCache from "./actions"; 2 | import * as Core from "../core/actions"; 3 | 4 | const initialState = {}; 5 | 6 | // These requests are never invalidated. 7 | const HARD_CACHED = { 8 | eth_accounts: true, 9 | eth_gasPrice: true, 10 | eth_getBlockByNumber: blockNumber => { 11 | return blockNumber != "latest" && blockNumber != "pending"; 12 | }, 13 | eth_getBlockTransactionCountByNumber: blockNumber => { 14 | return blockNumber != "latest" && blockNumber != "pending"; 15 | }, 16 | eth_getTransactionByHash: true, 17 | }; 18 | 19 | const isHardCached = function(id) { 20 | let method = id.substring(0, id.indexOf("(")); 21 | 22 | var hardCachedResult = HARD_CACHED[method]; 23 | 24 | if (typeof hardCachedResult == "function") { 25 | var split = id.split(/\(|\)/); 26 | var params = split[1]; 27 | 28 | if (params == "") { 29 | params = []; 30 | } else { 31 | params = params.split(","); 32 | } 33 | 34 | return hardCachedResult.apply(null, params); 35 | } else { 36 | return !!hardCachedResult; 37 | } 38 | }; 39 | 40 | export default function(state = initialState, action) { 41 | switch (action.type) { 42 | case RequestCache.CACHE_REQUEST: 43 | return Object.assign({}, state, { 44 | [action.id]: action.response, 45 | }); 46 | 47 | case Core.SET_BLOCK_NUMBER: { 48 | // Invalidate the cache on new block number 49 | let cache = Object.assign({}, state); 50 | 51 | Object.keys(cache).forEach(id => { 52 | if (!isHardCached(id)) { 53 | delete cache[id]; 54 | } 55 | }); 56 | 57 | return cache; 58 | } 59 | default: 60 | return state; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/integrations/ethereum/common/redux/web3/actions.js: -------------------------------------------------------------------------------- 1 | import ReduxWeb3Provider from "./helpers/ReduxWeb3Provider"; 2 | import Web3 from "web3"; 3 | 4 | const prefix = "WEB3"; 5 | 6 | export const SET_WEB3_INSTANCE = `${prefix}/SET_WEB3_INSTANCE`; 7 | export function setWeb3Instance(web3Instance) { 8 | return { type: SET_WEB3_INSTANCE, web3Instance }; 9 | } 10 | 11 | export const SET_RPC_PROVIDER_URL = `${prefix}/SET_RPC_PROVIDER_URL`; 12 | export function setRPCProviderUrl(url) { 13 | return function(dispatch, getState) { 14 | const provider = new ReduxWeb3Provider(url, dispatch, getState); 15 | const web3Instance = new Web3(provider); 16 | dispatch(setWeb3Instance(web3Instance)); 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /src/integrations/ethereum/common/redux/web3/helpers/Web3ActionCreator.js: -------------------------------------------------------------------------------- 1 | // const prefix = "WEB3ACTIONCREATOR" 2 | // export const WEB3_REQUEST_STARTED = `${prefix}/WEB3_REQUEST_STARTED` 3 | // export function Web3RequestStarted(name) { 4 | // return { type: WEB3_REQUEST_STARTED, name } 5 | // } 6 | 7 | // export const WEB3_REQUEST_FAILED = `${prefix}/WEB3_REQUEST_FAILED` 8 | // export function Web3RequestFailed(name, error) { 9 | // return { type: WEB3_REQUEST_FAILED, name, error} 10 | // } 11 | 12 | // export const WEB3_REQUEST_SUCCEEDED = `${prefix}/WEB3_REQUEST_SUCCEEDED` 13 | // export function Web3RequestSucceeded(name, result) { 14 | // return { type: WEB3_REQUEST_SUCCEEDED, name, result } 15 | // } 16 | 17 | export async function web3Request(name, args, web3Instance) { 18 | let fn = web3Instance.eth[name]; 19 | 20 | return await fn.apply(web3Instance.eth, args); 21 | } 22 | 23 | export async function web3ActionCreator(dispatch, getState, name, args) { 24 | // This specifically pulls state from the web3 reducer. Smell? 25 | let web3Instance = getState().web3.web3Instance; 26 | // TODO: ETHEREUM 27 | if (web3Instance) { 28 | return await web3Request(name, args, web3Instance); 29 | } 30 | } 31 | 32 | export function web3CleanUpHelper(dispatch, getState) { 33 | let web3Instance = getState().web3.web3Instance; 34 | if (web3Instance) { 35 | let provider = web3Instance.currentProvider; 36 | 37 | // clear out current provider to stop active subscription monitoring 38 | web3Instance.setProvider(null); 39 | 40 | if (provider && provider.connection && provider.connection.close) { 41 | provider.connection.close(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/integrations/ethereum/common/redux/web3/reducers.js: -------------------------------------------------------------------------------- 1 | import * as Web3Action from "./actions"; 2 | 3 | const initialState = { 4 | web3Instance: null, 5 | }; 6 | 7 | export default function(state = initialState, action) { 8 | switch (action.type) { 9 | case Web3Action.SET_WEB3_INSTANCE: 10 | return Object.assign({}, state, { 11 | web3Instance: action.web3Instance, 12 | }); 13 | 14 | default: 15 | return state; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/components/checksum-addresses/ChecksumAddress.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Web3 from "web3"; 3 | 4 | export default class ChecksumAddress extends Component { 5 | toChecksumAddress = address => { 6 | address = address.replace("0x", "").toLowerCase(); 7 | const hash = Web3.utils.sha3(address).replace("0x", ""); 8 | let ret = "0x"; 9 | 10 | for (let i = 0; i < address.length; i++) { 11 | if (parseInt(hash[i], 16) >= 8) { 12 | ret += address[i].toUpperCase(); 13 | } else { 14 | ret += address[i]; 15 | } 16 | } 17 | 18 | return ret; 19 | }; 20 | 21 | render() { 22 | return {this.toChecksumAddress(this.props.address)}; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/components/formatted-ether-value/FormattedEtherValue.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import PropTypes from "prop-types"; 3 | import EtherUnits from "ethereumjs-units"; 4 | 5 | export default class FormattedEtherValue extends Component { 6 | render() { 7 | const weiValueInEth = parseFloat( 8 | EtherUnits.convert( 9 | this.props.value, 10 | this.props.fromUnit, 11 | this.props.toUnit, 12 | ), 13 | ).toFixed(2); 14 | return ( 15 | {`${weiValueInEth} ${this.props.toUnit.toUpperCase()}`} 18 | ); 19 | } 20 | } 21 | 22 | FormattedEtherValue.propTypes = { 23 | value: PropTypes.string, 24 | fromUnit: PropTypes.string, 25 | toUnit: PropTypes.string, 26 | }; 27 | 28 | FormattedEtherValue.defaultProps = { 29 | fromUnit: "wei", 30 | toUnit: "eth", 31 | }; 32 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/init/Events.js: -------------------------------------------------------------------------------- 1 | import { ipcRenderer } from "electron"; 2 | 3 | import { 4 | SET_SUBSCRIBED_TOPICS, 5 | setSubscribedTopics, 6 | } from "../../common/redux/events/actions"; 7 | 8 | export function initEvents(store) { 9 | ipcRenderer.on(SET_SUBSCRIBED_TOPICS, (event, topics) => { 10 | store.dispatch(setSubscribedTopics(topics)); 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/accounts/AccountsScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | import connect from "../../../../../renderer/screens/helpers/connect"; 4 | 5 | import MnemonicAndHdPath from "./MnemonicAndHdPath"; 6 | import AccountList from "./AccountList"; 7 | 8 | class AccountsScreen extends Component { 9 | constructor(props) { 10 | super(props); 11 | 12 | this.state = {}; 13 | } 14 | 15 | render() { 16 | return ( 17 |
18 |
19 |
20 | 24 |
25 | 31 |
32 |
33 | ); 34 | } 35 | } 36 | 37 | export default connect( 38 | AccountsScreen, 39 | "core", 40 | "accounts", 41 | ); 42 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/accounts/AccountsScreen.scss: -------------------------------------------------------------------------------- 1 | .AccountsScreen { 2 | width: 100%; 3 | display: flex; 4 | flex: 1; 5 | flex-direction: column; 6 | position: relative; 7 | 8 | main { 9 | padding: 0; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/accounts/KeyModal.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from "react"; 2 | 3 | import ChecksumAddress from "../../components/checksum-addresses/ChecksumAddress"; 4 | import Modal from "../../../../../renderer/components/modal/Modal"; 5 | 6 | export default class KeyModal extends PureComponent { 7 | render() { 8 | return ( 9 | 10 |
11 |

ACCOUNT INFORMATION

12 |
13 | 14 |
15 |
16 |
ACCOUNT ADDRESS
17 |
18 | 19 |
20 |
21 |
22 |
PRIVATE KEY
23 |
24 | {this.props.privateKey} 25 |
26 |

27 | Do not use this private key on a public blockchain; use it for 28 | development purposes only! 29 |

30 |
31 |
32 | 35 |
36 |
37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/accounts/KeyModal.scss: -------------------------------------------------------------------------------- 1 | .KeyModal { 2 | section, header { 3 | width: 680px; 4 | } 5 | svg { 6 | width: 28px; 7 | height: 28px; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/accounts/MnemonicAndHdPath.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from "react"; 2 | 3 | import MnemonicInfoModal from "./MnemonicInfoModal"; 4 | 5 | import OnlyIf from "../../../../../renderer/components/only-if/OnlyIf"; 6 | 7 | export default class MnemonicAndHdPath extends PureComponent { 8 | constructor(props) { 9 | super(props); 10 | this.state = { 11 | showWarning: false, 12 | }; 13 | } 14 | 15 | showWarning() { 16 | this.setState({ 17 | showWarning: true, 18 | }); 19 | } 20 | 21 | hideWarning() { 22 | this.setState({ 23 | showWarning: false, 24 | }); 25 | } 26 | 27 | render() { 28 | return ( 29 |
30 |
31 |

32 | MNEMONIC{" "} 33 | 37 | ? 38 | 39 |

40 | 41 | {this.props.mnemonic}{" "} 42 | 43 | note: this mnemonic is not secure; don't use it on a public 44 | blockchain. 45 | 46 | 47 |
48 |
49 |

HD PATH

50 | {this.props.hdPath}account_index 51 |
52 | 53 | 54 | 55 |
56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/accounts/MnemonicAndHdPath.scss: -------------------------------------------------------------------------------- 1 | 2 | .MnemonicAndHdPath { 3 | display: flex; 4 | background-color: var(--app-mnemonic-background-color); 5 | font-size: 0.8em; 6 | margin: 0; 7 | border-bottom: 1px solid #aaa; 8 | 9 | .Mnemonic { 10 | flex-grow: 1; 11 | } 12 | 13 | .Mnemonic, .HDPath { 14 | margin: 1rem; 15 | 16 | h4 { 17 | margin: 0 0 .3rem 0; 18 | } 19 | 20 | span { 21 | font-family: "Fira Code Regular", monospace; 22 | color: var(--text-color); 23 | font-size: 0.9rem; 24 | } 25 | 26 | .WarningIndicator { 27 | font-size: 0.8rem; 28 | padding: 0 .125rem; 29 | border: 2px solid transparent; 30 | border-radius: .35rem; 31 | color: white; 32 | background: #DDD; 33 | display: inline-block; 34 | margin-left: .3rem; 35 | font-weight: bold; 36 | cursor: pointer; 37 | 38 | &:hover { 39 | background: #ebb67e; 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/accounts/MnemonicInfoModal.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from "react"; 2 | 3 | import Modal from "../../../../../renderer/components/modal/Modal"; 4 | 5 | export default class MnemonicInfoModal extends PureComponent { 6 | render() { 7 | return ( 8 | 9 |
10 |

About Your Mnemonic

11 |
12 | 13 |
14 |

15 | Your mnemonic is a special secret created for you by Ganache. It's 16 | used to generate the addresses available during development as well 17 | as sign transactions sent from those addresses. 18 |

19 |

20 | You should only use this mnemonic during development. If you use a 21 | wallet application configured with this mnemonic, ensure you switch 22 | to a separate configuration when using that wallet with production 23 | blockchains. 24 |

25 |

26 | This mnemonic is not secure. You should not trust it to manage 27 | blockchain assets. 28 |

29 | 32 |
33 |
34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/accounts/MnemonicInfoModal.scss: -------------------------------------------------------------------------------- 1 | .MnemonicInfoModal { 2 | section, header { 3 | width: 680px; 4 | } 5 | svg { 6 | width: 28px; 7 | height: 28px; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/blocks/BlockCard.scss: -------------------------------------------------------------------------------- 1 | .BlockCard { 2 | font-family: "Fira Code Regular", sans-serif; 3 | background: var(--app-card-default-background); 4 | display: flex; 5 | flex-direction: column; 6 | flex-grow: 1; 7 | margin: 0; 8 | border-radius: 4px; 9 | word-wrap: break-word; 10 | width: 100%; 11 | 12 | header { 13 | width: 100%; 14 | height: 64px; 15 | display: flex; 16 | flex-direction: row; 17 | justify-content: flex-start; 18 | background-color: #fff; 19 | border-bottom: 1px solid #aaa; 20 | line-height: 1; 21 | 22 | h1 { 23 | font-size: 1.5rem; 24 | padding: 1.25rem; 25 | margin: 0; 26 | } 27 | } 28 | 29 | .Button { 30 | text-decoration: none; 31 | text-align: center; 32 | margin: 0; 33 | } 34 | 35 | header button { 36 | border-radius: 0; 37 | line-height: 1; 38 | height: 100%; 39 | } 40 | 41 | .BlockBody { 42 | padding: 1rem 0; 43 | border-bottom: 1px solid #aaa; 44 | 45 | > div { 46 | padding: 1rem; 47 | } 48 | } 49 | 50 | .HeaderSecondaryInfo { 51 | display: flex; 52 | align-items: center; 53 | justify-content: space-between; 54 | width: 100%; 55 | padding: 1rem; 56 | } 57 | 58 | .HasTxs { 59 | border-color: var(--primary-color); 60 | } 61 | 62 | .TransactionList { 63 | .Waiting { 64 | margin-top: 10rem; 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/blocks/BlockList.scss: -------------------------------------------------------------------------------- 1 | .BlockList { 2 | width: 100%; 3 | background: white; 4 | } 5 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/blocks/BlocksScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import connect from "../../../../../renderer/screens/helpers/connect"; 3 | import * as Blocks from "../../../common/redux/blocks/actions"; 4 | import BlockList from "./BlockList"; 5 | import BlockCard from "./BlockCard"; 6 | 7 | class BlockContainer extends Component { 8 | constructor(props) { 9 | super(props); 10 | } 11 | 12 | componentDidMount() { 13 | this.props.dispatch(Blocks.requestPage()); 14 | } 15 | 16 | render() { 17 | var content; 18 | if (this.props.match.params.blockNumber != null) { 19 | content = ; 20 | } else { 21 | content = ; 22 | } 23 | return
{content}
; 24 | } 25 | } 26 | 27 | export default connect( 28 | BlockContainer, 29 | "blocks", 30 | ); 31 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/blocks/BlocksScreen.scss: -------------------------------------------------------------------------------- 1 | .BlocksScreen { 2 | display: flex; 3 | flex-direction: column; 4 | 5 | height: 100%; 6 | width: 100%; 7 | } 8 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/contracts/ContractDetailsScreen.scss: -------------------------------------------------------------------------------- 1 | .ContractDetailsScreen { 2 | 3 | .ContractDetailsBody { 4 | .Title { 5 | border: solid var(--app-card-default-border); 6 | border-width: 1px 0; 7 | h2 { 8 | margin: 18px; 9 | } 10 | } 11 | 12 | .Waiting { 13 | margin: 1rem 0; 14 | } 15 | } 16 | 17 | .DecodeError { 18 | padding: 28px 18px; 19 | background: var(--app-card-default-background); 20 | text-align: center; 21 | 22 | div { 23 | margin-top: 10px; 24 | } 25 | } 26 | 27 | .react-json-view .pretty-json-container { 28 | // TODO-DAVID: there are now a couple of `padding: 28px 18px;` in this file and other places too, maybe 29 | // this should be abstracted out as a global class? 30 | padding: 28px 18px; 31 | background: var(--app-card-default-background); 32 | } 33 | 34 | .TxList > a.Link:last-child > .MiniTxCard{ 35 | // the last transaction in the transactions list shouldn't have a border 36 | // because the next section starts with a border 37 | border-bottom:0; 38 | } 39 | 40 | .ContractInfoBody { 41 | padding: 28px 18px; 42 | background: var(--app-card-default-background); 43 | 44 | .data { 45 | display: grid; 46 | grid-template-columns: 1fr 1fr; 47 | grid-gap: 1rem; 48 | } 49 | 50 | .label { 51 | font-size: 0.75rem; 52 | font-family: "RobotoCondensed-Regular"; 53 | font-weight: 800; 54 | color: var(--app-card-label); 55 | } 56 | 57 | .value { 58 | font-size: .875rem; 59 | font-family: "Fira Code Regular"; 60 | color: var(--app-card-value); 61 | word-break: break-all; 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/contracts/ProjectContracts.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | import connect from "../../../../../renderer/screens/helpers/connect"; 4 | 5 | import ContractCard from "./ContractCard"; 6 | 7 | class ProjectContracts extends Component { 8 | render() { 9 | let content; 10 | if (this.props.contracts.length > 0) { 11 | content = this.props.contracts.map(contract => { 12 | const cache = this.props.contractCache[contract.address]; 13 | return ( 14 | 23 | ); 24 | }); 25 | } else { 26 | content = ( 27 |
28 | {" "} 29 | To see rich contract data compile the contracts 30 | within your Truffle Project. 31 |
32 | ); 33 | } 34 | 35 | return
{content}
; 36 | } 37 | } 38 | 39 | export default connect(ProjectContracts); 40 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/event-details/DecodedEventDetails.scss: -------------------------------------------------------------------------------- 1 | .DecodedEventDetails { 2 | margin: 0; 3 | 4 | .DataRow { 5 | display: flex; 6 | justify-content: space-between; 7 | } 8 | 9 | .DataSection { 10 | background: var(--app-card-default-background); 11 | border-bottom: 1px solid aaa; 12 | } 13 | 14 | .SectionHeading { 15 | background: #fff; 16 | padding: 1rem; 17 | border-bottom: 1px solid #aaa; 18 | 19 | h1 { 20 | font-size: 1.25rem; 21 | margin: 0; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/event-details/EncodedEventDetails.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Moment from "react-moment"; 3 | import { Link } from "react-router-dom"; 4 | 5 | const EncodedEventDetails = ({ event }) => { 6 | const { transactionHash, timestamp, contractAddress, logIndex } = event; 7 | return ( 8 |
9 |
10 | {" "} 11 | To see rich event data{" "} 12 | 13 | link a Truffle Project 14 | {" "} 15 | containing the contract that emits this event. 16 |
17 |
18 |
19 |
TX HASH
20 |
{transactionHash}
21 |
22 |
23 |
LOG INDEX
24 |
{logIndex}
25 |
26 |
27 |
BLOCK TIME
28 |
29 | 30 | {timestamp} 31 | 32 |
33 |
34 |
35 |
36 |
37 |
CONTRACT
38 |
{contractAddress}
39 |
40 |
41 |
42 | ); 43 | }; 44 | 45 | export default EncodedEventDetails; 46 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/event-details/EncodedEventDetails.scss: -------------------------------------------------------------------------------- 1 | .EncodedEventDetails { 2 | background: var(--app-card-default-background); 3 | margin: 0; 4 | padding: 1rem; 5 | border-bottom: 1px solid #aaa; 6 | 7 | .Notice { 8 | background: #ddd; 9 | padding: 1rem; 10 | border-radius: 6px; 11 | margin-bottom: 1rem; 12 | text-align: center; 13 | 14 | .settingsLink { 15 | color: #df7b0e; 16 | } 17 | } 18 | 19 | .Warning { 20 | font-size: 24px; 21 | vertical-align: middle; 22 | } 23 | 24 | .DataRow { 25 | display: flex; 26 | justify-content: space-between; 27 | margin-bottom: 1rem; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/event-details/EventDetailsScreen.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/src/integrations/ethereum/renderer/screens/event-details/EventDetailsScreen.scss -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/events/EventItem.scss: -------------------------------------------------------------------------------- 1 | .EventItem { 2 | font-family: "Fira Code Regular", monospaced; 3 | width: 100%; 4 | cursor: pointer; 5 | margin: .5rem 0; 6 | background: var(--app-card-default-background); 7 | padding: 28px 18px; 8 | word-wrap: break-word; 9 | cursor: pointer; 10 | display: flex; 11 | flex-direction: column; 12 | margin: 0; 13 | transition: all .3s; 14 | border-bottom: 1px solid #aaa; 15 | 16 | .Row { 17 | // display: flex; 18 | // align-items: center; 19 | // flex-direction: row; 20 | margin-bottom: 1rem; 21 | // justify-content: space-between; 22 | display: grid; 23 | grid-template-columns: 1fr 1fr 120px 200px; 24 | 25 | &.Top { 26 | display: flex; 27 | justify-content: space-between; 28 | } 29 | 30 | &:last-of-type { 31 | margin-bottom: 0; 32 | } 33 | } 34 | 35 | .RowItem { 36 | display: flex; 37 | flex-direction: row; 38 | justify-content: space-between; 39 | cursor: pointer; 40 | margin-right: 2rem; 41 | } 42 | 43 | .Name { 44 | .Value { 45 | font-weight: bold; 46 | } 47 | } 48 | 49 | .SecondaryItems { 50 | .Value { 51 | font-size: .825rem; 52 | } 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/events/EventList.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import MDSpinner from "react-md-spinner"; 3 | 4 | import connect from "../../../../../renderer/screens/helpers/connect"; 5 | 6 | import EventItem from "./EventItem"; 7 | 8 | class EventList extends Component { 9 | render() { 10 | if (this.props.eventList.length === 0) { 11 | if (this.props.loading) { 12 | return ( 13 |
14 |
15 | 22 |
23 |
24 | ); 25 | } else { 26 | return ( 27 |
28 |
No events
29 |
30 | ); 31 | } 32 | } 33 | 34 | return ( 35 |
36 | {this.props.eventList.map((event, index) => ( 37 | 38 | ))} 39 |
40 | ); 41 | } 42 | } 43 | 44 | export default connect(EventList); 45 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/events/EventList.scss: -------------------------------------------------------------------------------- 1 | .EventList { 2 | width: 100%; 3 | height: 100%; 4 | background: white; 5 | 6 | .EventItem { 7 | font-family: "Fira Code Regular", monospaced; 8 | width: 100%; 9 | cursor: pointer; 10 | margin: 0; 11 | background: var(--app-card-default-background); 12 | padding: 28px 18px; 13 | word-wrap: break-word; 14 | transition: all 0.3s; 15 | border-bottom: 1px solid #aaa; 16 | 17 | .name { 18 | margin-bottom: 1rem; 19 | 20 | > .label { 21 | font-size: 1.125rem; 22 | } 23 | } 24 | 25 | .data { 26 | // display: flex; 27 | // justify-content: space-between; 28 | display: grid; 29 | grid-template-columns: 1fr 1fr 120px 240px; 30 | } 31 | 32 | .blockTime { 33 | margin-left: auto; 34 | } 35 | 36 | .label { 37 | font-size: 0.75rem; 38 | font-family: "RobotoCondensed-Regular"; 39 | font-weight: 800; 40 | color: var(--app-card-label); 41 | margin-bottom: 0.25rem; 42 | } 43 | 44 | .value { 45 | font-size: 0.875rem; 46 | font-family: "Fira Code Regular"; 47 | color: var(--app-card-value); 48 | word-break: break-all; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/events/EventsScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import connect from "../../../../../renderer/screens/helpers/connect"; 3 | import EventList from "./EventList"; 4 | 5 | import * as Events from "../../../common/redux/events/actions"; 6 | 7 | class EventsScreen extends Component { 8 | componentDidMount() { 9 | this.props.dispatch(Events.requestPage()); 10 | } 11 | 12 | componentWillUnmount() { 13 | this.props.dispatch(Events.clearEventsInView()); 14 | } 15 | 16 | render() { 17 | return ( 18 |
19 | 23 |
24 | ); 25 | } 26 | } 27 | 28 | export default connect( 29 | EventsScreen, 30 | "core", 31 | "appshell", 32 | "events", 33 | ); 34 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/events/EventsScreen.scss: -------------------------------------------------------------------------------- 1 | .EventsScreen { 2 | height: 100%; 3 | width: 100%; 4 | 5 | > main { 6 | padding: 0; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/events/RecentEvents.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | import connect from "../helpers/connect"; 4 | 5 | import EventList from "./EventList"; 6 | 7 | import * as Events from "../../../common/redux/events/actions"; 8 | 9 | class RecentEvents extends Component { 10 | componentDidUpdate(prevProps) { 11 | // If the scroll position changed... 12 | if ( 13 | this.props.appshell.scrollPosition != prevProps.appshell.scrollPosition 14 | ) { 15 | if (this.props.appshell.scrollPosition == "top") { 16 | this.props.dispatch(Events.requestPreviousPage()); 17 | } else if (this.props.appshell.scrollPosition == "bottom") { 18 | this.props.dispatch(Events.requestNextPage()); 19 | } 20 | return; 21 | } 22 | 23 | // No change in scroll position? 24 | const blocksRequested = Object.keys(this.props.events.blocksRequested); 25 | const latestBlockRequested = Math.max.apply( 26 | Math, 27 | blocksRequested.concat(-1), 28 | ); 29 | if ( 30 | this.props.appshell.scrollPosition == "top" && 31 | this.props.core.latestBlock > latestBlockRequested 32 | ) { 33 | this.props.dispatch(Events.requestPreviousPage()); 34 | } 35 | } 36 | 37 | render() { 38 | return ( 39 |
40 | 41 |
42 | ); 43 | } 44 | } 45 | 46 | export default connect( 47 | RecentEvents, 48 | "appshell", 49 | "core", 50 | "events", 51 | ); 52 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/transactions/DestinationAddress.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | export default class DestinationAddress extends Component { 4 | render() { 5 | const isContractCall = 6 | (this.props.receipt.hasOwnProperty("contractAddress") && 7 | this.props.receipt.contractAddress !== null) || 8 | (this.props.tx.to && this.props.tx.input !== "0x"); 9 | 10 | const isContractCreationCall = 11 | this.props.receipt.hasOwnProperty("contractAddress") && 12 | this.props.receipt.contractAddress !== null; 13 | 14 | return ( 15 |
16 |
17 | {isContractCreationCall 18 | ? "CREATED CONTRACT ADDRESS" 19 | : isContractCall 20 | ? `TO CONTRACT ADDRESS` 21 | : `TO ADDRESS`} 22 |
23 |
24 | {isContractCreationCall ? ( 25 |
26 | {this.props.receipt.contractAddress} 27 |
28 | ) : ( 29 |
{this.props.contractName || this.props.tx.to}
30 | )} 31 |
32 |
33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/transactions/MiniTxCard.scss: -------------------------------------------------------------------------------- 1 | .MiniTxCard { 2 | font-family: "Fira Code Regular", monospaced; 3 | width: 100%; 4 | cursor: pointer; 5 | margin: .5rem 0; 6 | background: var(--app-card-default-background); 7 | padding: 28px 18px; 8 | word-wrap: break-word; 9 | cursor: pointer; 10 | display: flex; 11 | flex-direction: column; 12 | margin: 0; 13 | transition: all .3s; 14 | border-bottom: 1px solid #aaa; 15 | 16 | .Row { 17 | // display: flex; 18 | // align-items: center; 19 | // flex-direction: row; 20 | margin-bottom: 1rem; 21 | // justify-content: space-between; 22 | display: grid; 23 | grid-template-columns: 1fr 1fr 120px 200px; 24 | 25 | &.Top { 26 | display: flex; 27 | justify-content: space-between; 28 | } 29 | 30 | &:last-of-type { 31 | margin-bottom: 0; 32 | } 33 | } 34 | 35 | .RowItem { 36 | display: flex; 37 | flex-direction: row; 38 | justify-content: space-between; 39 | cursor: pointer; 40 | } 41 | 42 | .Nonce, 43 | .GasUsed, 44 | .Value, 45 | .MinedOn { 46 | .Label { 47 | font-size: .625rem; 48 | letter-spacing: 1.2px; 49 | } 50 | 51 | .Value { 52 | font-size: .825rem; 53 | } 54 | } 55 | 56 | .TxHash { 57 | .Value { 58 | font-weight: bold; 59 | } 60 | } 61 | 62 | .SecondaryItems { 63 | .Value { 64 | font-size: .825rem; 65 | } 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/transactions/RecentTransactions.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | import connect from "../../../../../renderer/screens/helpers/connect"; 4 | 5 | import * as Transactions from "../../../common/redux/transactions/actions"; 6 | 7 | import TxList from "./TxList"; 8 | 9 | class RecentTransactions extends Component { 10 | componentDidUpdate(prevProps) { 11 | // If the scroll position changed... 12 | if ( 13 | prevProps.appshell.scrollPosition != this.props.appshell.scrollPosition 14 | ) { 15 | if (prevProps.appshell.scrollPosition == "top") { 16 | this.props.dispatch(Transactions.requestPreviousPage()); 17 | } else if (prevProps.appshell.scrollPosition == "bottom") { 18 | this.props.dispatch(Transactions.requestNextPage()); 19 | } 20 | return; 21 | } 22 | 23 | // No change in scroll position? 24 | var blocksRequested = Object.keys(prevProps.transactions.blocksRequested); 25 | var latestBlockRequested = Math.max.apply(Math, blocksRequested.concat(-1)); 26 | if ( 27 | prevProps.appshell.scrollPosition == "top" && 28 | prevProps.core.latestBlock > latestBlockRequested 29 | ) { 30 | this.props.dispatch(Transactions.requestPreviousPage()); 31 | } 32 | } 33 | 34 | render() { 35 | return ( 36 |
37 | 42 |
43 | ); 44 | } 45 | } 46 | 47 | export default connect( 48 | RecentTransactions, 49 | "transactions", 50 | "core", 51 | "appshell", 52 | ); 53 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/transactions/RecentTransactions.scss: -------------------------------------------------------------------------------- 1 | .RecentTransactions { 2 | flex-grow: 1; 3 | display: flex; 4 | height: 100%; 5 | } -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/transactions/TransactionTypeBadge.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | export default class TransactionTypeBadge extends Component { 4 | render() { 5 | if ( 6 | this.props.receipt.hasOwnProperty("contractAddress") && 7 | this.props.receipt.contractAddress !== null 8 | ) { 9 | return ( 10 |
11 | CONTRACT CREATION 12 |
13 | ); 14 | } 15 | 16 | if (this.props.tx.to && this.props.tx.input !== "0x") { 17 | return ( 18 |
19 | CONTRACT CALL 20 |
21 | ); 22 | } 23 | 24 | if (this.props.tx.to && this.props.tx.value > 0) { 25 | return ( 26 |
27 | VALUE TRANSFER 28 |
29 | ); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/transactions/TransactionTypeBadge.scss: -------------------------------------------------------------------------------- 1 | .TransactionTypeBadge { 2 | font-size: .75rem; 3 | padding: 8px; 4 | height: 28px; 5 | border-radius: 25px; 6 | 7 | &.ContractCallBadge { 8 | background-color: var(--complimentary1); 9 | color: white; 10 | } 11 | 12 | &.ContractCreationBadge { 13 | background-color: darken(rgba(222, 142, 149, 1.000), 10%); 14 | color: white; 15 | } 16 | 17 | &.ValueTransferBadge { 18 | background-color: var(--truffle-green); 19 | color: white; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/transactions/TransactionsScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import connect from "../../../../../renderer/screens/helpers/connect"; 3 | import * as Transactions from "../../../common/redux/transactions/actions"; 4 | import RecentTransactions from "./RecentTransactions"; 5 | import TxCard from "./TxCard"; 6 | 7 | class TransactionsScreen extends Component { 8 | componentDidMount() { 9 | this.props.dispatch(Transactions.requestPage()); 10 | } 11 | 12 | componentWillUnmount() { 13 | this.props.dispatch(Transactions.clearTransactionsInView()); 14 | } 15 | 16 | render() { 17 | var content; 18 | if (this.props.match.params.transactionHash != null) { 19 | content = ; 20 | } else { 21 | content = ( 22 | 23 | ); 24 | } 25 | return
{content}
; 26 | } 27 | } 28 | 29 | export default connect(TransactionsScreen); 30 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/transactions/TransactionsScreen.scss: -------------------------------------------------------------------------------- 1 | .TransactionsScreen { 2 | display: flex; 3 | flex-direction: column; 4 | 5 | height: 100%; 6 | width: 100%; 7 | 8 | > main { 9 | padding: 0; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/integrations/ethereum/renderer/screens/transactions/TxList.scss: -------------------------------------------------------------------------------- 1 | .TxList { 2 | width: 100%; 3 | height: 100%; 4 | background: white; 5 | display: flex; 6 | flex-direction: column; 7 | flex-grow: 1; 8 | } 9 | -------------------------------------------------------------------------------- /src/integrations/filecoin/common/redux/accounts/reducers.js: -------------------------------------------------------------------------------- 1 | import * as Accounts from "./actions"; 2 | 3 | const initialState = { 4 | addresses: [], 5 | balances: {}, 6 | nonces: {}, 7 | }; 8 | 9 | export default function(state = initialState, action) { 10 | let balances; 11 | let nonces; 12 | switch (action.type) { 13 | case Accounts.GET_ACCOUNTS: 14 | var addresses = action.addresses; 15 | balances = Object.assign({}, state.balances); 16 | nonces = Object.assign({}, state.nonces); 17 | 18 | // Set default balance to zero if this is a new account 19 | addresses.forEach(address => { 20 | if (!balances[address]) balances[address] = "0"; 21 | if (!nonces[address]) nonces[address] = 0; 22 | }); 23 | 24 | return Object.assign({}, state, { 25 | addresses, 26 | balances, 27 | nonces, 28 | }); 29 | 30 | case Accounts.GET_ACCOUNT_BALANCE: 31 | balances = Object.assign({}, state.balances, { 32 | [action.address]: action.balance, 33 | }); 34 | return Object.assign({}, state, { 35 | balances, 36 | }); 37 | 38 | case Accounts.GET_ACCOUNT_NONCE: 39 | nonces = Object.assign({}, state.nonces, { 40 | [action.address]: action.nonce, 41 | }); 42 | return Object.assign({}, state, { 43 | nonces, 44 | }); 45 | 46 | default: 47 | return state; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/integrations/filecoin/common/redux/core/reducers.js: -------------------------------------------------------------------------------- 1 | const Core = require("./actions"); 2 | 3 | const initialState = { 4 | flavor: "filecoin", 5 | isMining: true, 6 | seed: "", 7 | ipfsUrl: "", 8 | options: {}, 9 | privateKeys: {}, 10 | latestTipset: 0, // Tipset the current chain is on 11 | minerEnabled: null, 12 | latestDeal: 0, 13 | StorageDealStatus: {} 14 | }; 15 | 16 | export default function(state = initialState, action) { 17 | switch (action.type) { 18 | case Core.SET_KEY_DATA: 19 | return Object.assign({}, state, { 20 | seed: action.seed, 21 | privateKeys: action.privateKeys, 22 | }); 23 | 24 | case Core.SET_IPFS_URL: 25 | return Object.assign({}, state, { 26 | ipfsUrl: action.url 27 | }); 28 | 29 | case Core.SET_CURRENT_OPTIONS: 30 | return Object.assign({}, state, { 31 | options: action.options 32 | }); 33 | 34 | case Core.SET_TIPSET_NUMBER: 35 | return Object.assign({}, state, { 36 | latestTipset: action.number, 37 | }); 38 | 39 | case Core.SET_MINER_ENABLED: 40 | return Object.assign({}, state, { 41 | minerEnabled: action.minerEnabled 42 | }); 43 | 44 | case Core.SET_LATEST_DEAL_ID: 45 | return Object.assign({}, state, { 46 | latestDeal: action.id 47 | }); 48 | 49 | case Core.SET_STORAGE_DEAL_STATUS_ENUM: 50 | return Object.assign({}, state, { 51 | StorageDealStatus: action.StorageDealStatus 52 | }); 53 | 54 | default: 55 | return state; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/integrations/filecoin/common/redux/lotus/actions.js: -------------------------------------------------------------------------------- 1 | import ReduxLotusProvider from "./helpers/ReduxLotusProvider"; 2 | import { LotusRPC } from "@filecoin-shipyard/lotus-client-rpc"; 3 | import FilecoinPrefix from "../prefix"; 4 | 5 | const prefix = `${FilecoinPrefix}/LOTUS`; 6 | 7 | export const SET_LOTUS_INSTANCE = `${prefix}/SET_LOTUS_INSTANCE`; 8 | export function setLotusInstance(lotusInstance) { 9 | return { type: SET_LOTUS_INSTANCE, lotusInstance }; 10 | } 11 | 12 | export const SET_LOTUS_SCHEMA = `${prefix}/SET_LOTUS_SCHEMA`; 13 | export function setLotusSchema(schema) { 14 | return { type: SET_LOTUS_SCHEMA, schema }; 15 | } 16 | 17 | export async function createLotusInstance(dispatch, getState, url) { 18 | const reduxProvider = new ReduxLotusProvider(url, dispatch, getState); 19 | await reduxProvider.initialize(); 20 | const schema = getState().filecoin.lotus.schema; 21 | const lotusInstance = new LotusRPC(reduxProvider, { schema }); 22 | return lotusInstance; 23 | } 24 | -------------------------------------------------------------------------------- /src/integrations/filecoin/common/redux/lotus/helpers/LotusActionCreator.js: -------------------------------------------------------------------------------- 1 | export async function lotusRequest(name, args, lotusInstance) { 2 | const fn = lotusInstance[name]; 3 | 4 | return await fn.apply(lotusInstance, args); 5 | } 6 | 7 | export async function lotusActionCreator(dispatch, getState, name, args) { 8 | // This specifically pulls state from the web3 reducer. Smell? 9 | const lotusInstance = getState().filecoin.lotus.lotusInstance; 10 | if (lotusInstance) { 11 | return await lotusRequest(name, args, lotusInstance); 12 | } else { 13 | return null; 14 | } 15 | } 16 | 17 | export function lotusCleanUpHelper(dispatch, getState) { 18 | const lotusInstance = getState().filecoin.lotus.lotusInstance; 19 | if (lotusInstance && lotusInstance.provider) { 20 | lotusInstance.provider.destroy(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/integrations/filecoin/common/redux/lotus/reducers.js: -------------------------------------------------------------------------------- 1 | import * as LotusAction from "./actions"; 2 | 3 | const initialState = { 4 | lotusInstance: null, 5 | schema: {} 6 | }; 7 | 8 | export default function(state = initialState, action) { 9 | switch (action.type) { 10 | case LotusAction.SET_LOTUS_INSTANCE: 11 | return Object.assign({}, state, { 12 | lotusInstance: action.lotusInstance, 13 | }); 14 | 15 | case LotusAction.SET_LOTUS_SCHEMA: 16 | return Object.assign({}, state, { 17 | schema: action.schema, 18 | }); 19 | 20 | default: 21 | return state; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/integrations/filecoin/common/redux/prefix.js: -------------------------------------------------------------------------------- 1 | export default "FILECOIN"; 2 | -------------------------------------------------------------------------------- /src/integrations/filecoin/common/redux/reducer.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from "redux"; 2 | 3 | import AccountsReducer from "./accounts/reducers"; 4 | import CoreReducer from "./core/reducers"; 5 | import LotusReducer from "./lotus/reducers"; 6 | import RequestCacheReducer from "./request-cache/reducers"; 7 | import TipsetsReducer from "./tipsets/reducers"; 8 | import MessagesReducer from "./messages/reducers"; 9 | import DealsReducer from "./deals/reducers"; 10 | import FilesReducer from "./files/reducers"; 11 | 12 | export default () => { 13 | const appReducer = combineReducers({ 14 | accounts: AccountsReducer, 15 | core: CoreReducer, 16 | lotus: LotusReducer, 17 | requestCache: RequestCacheReducer, 18 | tipsets: TipsetsReducer, 19 | messages: MessagesReducer, 20 | deals: DealsReducer, 21 | files: FilesReducer, 22 | }); 23 | 24 | return appReducer; 25 | }; 26 | -------------------------------------------------------------------------------- /src/integrations/filecoin/common/redux/request-cache/actions.js: -------------------------------------------------------------------------------- 1 | import FilecoinPrefix from "../prefix"; 2 | 3 | const prefix = `${FilecoinPrefix}/CACHE`; 4 | 5 | const createCacheId = function(payload) { 6 | let params = payload.params 7 | .map(param => { 8 | return param.toString(); 9 | }) 10 | .join(","); 11 | return `${payload.method}(${params})`; 12 | }; 13 | 14 | export const CACHE_REQUEST = `${prefix}/CACHE_REQUEST`; 15 | export const cacheRequest = function(payload, response) { 16 | let id = createCacheId(payload); 17 | return { type: CACHE_REQUEST, id, response }; 18 | }; 19 | 20 | export const checkCache = function(payload, getState) { 21 | // Never cache "Filecoin.ChainHead" 22 | if (payload.method == "Filecoin.ChainHead") { 23 | return null; 24 | } 25 | 26 | let cache = getState().requestCache; 27 | let id = createCacheId(payload); 28 | let hit = cache[id]; 29 | 30 | if (!hit) { 31 | return null; 32 | } 33 | 34 | // Tailor the response to the new rpc id 35 | var response = Object.assign({}, hit); 36 | response.id = payload.id; 37 | return response; 38 | }; 39 | -------------------------------------------------------------------------------- /src/integrations/filecoin/common/redux/request-cache/reducers.js: -------------------------------------------------------------------------------- 1 | import * as RequestCache from "./actions"; 2 | import * as Core from "../core/actions"; 3 | 4 | const initialState = {}; 5 | 6 | // These requests are never invalidated. 7 | const HARD_CACHED = { 8 | version: true, 9 | chainGetGenesis: true, 10 | actorAddress: true, 11 | stateListMiners: true, 12 | stateMinerPower: true, 13 | // stateMinerInfo: true, // TODO 14 | walletValidateAddress: true 15 | }; 16 | 17 | const isHardCached = function(id) { 18 | const method = id.substring(0, id.indexOf("(")); 19 | 20 | const hardCachedResult = HARD_CACHED[method]; 21 | 22 | if (typeof hardCachedResult == "function") { 23 | const split = id.split(/\(|\)/); 24 | let params = split[1]; 25 | 26 | if (params == "") { 27 | params = []; 28 | } else { 29 | params = params.split(","); 30 | } 31 | 32 | return hardCachedResult.apply(null, params); 33 | } else { 34 | return !!hardCachedResult; 35 | } 36 | }; 37 | 38 | export default function(state = initialState, action) { 39 | switch (action.type) { 40 | case RequestCache.CACHE_REQUEST: 41 | return Object.assign({}, state, { 42 | [action.id]: action.response, 43 | }); 44 | 45 | case Core.SET_TIPSET_NUMBER: { 46 | // Invalidate the cache on new block number 47 | const cache = Object.assign({}, state); 48 | 49 | Object.keys(cache).forEach(id => { 50 | if (!isHardCached(id)) { 51 | delete cache[id]; 52 | } 53 | }); 54 | 55 | return cache; 56 | } 57 | default: 58 | return state; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/integrations/filecoin/common/utils/cid.js: -------------------------------------------------------------------------------- 1 | export function abbreviateCid(cid, numChars) { 2 | return `${cid.slice(0, numChars)}…${cid.slice(cid.length - numChars)}`; 3 | } 4 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/components/formatted-fil-value/FormattedFILValue.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | export default class FormattedFILValue extends Component { 5 | render() { 6 | let attoFil; 7 | if (this.props.fromUnit === "fil") { 8 | // Using BigInt with the current versions of eslint 9 | // and babel-eslint require larger upgrades to both. 10 | // I chose not to do this currently as it may not be worth 11 | // the effort, especially if y'all are thinking of doing 12 | // a refactor 13 | // eslint-disable-next-line no-undef 14 | attoFil = BigInt(this.props.value) * 1000000000000000000n 15 | } else { 16 | // eslint-disable-next-line no-undef 17 | attoFil = BigInt(this.props.value); 18 | } 19 | 20 | let resultFil; 21 | if (this.props.toUnit === "fil") { 22 | resultFil = Number(attoFil * 100n / 1000000000000000000n) / 100; 23 | } else { 24 | resultFil = attoFil; 25 | } 26 | const value = resultFil.toFixed(2); 27 | return ( 28 | {`${value} ${this.props.toUnit.toUpperCase()}`} 31 | ); 32 | } 33 | } 34 | 35 | FormattedFILValue.propTypes = { 36 | value: PropTypes.string, 37 | fromUnit: PropTypes.string, 38 | toUnit: PropTypes.string, 39 | }; 40 | 41 | FormattedFILValue.defaultProps = { 42 | fromUnit: "attofil", 43 | toUnit: "fil", 44 | }; 45 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/accounts/AccountList.scss: -------------------------------------------------------------------------------- 1 | .FilecoinAccountList { 2 | width: 100%; 3 | padding: 0; 4 | 5 | position: relative; 6 | 7 | .AccountCard { 8 | display: flex; 9 | flex-direction: row; 10 | align-items: center; 11 | justify-content: space-between; 12 | padding: 1.3rem .8rem; 13 | background-color: var(--app-card-default-background); 14 | position: relative; 15 | border-bottom: 1px solid #aaa; 16 | 17 | svg { 18 | stroke-width: 4; 19 | width: 24px; 20 | height: 24px; 21 | } 22 | } 23 | 24 | .AccountAddress { 25 | font-weight: bold; 26 | margin-right: 2rem; 27 | 28 | .Value { 29 | font-size: 1.222222222rem; 30 | } 31 | } 32 | 33 | .AccountBalance { 34 | width: 20%; 35 | .Value { 36 | font-weight: bold; 37 | font-size: 1.222222222rem; 38 | } 39 | } 40 | 41 | .AddressAndBalance { 42 | display: flex; 43 | flex-direction: row; 44 | } 45 | 46 | .AccountPrivateKey { 47 | .Label, 48 | .Value { 49 | font-size: 14px; 50 | } 51 | } 52 | 53 | .AccountState { 54 | font-size: 2rem; 55 | background: #ffffff; 56 | border-radius: 50%; 57 | width: 3rem; 58 | height: 3rem; 59 | border: 2px solid gray; 60 | display: flex; 61 | align-items: center; 62 | justify-content: center; 63 | margin-left: 1rem; 64 | } 65 | 66 | .SecondaryInfo { 67 | display: flex; 68 | justify-content: flex-end; 69 | align-items: center; 70 | width: 20%; 71 | 72 | > div { 73 | width: 30%; 74 | margin-right: 0.5rem; 75 | } 76 | } 77 | 78 | .ShowKeys { 79 | cursor: pointer; 80 | margin-right: 0.7rem; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/accounts/AccountsBanner.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from "react"; 2 | 3 | import SeedInfoModal from "./SeedInfoModal"; 4 | 5 | import OnlyIf from "../../../../../renderer/components/only-if/OnlyIf"; 6 | 7 | export default class AccountsBanner extends PureComponent { 8 | constructor(props) { 9 | super(props); 10 | this.state = { 11 | showWarning: false, 12 | }; 13 | } 14 | 15 | showWarning() { 16 | this.setState({ 17 | showWarning: true, 18 | }); 19 | } 20 | 21 | hideWarning() { 22 | this.setState({ 23 | showWarning: false, 24 | }); 25 | } 26 | 27 | render() { 28 | return ( 29 |
30 |
31 |

32 | SEED{" "} 33 | 37 | ? 38 | 39 |

40 | {this.props.seed} 41 |
42 | 43 | 44 | 45 |
46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/accounts/AccountsBanner.scss: -------------------------------------------------------------------------------- 1 | 2 | .FilecoinAccountsBanner { 3 | display: flex; 4 | background-color: var(--app-mnemonic-background-color); 5 | font-size: 0.8em; 6 | margin: 0; 7 | border-bottom: 1px solid #aaa; 8 | 9 | .Seed { 10 | flex-grow: 1; 11 | } 12 | 13 | .Seed { 14 | margin: 1rem; 15 | 16 | h4 { 17 | margin: 0 0 .3rem 0; 18 | } 19 | 20 | span { 21 | font-family: "Fira Code Regular", monospace; 22 | color: var(--text-color); 23 | font-size: 0.9rem; 24 | } 25 | 26 | .WarningIndicator { 27 | font-size: 0.8rem; 28 | padding: 0 .125rem; 29 | border: 2px solid transparent; 30 | border-radius: .35rem; 31 | color: white; 32 | background: #DDD; 33 | display: inline-block; 34 | margin-left: .3rem; 35 | font-weight: bold; 36 | cursor: pointer; 37 | 38 | &:hover { 39 | background: #ebb67e; 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/accounts/AccountsScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | import connect from "../../../../../renderer/screens/helpers/connect"; 4 | 5 | import AccountsBanner from "./AccountsBanner"; 6 | import AccountList from "./AccountList"; 7 | 8 | class AccountsScreen extends Component { 9 | constructor(props) { 10 | super(props); 11 | 12 | this.state = {}; 13 | } 14 | 15 | render() { 16 | return ( 17 |
18 |
19 |
20 | 23 |
24 | 30 |
31 |
32 | ); 33 | } 34 | } 35 | 36 | export default connect( 37 | AccountsScreen, 38 | ["filecoin.core", "core"], 39 | ["filecoin.accounts", "accounts"] 40 | ); 41 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/accounts/AccountsScreen.scss: -------------------------------------------------------------------------------- 1 | .FilecoinAccountsScreen { 2 | width: 100%; 3 | display: flex; 4 | flex: 1; 5 | flex-direction: column; 6 | position: relative; 7 | 8 | main { 9 | padding: 0; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/accounts/KeyModal.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from "react"; 2 | 3 | import Modal from "../../../../../renderer/components/modal/Modal"; 4 | 5 | export default class KeyModal extends PureComponent { 6 | render() { 7 | return ( 8 | 9 |
10 |

ACCOUNT INFORMATION

11 |
12 | 13 |
14 |
15 |
ACCOUNT ADDRESS
16 |
17 | {this.props.accountAddress} 18 |
19 |
20 |
21 |
PRIVATE KEY
22 |
23 | {this.props.privateKey} 24 |
25 |

26 | Do not use this private key on a public blockchain; use it for 27 | development purposes only! 28 |

29 |
30 |
31 |
32 | 33 |
34 |
35 |
36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/accounts/KeyModal.scss: -------------------------------------------------------------------------------- 1 | .FilecoinKeyModal { 2 | section, header { 3 | width: 680px; 4 | } 5 | svg { 6 | width: 28px; 7 | height: 28px; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/accounts/SeedInfoModal.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from "react"; 2 | 3 | import Modal from "../../../../../renderer/components/modal/Modal"; 4 | 5 | export default class SeedInfoModal extends PureComponent { 6 | render() { 7 | return ( 8 | 9 |
10 |

About Your Seed

11 |
12 | 13 |
14 |

15 | Your seed is used by Ganache to initiate the random number generator. 16 | The random number generator is used to generate the addresses available 17 | during development as well as any other randomness. 18 |

19 |

20 | Using the same seed will generate the same accounts and be as close to 21 | deterministic behavior as possible. 22 |

23 |

24 | Ganache currently doesn't generate a mnemonic for Filecoin addresses. 25 |

26 |
27 | 28 |
29 |
30 |
31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/accounts/SeedInfoModal.scss: -------------------------------------------------------------------------------- 1 | .FilecoinSeedInfoModal { 2 | section, header { 3 | width: 680px; 4 | } 5 | svg { 6 | width: 28px; 7 | height: 28px; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/config/ConfigScreens/WorkspaceScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | class WorkspaceScreen extends Component { 4 | state = { selectedIdx: null }; 5 | 6 | validateChange = e => { 7 | this.props.validateChange(e, {}); 8 | }; 9 | 10 | render() { 11 | const { name } = this.props.config.settings.workspace; 12 | return ( 13 |
14 |

WORKSPACE

15 |
16 |

WORKSPACE NAME

17 |
18 |
19 | 26 |
27 |
28 |

A friendly name for this workspace.

29 |
30 |
31 |
32 |
33 | ); 34 | } 35 | } 36 | 37 | export default WorkspaceScreen; 38 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/deals/DealList.scss: -------------------------------------------------------------------------------- 1 | .DealList { 2 | width: 100%; 3 | height: 100%; 4 | background: white; 5 | } 6 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/deals/DealStatusBadge.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import connect from "../../../../../renderer/screens/helpers/connect"; 3 | 4 | class DealStatusBadge extends Component { 5 | render() { 6 | return ( 7 |
8 | {this.props.core.StorageDealStatus[this.props.status] || "UNKNOWN"} 9 |
10 | ); 11 | } 12 | } 13 | 14 | export default connect( 15 | DealStatusBadge, 16 | ["filecoin.core", "core"] 17 | ); 18 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/deals/DealStatusBadge.scss: -------------------------------------------------------------------------------- 1 | .DealStatusBadge { 2 | font-size: .75rem; 3 | padding: 8px; 4 | height: 28px; 5 | border-radius: 25px; 6 | font-weight: bold; 7 | background-color: var(--complimentary1); 8 | color: white; 9 | } 10 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/deals/DealsScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import connect from "../../../../../renderer/screens/helpers/connect"; 3 | import * as Deals from "../../../common/redux/deals/actions"; 4 | import DealList from "./DealList"; 5 | 6 | class DealsContainer extends Component { 7 | constructor(props) { 8 | super(props); 9 | } 10 | 11 | componentDidMount() { 12 | this.props.dispatch(Deals.requestPage()); 13 | } 14 | 15 | render() { 16 | return ( 17 |
18 | 19 |
20 | ); 21 | } 22 | } 23 | 24 | export default connect( 25 | DealsContainer, 26 | ["filecoin.deals", "deals"] 27 | ); 28 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/deals/DealsScreen.scss: -------------------------------------------------------------------------------- 1 | .DealsScreen { 2 | display: flex; 3 | flex-direction: column; 4 | 5 | height: 100%; 6 | width: 100%; 7 | } 8 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/deals/MiniDealCard.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from "react"; 2 | import Moment from "react-moment"; 3 | 4 | import DealStatusBadge from "./DealStatusBadge"; 5 | 6 | export default class MiniDealCard extends PureComponent { 7 | render() { 8 | const { deal } = this.props; 9 | const cardStyles = `MiniDealCard`; 10 | 11 | return ( 12 |
13 |
14 |
15 |
DEAL
16 |
{deal.DealID}
17 |
18 |
19 |
20 |
21 |
22 |
DEAL CID
23 |
{deal.ProposalCid["/"]}
24 |
25 |
26 |
27 |
28 |
CREATED
29 |
30 | 31 | {deal.CreationTime} 32 | 33 |
34 |
35 |
36 |
37 | 38 |
39 |
40 |
41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/deals/MiniDealCard.scss: -------------------------------------------------------------------------------- 1 | .MiniDealCard { 2 | width: 100%; 3 | background: var(--app-card-default-background); 4 | word-wrap: break-word; 5 | margin: 0; 6 | display: flex; 7 | align-items: center; 8 | transition: all .3s; 9 | border-bottom: 1px solid #aaa; 10 | 11 | &:hover { 12 | } 13 | 14 | .Link { 15 | text-decoration: none; 16 | width: 100%; 17 | } 18 | 19 | .RowItem { 20 | display: flex; 21 | flex-direction: row; 22 | justify-content: space-between; 23 | align-items: center; 24 | } 25 | 26 | .PrimaryItems { 27 | display: flex; 28 | align-items: center; 29 | justify-content: space-between; 30 | padding: 0 2rem; 31 | width: 100%; 32 | } 33 | 34 | .DealID, 35 | .DealCID { 36 | display: flex; 37 | flex-direction: column; 38 | } 39 | 40 | .DealID { 41 | align-items: center; 42 | padding: .8rem; 43 | background-color: var(--subtle-green); 44 | min-width: 8vw; 45 | 46 | .Value { 47 | font-weight: bold; 48 | } 49 | } 50 | 51 | .DealCID { 52 | .Value { 53 | font-size: 1.46vw; 54 | } 55 | } 56 | 57 | .MinedOn { 58 | .Label { 59 | font-size: .625rem; 60 | letter-spacing: 1.2px; 61 | } 62 | 63 | .Value { 64 | font-size: .825rem; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/files/FileList.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import MDSpinner from "react-md-spinner"; 3 | import connect from "../../../../../renderer/screens/helpers/connect"; 4 | 5 | import MiniFileCard from "./MiniFileCard"; 6 | 7 | class FileList extends Component { 8 | constructor(props) { 9 | super(props); 10 | } 11 | 12 | render() { 13 | var content; 14 | if (this.props.files.inView.length > 0) { 15 | content = this.props.files.inView.map(file => { 16 | return ( 17 | 21 | ); 22 | }); 23 | } else { 24 | if (this.props.loading) { 25 | content = ( 26 |
27 | 34 |
35 | ); 36 | } else { 37 | content =
No files
; 38 | } 39 | } 40 | 41 | return
{content}
; 42 | } 43 | } 44 | 45 | export default connect( 46 | FileList, 47 | ["filecoin.core", "core"], 48 | ["filecoin.files", "files"], 49 | "appshell", 50 | ); 51 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/files/FileList.scss: -------------------------------------------------------------------------------- 1 | .FileList { 2 | width: 100%; 3 | height: 100%; 4 | background: white; 5 | } 6 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/files/FilesScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import connect from "../../../../../renderer/screens/helpers/connect"; 3 | import * as Files from "../../../common/redux/files/actions"; 4 | import FileList from "./FileList"; 5 | 6 | class FilesContainer extends Component { 7 | constructor(props) { 8 | super(props); 9 | 10 | this.state = { 11 | pollInterval: null 12 | }; 13 | } 14 | 15 | componentDidMount() { 16 | this.props.dispatch(Files.addFilesToView()); 17 | const pollInterval = setInterval(() => { 18 | this.props.dispatch(Files.addFilesToView()); 19 | }, 5000); 20 | 21 | this.setState({ 22 | pollInterval 23 | }); 24 | } 25 | 26 | componentWillUnmount() { 27 | if (this.state.pollInterval) { 28 | clearInterval(this.state.pollInterval); 29 | this.setState({ 30 | pollInterval: null 31 | }); 32 | } 33 | } 34 | 35 | render() { 36 | return ( 37 |
38 |
39 |
40 |

Pinned IPFS Files

41 |
42 |
43 | 44 |
45 | ); 46 | } 47 | } 48 | 49 | export default connect( 50 | FilesContainer, 51 | ); 52 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/files/FilesScreen.scss: -------------------------------------------------------------------------------- 1 | .FilesScreen { 2 | display: flex; 3 | flex-direction: column; 4 | 5 | height: 100%; 6 | width: 100%; 7 | 8 | .Header { 9 | margin-left: 1rem; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/files/MiniFileCard.scss: -------------------------------------------------------------------------------- 1 | .MiniFileCard { 2 | width: 100%; 3 | background: var(--app-card-default-background); 4 | word-wrap: break-word; 5 | margin: 0; 6 | display: flex; 7 | align-items: center; 8 | transition: all .3s; 9 | border-bottom: 1px solid #aaa; 10 | 11 | &:first-child { 12 | border-top: 1px solid #aaa; 13 | } 14 | 15 | &:hover { 16 | } 17 | 18 | .Link { 19 | text-decoration: none; 20 | width: 100%; 21 | } 22 | 23 | .RowItem { 24 | display: flex; 25 | flex-direction: row; 26 | justify-content: space-between; 27 | align-items: center; 28 | } 29 | 30 | .PrimaryItems { 31 | display: flex; 32 | align-items: center; 33 | justify-content: space-between; 34 | padding: 1rem; 35 | width: 100%; 36 | } 37 | 38 | .Name { 39 | min-width: 15rem; 40 | } 41 | 42 | .Size { 43 | min-width: 7rem; 44 | } 45 | 46 | .Name, 47 | .Size { 48 | .Label { 49 | font-size: .625rem; 50 | letter-spacing: 1.2px; 51 | } 52 | 53 | .Value { 54 | font-size: .825rem; 55 | } 56 | } 57 | 58 | .Download { 59 | button.Error { 60 | background-color: var(--error); 61 | } 62 | 63 | button.Complete { 64 | background-color: var(--subtle-green); 65 | } 66 | 67 | span { 68 | font-size: 1rem; 69 | } 70 | 71 | svg { 72 | margin-left: 0.5rem; 73 | width: 1rem; 74 | vertical-align: text-bottom; 75 | fill: var(--app-button-primary-background-color); 76 | } 77 | 78 | .ProgressBarOuter { 79 | height: 0.3rem; 80 | border-width: 0; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/messages/MessageList.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import MDSpinner from "react-md-spinner"; 3 | 4 | import MiniMessageCard from "./MiniMessageCard"; 5 | 6 | class MessageList extends Component { 7 | render() { 8 | var content; 9 | if (this.props.messages.length > 0) { 10 | content = this.props.messages.map(message => { 11 | return ( 12 | 16 | ); 17 | }); 18 | } else { 19 | if (this.props.loading) { 20 | content = ( 21 |
22 | 29 |
30 | ); 31 | } else { 32 | content =
No messages
; 33 | } 34 | } 35 | 36 | return
{content}
; 37 | } 38 | } 39 | 40 | export default MessageList; 41 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/messages/MessageList.scss: -------------------------------------------------------------------------------- 1 | .MessageList { 2 | width: 100%; 3 | height: 100%; 4 | background: white; 5 | display: flex; 6 | flex-direction: column; 7 | flex-grow: 1; 8 | } 9 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/messages/MessageTypeBadge.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | export default class MessageTypeBadge extends Component { 4 | render() { 5 | if (this.props.method === 0) { 6 | return ( 7 |
8 | VALUE TRANSFER 9 |
10 | ); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/messages/MessageTypeBadge.scss: -------------------------------------------------------------------------------- 1 | .MessageTypeBadge { 2 | font-size: .75rem; 3 | padding: 8px; 4 | height: 28px; 5 | border-radius: 25px; 6 | font-weight: bold; 7 | min-width: 7.5rem; 8 | 9 | &.ValueTransferBadge { 10 | background-color: var(--truffle-green); 11 | color: white; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/messages/MessagesScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import connect from "../../../../../renderer/screens/helpers/connect"; 3 | import * as Messages from "../../../common/redux/messages/actions"; 4 | import RecentMessages from "./RecentMessages"; 5 | import MessageCard from "./MessageCard"; 6 | 7 | class MessagesScreen extends Component { 8 | componentDidMount() { 9 | this.props.dispatch(Messages.requestPage()); 10 | } 11 | 12 | componentWillUnmount() { 13 | this.props.dispatch(Messages.clearMessagesInView()); 14 | } 15 | 16 | render() { 17 | var content; 18 | if (this.props.match.params.messageCid != null) { 19 | content = ; 20 | } else { 21 | content = ( 22 | 23 | ); 24 | } 25 | return
{content}
; 26 | } 27 | } 28 | 29 | export default connect(MessagesScreen); 30 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/messages/MessagesScreen.scss: -------------------------------------------------------------------------------- 1 | .MessagesScreen { 2 | display: flex; 3 | flex-direction: column; 4 | 5 | height: 100%; 6 | width: 100%; 7 | 8 | > main { 9 | padding: 0; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/messages/MiniMessageCard.scss: -------------------------------------------------------------------------------- 1 | .MiniMessageCard { 2 | font-family: "Fira Code Regular", monospaced; 3 | width: 100%; 4 | cursor: pointer; 5 | margin: .5rem 0; 6 | background: var(--app-card-default-background); 7 | padding: 28px 18px; 8 | word-wrap: break-word; 9 | cursor: pointer; 10 | display: flex; 11 | flex-direction: column; 12 | margin: 0; 13 | transition: all .3s; 14 | border-bottom: 1px solid #aaa; 15 | 16 | .Row { 17 | // display: flex; 18 | // align-items: center; 19 | // flex-direction: row; 20 | margin-bottom: 1rem; 21 | // justify-content: space-between; 22 | display: grid; 23 | grid-template-columns: 1fr 1fr 120px 200px; 24 | 25 | &.Top { 26 | display: flex; 27 | justify-content: space-between; 28 | } 29 | 30 | &:last-of-type { 31 | margin-bottom: 0; 32 | } 33 | } 34 | 35 | .RowItem { 36 | display: flex; 37 | flex-direction: row; 38 | justify-content: space-between; 39 | cursor: pointer; 40 | 41 | &:not(:last-child) { 42 | padding-right: 1rem; 43 | } 44 | } 45 | 46 | .Nonce, 47 | .GasUsed, 48 | .Value, 49 | .MinedOn { 50 | .Label { 51 | font-size: .625rem; 52 | letter-spacing: 1.2px; 53 | } 54 | 55 | .Value { 56 | font-size: .825rem; 57 | } 58 | } 59 | 60 | .MessageCID { 61 | .Value { 62 | font-weight: bold; 63 | } 64 | } 65 | 66 | .SecondaryItems { 67 | .Value { 68 | font-size: .825rem; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/messages/RecentMessages.scss: -------------------------------------------------------------------------------- 1 | .RecentMessages { 2 | flex-grow: 1; 3 | display: flex; 4 | height: 100%; 5 | } 6 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/tipsets/BlockCard.scss: -------------------------------------------------------------------------------- 1 | .FilecoinBlockCard { 2 | font-family: "Fira Code Regular", sans-serif; 3 | background: var(--app-card-default-background); 4 | display: flex; 5 | flex-direction: column; 6 | flex-grow: 1; 7 | margin: 0; 8 | border-radius: 4px; 9 | word-wrap: break-word; 10 | width: 100%; 11 | 12 | header { 13 | width: 100%; 14 | height: 64px; 15 | display: flex; 16 | flex-direction: row; 17 | justify-content: flex-start; 18 | background-color: #fff; 19 | border-bottom: 1px solid #aaa; 20 | line-height: 1; 21 | 22 | h1 { 23 | font-size: 1.5rem; 24 | padding: 1.25rem; 25 | margin: 0; 26 | } 27 | } 28 | 29 | .Button { 30 | text-decoration: none; 31 | text-align: center; 32 | margin: 0; 33 | } 34 | 35 | header button { 36 | border-radius: 0; 37 | line-height: 1; 38 | height: 100%; 39 | } 40 | 41 | .BlockBody { 42 | padding: 1rem 0; 43 | border-bottom: 1px solid #aaa; 44 | 45 | > div { 46 | padding: 1rem; 47 | } 48 | } 49 | 50 | .HeaderSecondaryInfo { 51 | display: flex; 52 | align-items: center; 53 | justify-content: space-between; 54 | width: 100%; 55 | padding: 1rem; 56 | } 57 | 58 | .MessageList { 59 | .Waiting { 60 | margin-top: 10rem; 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/tipsets/BlockList.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import connect from "../../../../../renderer/screens/helpers/connect"; 3 | 4 | import MiniBlockCard from "./MiniBlockCard"; 5 | 6 | class BlockList extends Component { 7 | constructor(props) { 8 | super(props); 9 | } 10 | 11 | render() { 12 | const blocksWithCids = []; 13 | for (let i = 0; i < this.props.blocks.length; i++) { 14 | blocksWithCids.push({ 15 | block: this.props.blocks[i], 16 | cid: this.props.cids[i]["/"] 17 | }); 18 | } 19 | 20 | return ( 21 |
22 | {blocksWithCids.map(blockWithCid => { 23 | return ( 24 | 29 | ); 30 | })} 31 |
32 | ); 33 | } 34 | } 35 | 36 | export default connect( 37 | BlockList, 38 | ["filecoin.core", "core"], 39 | ["filecoin.tipsets", "tipsets"], 40 | "appshell", 41 | ); 42 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/tipsets/TipsetCard.scss: -------------------------------------------------------------------------------- 1 | .TipsetCard { 2 | font-family: "Fira Code Regular", sans-serif; 3 | background: var(--app-card-default-background); 4 | display: flex; 5 | flex-direction: column; 6 | flex-grow: 1; 7 | margin: 0; 8 | border-radius: 4px; 9 | word-wrap: break-word; 10 | width: 100%; 11 | 12 | header { 13 | width: 100%; 14 | height: 64px; 15 | display: flex; 16 | flex-direction: row; 17 | justify-content: flex-start; 18 | background-color: #fff; 19 | border-bottom: 1px solid #aaa; 20 | line-height: 1; 21 | 22 | h1 { 23 | font-size: 1.5rem; 24 | padding: 1.25rem; 25 | margin: 0; 26 | } 27 | } 28 | 29 | .Button { 30 | text-decoration: none; 31 | text-align: center; 32 | margin: 0; 33 | } 34 | 35 | header button { 36 | border-radius: 0; 37 | line-height: 1; 38 | height: 100%; 39 | } 40 | 41 | .TipsetBody { 42 | padding: 1rem 0; 43 | border-bottom: 1px solid #aaa; 44 | 45 | > div { 46 | padding: 1rem; 47 | } 48 | } 49 | 50 | .HeaderSecondaryInfo { 51 | display: flex; 52 | align-items: center; 53 | width: 100%; 54 | padding: 1rem; 55 | 56 | > div { 57 | margin-right: 2rem; 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/tipsets/TipsetList.scss: -------------------------------------------------------------------------------- 1 | .TipsetList { 2 | width: 100%; 3 | background: white; 4 | } 5 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/tipsets/TipsetsScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import connect from "../../../../../renderer/screens/helpers/connect"; 3 | import * as Tipsets from "../../../common/redux/tipsets/actions"; 4 | import TipsetList from "./TipsetList"; 5 | import TipsetCard from "./TipsetCard"; 6 | 7 | class TipsetsContainer extends Component { 8 | constructor(props) { 9 | super(props); 10 | } 11 | 12 | componentDidMount() { 13 | this.props.dispatch(Tipsets.requestPage()); 14 | } 15 | 16 | render() { 17 | var content; 18 | if (this.props.match.params.tipsetHeight != null) { 19 | content = ; 20 | } else { 21 | content = ; 22 | } 23 | return
{content}
; 24 | } 25 | } 26 | 27 | export default connect( 28 | TipsetsContainer, 29 | ["filecoin.tipsets", "tipsets"] 30 | ); 31 | -------------------------------------------------------------------------------- /src/integrations/filecoin/renderer/screens/tipsets/TipsetsScreen.scss: -------------------------------------------------------------------------------- 1 | .TipsetsScreen { 2 | display: flex; 3 | flex-direction: column; 4 | 5 | height: 100%; 6 | width: 100%; 7 | } 8 | -------------------------------------------------------------------------------- /src/integrations/integrations.js: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from "events"; 2 | 3 | class Integrations extends EventEmitter { 4 | constructor(integrationManager) { 5 | super(); 6 | this._integrationManager = integrationManager; 7 | this.ipc = this._integrationManager.ipc; 8 | this.chain = null; 9 | this.send = integrationManager.emit.bind(integrationManager, "send"); 10 | } 11 | async start() { 12 | if (this.chain) { 13 | return this.chain.start(); 14 | } 15 | } 16 | async startServer(workspaceSettings, workspaceDirectory) { 17 | if (this.chain) { 18 | return this.chain.startServer(workspaceSettings, workspaceDirectory); 19 | } 20 | } 21 | async stop() { 22 | if (this.chain) { 23 | return this.chain.stop(); 24 | } 25 | } 26 | async stopServer() { 27 | if (this.chain) { 28 | return this.chain.stopServer(); 29 | } 30 | } 31 | } 32 | 33 | export default Integrations; 34 | -------------------------------------------------------------------------------- /src/main/types/json/JsonStorage.js: -------------------------------------------------------------------------------- 1 | import { LocalStorage } from "node-localstorage"; 2 | 3 | import JsonWithKeyPaths from "./JsonWithKeyPaths"; 4 | 5 | class JsonStorage { 6 | constructor(directory, name, obj) { 7 | this.obj = new JsonWithKeyPaths(obj); 8 | this.name = name; 9 | this.setStorageDirectory(directory); 10 | } 11 | 12 | setStorageDirectory(directory) { 13 | this.directory = directory; 14 | this.storage = new LocalStorage(this.directory, Infinity); 15 | } 16 | 17 | getFromStorage() { 18 | const data = JSON.parse(this.storage.getItem(this.name)); 19 | this.obj.setAll(data); 20 | return data; 21 | } 22 | 23 | // if defaultValue is set, and neither the cache nor storage has the key 24 | // the cache and storage will be set to the defaultValue 25 | get(key, defaultValue = null) { 26 | if (this.obj.has(key)) { 27 | return this.obj.get(key); 28 | } else { 29 | // check storage 30 | this.getFromStorage(); 31 | // we use this.obj again because getFromStorage refreshes the cache 32 | if (this.obj.has(key)) { 33 | return this.obj.get(key); 34 | } else if (defaultValue !== null) { 35 | this.set(key, defaultValue); 36 | return defaultValue; 37 | } else { 38 | return undefined; 39 | } 40 | } 41 | } 42 | 43 | getAll() { 44 | return this.getFromStorage(); 45 | } 46 | 47 | setToStorage() { 48 | this.storage.setItem(this.name, JSON.stringify(this.obj.obj)); 49 | } 50 | 51 | set(key, value) { 52 | this.obj.set(key, value); 53 | this.setToStorage(); 54 | } 55 | 56 | setAll(value) { 57 | this.obj.setAll(value); 58 | this.setToStorage(); 59 | } 60 | } 61 | 62 | export default JsonStorage; 63 | -------------------------------------------------------------------------------- /src/main/types/settings/GlobalSettings.js: -------------------------------------------------------------------------------- 1 | import Settings from "./Settings"; 2 | 3 | const initialSettings = { 4 | googleAnalyticsTracking: true, 5 | cpuAndMemoryProfiling: false, 6 | firstRun: true, 7 | }; 8 | 9 | class GlobalSettings extends Settings { 10 | constructor(directory) { 11 | super(directory, initialSettings); 12 | } 13 | 14 | bootstrap() { 15 | super.bootstrap(); 16 | } 17 | } 18 | 19 | export default GlobalSettings; 20 | -------------------------------------------------------------------------------- /src/main/types/settings/flavors/ethereum.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: "Quickstart", 3 | isDefault: true, 4 | verboseLogging: false, 5 | randomizeMnemonicOnStart: false, 6 | logsDirectory: null, 7 | server: { 8 | hostname: "127.0.0.1", 9 | port: 7545, 10 | network_id: 5777, 11 | default_balance_ether: 100, 12 | total_accounts: 10, 13 | unlocked_accounts: [], 14 | locked: false, 15 | vmErrorsOnRPCResponse: true, 16 | logger: null, 17 | verbose: false, 18 | gasLimit: 6721975, 19 | gasPrice: 20000000000, 20 | hardfork: "merge", 21 | fork: null, 22 | fork_block_number: null 23 | }, 24 | projects: [], 25 | } -------------------------------------------------------------------------------- /src/main/types/settings/flavors/filecoin.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: "Quickstart", 3 | isDefault: true, 4 | verboseLogging: false, 5 | randomizeSeedOnStart: true, 6 | logsDirectory: null, 7 | server: { 8 | hostname: "127.0.0.1", 9 | port: 7777, 10 | chain: {}, 11 | database: {}, 12 | logging: {}, 13 | miner: {}, 14 | wallet: {} 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/renderer/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import routes from "./routes"; 3 | import { ConnectedRouter } from "connected-react-router"; 4 | 5 | class App extends Component { 6 | render() { 7 | return ({routes}); 8 | } 9 | } 10 | 11 | export default App; 12 | -------------------------------------------------------------------------------- /src/renderer/README.md: -------------------------------------------------------------------------------- 1 | Here you can add all of your renderer process code. Bundling of the renderer process is optional for cases where you may want to use an external tool such as electron-next: https://github.com/leo/electron-next 2 | 3 | 4 | Notice that there isn’t a entry index.html, that’s because it is created for you 5 | More info: https://webpack.electron.build/development#use-of-html-webpack-plugin 6 | -------------------------------------------------------------------------------- /src/renderer/components/google-analytics/GoogleAnalytics.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from "react"; 2 | 3 | export default class HeaderBar extends PureComponent { 4 | render() { 5 | return ( 6 | <> 7 |

ANALYTICS

8 |
9 |

GOOGLE ANALYTICS

10 |
11 |
12 |
13 | 23 | 26 |
27 |
28 |
29 |

30 | We use Google Analytics to track Ganache usage. This information 31 | helps us gain more insight into how Ganache is used. This 32 | tracking is anonymous. We do not track personally identifiable 33 | information, account data or private keys. 34 |
35 | Note: This setting is global and will persist between 36 | workspaces. 37 |

38 |
39 |
40 |
41 | 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/renderer/components/header-bar/HeaderBar.css: -------------------------------------------------------------------------------- 1 | @import "colors.css"; 2 | 3 | .Header { 4 | position: -webkit-sticky; 5 | position: sticky; 6 | top: -1px; 7 | background: white; 8 | font-size: 2.5vw; 9 | height: 32px; 10 | font-family: "RobotoCondensed-Bold"; 11 | margin: .5em; 12 | padding: .8em 0; 13 | border-bottom: 2px solid rgba(155, 152, 150, 1.000); 14 | z-index: 2; 15 | display: flex; 16 | align-items: center; 17 | 18 | * { 19 | stroke: var(--primary-color) !important; 20 | } 21 | 22 | > div { 23 | margin-right: .25em; 24 | } 25 | 26 | > h4 { 27 | flex: 1; 28 | line-height: 1; 29 | margin: 0; 30 | } 31 | 32 | span { 33 | a { 34 | background: rgba(44, 54, 67, 1.000); 35 | border-radius: 4px; 36 | color: var(--app-button-primary-color); 37 | padding: .5rem; 38 | text-decoration: none; 39 | font-size: .8rem; 40 | text-transform: uppercase; 41 | 42 | &:hover { 43 | color: lighten( var(--app-button-primary-color), 10%); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/renderer/components/header-bar/HeaderBar.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from "react"; 2 | 3 | import Styles from "./HeaderBar.css"; 4 | 5 | export default class HeaderBar extends PureComponent { 6 | render() { 7 | return
{this.props.children}
; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/renderer/components/logo/Logo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import LogoSvg from "../../icons/logo.svg"; 3 | 4 | class Logo extends React.Component { 5 | render() { 6 | const className = "Logo" + (this.props.fadeInElement ? " FadeInElement" : "") 7 | return ( 8 |
9 |
10 | 11 |
12 |
13 | ); 14 | } 15 | } 16 | 17 | export default Logo; 18 | -------------------------------------------------------------------------------- /src/renderer/components/modal/Modal.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from "react"; 2 | 3 | export default class Modal extends PureComponent { 4 | render() { 5 | return ( 6 |
7 | {this.props.children} 8 |
9 | ); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/renderer/components/modal/ModalDetails.js: -------------------------------------------------------------------------------- 1 | import { 2 | setModalError, 3 | dismissModalError, 4 | } from "../../../common/redux/core/actions"; 5 | 6 | export default class ModalDetails extends Error { 7 | static types = { 8 | WARNING: 0, 9 | ERROR: 1, 10 | }; 11 | 12 | static actions = { 13 | setModalError, 14 | dismissModalError, 15 | }; 16 | 17 | constructor(type, buttons, title, ...params) { 18 | super(...params); 19 | 20 | if (Error.captureStackTrace) { 21 | Error.captureStackTrace(this, ModalDetails); 22 | } 23 | 24 | this.data = { 25 | type, 26 | buttons, 27 | title, 28 | message: params[0], 29 | }; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/renderer/components/only-if/OnlyIf.js: -------------------------------------------------------------------------------- 1 | import { PureComponent } from "react"; 2 | 3 | class OnlyIf extends PureComponent { 4 | render() { 5 | return this.props.test ? this.props.children : null; 6 | } 7 | } 8 | 9 | export default OnlyIf; 10 | -------------------------------------------------------------------------------- /src/renderer/components/progress-bar/ProgressBar.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | export default class ProgressBar extends Component { 4 | render() { 5 | return ( 6 |
7 |
8 |
14 |   15 |
16 |
17 | {this.props.children} 18 |
19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/renderer/components/progress-bar/ProgressBar.scss: -------------------------------------------------------------------------------- 1 | .ProgressBar { 2 | .ProgressBarOuter { 3 | display: block; 4 | border-width:1px; 5 | border-style:solid; 6 | border-color: black; 7 | border-radius: 0.25rem; 8 | height: 1rem; 9 | overflow-x: hidden; 10 | overflow-y: hidden; 11 | width: 100%; 12 | 13 | .ProgressBarInner { 14 | height: 100%; 15 | width: 0%; 16 | background-color: var(--primary-color); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/renderer/components/spinner/Spinner.css: -------------------------------------------------------------------------------- 1 | .Spinner, 2 | .Spinner:before, 3 | .Spinner:after { 4 | color: var(--primary-color); 5 | font-size: 3px; 6 | text-indent: -99999em; 7 | border-radius: 50%; 8 | position: absolute; 9 | content: ''; 10 | background:var(--app-statusbar-background); 11 | } 12 | .Spinner { 13 | margin: 5em auto; 14 | position: relative; 15 | width: 10em; 16 | height: 10em; 17 | box-shadow: inset 0 0 0 1em; 18 | transform: translateZ(0); 19 | } 20 | .Spinner:before { 21 | width: 5.2em; 22 | height: 10.2em; 23 | border-radius: 10.2em 0 0 10.2em; 24 | top: 0; 25 | left: -0.1em; 26 | transform-origin: 5.2em 5.1em; 27 | animation: load2 2s infinite ease 1.5s; 28 | } 29 | .Spinner:after { 30 | width: 5.2em; 31 | height: 10.2em; 32 | border-radius: 0 10.2em 10.2em 0; 33 | top: 0; 34 | left: 5.1em; 35 | transform-origin: 0px 5.1em; 36 | animation: load2 2s infinite ease; 37 | } 38 | @keyframes load2 { 39 | 0% { 40 | transform: rotate(0deg); 41 | } 42 | 100% { 43 | transform: rotate(360deg); 44 | } 45 | } -------------------------------------------------------------------------------- /src/renderer/components/spinner/Spinner.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | class Spinner extends Component { 4 | render() { 5 | return
; 6 | } 7 | } 8 | 9 | export default Spinner; 10 | -------------------------------------------------------------------------------- /src/renderer/components/spinner/SpinnerButton.css: -------------------------------------------------------------------------------- 1 | .SpinnerButton { 2 | border: none; 3 | cursor: pointer; 4 | display: inline-block; 5 | position: relative; 6 | text-align: center; 7 | text-decoration: none; 8 | appearance: none; 9 | transition:padding .25s cubic-bezier(0.175, 0.885, 0.32, 1.275); 10 | } 11 | 12 | .Active { 13 | padding-right:3rem; 14 | } 15 | 16 | .Label { 17 | cursor: pointer; 18 | } 19 | 20 | .Loader { 21 | display:inline-block; 22 | font-size:3px; 23 | position:absolute; 24 | overflow:hidden; 25 | opacity:0; 26 | pointer-events: none; 27 | right:1rem; 28 | top:50%; 29 | margin-top:-3.25em; 30 | animation: spin 1s infinite steps(8); 31 | transition:opacity .5s ease-out .2s; 32 | transform-origin: 3.25em; 33 | 34 | } 35 | 36 | .Active .Loader { 37 | height:6.5em; 38 | opacity:1; 39 | width:6.5em; 40 | } 41 | 42 | .Loader:before, 43 | .Loader:after, 44 | .Loader div:before, 45 | .Loader div:after { 46 | top:0; 47 | left:3em; 48 | background-color:#999; 49 | border-radius:.2em; 50 | content:""; 51 | height:2.5em; 52 | position:absolute; 53 | width:.5em; 54 | box-shadow:0 4em #eee; 55 | transform-origin:50% 3.25em; 56 | } 57 | 58 | .Loader:after { 59 | background-color:#aaa; 60 | transform:rotate(45deg)} 61 | 62 | .Loader div:before { 63 | background-color:#bbb; 64 | transform:rotate(90deg)} 65 | 66 | .Loader div:after { 67 | background-color:#ccc; 68 | transform:rotate(135deg)} 69 | 70 | @keyframes spin { 71 | to { transform: rotate(1turn); } 72 | } 73 | -------------------------------------------------------------------------------- /src/renderer/components/spinner/SpinnerButton.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | import Styles from "./SpinnerButton.css"; 4 | 5 | export default class SpinnerButton extends Component { 6 | render() { 7 | const className = `${Styles.SpinnerButton} ${ 8 | this.props.isActive ? Styles.Active : "" 9 | } ${this.props.className ? this.props.className : ""}`; 10 | 11 | return ( 12 | 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/renderer/components/status-indicator/StatusIndicator.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | import OnlyIf from "../only-if/OnlyIf"; 4 | 5 | export default class StatusIndicator extends Component { 6 | render() { 7 | return ( 8 |
9 |
10 |

{this.props.title}

11 | {this.props.value} 12 |
13 | 14 |
{this.props.children}
15 |
16 |
17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/renderer/components/status-indicator/StatusIndicator.scss: -------------------------------------------------------------------------------- 1 | .StatusIndicator { 2 | padding: 0 0rem 0 1rem; 3 | display: flex; 4 | flex-direction: row; 5 | justify-content: space-between; 6 | height: 55px; 7 | border-right: 1px solid var(--app-statusbar-item-border-color); 8 | 9 | .Metric { 10 | display: flex; 11 | flex-direction: column; 12 | justify-content: center; 13 | flex-shrink: 0; 14 | 15 | padding-right: 1rem; 16 | 17 | h4 { 18 | font-size: 10px; 19 | margin: 0; 20 | padding: 0; 21 | } 22 | 23 | span { 24 | font-size: 13px; 25 | color: var(--primary-color); 26 | 27 | &.upper { 28 | text-transform: uppercase; 29 | } 30 | } 31 | } 32 | 33 | .Indicator { 34 | display: flex; 35 | flex: 2; 36 | align-items: center; 37 | justify-content: flex-end; 38 | } 39 | 40 | .Metric + .Indicator { 41 | padding-right: 1rem; 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/renderer/components/styled-select/StyledSelect.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | class StyledSelect extends Component { 4 | render() { 5 | return ( 6 |
7 | 16 |
17 | ); 18 | } 19 | } 20 | 21 | export default StyledSelect; 22 | -------------------------------------------------------------------------------- /src/renderer/components/styled-select/StyledSelect.scss: -------------------------------------------------------------------------------- 1 | div.StyledSelect { 2 | display: flex; 3 | position: relative; 4 | align-items: center; 5 | 6 | select { 7 | -webkit-appearance: none; 8 | color: var(--text-color); 9 | border: 1px solid var(--text-color); 10 | background-color: transparent; 11 | padding: .5rem; 12 | } 13 | 14 | &::after { 15 | display: inline-flex; 16 | position: absolute; 17 | right: 0.5rem; 18 | content: '\25bc'; 19 | color: var(--text-color); 20 | pointer-events: none; 21 | } 22 | } -------------------------------------------------------------------------------- /src/renderer/components/with-empty-state/WithEmptyState.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from "react"; 2 | 3 | export default class WithEmptyState extends PureComponent { 4 | render() { 5 | const EmptyStateComponent = this.props.emptyStateComponent; 6 | 7 | if (this.props.test) { 8 | return ; 9 | } 10 | 11 | return this.props.children; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/renderer/icons/account.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/icons/blocks.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/renderer/icons/chain.svg: -------------------------------------------------------------------------------- 1 | chain-icon -------------------------------------------------------------------------------- /src/renderer/icons/chevron-up-o.svg: -------------------------------------------------------------------------------- 1 | icon-chevron-up-o -------------------------------------------------------------------------------- /src/renderer/icons/chevron-up.svg: -------------------------------------------------------------------------------- 1 | icon-chevron-up -------------------------------------------------------------------------------- /src/renderer/icons/console.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/renderer/icons/contract-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/icons/dots.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/icons/download.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/icons/eject.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/icons/error.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/icons/events-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/icons/file-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/icons/force_mine.svg: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /src/renderer/icons/key.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/icons/list.svg: -------------------------------------------------------------------------------- 1 | list-icon -------------------------------------------------------------------------------- /src/renderer/icons/locked.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/icons/no_transactions.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/renderer/icons/restart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/renderer/icons/revert.svg: -------------------------------------------------------------------------------- 1 | 3 | 5 | 7 | -------------------------------------------------------------------------------- /src/renderer/icons/save-icon.svg: -------------------------------------------------------------------------------- 1 | save-icon -------------------------------------------------------------------------------- /src/renderer/icons/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/renderer/icons/settings.svg: -------------------------------------------------------------------------------- 1 | gear-icon -------------------------------------------------------------------------------- /src/renderer/icons/snapshot.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/icons/start.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/icons/stop.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/icons/transactions.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/renderer/icons/trash-icon.svg: -------------------------------------------------------------------------------- 1 | trash-icon -------------------------------------------------------------------------------- /src/renderer/icons/unlocked.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/icons/warning.svg: -------------------------------------------------------------------------------- 1 | warning-icon_1 -------------------------------------------------------------------------------- /src/renderer/index.js: -------------------------------------------------------------------------------- 1 | import { showTitleScreen } from "../common/redux/core/actions"; 2 | 3 | import React from "react"; 4 | import ReactDOM from "react-dom"; 5 | import { AppContainer } from "react-hot-loader"; 6 | import { Provider } from "react-redux"; 7 | import createRootReducer from "../common/redux/reducer"; 8 | import { createHashHistory } from "history"; 9 | import createStore from "./init/store/createStore"; 10 | import { initRenderer } from "./init/index"; 11 | import { ipcRenderer } from "electron"; 12 | import App from "./App"; 13 | import "./css"; 14 | 15 | const hashHistory = createHashHistory(); 16 | const rootReducer = createRootReducer(hashHistory); 17 | const store = createStore(rootReducer, hashHistory); 18 | initRenderer(store); 19 | 20 | ipcRenderer.on("navigate", (_, path) => { 21 | hashHistory.push(path); 22 | }); 23 | 24 | const render = () => { 25 | ReactDOM.render( 26 | 27 | 28 | 29 | 30 | , 31 | document.getElementById("app"), 32 | ); 33 | }; 34 | 35 | render(); 36 | 37 | store.dispatch(showTitleScreen()); 38 | 39 | 40 | if (module.hot) { 41 | module.hot.accept('./App', () => { 42 | render() 43 | }); 44 | 45 | module.hot.accept('../common/redux/reducer', () => { 46 | store.replaceReducer(rootReducer(history)) 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /src/renderer/init/AutoUpdate.js: -------------------------------------------------------------------------------- 1 | import { ipcRenderer } from "electron"; 2 | 3 | import { 4 | UPDATE_AVAILABLE, 5 | DOWNLOAD_PROGRESS, 6 | UPDATE_DOWNLOADED, 7 | DOWNLOAD_ERROR, 8 | setUpdateAvailable, 9 | setDownloadProgress, 10 | setUpdateDownloaded, 11 | setDownloadError, 12 | } from "../../common/redux/auto-update/actions"; 13 | 14 | export function initAutoUpdates(store) { 15 | ipcRenderer.on(UPDATE_AVAILABLE, (event, updateInfo) => { 16 | store.dispatch(setUpdateAvailable(updateInfo)); 17 | }); 18 | ipcRenderer.on(DOWNLOAD_PROGRESS, (event, progressInfo) => { 19 | store.dispatch(setDownloadProgress(progressInfo)); 20 | }); 21 | ipcRenderer.on(UPDATE_DOWNLOADED, () => { 22 | store.dispatch(setUpdateDownloaded()); 23 | }); 24 | ipcRenderer.on(DOWNLOAD_ERROR, (event, errorInfo) => { 25 | store.dispatch(setDownloadError(errorInfo)); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /src/renderer/init/Config.js: -------------------------------------------------------------------------------- 1 | import { ipcRenderer } from "electron"; 2 | 3 | import { 4 | SHOW_CONFIG_SCREEN, 5 | SET_SETTING_ERROR, 6 | CLEAR_SETTING_ERROR, 7 | CLEAR_ALL_SETTING_ERRORS, 8 | SET_SETTINGS, 9 | showConfigScreen, 10 | setSettingError, 11 | clearSettingError, 12 | clearAllSettingErrors, 13 | setSettings, 14 | setVaultDataUpdated, 15 | } from "../../common/redux/config/actions"; 16 | 17 | export function initConfig(store) { 18 | ipcRenderer.on(SHOW_CONFIG_SCREEN, () => { 19 | store.dispatch(showConfigScreen()); 20 | }); 21 | 22 | ipcRenderer.on(SET_SETTING_ERROR, (event, key, value) => { 23 | store.dispatch(setSettingError(key, value)); 24 | }); 25 | 26 | ipcRenderer.on(CLEAR_SETTING_ERROR, (event, key) => { 27 | store.dispatch(clearSettingError(key)); 28 | }); 29 | 30 | ipcRenderer.on(CLEAR_ALL_SETTING_ERRORS, () => { 31 | store.dispatch(clearAllSettingErrors()); 32 | }); 33 | 34 | ipcRenderer.on(SET_SETTINGS, (event, globalSettings, workspaceSettings) => { 35 | store.dispatch(setSettings(globalSettings, workspaceSettings)); 36 | }); 37 | 38 | ipcRenderer.on("VAULT_DATA", (event, message) => { 39 | store.dispatch(setVaultDataUpdated()); 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /src/renderer/init/Logs.js: -------------------------------------------------------------------------------- 1 | import { ipcRenderer } from "electron"; 2 | 3 | import { ADD_LOG_LINES, addLogLines } from "../../common/redux/logs/actions"; 4 | 5 | export function initLogs(store) { 6 | ipcRenderer.on(ADD_LOG_LINES, (event, lines, context) => { 7 | store.dispatch(addLogLines(lines, context)); 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /src/renderer/init/Network.js: -------------------------------------------------------------------------------- 1 | import { ipcRenderer } from "electron"; 2 | 3 | import { 4 | SET_INTERFACES, 5 | setInterfaces, 6 | } from "../../common/redux/network/actions"; 7 | 8 | export function initNetwork(store) { 9 | ipcRenderer.on(SET_INTERFACES, (event, interfaces) => { 10 | store.dispatch(setInterfaces(interfaces)); 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /src/renderer/init/Workspaces.js: -------------------------------------------------------------------------------- 1 | import { ipcRenderer } from "electron"; 2 | 3 | import { 4 | SET_WORKSPACES, 5 | SET_CURRENT_WORKSPACE, 6 | setWorkspaces, 7 | setCurrentWorkspace 8 | } from "../../common/redux/workspaces/actions"; 9 | 10 | import { 11 | PROJECT_UPDATED, 12 | CONTRACT_DEPLOYED, 13 | CONTRACT_EVENT, 14 | CONTRACT_TRANSACTION, 15 | contractDeployed, 16 | contractTransaction, 17 | contractEvent, 18 | projectUpdated 19 | } from "../../integrations/ethereum/common/redux/workspaces/actions"; 20 | 21 | export function initWorkspaces(store) { 22 | ipcRenderer.on(SET_WORKSPACES, (event, workspaceNames) => { 23 | store.dispatch(setWorkspaces(workspaceNames)); 24 | }); 25 | 26 | ipcRenderer.on(SET_CURRENT_WORKSPACE, (event, workspace, contractCache) => { 27 | store.dispatch(setCurrentWorkspace(workspace, contractCache)); 28 | }); 29 | 30 | ipcRenderer.on(CONTRACT_DEPLOYED, (event, data) => { 31 | store.dispatch(contractDeployed(data)); 32 | }); 33 | 34 | ipcRenderer.on(CONTRACT_TRANSACTION, (event, data) => { 35 | store.dispatch(contractTransaction(data)); 36 | }); 37 | 38 | ipcRenderer.on(CONTRACT_EVENT, (event, data) => { 39 | store.dispatch(contractEvent(data)); 40 | }); 41 | 42 | ipcRenderer.on(PROJECT_UPDATED, (event, data) => { 43 | store.dispatch(projectUpdated(data)); 44 | }); 45 | } 46 | -------------------------------------------------------------------------------- /src/renderer/init/index.js: -------------------------------------------------------------------------------- 1 | import { initAutoUpdates } from "./AutoUpdate"; 2 | import { initCore } from "./Core"; 3 | import { initConfig } from "./Config"; 4 | import { initLogs } from "./Logs"; 5 | import { initNetwork } from "./Network"; 6 | import { initWorkspaces } from "./Workspaces"; 7 | import { initEvents } from "../../integrations/ethereum/renderer/init/Events"; 8 | 9 | // This will be called before the very first render, so you can do whatever 10 | // you want here. The Redux Store is available at this point, so you can 11 | // dispatch any action you want 12 | export function initRenderer(store) { 13 | initCore(store); 14 | initConfig(store); 15 | initLogs(store); 16 | initNetwork(store); 17 | initAutoUpdates(store); 18 | initWorkspaces(store); 19 | initEvents(store); 20 | } 21 | -------------------------------------------------------------------------------- /src/renderer/init/store/createStore.js: -------------------------------------------------------------------------------- 1 | if (process.env.NODE_ENV === "production") { 2 | module.exports = require("./createStore.production"); 3 | } else { 4 | module.exports = require("./createStore.development"); 5 | } 6 | -------------------------------------------------------------------------------- /src/renderer/init/store/createStore.production.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from "redux"; 2 | import thunk from "redux-thunk"; 3 | import { routerMiddleware } from 'connected-react-router'; 4 | import { 5 | processAction, 6 | processPage, 7 | } from "../../../common/redux/middleware/analytics/index"; 8 | 9 | export default function configureStore(reducers, history, initialState) { 10 | const router = routerMiddleware(history); 11 | 12 | const enhancer = compose(applyMiddleware(thunk, router, processAction)); 13 | 14 | const store = createStore(reducers, initialState, enhancer); 15 | 16 | // add google analytics to production 17 | history.listen(location => { 18 | processPage(location.pathname + (location.search ? location.search : ""), store.getState()) 19 | }); 20 | 21 | return store; 22 | } 23 | -------------------------------------------------------------------------------- /src/renderer/screens/appshell/AppShell.scss: -------------------------------------------------------------------------------- 1 | .AppShell { 2 | display: flex; 3 | flex-direction: column; 4 | height: 100%; 5 | 6 | .ShellContainer { 7 | display: flex; 8 | flex-grow: 1; 9 | justify-content: center; 10 | overflow: hidden; // Scrolling is handled using react-custom-scrollbars 11 | height: 100%; 12 | } 13 | 14 | .ShellContainer > span { 15 | position: relative; 16 | width: 100%; 17 | top: 0; 18 | bottom: 0; 19 | left: 0; 20 | right: 0; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/renderer/screens/appshell/BugModal.scss: -------------------------------------------------------------------------------- 1 | .Modal { 2 | &.BugModal { 3 | .Bug { 4 | textarea { 5 | min-height: 5rem; /* Overwrite Modal textarea styles */ 6 | max-height: 5rem; 7 | background-color: var(--app-button-primary-disabled-border-color); 8 | width: 100%; 9 | margin-bottom: 1rem; 10 | } 11 | 12 | svg { 13 | width: 192px; 14 | height: 192px; 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/renderer/screens/auto-update/UpdateModal.scss: -------------------------------------------------------------------------------- 1 | .Modal { 2 | &.UpdateModal { 3 | .Update { 4 | .releaseName { 5 | width: 100% 6 | } 7 | 8 | .releaseNotesOrError { 9 | height: 5rem; 10 | background-color: var(--app-button-primary-disabled-border-color); 11 | width: 100%; 12 | margin-bottom: 1rem; 13 | border: 1px solid #888888; 14 | overflow-y: scroll; 15 | } 16 | 17 | .updateDetails { 18 | display: flex; 19 | flex-direction: column; 20 | align-items: center; 21 | } 22 | 23 | .ProgressBar { 24 | width: 200px 25 | } 26 | 27 | .downloadSpeed { 28 | text-align: right; 29 | } 30 | 31 | footer { 32 | .delayButton { 33 | background-color: rgba(227, 227, 227, 0.9); 34 | float: left; 35 | } 36 | 37 | .ctaButton { 38 | float: right; 39 | } 40 | } 41 | 42 | svg { 43 | width: 192px; 44 | height: 192px; 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/renderer/screens/auto-update/UpdateNotification.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | import connect from "../helpers/connect"; 4 | 5 | import { showUpdateModal } from "../../../common/redux/auto-update/actions"; 6 | 7 | import UpdateIcon from "../../icons/chevron-up-o.svg"; 8 | 9 | class UpdateNotification extends Component { 10 | constructor() { 11 | super(); 12 | this.state = {}; 13 | } 14 | 15 | handleUpdateClick() { 16 | this.props.dispatch(showUpdateModal()); 17 | } 18 | 19 | render() { 20 | return ( 21 | 26 | ); 27 | } 28 | } 29 | 30 | export default connect(UpdateNotification); 31 | -------------------------------------------------------------------------------- /src/renderer/screens/auto-update/UpdateNotification.scss: -------------------------------------------------------------------------------- 1 | .UpdateNotification { 2 | display: flex; 3 | justify-content: flex-end; 4 | align-items: center; 5 | 6 | a { 7 | padding: .5rem; 8 | margin: .3rem; 9 | color: var(--app-navbar-text); 10 | cursor: pointer; 11 | text-decoration: none; 12 | position: relative; 13 | text-transform: uppercase; 14 | display: flex; 15 | align-items: center; 16 | font-size: 0.89rem; 17 | line-height: 0.89rem; 18 | transition: all .25s; 19 | 20 | > div { 21 | margin-right: .5rem; 22 | } 23 | 24 | svg { 25 | width: 36px; 26 | height: 36px; 27 | color: var(--primary-color); 28 | stroke: var(--primary-color); 29 | margin-right: 0.5rem; 30 | } 31 | 32 | &:hover { 33 | color: white; 34 | 35 | svg { 36 | color: var(--primary-color); 37 | stroke: var(--primary-color); 38 | cursor: pointer; 39 | } 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /src/renderer/screens/config/ConfigScreens/AboutScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import * as pkg from "../../../../../package.json"; 3 | import Logo from "../../../components/logo/Logo.js"; 4 | 5 | class AccountsScreen extends Component { 6 | render() { 7 | return ( 8 |
9 |
10 | 11 |

12 | Ganache 13 |
v{pkg.version}
14 |

15 |
16 | Ganache is created with by{" "} 17 | Truffle 18 |
19 | Follow development and report issues on{" "} 20 | GitHub 21 |
22 |
23 |
24 | ); 25 | } 26 | } 27 | 28 | export default AccountsScreen; 29 | -------------------------------------------------------------------------------- /src/renderer/screens/helpers/connect.js: -------------------------------------------------------------------------------- 1 | import { compose } from "redux"; 2 | import { connect } from "react-redux"; 3 | import { withRouter } from "react-router"; 4 | 5 | // Connect a component to specific reducer names. 6 | // e.g., connect(MyComponent, "core", "config") 7 | export default function(component, ...reducers) { 8 | let connector = connect( 9 | state => { 10 | var props = {}; 11 | reducers.forEach(reducer => { 12 | let name = reducer; 13 | let propName = reducer; 14 | if (Array.isArray(reducer)) { 15 | name = reducer[0]; 16 | propName = reducer[1]; 17 | } 18 | 19 | const reducerTree = name.split("."); 20 | let value = state; 21 | for (let i = 0; i < reducerTree.length; i++) { 22 | value = value[reducerTree[i]]; 23 | 24 | if (!value) { 25 | throw new Error( 26 | `Tried connecting '${component.name}' to unknown state: ${name}`, 27 | ); 28 | } 29 | } 30 | 31 | props[propName] = value; 32 | }); 33 | 34 | return props; 35 | } 36 | ); 37 | return compose(withRouter, connector)(component); 38 | } 39 | -------------------------------------------------------------------------------- /src/renderer/screens/helpers/sanitize.js: -------------------------------------------------------------------------------- 1 | const { app } = require("@electron/remote"); 2 | 3 | // return a sanitized error string from an Error object or string which contains an error message 4 | export function sanitizeError(errorUnsanitized) { 5 | return sanitizePaths(errorUnsanitized.stack || errorUnsanitized); 6 | } 7 | 8 | // Remove any user-specific paths in exception messages 9 | export function sanitizePaths(message) { 10 | // Prepare our paths so we *always* will get a match no matter 11 | // path separator (oddly, on Windows, different errors will give 12 | // us different path separators) 13 | var appPath = app.getAppPath().replace(/\\/g, "/"); 14 | // I couldn't figure out the regex, so a loop will do. 15 | while (message && message.indexOf && message.indexOf(appPath) >= 0) { 16 | message = message.replace(appPath, ""); 17 | } 18 | 19 | return message; 20 | } 21 | -------------------------------------------------------------------------------- /src/renderer/screens/logs/LogContainer.scss: -------------------------------------------------------------------------------- 1 | 2 | .LogContainer { 3 | overflow: hidden; 4 | position: relative; 5 | height: 100%; 6 | width: 100%; 7 | 8 | ul { 9 | list-style:none; 10 | font-size: 13px; 11 | color: #eff1f5; 12 | border-radius: 0; 13 | background: #333; 14 | white-space: pre-wrap; 15 | word-wrap: break-word; 16 | box-sizing: border-box; 17 | margin: 0; 18 | padding: 1rem; 19 | font-family: "Fira Code Regular", monospace; 20 | position: absolute; 21 | top: 0em; 22 | left: 0em; 23 | right: 0em; 24 | bottom: 0em; 25 | 26 | li:not(:last-child) { 27 | margin-bottom: .3rem; 28 | } 29 | } 30 | } 31 | 32 | .LogContainer .plain { 33 | font-weight: 100; 34 | } 35 | 36 | .LogContainer .command { 37 | font-weight: 500; 38 | } 39 | 40 | .LogContainer .error { 41 | font-weight: 500; 42 | color: red; 43 | } 44 | -------------------------------------------------------------------------------- /src/renderer/screens/logs/LogContainerLazy.js: -------------------------------------------------------------------------------- 1 | import LogsLazy from "./LogsLazy"; 2 | import connect from "../helpers/connect"; 3 | 4 | export default connect( 5 | LogsLazy, 6 | "logs", 7 | ); 8 | -------------------------------------------------------------------------------- /src/renderer/screens/logs/LogsLazy.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { List, AutoSizer, CellMeasurer } from "react-virtualized"; 3 | import Row from "./Row"; 4 | 5 | class LogsLazy extends Component { 6 | shouldComponentUpdate(nextProps) { 7 | const isDifferentContext = nextProps.context !== this.props.context; 8 | if (isDifferentContext) { 9 | return true; 10 | } 11 | 12 | const isLineLengthDiff = 13 | nextProps.logs[this.props.context].lines.length !== 14 | this.props.logs[this.props.context].lines.length; 15 | 16 | return isLineLengthDiff; 17 | } 18 | 19 | renderRow = ({ index, style, parent }) => ( 20 | 27 | 32 | 33 | ); 34 | 35 | clearCache = () => this.props.cache.clearAll(); 36 | 37 | render() { 38 | const { logs } = this.props; 39 | 40 | return ( 41 |
42 |
    43 | 44 | {({ height, width }) => ( 45 | 52 | )} 53 | 54 |
55 |
56 | ); 57 | } 58 | } 59 | 60 | export default LogsLazy; 61 | -------------------------------------------------------------------------------- /src/renderer/screens/logs/LogsScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import connect from "../helpers/connect"; 3 | import LogContainerLazy from "./LogContainerLazy"; 4 | import { CellMeasurerCache } from "react-virtualized"; 5 | 6 | class Logs extends Component { 7 | constructor() { 8 | super(); 9 | } 10 | 11 | caches = new Map(); 12 | 13 | render() { 14 | const context = this.props.match.params.context || "default"; 15 | 16 | let logCache; 17 | if (this.caches.has(context)) { 18 | logCache = this.caches.get(context); 19 | } else { 20 | logCache = new CellMeasurerCache({ 21 | defaultHeight: 50, 22 | fixedWidth: true, 23 | }); 24 | this.caches.set(context, logCache); 25 | } 26 | 27 | return ( 28 |
29 |
30 | 31 |
32 |
33 | ); 34 | } 35 | } 36 | 37 | export default connect(Logs, "config"); 38 | -------------------------------------------------------------------------------- /src/renderer/screens/logs/LogsScreen.scss: -------------------------------------------------------------------------------- 1 | .LogsScreen { 2 | position: relative; 3 | display: flex !important; 4 | flex-direction: column; 5 | flex: 1; 6 | width: 100%; 7 | height: 100%; 8 | 9 | > h4 { 10 | position: -webkit-sticky; 11 | position: sticky; 12 | top: -5px; 13 | background: white; 14 | font-size: 32px; 15 | font-family: "RobotoCondensed-Bold"; 16 | margin: 1rem .8rem; 17 | border-bottom: 2px solid #9b9896; 18 | z-index: 2; 19 | display: flex; 20 | align-items: center; 21 | line-height: 52px; 22 | 23 | > div { 24 | margin-right: .5rem; 25 | margin-bottom: 10px; 26 | } 27 | } 28 | 29 | main { 30 | height: 100%; 31 | display: flex; 32 | flex: 1; 33 | padding: 0; 34 | } 35 | 36 | footer { 37 | padding: 0 1rem 1rem 1rem; 38 | } 39 | 40 | .xtermContainer { 41 | background: #333; 42 | &, > div, > div > div { 43 | height: 100%; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/renderer/screens/logs/Row.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | // react-virtualized doesn't respect the margin-bottom from LogContainer.scss, so do it here 5 | const myStyle = { 6 | padding: "0 0 0.3rem 0", 7 | }; 8 | 9 | const Row = ({ index, log, style }) => ( 10 |
  • 11 | {`[${new Date(log.time).toLocaleTimeString()}]`} {log.line} 12 |
  • 13 | ); 14 | 15 | Row.propTypes = { 16 | index: PropTypes.number, 17 | log: PropTypes.shape({ 18 | time: PropTypes.instanceOf(Date).isRequired, 19 | line: PropTypes.string.isRequired, 20 | }).isRequired, 21 | style: PropTypes.object, // passed down by react-virtualized 22 | }; 23 | 24 | export default Row; 25 | -------------------------------------------------------------------------------- /src/renderer/screens/not-found/NotFoundScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | class NotFoundScreen extends Component { 4 | render() { 5 | return ( 6 |
    7 |
    Nothing found
    8 |
    9 | ); 10 | } 11 | } 12 | 13 | export default NotFoundScreen; 14 | -------------------------------------------------------------------------------- /src/renderer/screens/not-found/NotFoundScreen.scss: -------------------------------------------------------------------------------- 1 | .NotFoundScreen { 2 | display: flex; 3 | 4 | height: 100%; 5 | width: 100%; 6 | 7 | main { 8 | width: 100%; 9 | height: 100%; 10 | display: flex; 11 | flex-direction: column; 12 | align-items: center; 13 | justify-content: center; 14 | flex-grow: 1; 15 | font-family: RobotoCondensed-Bold; 16 | text-transform: uppercase; 17 | font-size: 1.3rem; 18 | color: var(--app-page-intentionally-left-blank); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/renderer/styles/buttons.scss: -------------------------------------------------------------------------------- 1 | button, 2 | .Button { 3 | margin: 0; 4 | padding: 0; 5 | resize: none; 6 | border: 0; 7 | background-color: var(--app-button-primary-color); 8 | height: 32px; 9 | padding: 0 20px; 10 | cursor: pointer; 11 | white-space: nowrap; 12 | text-transform: uppercase; 13 | color: var(--app-button-primary-background-color); 14 | border-radius: 3px; 15 | font-family: "RobotoCondensed-Bold"; 16 | font-size: 12px; 17 | font-weight: 600; 18 | line-height: 32px; 19 | transition: all .3s; 20 | 21 | &[disabled] { 22 | background-color: transparent; 23 | color: #888; 24 | border: 1px solid #888; 25 | cursor: not-allowed; 26 | } 27 | 28 | &:hover { 29 | filter: brightness(108%); 30 | color: color(var(--app-button-primary-background-color) shade(20%)); 31 | box-shadow: none; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/renderer/styles/cards.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/src/renderer/styles/cards.scss -------------------------------------------------------------------------------- /src/renderer/styles/clean_colors.scss: -------------------------------------------------------------------------------- 1 | :root { 2 | --primary-color: rgba(38, 191, 149, 1); 3 | --text-color: rgba(54, 65, 83, 1.000); 4 | 5 | --app-highlight-color: rgba(138, 193, 238, 1.000); 6 | 7 | --app-background-color: rgba(247, 249, 250, 1.000); 8 | 9 | --app-panel-background-color: rgba(247, 249, 250, 1.000); 10 | --app-panel-text-color: rgba(54, 65, 83, 1.000); 11 | --app-panel-divider-color: rgba(223, 226, 229, 1.000); 12 | 13 | --app-sidebar-background-color: rgba(255, 255, 255, 1.000); 14 | --app-sidebar-header-color: rgba(127, 143, 164, 1.000); 15 | --app-sidebar-divider-color: rgba(223, 226, 229, 1.000); 16 | 17 | --app-button-primary-background-color: rgba(138, 193, 238, 1.000); 18 | --app-button-primary-color: rgba(53, 64, 82, 1.000); 19 | --app-button-primary-disabled-border-color: rgba(227, 227, 227, 1.000); 20 | 21 | --app-account-list-background: rgba(255, 255, 255, 1.000); 22 | --app-account-list-header-divider-color: rgba(223, 226, 229, 1.000); 23 | 24 | --app-mnemonic-background-color: rgba(255, 255, 255, 1.000); 25 | 26 | --app-title-bar-btn-close: rgba(255, 96, 88, 1.000); 27 | --app-title-bar-btn-min: rgba(255, 191, 47, 1.000); 28 | --app-title-bar-btn-max: rgba(40, 201, 64, 1.000); 29 | } 30 | -------------------------------------------------------------------------------- /src/renderer/styles/forms.scss: -------------------------------------------------------------------------------- 1 | input { 2 | color: var(--text-color); 3 | border: 1px solid var(--text-color); 4 | background-color: transparent; 5 | padding: .5rem; 6 | } 7 | 8 | $switch-width: 60px; 9 | $switch-height: 30px; 10 | 11 | .Switch { 12 | :hover, :focus { 13 | filter: brightness(105%); 14 | } 15 | display: flex; 16 | align-items: center; 17 | 18 | i { 19 | margin-left: 1rem; 20 | } 21 | 22 | input[type=checkbox] { 23 | height: 0; 24 | width: 0; 25 | visibility: hidden; 26 | } 27 | 28 | label { 29 | cursor: pointer; 30 | text-indent: -9999px; 31 | width: $switch-width; 32 | height: $switch-height; 33 | background: grey; 34 | display: block; 35 | border-radius: 4px; 36 | position: relative; 37 | } 38 | 39 | label:after { 40 | content: ''; 41 | position: absolute; 42 | top: 5px; 43 | left: 5px; 44 | width: $switch-height - 10px; 45 | height: $switch-height - 10px; 46 | background: #fff; 47 | border-radius: 4px; 48 | transition: 0.3s; 49 | } 50 | 51 | input:checked + label { 52 | background: var(--primary-color); 53 | } 54 | 55 | input:checked + label:after { 56 | left: calc(100% - 5px); 57 | transform: translateX(-100%); 58 | } 59 | 60 | label:active:after { 61 | width: $switch-width - 30px; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/renderer/styles/logo.scss: -------------------------------------------------------------------------------- 1 | .LogoWrapper { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | flex-direction: column; 6 | font-size: 20px; 7 | 8 | font-weight: 700; 9 | line-height: 3.2; 10 | 11 | .Logo { 12 | height: 145px; 13 | width: 128px; 14 | background-image: url(~@/icons/logo.svg); 15 | background-position: center center; 16 | background-repeat: no-repeat; 17 | background-size: 128px 145px; 18 | will-change: opacity; 19 | 20 | opacity: 0; 21 | 22 | animation: fadeIn 500ms .8s forwards; 23 | svg { 24 | g, 25 | path, 26 | circle { 27 | stroke: none !important; 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/renderer/styles/panels.scss: -------------------------------------------------------------------------------- 1 | @import './colors'; 2 | 3 | .Panel { 4 | background: var(--app-panel-background-color); 5 | padding: 0 1em 1em 1em; 6 | 7 | border-bottom: 1px solid var(--app-panel-divider-color); 8 | 9 | overflow-y: scroll; 10 | 11 | > h3 { 12 | color: var(--app-panel-text-color); 13 | } 14 | 15 | > h4 { 16 | margin: 0 0 1em 0; 17 | padding-top: 4px; 18 | color: var(--app-panel-text-color); 19 | } 20 | 21 | > h3, 22 | > h4 { 23 | border-top: 4px solid var(--app-panel-header-border-color); 24 | display: inline-table; 25 | } 26 | 27 | header, 28 | main, 29 | footer { 30 | display: flex; 31 | } 32 | 33 | header, 34 | footer { 35 | flex-shrink: 0; 36 | } 37 | 38 | main { 39 | overflow: scroll; 40 | height: 100%; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /static/README.md: -------------------------------------------------------------------------------- 1 | STATIC ASSETS 2 | There are some instances were we may not want webpack to bundle particular assets, like those being consumed by modules like fs. Here is where we can put them and then reliably access them in both development and production 3 | 4 | More info: https://webpack.electron.build/using-static-assets 5 | -------------------------------------------------------------------------------- /static/fonts/FiraCode-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/static/fonts/FiraCode-Regular.ttf -------------------------------------------------------------------------------- /static/fonts/FiraSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/static/fonts/FiraSans-Bold.ttf -------------------------------------------------------------------------------- /static/fonts/FiraSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/static/fonts/FiraSans-Regular.ttf -------------------------------------------------------------------------------- /static/fonts/FiraSans-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/static/fonts/FiraSans-SemiBold.ttf -------------------------------------------------------------------------------- /static/fonts/GrandHotel-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/static/fonts/GrandHotel-Regular.ttf -------------------------------------------------------------------------------- /static/fonts/RobotoCondensed-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/static/fonts/RobotoCondensed-Bold.ttf -------------------------------------------------------------------------------- /static/fonts/RobotoCondensed-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/static/fonts/RobotoCondensed-Regular.ttf -------------------------------------------------------------------------------- /static/icons/mac/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/static/icons/mac/icon.icns -------------------------------------------------------------------------------- /static/icons/png/1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/static/icons/png/1024x1024.png -------------------------------------------------------------------------------- /static/icons/png/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/static/icons/png/128x128.png -------------------------------------------------------------------------------- /static/icons/png/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/static/icons/png/16x16.png -------------------------------------------------------------------------------- /static/icons/png/24x24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/static/icons/png/24x24.png -------------------------------------------------------------------------------- /static/icons/png/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/static/icons/png/256x256.png -------------------------------------------------------------------------------- /static/icons/png/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/static/icons/png/32x32.png -------------------------------------------------------------------------------- /static/icons/png/48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/static/icons/png/48x48.png -------------------------------------------------------------------------------- /static/icons/png/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/static/icons/png/512x512.png -------------------------------------------------------------------------------- /static/icons/png/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/static/icons/png/64x64.png -------------------------------------------------------------------------------- /static/icons/png/96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/static/icons/png/96x96.png -------------------------------------------------------------------------------- /static/icons/win/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/static/icons/win/icon.ico -------------------------------------------------------------------------------- /static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trufflesuite/ganache-ui/6dbf54c00d9d89ab5321d7e2f6ffa2d94b4995c1/static/logo.png -------------------------------------------------------------------------------- /static/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@ganache/filecoin": "0.9.2", 4 | "@truffle/config": "1.2.17", 5 | "@truffle/decoder": "3.0.16", 6 | "electron": "^26.1.0", 7 | "electron-fetch": "1.7.3", 8 | "fs-extra": "9.0.0", 9 | "ganache": "7.9.1", 10 | "ganache-core": "2.13.1", 11 | "lodash.clonedeep": "4.5.0", 12 | "lodash.merge": "4.6.2", 13 | "lodash.padstart": "4.6.1", 14 | "temp": "0.9.1", 15 | "web3-eth-abi": "1.2.6", 16 | "web3-providers-http": "1.2.6", 17 | "web3-providers-ws": "1.2.6" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /static/node/truffle-integration/__tests__/projectFsWatcherUtils.test.js: -------------------------------------------------------------------------------- 1 | const { getAncestorDirs } = require("../projectFsWatcherUtils"); 2 | 3 | describe("projectFsWatcherUtils", () => { 4 | describe("getAncestorDirs", () => { 5 | const truffle_directory = "/path/to/awesome/project"; 6 | 7 | test("returns a list of all ancestor dirs up to truffle_directory when contracts_build_directory is a child of truffle_directory", () => { 8 | const contracts_build_directory = `${truffle_directory}/contracts`; 9 | const dirs = getAncestorDirs( 10 | truffle_directory, 11 | contracts_build_directory, 12 | ); 13 | 14 | expect(dirs).toEqual([truffle_directory]); 15 | }); 16 | 17 | test("returns a list of all ancestor dirs up to truffle_directory when contracts_build_directory is a descendant of truffle_directory", () => { 18 | const contracts_build_directory = `${truffle_directory}/build/contracts`; 19 | const dirs = getAncestorDirs( 20 | truffle_directory, 21 | contracts_build_directory, 22 | ); 23 | 24 | expect(dirs).toEqual([truffle_directory, `${truffle_directory}/build`]); 25 | }); 26 | 27 | test("returns a list of all ancestor dirs when contracts_build_directory is not a descendent of truffle_directory", () => { 28 | const contracts_build_directory = `/path/to/build/contracts`; 29 | const dirs = getAncestorDirs( 30 | truffle_directory, 31 | contracts_build_directory, 32 | ); 33 | 34 | expect(dirs).toEqual(["/", "/path", "/path/to", "/path/to/build"]); 35 | }); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /static/node/truffle-integration/projectFsWatcherUtils.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | /** 4 | * 5 | * @param {string} truffle_directory The directory of your Truffle project 6 | * @param {string} currDir The current directory 7 | * @param {string[]} ancestorDirs The current array of ancestor dirs found 8 | * @returns {string[]} An array of directory names which are ancestors to currDir 9 | */ 10 | const getAncestorDirs = (truffle_directory, currDir, ancestorDirs = []) => { 11 | if (currDir == null) return []; 12 | const parent = path.dirname(currDir); 13 | 14 | if (parent === currDir) return ancestorDirs; 15 | else if (parent === truffle_directory) return [parent, ...ancestorDirs]; 16 | return getAncestorDirs(truffle_directory, parent, [parent, ...ancestorDirs]); 17 | }; 18 | 19 | module.exports = { 20 | getAncestorDirs, 21 | }; 22 | -------------------------------------------------------------------------------- /static/node/truffle-project-loader/index.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | if (process.argv.length < 3) { 5 | throw new Error( 6 | `The truffle-project-loader process requires atleast 3 arguments, received ${ 7 | process.argv.length 8 | }.`, 9 | ); 10 | } 11 | 12 | const projectFile = process.argv[2]; 13 | 14 | const configFileDirectory = path.dirname(projectFile); 15 | const name = path.basename(configFileDirectory); 16 | if (!fs.existsSync(projectFile)) { 17 | process.send({ 18 | name, 19 | configFile: projectFile, 20 | error: "project-does-not-exist", 21 | }); 22 | } else if ( 23 | path.basename(projectFile).match(/^truffle(-config)?.js$/) === null 24 | ) { 25 | process.send({ 26 | name, 27 | configFile: projectFile, 28 | error: "invalid-project-file", 29 | }); 30 | } else { 31 | const output = require(projectFile); 32 | 33 | // it isn't "safe" to serialize the output itself, as it may have functions and circular dependencies 34 | // all we actually need are the following three properties, and I'm type checking them just to be on the safe side. 35 | process.send({ 36 | truffle_directory: 37 | typeof output.truffle_directory === "string" 38 | ? output.truffle_directory 39 | : undefined, 40 | build_directory: 41 | typeof output.build_directory === "string" 42 | ? output.build_directory 43 | : undefined, 44 | contracts_build_directory: 45 | typeof output.contracts_build_directory === "string" 46 | ? output.contracts_build_directory 47 | : undefined, 48 | }); 49 | } 50 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # Contributing to Ganache UI Tests 2 | 3 | Tests are to live in as many subdirectories as necessary for organizational purposes. Tests **are not** supposed to live in the root `/test` directory or the `/test/mocha` or `/test/spectron` (doesn't exist yet) directories. 4 | 5 | ## Executing Tests 6 | You can execute tests by running `npm test`. This will `npm run test-mocha` ~~and `npm run test-spectron`~~ (not added yet). 7 | 8 | ## Mocha 9 | For components that do not require Electron, it is recommended to write Mocha tests instead as they are lighter. 10 | 11 | For a Mocha test to be executed by `npm test` or `npm run test-mocha`, it must be located in a subdirectory of `/test/mocha` matching the glob pattern `*.test.js`. 12 | 13 | ### Subtests 14 | You may see files with the glob pattern `*.subtest.js`. These files are not named `*.test.js` for the purpose of organizing them in a single Mocha `describe()`. A main loader `*.test.js` file will require the `*.subtest.js` files for execution. See how [workspace tests implement this](mocha/workspaces/workspaces.test.js). 15 | 16 | ## Spectron 17 | Currently, there are no Spectron tests, nor is there the framework necessary to execute Spectron tests. However, Spectron test support will be added eventually. -------------------------------------------------------------------------------- /test/mocha/project-loader/truffle-project/myfile.json: -------------------------------------------------------------------------------- 1 | { 2 | "dir": "./123" 3 | } -------------------------------------------------------------------------------- /test/mocha/project-loader/truffle-project/truffle.js: -------------------------------------------------------------------------------- 1 | //make sure we execute the config file at the proper `cwd`: 2 | // by attempting to read a local file: 3 | const json = JSON.parse(require("fs").readFileSync("./myfile.json", "utf-8")); 4 | 5 | // make sure we don't throw if something is logged to console.error: 6 | // eslint-disable-next-line no-console 7 | console.error("Just a friendly error message. Don't be alarmed."); 8 | 9 | // make sure we can handle circular json in the project file: 10 | const circles = {}; 11 | circles.circles = circles; 12 | module.exports = { 13 | networks: { 14 | provider: circles, 15 | }, 16 | otherThings: 123, 17 | build_directory: json.dir, 18 | }; 19 | -------------------------------------------------------------------------------- /test/mocha/workspaces/test-workspaces/default/Settings: -------------------------------------------------------------------------------- 1 | {"server":{"gasLimit":6721975,"gasPrice":20000000000,"hostname":"127.0.0.1","port":7545,"network_id":5777,"default_balance_ether":100,"total_accounts":10,"unlocked_accounts":[],"locked":false,"vmErrorsOnRPCResponse":true,"logger":null,"verbose":false},"name":"Quickstart","isDefault":true,"verboseLogging":false,"randomizeMnemonicOnStart":false,"logsDirectory":null,"projects":[],"uuid":"b1ef5e49-3485-482e-96b5-91826e7d4c85"} -------------------------------------------------------------------------------- /test/mocha/workspaces/test-workspaces/global/Settings: -------------------------------------------------------------------------------- 1 | {"googleAnalyticsTracking":true,"cpuAndMemoryProfiling":false,"firstRun":false,"uuid":"8b56511c-135f-4842-9f6a-f656ad69aa3d"} -------------------------------------------------------------------------------- /test/mocha/workspaces/test-workspaces/ui/workspaces/Test-1/Settings: -------------------------------------------------------------------------------- 1 | {"server":{"gasLimit":6721975,"gasPrice":20000000000,"hostname":"127.0.0.1","port":7545,"network_id":5777,"default_balance_ether":100,"total_accounts":10,"unlocked_accounts":[],"locked":false,"vmErrorsOnRPCResponse":true,"logger":null,"verbose":false,"db_path":"/home/mike/work/consensys/ganache/test/mocha/workspaces/test-workspaces/workspaces/Test-1/chaindata"},"name":"Test 1","isDefault":false,"verboseLogging":false,"randomizeMnemonicOnStart":false,"logsDirectory":null,"projects":[],"uuid":"b1ef5e49-3485-482e-96b5-91826e7d4c85"} -------------------------------------------------------------------------------- /test/mocha/workspaces/test-workspaces/ui/workspaces/Test-2/Settings: -------------------------------------------------------------------------------- 1 | {"server":{"gasLimit":6721975,"gasPrice":20000000000,"hostname":"127.0.0.1","port":7546,"network_id":5777,"default_balance_ether":50,"total_accounts":5,"unlocked_accounts":[],"locked":false,"vmErrorsOnRPCResponse":true,"logger":null,"verbose":false,"db_path":"/home/mike/work/consensys/ganache/test/mocha/workspaces/test-workspaces/workspaces/Test-2/chaindata"},"name":"Test 2","isDefault":false,"verboseLogging":false,"randomizeMnemonicOnStart":false,"logsDirectory":null,"projects":[],"uuid":"b1ef5e49-3485-482e-96b5-91826e7d4c85"} -------------------------------------------------------------------------------- /test/mocha/workspaces/workspaces.test.js: -------------------------------------------------------------------------------- 1 | describe("Workspaces", function() { 2 | require("./new-workspace.subtest"); 3 | require("./workspace-manager.subtest"); 4 | }); 5 | -------------------------------------------------------------------------------- /webpack/webpack.main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | output: { 3 | hashFunction: "sha256", 4 | }, 5 | resolve: { 6 | alias: { 7 | "scrypt": "js-scrypt" 8 | } 9 | }, 10 | externals: ["@ganache/filecoin"] 11 | }; 12 | --------------------------------------------------------------------------------