diff --git a/.github/workflows/release_win.yml b/.github/workflows/release_win.yml
index de00e3d1..55bba7b5 100644
--- a/.github/workflows/release_win.yml
+++ b/.github/workflows/release_win.yml
@@ -8,6 +8,13 @@ on:
jobs:
build-windows:
runs-on: windows-latest
+ strategy:
+ matrix:
+ include:
+ - env: "production"
+ rpc: "https://rpc-gate.autonolas.tech/gnosis-rpc/"
+ - env: "development"
+ rpc: "https://virtual.gnosis.rpc.tenderly.co/78ca845d-2b24-44a6-9ce2-869a979e8b5b"
defaults:
run:
shell: bash
@@ -33,15 +40,15 @@ jobs:
- name: Install dependencies
run: poetry install
- - name: install node deps
+ - name: install all deps
run: yarn install-deps
- name: set env vars to prod.env
env:
- NODE_ENV: production
- DEV_RPC: https://rpc-gate.autonolas.tech/gnosis-rpc/
+ NODE_ENV: ${{ matrix.env }}
+ DEV_RPC: ${{ matrix.rpc }}
IS_STAGING: ${{ github.ref != 'refs/heads/main' && 'true' || 'false' }}
- FORK_URL: https://rpc-gate.autonolas.tech/gnosis-rpc/
+ FORK_URL: ${{ matrix.rpc }}
GH_TOKEN: ${{ secrets.github_token}}
run: |
echo NODE_ENV=$NODE_ENV >> prod.env
diff --git a/.gitleaks.toml b/.gitleaks.toml
index 7dbdf15e..975e353c 100755
--- a/.gitleaks.toml
+++ b/.gitleaks.toml
@@ -534,8 +534,12 @@ regex = '''(?i)((key|api|token|secret|password)[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|
entropy = 3.7
secretGroup = 4
-
[allowlist]
-description = "global allow lists"
-regexes = ['''219-09-9999''', '''078-05-1120''', '''(9[0-9]{2}|666)-\d{2}-\d{4}''']
-paths = ['''(.*?)(jpg|gif|doc|pdf|bin|svg|socket)$''']
+description = "allowlist"
+regexTarget = "match"
+regexes = [
+ '''\b(0x)?[0-9a-fA-F]{40}\b''', # ignore evm public keys
+ '''219-09-9999''', # global allow lists
+ '''078-05-1120''',
+ '''(9[0-9]{2}|666)-\d{2}-\d{4}'''
+]
\ No newline at end of file
diff --git a/.nvmrc b/.nvmrc
index 922f10a1..2edeafb0 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-20.x
+20
\ No newline at end of file
diff --git a/README.md b/README.md
index 76b4ede7..ebb5de65 100644
--- a/README.md
+++ b/README.md
@@ -2,223 +2,47 @@
Pearl
-Pearl is an application used to run autonomous agents powered by the OLAS Network.
-
-## Technologies Used
-
-- Electron
-- NodeJS (20.11 LTS)
-- AntD (^5)
-- NextJS (^14)
-- Javascript / TypeScript
-- Python (3.10)
-- Poetry (^1.7.1)
+A cross-platform desktop application used to run autonomous agents powered by the OLAS Network.
## Getting Started
-### Installing system dependencies
-
-The following installation steps assume you have the following on each OS:
-
-- Linux: a debian based operating system such as Ubuntu with `apt` to install packages.
-- MacOS: [Homebrew](https://brew.sh/)
-
-
NodeJS
-
-NodeJS is best installed and managed through NVM. It allows you to install and select specific versions of NodeJS. Pearl has been built using version 20 LTS.
-
-
-
-```bash
-brew install nvm
-```
-
-Set up NVM for console usage. Dependant on the shell, you should edit the config file to contain the following code.
-If you're using Bash or Zsh, you might add them to your `~/.bash_profile`, `~/.bashrc`, or `~/.zshrc` file:
-
-```bash
-export NVM_DIR="$HOME/.nvm"
-[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
-[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
-```
-
-Close and reopen Terminal, or run `source ~/.bash_profile`, `source ~/.zshrc`, or `source ~/.bashrc` to reload the shell configuration.
-
-Verify your installation by running `nvm --version`. Then run:
-
-```bash
-nvm install --lts
-nvm use --lts
-```
-
-
-
-
Yarn
-
-Yarn is the package manager used for dependency management of the Electron app and NextJS frontend.
-
-```bash
-npm install --global yarn
-```
-
-
-
Python
-
-
Linux
-
-```bash
-sudo apt install python3
-```
-
-
MacOS
-
-```bash
-brew install python
-```
-
-
-
-
PIPX
-
-
Linux
-
-```bash
-sudo apt install pipx
-```
-
-
MacOS
-
-```bash
-brew install pipx
-```
-
-
-
-
Poetry
-
-Poetry is used on the backend to install and manage dependencies, and create a virtual environment for the backend API.
-
-```bash
-pipx install poetry
-```
-
-If promoted to run `pipx ensurepath`, run it.
-
-
-
-
Setting up your .env file
-
-Create an `.env` file in the root directory, or rename `.env.example` to `.env`.
-Then set the following environment variables.
-
-
NODE_ENV
-
-For development usage, set `NODE_ENV=development`.
-For production usage, set `NODE_ENV=production`.
-
-
-
-
FORK_URL
-
-**This variable is required for both development and production.**
-**Must be a Gnosis Mainnet RPC URL.**
-
-- In `development` this RPC url is only used if/when forking mainnet with Hardhat (covered later). This process allows you to test without losing funds.
-- In `production` this RPC URL is used as the main RPC for Pearl.
-
-You can get a Gnosis RPC from [Nodies](https://www.nodies.app/).
-
-Once you have a Gnosis Mainnet RPC URL, set `FORK_URL=YOUR_RPC_URL_HERE` in your .env file.
-
-Note: this must be an external RPC. If you decide to use Hardhat for testing on a mainnet fork, do _not_ set your Hardhat Node URL here.
-
-
-
DEV_RPC
-
-This environment variable is only used when `NODE_ENV=development` is set.
-
-In `development` mode, it is used throughout Pearl as the main RPC.
-
-If you're using Hardhat, you can set `DEV_RPC=http://localhost:8545`.
-Or, you can use another, external RPC URL that wish to test on, ensuring that the chain ID is 100 (Gnosis Mainnet's chain ID).
-
-
-
-
Installing project dependencies
-
-Run the following command to install all project dependencies.
-
-```bash
-yarn install-deps
-```
-
-
Running the application
-
-Provided your system dependencies are installed, environment variables are set, and your RPC is running.
-
-You can start Pearl by running the following command in the root directory:
-
-```bash
-yarn dev
-```
-
-This will run Electron, which launches the NextJS frontend and the Python backend as child processes.
-
-
Chain forking (for development)
-
-In the interest of protecting your funds during development, you can run a forked version of Gnosis Mainnet.
-
-There are two recommended options, choose one:
-
-
Tenderly (preferred)
+### For Users
-[Tenderly](https://tenderly.co/) is a service with a plethora of useful blockchain development tools. The tool required here gives you the ability to **fork networks**.
+#### Downloading the latest release
-You can also monitor all transactions, and fund your accounts with any token that you please.
+**Note:** The release pages also contain Source Code `.zip` files and `dev-` prefixed builds. These are not intended for general use. Ignore them unless you're a developer!
-1. Signup to [Tenderly](https://tenderly.co/), and select the plan you desire. **The Free plan should suffice for most users**.
-2. Go to *Forks* under the *Development* tab -- in the left sidebar of your dashboard.
-3. Click *Create Fork*, select "Gnosis Chain" as the network, and use Chain ID `100`.
-4. Copy the RPC url into the appropriate .env variables in your repository. (Recommended to set both `FORK_URL` & `DEV_RPC` to this RPC url during development).
-5. Click the *Fund Accounts* button to fund your accounts with XDAI (native token) and [OLAS](https://gnosisscan.io/token/0xce11e14225575945b8e6dc0d4f2dd4c570f79d9f).
+- Go to the [Releases](https://github.com/valory-xyz/olas-operate-app/releases) page.
+- Download the latest release for your operating system.
+ - If you're on Windows, download the `.exe` file.
+ - If you're on MacOS, download the `.dmg` file.
+ - Both Intel x64 and Apple Silicon ARM64 builds are available.
+- Install the application by running the downloaded file.
-
+### For Developers
-
Hardhat
-Note: using Hardhat will result in the loss of chain state once your Hardhat node is turned off.
+#### Setting up your development environment
-Run the following command in the root of your project folder to start your Hardhat node:
+- [Ubuntu Setup Guide](docs/dev/ubuntu-setup.md)
+- [MacOS Setup Guide](docs/dev/macos-setup.md)
+- [Windows Setup Guide](docs/dev/windows-setup.md)
-```bash
-npx hardhat node
-```
+#### Setting up a development RPC endpoint
-**Once Hardhat is running, you will be able to use `http://localhost:8545` as your development RPC.**
+- [RPC Setup Guide](docs/dev/rpcs.md)
-
Funding your addresses with Hardhat
+## Project Dependencies
-There are scripts to fund addresses during testing/development:
+There are three parts to the project: the Electron app (CommonJS), the NextJS frontend (TypeScript), and the Python backend/middleware.
-- XDAI funding:
+- [Electron dependencies](package.json)
+- [Frontend dependencies](package.json)
+- [Backend dependencies](backend/pyproject.toml)
-```bash
-poetry run python scripts/fund.py 0xYOURADDRESS
-```
+## License
-- OLAS funding:
+- [Apache 2.0](LICENSE)
-```bash
-poetry run python scripts/transfer_olas.py PATH_TO_KEY_CONTAINING_OLAS ADDRESS_TO_TRANSFER AMOUNT
-```
+## Security
-
\ No newline at end of file
+- [Security Policy](SECURITY.md)
diff --git a/docs/dev/macos-setup.md b/docs/dev/macos-setup.md
new file mode 100644
index 00000000..0193e72e
--- /dev/null
+++ b/docs/dev/macos-setup.md
@@ -0,0 +1,97 @@
+# Setting up Pearl for development on MacOS
+
+### System dependencies
+
+## 1. Brew
+
+Brew is a package manager for MacOS, allowing you to install packages from the command line.
+
+```bash
+/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
+```
+
+## 2. Node Version Manager (NVM)
+
+NVM is a version manager for Node.js, allowing you to switch between different versions of Node.js.
+
+```bash
+brew install nvm
+```
+
+Set up NVM for console usage. Dependant on the shell, you should edit the config file to contain the following code.
+
+If you're using Bash or Zsh, you might add them to your `~/.bash_profile`, `~/.bashrc`, or `~/.zshrc` file:
+
+```bash
+export NVM_DIR="$HOME/.nvm"
+[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
+[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
+```
+
+Close and reopen Terminal, or run `source ~/.bash_profile`, `source ~/.zshrc`, or `source ~/.bashrc` to reload the shell configuration.
+
+## 3. Node.js
+
+```bash
+nvm install
+nvm use
+```
+
+## 4. Yarn
+
+Yarn is the package manager used for dependency management of the Electron app and NextJS frontend.
+
+```bash
+npm install --global yarn
+```
+
+## 5. Python
+
+Use Python 3.10 for the project.
+
+```bash
+brew install python@3.10
+```
+
+## 6. Pipx
+
+```bash
+brew install pipx
+```
+
+## 7. Poetry
+
+```bash
+pipx install poetry
+```
+
+If prompted to add the `poetry` command to your shell's config file, accept the prompt.
+
+### Installing project dependencies
+
+The `install-deps` script will install the dependencies for all parts of the project.
+The Electron app, the NextJS frontend, and the Python backend.
+
+```bash
+yarn install-deps
+```
+
+### Setup the .env file
+
+Duplicate the `.env.example` file and rename it to `.env`.
+
+```bash
+cp .env.example .env
+```
+
+Then fill in the required environment variables.
+
+- `NODE_ENV` - Set to `development` for development. `production` is only used for production builds built through the release script.
+- `FORK_URL` - Set to your desired HTTP RPC endpoint.
+- `DEV_RPC` - Set to the same value as `FORK_URL`.
+
+### Run the project
+
+```bash
+yarn dev
+```
diff --git a/docs/dev/rpcs.md b/docs/dev/rpcs.md
new file mode 100644
index 00000000..bbd4502d
--- /dev/null
+++ b/docs/dev/rpcs.md
@@ -0,0 +1,43 @@
+# Acquiring RPC Endpoints for Development
+
+## Tenderly
+
+We use Tenderly to fork the Gnosis Mainnet chain for development purposes. This allows us to interact with the chain without risking real funds.
+
+### 1. Create a Tenderly Account
+
+Go to [Tenderly](https://tenderly.co/) and create an account.
+
+### 2. Create a Project
+
+Create a new project in Tenderly.
+
+### 3. Fork the Gnosis Mainnet
+
+1. Go to the _Forks_ section under the _Development_ tab in your Tenderly dashboard.
+
+2. Click _Create Fork_.
+
+3. Select "Gnosis Chain" as the network.
+
+4. Use Chain ID `100`.
+
+5. Copy the RPC URL provided by Tenderly.
+
+### 4. Set the RPC URL
+
+Set the `FORK_URL` and `DEV_RPC` environment variables in your `.env` file to the RPC URL provided by Tenderly.
+
+### 5. Fund Your Accounts
+
+Click the _Fund Accounts_ button in Tenderly to fund your accounts with XDAI (native token) and [OLAS](https://gnosisscan.io/token/0xce11e14225575945b8e6dc0d4f2dd4c570f79d9f).
+
+### 6. Keeping Your Fork Up-to-Date
+
+It is important to update your fork periodically to ensure that your forked chain is up-to-date with mainnet. You can do this by creating a new fork in Tenderly and updating your `FORK_URL` and `DEV_RPC` environment variables.
+
+Alternatively, you can try the Tenderly's virtual testnet feature, which can automatically update your fork for you relative to mainnet. Though, this sometimes results in instability.
+
+## Hardhat (deprecated)
+
+Hardhat is a local alternative to Tenderly for forking EVM chains. It is useful for development purposes, though the chain state is lost once the Hardhat node is turned off.
diff --git a/docs/dev/ubuntu-setup.md b/docs/dev/ubuntu-setup.md
new file mode 100644
index 00000000..47875b68
--- /dev/null
+++ b/docs/dev/ubuntu-setup.md
@@ -0,0 +1,79 @@
+# Setting up Pearl for development on Ubuntu
+
+### System dependencies
+
+## 1. Node Version Manager (NVM)
+
+NVM is a version manager for Node.js, allowing you to switch between different versions of Node.js.
+
+```bash
+sudo apt install curl
+curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash
+source ~/.bashrc
+```
+
+## 3. Node.js
+
+```bash
+nvm install
+nvm use
+```
+
+## 4. Yarn
+
+Yarn is the package manager used for dependency management of the Electron app and NextJS frontend.
+
+```bash
+npm install --global yarn
+```
+
+## 5. Python
+
+Use Python 3.10 for the project.
+
+```bash
+sudo apt install python3.10
+```
+
+## 6. Pipx
+
+```bash
+sudo apt install pipx
+```
+
+## 7. Poetry
+
+```bash
+pipx install poetry
+```
+
+If prompted to add the `poetry` command to your shell's config file, accept the prompt.
+
+### Installing project dependencies
+
+The `install-deps` script will install the dependencies for all parts of the project.
+The Electron app, the NextJS frontend, and the Python backend.
+
+```bash
+yarn install-deps
+```
+
+### Setup the .env file
+
+Duplicate the `.env.example` file and rename it to `.env`.
+
+```bash
+cp .env.example .env
+```
+
+Then fill in the required environment variables.
+
+- `NODE_ENV` - Set to `development` for development. `production` is only used for production builds built through the release script.
+- `FORK_URL` - Set to your desired HTTP RPC endpoint.
+- `DEV_RPC` - Set to the same value as `FORK_URL`.
+
+### Run the project
+
+```bash
+yarn dev
+```
diff --git a/docs/dev/windows-setup.md b/docs/dev/windows-setup.md
new file mode 100644
index 00000000..c56a1972
--- /dev/null
+++ b/docs/dev/windows-setup.md
@@ -0,0 +1,98 @@
+# Setting up Pearl for development on Windows
+
+- Development on Windows is experimental, but included here for reference.
+- Please report any issues you encounter while setting up the project on Windows.
+- You must be able to run PowerShell as an administrator to install the system dependencies.
+
+### Installing system dependencies
+
+## 1. Chocolatey
+
+Chocolatey is a package manager for Windows, allowing you to install packages from the command line.
+
+```powershell
+# run as administrator
+# https://chocolatey.org/install
+
+Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
+```
+
+## 2. Node Version Manager (NVM)
+
+NVM is a version manager for Node.js, allowing you to switch between different versions of Node.js.
+
+```powershell
+# run as administrator
+choco install nvm
+```
+
+## 3. Node.js v20
+
+```powershell
+# run as administrator
+nvm install 20
+nvm use 20
+```
+
+## 4. Yarn
+
+Yarn is the package manager used for dependency management of the Electron app and NextJS frontend.
+
+```powershell
+npm install --global yarn
+```
+
+## 5. Python 3.10
+
+```powershell
+# run as administrator
+choco install python3.10
+```
+
+## 6. Pipx
+
+```powershell
+# run as administrator
+python3.10 -m pip install pipx
+```
+
+## 7. Poetry
+
+```powershell
+# run as administrator
+pipx install poetry
+```
+
+If prompted to add `poetry` to your PATH, follow the prompt.
+
+### Installing project dependencies
+
+The `install-deps` script will install the dependencies for all parts of the project.
+The Electron app, the NextJS frontend, and the Python backend.
+
+```powershell
+# run from the project root
+poetry shell
+yarn install-deps
+```
+
+### Setup the .env file
+
+Duplicate the `.env.example` file and rename it to `.env`.
+
+```powershell
+# run from the project root
+cp .env.example .env
+```
+
+Then fill in the required environment variables.
+
+- `NODE_ENV` - Set to `development` for development. `production` is only used for production builds built through the release script.
+- `FORK_URL` - Set to your desired HTTP RPC endpoint.
+- `DEV_RPC` - Set to the _same_ value as `FORK_URL`.
+
+### Run the project
+
+```powershell
+yarn dev
+```
diff --git a/electron/assets/icons/tray-logged-out.png b/electron/assets/icons/tray-logged-out.png
deleted file mode 100644
index 16311b50..00000000
Binary files a/electron/assets/icons/tray-logged-out.png and /dev/null differ
diff --git a/electron/assets/icons/tray-low-gas.png b/electron/assets/icons/tray-low-gas.png
deleted file mode 100644
index 189b5db8..00000000
Binary files a/electron/assets/icons/tray-low-gas.png and /dev/null differ
diff --git a/electron/assets/icons/tray-paused.png b/electron/assets/icons/tray-paused.png
deleted file mode 100644
index 86a3b150..00000000
Binary files a/electron/assets/icons/tray-paused.png and /dev/null differ
diff --git a/electron/assets/icons/tray-running.png b/electron/assets/icons/tray-running.png
deleted file mode 100644
index ba2387c7..00000000
Binary files a/electron/assets/icons/tray-running.png and /dev/null differ
diff --git a/electron/assets/icons/tray/logged-out.png b/electron/assets/icons/tray/logged-out.png
new file mode 100644
index 00000000..6f26a5e1
Binary files /dev/null and b/electron/assets/icons/tray/logged-out.png differ
diff --git a/electron/assets/icons/tray/logged-out@2x.png b/electron/assets/icons/tray/logged-out@2x.png
new file mode 100644
index 00000000..8748464f
Binary files /dev/null and b/electron/assets/icons/tray/logged-out@2x.png differ
diff --git a/electron/assets/icons/tray/logged-out@3x.png b/electron/assets/icons/tray/logged-out@3x.png
new file mode 100644
index 00000000..b6b2ec8f
Binary files /dev/null and b/electron/assets/icons/tray/logged-out@3x.png differ
diff --git a/electron/assets/icons/tray/low-gas.png b/electron/assets/icons/tray/low-gas.png
new file mode 100644
index 00000000..856cfbb9
Binary files /dev/null and b/electron/assets/icons/tray/low-gas.png differ
diff --git a/electron/assets/icons/tray/low-gas@2x.png b/electron/assets/icons/tray/low-gas@2x.png
new file mode 100644
index 00000000..8de86da7
Binary files /dev/null and b/electron/assets/icons/tray/low-gas@2x.png differ
diff --git a/electron/assets/icons/tray/low-gas@3x.png b/electron/assets/icons/tray/low-gas@3x.png
new file mode 100644
index 00000000..23e9a229
Binary files /dev/null and b/electron/assets/icons/tray/low-gas@3x.png differ
diff --git a/electron/assets/icons/tray/paused.png b/electron/assets/icons/tray/paused.png
new file mode 100644
index 00000000..4d13d7b7
Binary files /dev/null and b/electron/assets/icons/tray/paused.png differ
diff --git a/electron/assets/icons/tray/paused@2x.png b/electron/assets/icons/tray/paused@2x.png
new file mode 100644
index 00000000..eab10400
Binary files /dev/null and b/electron/assets/icons/tray/paused@2x.png differ
diff --git a/electron/assets/icons/tray/paused@3x.png b/electron/assets/icons/tray/paused@3x.png
new file mode 100644
index 00000000..2e398321
Binary files /dev/null and b/electron/assets/icons/tray/paused@3x.png differ
diff --git a/electron/assets/icons/tray/running.png b/electron/assets/icons/tray/running.png
new file mode 100644
index 00000000..858f061d
Binary files /dev/null and b/electron/assets/icons/tray/running.png differ
diff --git a/electron/assets/icons/tray/running@2x.png b/electron/assets/icons/tray/running@2x.png
new file mode 100644
index 00000000..635ac5e0
Binary files /dev/null and b/electron/assets/icons/tray/running@2x.png differ
diff --git a/electron/assets/icons/tray/running@3x.png b/electron/assets/icons/tray/running@3x.png
new file mode 100644
index 00000000..e502fce2
Binary files /dev/null and b/electron/assets/icons/tray/running@3x.png differ
diff --git a/electron/components/PearlTray.js b/electron/components/PearlTray.js
new file mode 100644
index 00000000..506bea30
--- /dev/null
+++ b/electron/components/PearlTray.js
@@ -0,0 +1,147 @@
+const Electron = require('electron');
+const { isMac, isLinux, isWindows, isDev } = require('../constants');
+const { logger } = require('../logger');
+
+// Used to resize the tray icon on macOS
+const macTrayIconSize = { width: 16, height: 16 };
+
+/** Status supported by tray icons.
+ * @readonly
+ * @enum {'logged-out' | 'low-gas' | 'paused' | 'running'}
+ */
+const TrayIconStatus = {
+ LoggedOut: 'logged-out',
+ LowGas: 'low-gas',
+ Paused: 'paused',
+ Running: 'running',
+};
+
+const appPath = Electron.app.getAppPath();
+
+/** Paths to tray icons for different statuses.
+ * @readonly
+ * @type {Record}
+ */
+const trayIconPaths = {
+ [TrayIconStatus.LoggedOut]: `${appPath}/electron/assets/icons/tray/logged-out.png`,
+ [TrayIconStatus.LowGas]: `${appPath}/electron/assets/icons/tray/low-gas.png`,
+ [TrayIconStatus.Paused]: `${appPath}/electron/assets/icons/tray/paused.png`,
+ [TrayIconStatus.Running]: `${appPath}/electron/assets/icons/tray/running.png`,
+};
+
+/** Tray icons as native images
+ * @note macOS icons are resized
+ * @readonly
+ * @type {Record} */
+const trayIcons = Object.entries(trayIconPaths).reduce(
+ (acc, [status, path]) => ({
+ ...acc,
+ [status]: (() => {
+ // Linux does not support nativeImage
+ if (isLinux) return path;
+
+ // Windows and macOS support nativeImage
+ let trayIcon = Electron.nativeImage.createFromPath(path);
+
+ if (isMac) {
+ // Resize icon for tray
+ trayIcon = trayIcon.resize(macTrayIconSize);
+ // Mark the image as a template image for MacOS to apply correct color
+ trayIcon.setTemplateImage(true);
+ }
+
+ return trayIcon;
+ })(),
+ }),
+ {},
+);
+
+/** Cross-platform Electron Tray for Pearl, with context menu, icon, events. */
+class PearlTray extends Electron.Tray {
+ /** @param {() => Electron.BrowserWindow | null} activeWindowCallback */
+ constructor(activeWindowCallback) {
+ // Set the tray icon to the logged-out state by default
+ super(trayIcons[TrayIconStatus.LoggedOut]);
+
+ // Store the callback to retrieve the active window
+ this.activeWindowCallback = activeWindowCallback;
+
+ this.setContextMenu(new PearlTrayContextMenu(activeWindowCallback));
+ this.setToolTip('Pearl');
+
+ this.#bindClickEvents();
+ this.#bindIpcListener();
+ }
+
+ #bindClickEvents = () => {
+ if (isWindows) {
+ isDev && logger.electron('binding windows click events to tray');
+ // Windows: Handle single and double-clicks to show the window
+ this.on('click', () => this.activeWindowCallback()?.show());
+ this.on('double-click', () => this.activeWindowCallback()?.show());
+ this.on('right-click', () => this.popUpContextMenu());
+ return;
+ }
+ isDev &&
+ logger.electron('no click events bound to tray as not using win32');
+ // macOS and Linux handle all clicks by displaying the context menu
+ // can show window by selecting 'Show app' on dropdown
+ // or clicking the app icon in the dock
+ };
+
+ #bindIpcListener = () => {
+ isDev && logger.electron('binding ipc listener for tray icon status');
+ Electron.ipcMain.on('tray', (_event, status) => {
+ isDev && logger.electron('received tray icon status:', status);
+ switch (status) {
+ case TrayIconStatus.LoggedOut: {
+ this.setImage(trayIcons[TrayIconStatus.LoggedOut]);
+ break;
+ }
+ case TrayIconStatus.Running: {
+ this.setImage(trayIcons[TrayIconStatus.Running]);
+ break;
+ }
+ case TrayIconStatus.Paused: {
+ this.setImage(trayIcons[TrayIconStatus.Paused]);
+ break;
+ }
+ case TrayIconStatus.LowGas: {
+ this.setImage(trayIcons[TrayIconStatus.LowGas]);
+ break;
+ }
+ default: {
+ logger.electron('Unknown tray icon status:', status);
+ }
+ }
+ });
+ };
+}
+
+/**
+ * Builds the context menu for the tray.
+ * @param {() => Electron.BrowserWindow | null} activeWindowCallback - A callback to retrieve the active window.
+ * @returns {Electron.Menu} The context menu for the tray.
+ */
+class PearlTrayContextMenu {
+ constructor(activeWindowCallback) {
+ return Electron.Menu.buildFromTemplate([
+ {
+ label: 'Show app',
+ click: () => activeWindowCallback()?.show(),
+ },
+ {
+ label: 'Hide app',
+ click: () => activeWindowCallback()?.hide(),
+ },
+ {
+ label: 'Quit',
+ click: async () => {
+ Electron.app.quit();
+ },
+ },
+ ]);
+ }
+}
+
+module.exports = { PearlTray };
diff --git a/electron/icons.js b/electron/icons.js
deleted file mode 100644
index 7c6c72e6..00000000
--- a/electron/icons.js
+++ /dev/null
@@ -1,30 +0,0 @@
-const { nativeImage } = require('electron');
-
-const TRAY_ICONS_PATHS = {
- LOGGED_OUT: `${__dirname}/assets/icons/tray-logged-out.png`,
- LOW_GAS: `${__dirname}/assets/icons/tray-low-gas.png`,
- PAUSED: `${__dirname}/assets/icons/tray-paused.png`,
- RUNNING: `${__dirname}/assets/icons/tray-running.png`,
-};
-
-const TRAY_ICONS = {
- LOGGED_OUT: nativeImage.createFromPath(TRAY_ICONS_PATHS.LOGGED_OUT),
- LOW_GAS: nativeImage.createFromPath(TRAY_ICONS_PATHS.LOW_GAS),
- PAUSED: nativeImage.createFromPath(TRAY_ICONS_PATHS.PAUSED),
- RUNNING: nativeImage.createFromPath(TRAY_ICONS_PATHS.RUNNING),
-};
-
-try {
- if (process.platform === 'darwin') {
- // resize icons for macOS
- const size = { width: 16, height: 16 };
- TRAY_ICONS.LOGGED_OUT = TRAY_ICONS.LOGGED_OUT.resize(size);
- TRAY_ICONS.LOW_GAS = TRAY_ICONS.LOW_GAS.resize({ width: 16, height: 16 });
- TRAY_ICONS.PAUSED = TRAY_ICONS.PAUSED.resize({ width: 16, height: 16 });
- TRAY_ICONS.RUNNING = TRAY_ICONS.RUNNING.resize({ width: 16, height: 16 });
- }
-} catch (e) {
- console.log('Error resizing tray icons', e);
-}
-
-module.exports = { TRAY_ICONS_PATHS, TRAY_ICONS };
diff --git a/electron/install.js b/electron/install.js
index 634b2d53..d37d05f2 100644
--- a/electron/install.js
+++ b/electron/install.js
@@ -14,7 +14,7 @@ const homedir = os.homedir();
* - use "" (nothing as a suffix) for latest release candidate, for example "0.1.0rc26"
* - use "alpha" for alpha release, for example "0.1.0rc26-alpha"
*/
-const OlasMiddlewareVersion = '0.1.0rc139';
+const OlasMiddlewareVersion = '0.1.0rc153';
const path = require('path');
const { app } = require('electron');
diff --git a/electron/main.js b/electron/main.js
index 6752d08c..139bc2de 100644
--- a/electron/main.js
+++ b/electron/main.js
@@ -1,8 +1,6 @@
const {
app,
BrowserWindow,
- Tray,
- Menu,
Notification,
ipcMain,
dialog,
@@ -15,18 +13,17 @@ const os = require('os');
const next = require('next');
const http = require('http');
const AdmZip = require('adm-zip');
-const { TRAY_ICONS, TRAY_ICONS_PATHS } = require('./icons');
const { setupDarwin, setupUbuntu, setupWindows, Env } = require('./install');
const { paths } = require('./constants');
const { killProcesses } = require('./processes');
const { isPortAvailable, findAvailablePort } = require('./ports');
-const { PORT_RANGE, isWindows, isMac } = require('./constants');
-const { macUpdater } = require('./update');
+const { PORT_RANGE } = require('./constants');
const { setupStoreIpc } = require('./store');
const { logger } = require('./logger');
const { isDev } = require('./constants');
+const { PearlTray } = require('./components/PearlTray');
// Attempt to acquire the single instance lock
const singleInstanceLock = app.requestSingleInstanceLock();
@@ -65,17 +62,17 @@ let appConfig = {
},
};
-/**
- * @type {BrowserWindow}
- */
-let mainWindow;
+/** @type {Electron.BrowserWindow | null} */
+let mainWindow = null;
+/** @type {Electron.BrowserWindow | null} */
+let splashWindow = null;
+
+/** @type {Electron.Tray | null} */
+let tray = null;
+
+let operateDaemon, operateDaemonPid, nextAppProcess, nextAppProcessPid;
-let tray,
- splashWindow,
- operateDaemon,
- operateDaemonPid,
- nextAppProcess,
- nextAppProcessPid;
+const getActiveWindow = () => splashWindow ?? mainWindow;
function showNotification(title, body) {
new Notification({ title, body }).show();
@@ -100,82 +97,10 @@ async function beforeQuit() {
}
tray?.destroy();
- mainWindow?.destroy();
splashWindow?.destroy();
+ mainWindow?.destroy();
}
-const getUpdatedTrayIcon = (iconPath) => {
- const icon = iconPath;
- if (icon.resize) {
- icon.resize({ width: 16 });
- icon.setTemplateImage(true);
- }
-
- return icon;
-};
-
-/**
- * Creates the tray
- */
-const createTray = () => {
- const trayPath = getUpdatedTrayIcon(
- isWindows || isMac ? TRAY_ICONS.LOGGED_OUT : TRAY_ICONS_PATHS.LOGGED_OUT,
- );
- tray = new Tray(trayPath);
-
- const contextMenu = Menu.buildFromTemplate([
- {
- label: 'Show app',
- click: () => mainWindow?.show(),
- },
- {
- label: 'Hide app',
- click: () => mainWindow?.hide(),
- },
- {
- label: 'Quit',
- click: () => app.quit(),
- },
- ]);
- tray.setToolTip('Pearl');
- tray.setContextMenu(contextMenu);
-
- ipcMain.on('tray', (_event, status) => {
- const isSupportedOS = isWindows || isMac;
- switch (status) {
- case 'low-gas': {
- const icon = getUpdatedTrayIcon(
- isSupportedOS ? TRAY_ICONS.LOW_GAS : TRAY_ICONS_PATHS.LOW_GAS,
- );
- tray.setImage(icon);
- break;
- }
- case 'running': {
- const icon = getUpdatedTrayIcon(
- isSupportedOS ? TRAY_ICONS.RUNNING : TRAY_ICONS_PATHS.RUNNING,
- );
- tray.setImage(icon);
-
- break;
- }
- case 'paused': {
- const icon = getUpdatedTrayIcon(
- isSupportedOS ? TRAY_ICONS.PAUSED : TRAY_ICONS_PATHS.PAUSED,
- );
- tray.setImage(icon);
- break;
- }
- case 'logged-out': {
- const icon = getUpdatedTrayIcon(
- isSupportedOS ? TRAY_ICONS.LOGGED_OUT : TRAY_ICONS_PATHS.LOGGED_OUT,
- );
- tray.setImage(icon);
- break;
- }
- }
- });
-};
-
const APP_WIDTH = 460;
/**
@@ -228,23 +153,23 @@ const createMainWindow = async () => {
mainWindow.setMenuBarVisibility(true);
ipcMain.on('close-app', () => {
- mainWindow.close();
+ mainWindow?.close();
});
ipcMain.on('minimize-app', () => {
- mainWindow.minimize();
+ mainWindow?.minimize();
});
app.on('activate', () => {
- if (mainWindow.isMinimized()) {
- mainWindow.restore();
+ if (mainWindow?.isMinimized()) {
+ mainWindow?.restore();
} else {
- mainWindow.show();
+ mainWindow?.show();
}
});
ipcMain.on('set-height', (_event, height) => {
- mainWindow.setSize(width, height);
+ mainWindow?.setSize(width, height);
});
ipcMain.on('show-notification', (_event, title, description) => {
@@ -517,7 +442,7 @@ ipcMain.on('check', async function (event, _argument) {
event.sender.send('response', 'Launching App');
await createMainWindow();
- createTray();
+ tray = new PearlTray(getActiveWindow);
} catch (e) {
logger.electron(e);
new Notification({
@@ -565,11 +490,6 @@ app.once('ready', async () => {
createSplashWindow();
});
-// UPDATER EVENTS
-macUpdater.on('update-downloaded', () => {
- macUpdater.quitAndInstall();
-});
-
// PROCESS SPECIFIC EVENTS (HANDLES NON-GRACEFUL TERMINATION)
process.on('uncaughtException', (error) => {
logger.electron('Uncaught Exception:', error);
diff --git a/electron/update.js b/electron/update.js
index 3946655a..fe71d90b 100644
--- a/electron/update.js
+++ b/electron/update.js
@@ -2,15 +2,26 @@ const { publishOptions } = require('./constants');
const electronUpdater = require('electron-updater');
const logger = require('./logger');
-const macUpdater = new electronUpdater.MacUpdater({
+const updateOptions = {
...publishOptions,
- channels: ['latest', 'beta', 'alpha'], // automatically update to all channels
+ // token is not required for macUpdater as repo is public, should overwrite it to undefined
+ token: undefined,
+ channels: ['latest', 'beta', 'alpha'],
+};
+
+const macUpdater = new electronUpdater.MacUpdater({
+ ...updateOptions,
});
-macUpdater.setFeedURL({ ...publishOptions });
+macUpdater.setFeedURL({ ...updateOptions });
-macUpdater.autoDownload = true;
-macUpdater.autoInstallOnAppQuit = true;
+macUpdater.autoDownload = false;
+macUpdater.autoInstallOnAppQuit = false;
macUpdater.logger = logger;
+// UPDATER EVENTS
+macUpdater.on('update-downloaded', () => {
+ macUpdater.quitAndInstall();
+});
+
module.exports = { macUpdater };
diff --git a/frontend/components/AddressLink.tsx b/frontend/components/AddressLink.tsx
new file mode 100644
index 00000000..83c4dfa9
--- /dev/null
+++ b/frontend/components/AddressLink.tsx
@@ -0,0 +1,25 @@
+import { UNICODE_SYMBOLS } from '@/constants/symbols';
+import { Address } from '@/types/Address';
+import { truncateAddress } from '@/utils/truncate';
+
+type AddressLinkProps = { address?: Address; hideLinkArrow?: boolean };
+
+export const AddressLink = ({
+ address,
+ hideLinkArrow = false,
+}: AddressLinkProps) => {
+ if (!address) return null;
+
+ return (
+
+ {truncateAddress(address)}
+
+ {hideLinkArrow ? null : (
+ <>
+
+ {UNICODE_SYMBOLS.EXTERNAL_LINK}
+ >
+ )}
+
+ );
+};
diff --git a/frontend/components/InfoBreakdown.tsx b/frontend/components/InfoBreakdown.tsx
index 6fff80bc..ed26b11e 100644
--- a/frontend/components/InfoBreakdown.tsx
+++ b/frontend/components/InfoBreakdown.tsx
@@ -37,7 +37,13 @@ const Line = styled.span<{ color?: Color }>`
`1px solid ${color === 'primary' ? COLOR.PURPLE_LIGHT : COLOR.BORDER_GRAY}`};
`;
-type Info = { id?: number | string; left: ReactNode; right: ReactNode };
+type Info = {
+ id?: number | string;
+ left: ReactNode;
+ leftClassName?: string;
+ right: ReactNode;
+ rightClassName?: string;
+};
type InfoBreakdownListProps = {
list: Info[];
parentStyle?: CSSProperties;
@@ -54,9 +60,11 @@ export const InfoBreakdownList = ({
{list.map((item, index) => (
- {item.left}
+ {item.left}
- {item.right}
+
+ {item.right}
+
))}
diff --git a/frontend/components/InfoTooltip.tsx b/frontend/components/InfoTooltip.tsx
new file mode 100644
index 00000000..54529d0d
--- /dev/null
+++ b/frontend/components/InfoTooltip.tsx
@@ -0,0 +1,16 @@
+import { InfoCircleOutlined } from '@ant-design/icons';
+import Tooltip, { TooltipPlacement } from 'antd/es/tooltip';
+
+import { COLOR } from '@/constants/colors';
+
+export const InfoTooltip = ({
+ placement = 'topLeft',
+ children,
+}: {
+ placement?: TooltipPlacement;
+ children: React.ReactNode;
+}) => (
+
+
+
+);
diff --git a/frontend/components/Layout/TopBar.tsx b/frontend/components/Layout/TopBar.tsx
index ddaf1701..9428fc7f 100644
--- a/frontend/components/Layout/TopBar.tsx
+++ b/frontend/components/Layout/TopBar.tsx
@@ -35,8 +35,10 @@ const TrafficLights = styled.div`
`;
const TopBarContainer = styled.div`
- position: sticky;
+ position: fixed;
top: 0;
+ left: 0;
+ right: 0;
z-index: 1;
display: flex;
align-items: center;
diff --git a/frontend/components/Layout/index.tsx b/frontend/components/Layout/index.tsx
index 42d6f1fd..42610c1b 100644
--- a/frontend/components/Layout/index.tsx
+++ b/frontend/components/Layout/index.tsx
@@ -32,6 +32,13 @@ const Container = styled.div<{ blur: 'true' | 'false' }>`
`}
`;
+const Body = styled.div`
+ // check main.js for the height of the app ie, 700px
+ max-height: calc(700px - 45px);
+ padding-top: 45px;
+ overflow-y: auto;
+`;
+
export const Layout = ({
children,
}: PropsWithChildren & { vertical?: boolean }) => {
@@ -53,7 +60,7 @@ export const Layout = ({
return (
- {children}
+ {children}
);
};
diff --git a/frontend/components/MainPage/MainHeader/FirstRunModal.tsx b/frontend/components/MainPage/MainHeader/FirstRunModal.tsx
index 73f017d7..ce75f568 100644
--- a/frontend/components/MainPage/MainHeader/FirstRunModal.tsx
+++ b/frontend/components/MainPage/MainHeader/FirstRunModal.tsx
@@ -2,6 +2,7 @@ import { Button, Flex, Modal, Typography } from 'antd';
import Image from 'next/image';
import { FC } from 'react';
+import { MODAL_WIDTH } from '@/constants/width';
import { useServiceTemplates } from '@/hooks/useServiceTemplates';
import { getMinimumStakedAmountRequired } from '@/utils/service';
@@ -19,7 +20,7 @@ export const FirstRunModal: FC = ({ open, onClose }) => {
return (
(
arrow={false}
title={
- Your agent earned rewards for this epoch and stopped working. It’ll
- return to work once the next epoch starts.
+ Your agent earned rewards for this epoch, so decided to stop working
+ until the next epoch.
}
>
@@ -222,7 +223,7 @@ const AgentNotRunningButton = () => {
setServiceStatus(DeploymentStatus.DEPLOYED);
// TODO: remove this workaround, middleware should respond when agent is staked & confirmed running after `createService` call
- await new Promise((resolve) => setTimeout(resolve, 5000));
+ await delayInSeconds(5);
// update provider states sequentially
// service id is required before activeStakingContractInfo & balances can be updated
diff --git a/frontend/components/MainPage/header/LastTransaction.tsx b/frontend/components/MainPage/header/LastTransaction.tsx
index 91595e09..f229d1ce 100644
--- a/frontend/components/MainPage/header/LastTransaction.tsx
+++ b/frontend/components/MainPage/header/LastTransaction.tsx
@@ -1,14 +1,25 @@
-import { Typography } from 'antd';
-import { useCallback, useState } from 'react';
+import { Skeleton, Typography } from 'antd';
+import { useCallback, useEffect, useState } from 'react';
+import styled from 'styled-components';
import { useInterval } from 'usehooks-ts';
import { useAddress } from '@/hooks/useAddress';
+import { usePageState } from '@/hooks/usePageState';
import { getLatestTransaction } from '@/service/Ethers';
import { TransactionInfo } from '@/types/TransactionInfo';
import { getTimeAgo } from '@/utils/time';
const { Text } = Typography;
+const Loader = styled(Skeleton.Input)`
+ line-height: 1;
+ span {
+ width: 120px !important;
+ height: 12px !important;
+ margin-top: 6px !important;
+ }
+`;
+
const POLLING_INTERVAL = 60 * 1000; // 1 minute
/**
@@ -16,6 +27,7 @@ const POLLING_INTERVAL = 60 * 1000; // 1 minute
* by agent safe.
*/
export const LastTransaction = () => {
+ const { isPageLoadedAndOneMinutePassed } = usePageState();
const { multisigAddress } = useAddress();
const [isFetching, setIsFetching] = useState(true);
@@ -35,7 +47,17 @@ export const LastTransaction = () => {
// Poll for the latest transaction
useInterval(() => fetchTransaction(), POLLING_INTERVAL);
- if (isFetching) return null;
+ // Fetch the latest transaction on mount
+ useEffect(() => {
+ fetchTransaction();
+ }, [fetchTransaction]);
+
+ // Do not show the last transaction if the delay is not reached
+ if (!isPageLoadedAndOneMinutePassed) return null;
+
+ if (isFetching) {
+ return ;
+ }
if (!transaction) {
return (
diff --git a/frontend/components/MainPage/modals/FirstRunModal.tsx b/frontend/components/MainPage/modals/FirstRunModal.tsx
index 78de3d55..0c57b3b0 100644
--- a/frontend/components/MainPage/modals/FirstRunModal.tsx
+++ b/frontend/components/MainPage/modals/FirstRunModal.tsx
@@ -2,6 +2,7 @@ import { Button, Flex, Modal, Typography } from 'antd';
import Image from 'next/image';
import { FC } from 'react';
+import { MODAL_WIDTH } from '@/constants/width';
import { useServiceTemplates } from '@/hooks/useServiceTemplates';
import { getMinimumStakedAmountRequired } from '@/utils/service';
@@ -21,7 +22,7 @@ export const FirstRunModal: FC = ({ open, onClose }) => {
return (
`
`;
export const AddFundsSection = () => {
+ const fundSectionRef = useRef(null);
const [isAddFundsVisible, setIsAddFundsVisible] = useState(false);
+ const addFunds = useCallback(async () => {
+ setIsAddFundsVisible(true);
+
+ await delayInSeconds(0.1);
+ fundSectionRef?.current?.scrollIntoView({ behavior: 'smooth' });
+ }, []);
+ const closeAddFunds = useCallback(() => setIsAddFundsVisible(false), []);
+
return (
<>
@@ -57,12 +67,12 @@ export const AddFundsSection = () => {
- {isAddFundsVisible && }
+ {isAddFundsVisible && }
>
);
};
-export const OpenAddFundsSection = () => {
+export const OpenAddFundsSection = forwardRef((_, ref) => {
const { masterSafeAddress } = useWallet();
const truncatedFundingAddress: string | undefined = useMemo(
@@ -79,7 +89,7 @@ export const OpenAddFundsSection = () => {
[masterSafeAddress],
);
return (
- <>
+ {
handleCopy={handleCopyAddress}
/>
- >
+
);
-};
+});
+OpenAddFundsSection.displayName = 'OpenAddFundsSection';
const AddFundsWarningAlertSection = () => (
diff --git a/frontend/components/MainPage/sections/NeedsFundsSection.tsx b/frontend/components/MainPage/sections/NeedsFundsSection.tsx
index 71d0604f..b153611c 100644
--- a/frontend/components/MainPage/sections/NeedsFundsSection.tsx
+++ b/frontend/components/MainPage/sections/NeedsFundsSection.tsx
@@ -51,7 +51,6 @@ export const MainNeedsFunds = () => {
)}
-
Do not add more than these amounts.
Use the address in the “Add Funds” section below.
diff --git a/frontend/components/MainPage/sections/OlasBalanceSection.tsx b/frontend/components/MainPage/sections/OlasBalanceSection.tsx
index 8b8dfd9a..45e5d8a5 100644
--- a/frontend/components/MainPage/sections/OlasBalanceSection.tsx
+++ b/frontend/components/MainPage/sections/OlasBalanceSection.tsx
@@ -1,15 +1,15 @@
-import { InfoCircleOutlined } from '@ant-design/icons';
-import { Button, Flex, Skeleton, Tooltip, Typography } from 'antd';
+import { RightOutlined } from '@ant-design/icons';
+import { Button, Flex, Skeleton, Typography } from 'antd';
import { useMemo } from 'react';
import styled from 'styled-components';
import { CustomAlert } from '@/components/Alert';
-import { InfoBreakdownList } from '@/components/InfoBreakdown';
import { UNICODE_SYMBOLS } from '@/constants/symbols';
import { LOW_MASTER_SAFE_BALANCE } from '@/constants/thresholds';
+import { Pages } from '@/enums/PageState';
import { useBalance } from '@/hooks/useBalance';
import { useElectronApi } from '@/hooks/useElectronApi';
-import { useReward } from '@/hooks/useReward';
+import { usePageState } from '@/hooks/usePageState';
import { useStore } from '@/hooks/useStore';
import { balanceFormat } from '@/utils/numberFormatters';
@@ -21,59 +21,6 @@ const Balance = styled.span`
margin-right: 4px;
`;
-const OVERLAY_STYLE = { maxWidth: '300px', width: '300px' };
-
-const CurrentBalance = () => {
- const { totalOlasBalance, totalOlasStakedBalance } = useBalance();
- const { accruedServiceStakingRewards } = useReward();
-
- const balances = useMemo(() => {
- return [
- {
- title: 'Staked amount',
- value: balanceFormat(totalOlasStakedBalance ?? 0, 2),
- },
- {
- title: 'Unclaimed rewards',
- value: balanceFormat(accruedServiceStakingRewards ?? 0, 2),
- },
- {
- // Unused funds should only be ‘free-floating’ OLAS that is neither unclaimed nor staked.
- title: 'Unused funds',
- value: balanceFormat(
- (totalOlasBalance ?? 0) -
- (totalOlasStakedBalance ?? 0) -
- (accruedServiceStakingRewards ?? 0),
- 2,
- ),
- },
- ];
- }, [accruedServiceStakingRewards, totalOlasBalance, totalOlasStakedBalance]);
-
- return (
-
- Current balance
- ({
- left: item.title,
- right: `${item.value} OLAS`,
- }))}
- size="small"
- parentStyle={{ padding: 4, gap: 8 }}
- />
- }
- >
-
-
-
- );
-};
-
const MainOlasBalanceAlert = styled.div`
.ant-alert {
margin-bottom: 8px;
@@ -111,8 +58,8 @@ const LowTradingBalanceAlert = () => {
{`To run your agent, add at least $${LOW_MASTER_SAFE_BALANCE} XDAI to your account.`}
- Do it quickly to avoid your agent missing its targets and getting
- suspended!
+ Your agent is at risk of missing its targets, which would result
+ in several days' suspension.
}
@@ -136,10 +83,10 @@ const AvoidSuspensionAlert = () => {
Avoid suspension!
- Run your agent for at least half an hour a day to make sure it
- hits its targets. If it misses its targets 2 days in a row, it’ll
- be suspended. You won’t be able to run it or earn rewards for
- several days.
+ Run your agent for at least half an hour a day to avoid missing
+ targets. If it misses its targets 2 days in a row, it’ll be
+ suspended. You won’t be able to run it or earn rewards for several
+ days.