Compare commits

..

30 Commits

Author SHA1 Message Date
fmtabbara 77d7fede76 update build script 2024-07-18 15:55:12 +01:00
fmtabbara f6929e365b refactor: Remove unused imports in nodemap page component 2024-07-18 15:27:53 +01:00
fmtabbara f98911f904 temp fix for ci 2024-07-18 15:04:20 +01:00
fmtabbara c1be720e49 fix linting 2024-07-18 14:41:20 +01:00
fmtabbara b4a719dbab fix next js explorer 2024-07-18 12:44:54 +01:00
fmtabbara ce99ccea44 fix pnpm audit recommendations 2024-07-18 12:37:30 +01:00
fmtabbara 595a76ee50 fix next explorer build 2024-07-18 12:37:30 +01:00
fmtabbara dda7c09ec8 fix wallet linting 2024-07-18 12:37:30 +01:00
fmtabbara c27713c678 fix wallet build + storybook 2024-07-18 12:37:30 +01:00
fmtabbara b8125d735e fix docs ci 2024-07-18 12:37:30 +01:00
fmtabbara 96617469e9 fix storybook build 2024-07-18 12:37:30 +01:00
fmtabbara 10ade80da0 fix storybook for react components lib 2024-07-18 12:37:28 +01:00
fmtabbara 6c8d5c129d fix up linting 2024-07-18 12:37:06 +01:00
fmtabbara 4c01ee0150 fix up CI 2024-07-18 12:37:06 +01:00
fmtabbara 9c85d51e34 fix multi package scripts 2024-07-18 12:37:06 +01:00
fmtabbara 30dc257ba7 ci updates 2024-07-18 12:37:06 +01:00
fmtabbara 1878720a31 wallet updates 2024-07-18 12:37:06 +01:00
fmtabbara ae0d9f3179 package updates 2024-07-18 12:37:06 +01:00
fmtabbara 1688eb9477 use components from react-components lib 2024-07-18 12:37:06 +01:00
fmtabbara 3c1939953c new wallet build using vite 2024-07-18 12:37:06 +01:00
fmtabbara 7de43540b9 bundle react components library with vite 2024-07-18 12:37:04 +01:00
fmtabbara 979244f9da update wallet deps 2024-07-18 12:34:39 +01:00
fmtabbara 9e555ebd8a fix types package build 2024-07-18 12:34:39 +01:00
fmtabbara 579ed7bb6e update sdk build scripts 2024-07-18 12:34:39 +01:00
fmtabbara 1655933678 remove lerna 2024-07-18 12:34:39 +01:00
fmtabbara cd2978a8b0 finish next-explorer build 2024-07-18 12:34:39 +01:00
fmtabbara 6fcdbc15ce update ts CI 2024-07-18 12:34:39 +01:00
fmtabbara ceee2d4466 yarn -> pnpm 2024-07-18 12:34:37 +01:00
fmtabbara 8ee3ec2f4e replace yarn with pnpm 2024-07-18 12:32:50 +01:00
import this 3ad3837c87 done: fix wrong URL and picture formatting (#4714) 2024-07-17 10:47:41 +00:00
568 changed files with 80660 additions and 46503 deletions
+58 -54
View File
@@ -10,57 +10,61 @@ jobs:
build:
runs-on: ubuntu-20.04-16-core
steps:
- uses: actions/checkout@v2
- name: Install rsync
run: sudo apt-get install rsync
continue-on-error: true
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup yarn
run: npm install -g yarn
- name: Build
run: yarn && yarn build && yarn build:ci:storybook
- name: Deploy branch to CI www (storybook)
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "ts-packages/dist/storybook/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/ts-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Deploy branch to CI www (example)
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "ts-packages/dist/example/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/ts-${{ env.GITHUB_REF_SLUG }}-example
EXCLUDE: "/dist/, /node_modules/"
- name: Matrix - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Matrix - Send Notification
env:
NYM_NOTIFICATION_KIND: ts-packages
NYM_PROJECT_NAME: "ts-packages"
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
NYM_CI_WWW_LOCATION: "ts-${{ env.GITHUB_REF_SLUG }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
IS_SUCCESS: "${{ job.status == 'success' }}"
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
- uses: actions/checkout@v2
- name: Install rsync
run: sudo apt-get install rsync
continue-on-error: true
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
run_install: true
- name: Build
run: pnpm i && pnpm run build && pnpm run build:ci:storybook
- name: Deploy branch to CI www (storybook)
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "ts-packages/dist/storybook/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/ts-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Deploy branch to CI www (example)
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "ts-packages/dist/example/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/ts-${{ env.GITHUB_REF_SLUG }}-example
EXCLUDE: "/dist/, /node_modules/"
- name: Matrix - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Matrix - Send Notification
env:
NYM_NOTIFICATION_KIND: ts-packages
NYM_PROJECT_NAME: "ts-packages"
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
NYM_CI_WWW_LOCATION: "ts-${{ env.GITHUB_REF_SLUG }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
IS_SUCCESS: "${{ job.status == 'success' }}"
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
+11 -10
View File
@@ -21,8 +21,12 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup yarn
run: npm install -g yarn
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
run_install: true
- name: Install Rust stable
uses: actions-rs/toolchain@v1
@@ -35,23 +39,20 @@ jobs:
- name: Install wasm-opt
uses: ./.github/actions/install-wasm-opt
with:
version: '116'
version: "116"
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.20'
- name: Install
run: yarn
go-version: "1.20"
- name: Build packages
run: yarn build:ci
run: pnpm run build:ci
- name: Lint
run: yarn lint
run: pnpm run lint
- name: Typecheck with tsc
run: yarn tsc
run: pnpm run tsc
- name: Matrix - Node Install
run: npm install
+80 -76
View File
@@ -4,7 +4,7 @@ on:
workflow_dispatch:
push:
paths:
- 'explorer/**'
- "explorer/**"
defaults:
run:
@@ -14,78 +14,82 @@ jobs:
build:
runs-on: custom-linux
steps:
- uses: actions/checkout@v2
- name: Install rsync
run: sudo apt-get install rsync
continue-on-error: true
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup yarn
run: npm install -g yarn
continue-on-error: true
- name: Build shared packages
run: cd .. && yarn && yarn build
- name: Set environment from the example
run: cp .env.prod .env
# - run: yarn test
# continue-on-error: true
- run: yarn && yarn build
continue-on-error: true
- run: yarn storybook:build
name: Build storybook
- name: Deploy branch to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "explorer/dist/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/network-explorer-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Deploy storybook to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "explorer/storybook-static/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/ne-sb-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Matrix - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Matrix - Send Notification
env:
NYM_NOTIFICATION_KIND: network-explorer
NYM_PROJECT_NAME: "Network Explorer"
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
NYM_CI_WWW_LOCATION: "network-explorer-${{ env.GITHUB_REF_SLUG }}"
NYM_CI_WWW_LOCATION_STORYBOOK: "ne-sb-${{ env.GITHUB_REF_SLUG }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
IS_SUCCESS: "${{ job.status == 'success' }}"
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
- name: Deploy
if: github.event_name == 'workflow_dispatch'
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CD_PROD_NE_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "explorer/dist/"
REMOTE_HOST: ${{ secrets.CD_PROD_NE_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CD_PROD_NE_REMOTE_USER }}
TARGET: ${{ secrets.CD_PROD_NE_REMOTE_TARGET }}
EXCLUDE: "/dist/, /node_modules/"
- uses: actions/checkout@v2
- name: Install rsync
run: sudo apt-get install rsync
continue-on-error: true
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
continue-on-error: true
- name: Build shared packages
run: cd .. && yarn && yarn build
- name: Set environment from the example
run: cp .env.prod .env
# - run: yarn test
# continue-on-error: true
- run: yarn && yarn build
continue-on-error: true
- run: yarn storybook:build
name: Build storybook
- name: Deploy branch to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "explorer/dist/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/network-explorer-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Deploy storybook to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "explorer/storybook-static/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/ne-sb-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Matrix - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Matrix - Send Notification
env:
NYM_NOTIFICATION_KIND: network-explorer
NYM_PROJECT_NAME: "Network Explorer"
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
NYM_CI_WWW_LOCATION: "network-explorer-${{ env.GITHUB_REF_SLUG }}"
NYM_CI_WWW_LOCATION_STORYBOOK: "ne-sb-${{ env.GITHUB_REF_SLUG }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
IS_SUCCESS: "${{ job.status == 'success' }}"
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
- name: Deploy
if: github.event_name == 'workflow_dispatch'
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CD_PROD_NE_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "explorer/dist/"
REMOTE_HOST: ${{ secrets.CD_PROD_NE_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CD_PROD_NE_REMOTE_USER }}
TARGET: ${{ secrets.CD_PROD_NE_REMOTE_TARGET }}
EXCLUDE: "/dist/, /node_modules/"
+56 -53
View File
@@ -3,72 +3,75 @@ name: ci-nym-wallet-storybook
on:
pull_request:
paths:
- 'nym-wallet/**'
- "nym-wallet/**"
jobs:
build:
runs-on: custom-linux
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v2
- name: Install rsync
run: sudo apt-get install rsync
continue-on-error: true
- name: Install rsync
run: sudo apt-get install rsync
continue-on-error: true
- uses: rlespinasse/github-slug-action@v3.x
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: 18
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup yarn
run: npm install -g yarn
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
run_install: true
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Build dependencies
run: yarn && yarn build
- name: Build dependencies
run: pnpm build
- name: Build storybook
run: yarn storybook:build
working-directory: ./nym-wallet
- name: Build storybook
run: pnpm run storybook:build
working-directory: ./wallet
- name: Deploy branch to CI www (storybook)
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "nym-wallet/storybook-static/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/wallet-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Deploy branch to CI www (storybook)
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "nym-wallet/storybook-static/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/wallet-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Matrix - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Matrix - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Matrix - Send Notification
env:
NYM_NOTIFICATION_KIND: nym-wallet
NYM_PROJECT_NAME: "nym-wallet"
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
NYM_CI_WWW_LOCATION: "wallet-${{ env.GITHUB_REF_SLUG }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
IS_SUCCESS: "${{ job.status == 'success' }}"
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
- name: Matrix - Send Notification
env:
NYM_NOTIFICATION_KIND: nym-wallet
NYM_PROJECT_NAME: "nym-wallet"
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
NYM_CI_WWW_LOCATION: "wallet-${{ env.GITHUB_REF_SLUG }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
IS_SUCCESS: "${{ job.status == 'success' }}"
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
+15 -11
View File
@@ -17,18 +17,23 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: 18.17
node-version: 18
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Setup yarn
run: npm install -g yarn
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
run_install: true
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.20'
go-version: "1.20"
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
@@ -36,16 +41,15 @@ jobs:
- name: Install wasm-opt
uses: ./.github/actions/install-wasm-opt
with:
version: '116'
version: "116"
- name: Build branch WASM packages
run: make sdk-wasm-build
- name: Install
run: yarn
- name: Build
run: yarn docs:prod:build
- name: Deploy branch to CI www (docs)
run: pnpm docs:prod:build
- name: Deploy branch to CI www (docs)
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
@@ -57,7 +61,7 @@ jobs:
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/sdk-ts-docs-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Matrix - Node Install
run: npm install
run: pnpm install
working-directory: .github/workflows/support-files
- name: Matrix - Send Notification
env:
@@ -75,4 +79,4 @@ jobs:
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
args: .github/workflows/support-files/notifications/entry_point.sh
@@ -61,12 +61,12 @@ jobs:
- name: Create env file
uses: timheuer/base64-to-file@v1.2
with:
fileName: '.env'
fileName: ".env"
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
run: cd .. && pnpm i --network-timeout 100000
- name: Install app dependencies and build it
env:
@@ -80,7 +80,7 @@ jobs:
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
run: yarn && yarn build
run: pnpm i && pnpm build
- name: Upload Artifact
uses: actions/upload-artifact@v3
@@ -112,7 +112,7 @@ jobs:
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/${{ github.ref_name }}/nym-wallet
EXCLUDE: "/dist/, /node_modules/"
EXCLUDE: "/dist/, /node_modules/"
push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
Generated
+7 -7
View File
@@ -2221,7 +2221,7 @@ dependencies = [
"atomic 0.6.0",
"pear",
"serde",
"toml 0.8.14",
"toml 0.8.15",
"uncased",
"version_check",
]
@@ -5037,7 +5037,7 @@ dependencies = [
"sysinfo 0.30.12",
"thiserror",
"tokio",
"toml 0.8.14",
"toml 0.8.15",
"tracing",
"url",
"zeroize",
@@ -8626,14 +8626,14 @@ dependencies = [
[[package]]
name = "toml"
version = "0.8.14"
version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335"
checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit 0.22.14",
"toml_edit 0.22.16",
]
[[package]]
@@ -8660,9 +8660,9 @@ dependencies = [
[[package]]
name = "toml_edit"
version = "0.22.14"
version = "0.22.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788"
dependencies = [
"indexmap 2.2.6",
"serde",
+7 -7
View File
@@ -107,12 +107,12 @@ sdk-wasm-build:
$(MAKE) -C wasm/zknym-lib
#$(MAKE) -C wasm/full-nym-wasm
# run this from npm/yarn to ensure tools are in the path, e.g. yarn build:sdk from root of repo
# run this from npm/pnpm to ensure tools are in the path, e.g. pnpm build:sdk from root of repo
sdk-typescript-build:
npx lerna run --scope @nymproject/sdk build --stream
npx lerna run --scope @nymproject/mix-fetch build --stream
npx lerna run --scope @nymproject/node-tester build --stream
yarn --cwd sdk/typescript/codegen/contract-clients build
pnpm run --filter @nymproject/sdk --stream build
pnpm run --filter @nymproject/mix-fetch --stream build
pnpm run --filter @nymproject/node-tester --stream build
pnpm --cwd sdk/typescript/codegen/contract-clients build
# NOTE: These targets are part of the main workspace (but not as wasm32-unknown-unknown)
WASM_CRATES = extension-storage nym-client-wasm nym-node-tester-wasm zknym-lib
@@ -170,10 +170,10 @@ build-nym-mixnode:
generate-typescript:
cd tools/ts-rs-cli && cargo run && cd ../..
yarn types:lint:fix
pnpm types:lint:fix
run-api-tests:
cd nym-api/tests/functional_test && yarn test:qa
cd nym-api/tests/functional_test && pnpm test:qa
# Build debian package, and update PPA
deb-mixnode: build-nym-mixnode
@@ -1,26 +1,15 @@
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use cosmwasm_std::Decimal;
use log::{debug, info};
use nym_mixnet_contract_common::{
InitialRewardingParams, InstantiateMsg, OperatingCostRange, Percent, ProfitMarginRange,
};
use nym_network_defaults::mainnet::MIX_DENOM;
use nym_network_defaults::TOTAL_SUPPLY;
use nym_validator_client::nyxd::{AccountId, Coin};
use cosmwasm_std::Decimal;
use nym_mixnet_contract_common::{InitialRewardingParams, InstantiateMsg, Percent};
use nym_validator_client::nyxd::AccountId;
use std::str::FromStr;
use std::time::Duration;
pub fn default_maximum_operating_cost() -> Coin {
Coin::new(TOTAL_SUPPLY, MIX_DENOM.base)
}
pub fn default_minimum_operating_cost() -> Coin {
Coin::new(0, MIX_DENOM.base)
}
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
@@ -61,18 +50,6 @@ pub struct Args {
#[clap(long, default_value_t = 240)]
pub active_set_size: u32,
#[clap(long, default_value_t = Percent::zero())]
pub minimum_profit_margin_percent: Percent,
#[clap(long, default_value_t = Percent::hundred())]
pub maximum_profit_margin_percent: Percent,
#[clap(long, default_value_t = default_minimum_operating_cost())]
pub minimum_interval_operating_cost: Coin,
#[clap(long, default_value_t = default_maximum_operating_cost())]
pub maximum_interval_operating_cost: Coin,
}
pub async fn generate(args: Args) {
@@ -120,10 +97,6 @@ pub async fn generate(args: Args) {
.expect("Rewarding (mix) denom has to be set")
});
if args.minimum_interval_operating_cost.denom != args.maximum_interval_operating_cost.denom {
panic!("different denoms for operating cost bounds")
}
let instantiate_msg = InstantiateMsg {
rewarding_validator_address: rewarding_validator_address.to_string(),
vesting_contract_address: vesting_contract_address.to_string(),
@@ -131,14 +104,6 @@ pub async fn generate(args: Args) {
epochs_in_interval: args.epochs_in_interval,
epoch_duration: Duration::from_secs(args.epoch_duration),
initial_rewarding_params,
profit_margin: ProfitMarginRange {
minimum: args.minimum_profit_margin_percent,
maximum: args.maximum_profit_margin_percent,
},
interval_operating_cost: OperatingCostRange {
minimum: args.minimum_interval_operating_cost.amount.into(),
maximum: args.maximum_interval_operating_cost.amount.into(),
},
};
debug!("instantiate_msg: {:?}", instantiate_msg);
@@ -1,9 +1,8 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{EpochEventId, EpochState, IdentityKey, MixId, OperatingCostRange, ProfitMarginRange};
use crate::{EpochEventId, EpochState, IdentityKey, MixId};
use contracts_common::signing::verifier::ApiVerifierError;
use contracts_common::Percent;
use cosmwasm_std::{Addr, Coin, Decimal, Uint128};
use thiserror::Error;
@@ -240,19 +239,6 @@ pub enum MixnetContractError {
#[from]
source: ApiVerifierError,
},
#[error("the provided profit margin ({provided}) is outside the allowed range: {range}")]
ProfitMarginOutsideRange {
provided: Percent,
range: ProfitMarginRange,
},
#[error("the provided interval operating cost ({provided}{denom}) is outside the allowed range: {range}")]
OperatingCostOutsideRange {
denom: String,
provided: Uint128,
range: OperatingCostRange,
},
}
impl MixnetContractError {
@@ -10,10 +10,7 @@ use crate::helpers::IntoBaseDecimal;
use crate::reward_params::{NodeRewardParams, RewardingParams};
use crate::rewarding::helpers::truncate_reward;
use crate::rewarding::RewardDistribution;
use crate::{
Delegation, EpochEventId, EpochId, IdentityKey, MixId, OperatingCostRange, Percent,
ProfitMarginRange, SphinxKey,
};
use crate::{Delegation, EpochEventId, EpochId, IdentityKey, MixId, Percent, SphinxKey};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Addr, Coin, Decimal, StdResult, Uint128};
use schemars::JsonSchema;
@@ -155,16 +152,6 @@ impl MixNodeRewarding {
})
}
pub fn normalise_profit_margin(&mut self, allowed_range: ProfitMarginRange) {
self.cost_params.profit_margin_percent =
allowed_range.normalise(self.cost_params.profit_margin_percent)
}
pub fn normalise_operating_cost(&mut self, allowed_range: OperatingCostRange) {
self.cost_params.interval_operating_cost.amount =
allowed_range.normalise(self.cost_params.interval_operating_cost.amount)
}
/// Determines whether this node is still bonded. This is performed via a simple check,
/// if there are no tokens left associated with the operator, it means they have unbonded
/// and those params only exist for the purposes of calculating rewards for delegators that
@@ -1,4 +1,4 @@
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::delegation::{self, OwnerProxySubKey};
@@ -12,7 +12,6 @@ use crate::reward_params::{
IntervalRewardParams, IntervalRewardingParamsUpdate, Performance, RewardingParams,
};
use crate::types::{ContractStateParams, LayerAssignment, MixId};
use crate::{OperatingCostRange, ProfitMarginRange};
use contracts_common::{signing::MessageSignature, IdentityKey, Percent};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Coin, Decimal};
@@ -58,12 +57,6 @@ pub struct InstantiateMsg {
pub epochs_in_interval: u32,
pub epoch_duration: Duration,
pub initial_rewarding_params: InitialRewardingParams,
#[serde(default)]
pub profit_margin: ProfitMarginRange,
#[serde(default)]
pub interval_operating_cost: OperatingCostRange,
}
#[cw_serde]
@@ -3,11 +3,9 @@
use crate::error::MixnetContractError;
use crate::Layer;
use contracts_common::Percent;
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Addr;
use cosmwasm_std::Coin;
use cosmwasm_std::{Addr, Uint128};
use std::fmt::{Display, Formatter};
use std::ops::Index;
// type aliases for better reasoning about available data
@@ -17,65 +15,6 @@ pub type SphinxKeyRef<'a> = &'a str;
pub type MixId = u32;
pub type BlockHeight = u64;
#[cw_serde]
pub struct RangedValue<T> {
pub minimum: T,
pub maximum: T,
}
impl<T> Copy for RangedValue<T> where T: Copy {}
pub type ProfitMarginRange = RangedValue<Percent>;
pub type OperatingCostRange = RangedValue<Uint128>;
impl<T> Display for RangedValue<T>
where
T: Display,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{} - {}", self.minimum, self.maximum)
}
}
impl Default for ProfitMarginRange {
fn default() -> Self {
ProfitMarginRange {
minimum: Percent::zero(),
maximum: Percent::hundred(),
}
}
}
impl Default for OperatingCostRange {
fn default() -> Self {
OperatingCostRange {
minimum: Uint128::zero(),
// 1 billion (native tokens, i.e. 1 billion * 1'000'000 base tokens) - the total supply
maximum: Uint128::new(1_000_000_000_000_000),
}
}
}
impl<T> RangedValue<T>
where
T: Copy + PartialOrd + PartialEq,
{
pub fn normalise(&self, value: T) -> T {
if value < self.minimum {
self.minimum
} else if value > self.maximum {
self.maximum
} else {
value
}
}
pub fn within_range(&self, value: T) -> bool {
value >= self.minimum && value <= self.maximum
}
}
/// Specifies layer assignment for the given mixnode.
#[cw_serde]
pub struct LayerAssignment {
@@ -215,14 +154,4 @@ pub struct ContractStateParams {
/// Minimum amount a gateway must pledge to get into the system.
pub minimum_gateway_pledge: Coin,
/// Defines the allowed profit margin range of operators.
/// default: 0% - 100%
#[serde(default)]
pub profit_margin: ProfitMarginRange,
/// Defines the allowed interval operating cost range of operators.
/// default: 0 - 1'000'000'000'000'000 (1 Billion native tokens - the total supply)
#[serde(default)]
pub interval_operating_cost: OperatingCostRange,
}
-4
View File
@@ -1,4 +1,3 @@
use nym_mixnet_contract_common::ContractsCommonError;
use nym_validator_client::error::TendermintRpcError;
use nym_validator_client::nym_api::error::NymAPIError;
use nym_validator_client::{nyxd::error::NyxdError, ValidatorClientError};
@@ -9,9 +8,6 @@ use thiserror::Error;
// TODO: ask @MS why this even exists
#[derive(Error, Debug)]
pub enum TypesError {
#[error(transparent)]
ContractsCommon(#[from] ContractsCommonError),
#[error("{source}")]
NyxdError {
#[from]
@@ -24,7 +24,5 @@ pub fn default_mixnet_init_msg() -> nym_mixnet_contract_common::InstantiateMsg {
rewarded_set_size: 240,
active_set_size: 100,
},
profit_margin: Default::default(),
interval_operating_cost: Default::default(),
}
}
@@ -26,28 +26,6 @@
"initial_rewarding_params": {
"$ref": "#/definitions/InitialRewardingParams"
},
"interval_operating_cost": {
"default": {
"maximum": "1000000000000000",
"minimum": "0"
},
"allOf": [
{
"$ref": "#/definitions/RangedValue_for_Uint128"
}
]
},
"profit_margin": {
"default": {
"maximum": "1",
"minimum": "0"
},
"allOf": [
{
"$ref": "#/definitions/RangedValue_for_Percent"
}
]
},
"rewarding_denom": {
"type": "string"
},
@@ -134,42 +112,6 @@
"$ref": "#/definitions/Decimal"
}
]
},
"RangedValue_for_Percent": {
"type": "object",
"required": [
"maximum",
"minimum"
],
"properties": {
"maximum": {
"$ref": "#/definitions/Percent"
},
"minimum": {
"$ref": "#/definitions/Percent"
}
},
"additionalProperties": false
},
"RangedValue_for_Uint128": {
"type": "object",
"required": [
"maximum",
"minimum"
],
"properties": {
"maximum": {
"$ref": "#/definitions/Uint128"
},
"minimum": {
"$ref": "#/definitions/Uint128"
}
},
"additionalProperties": false
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
}
}
},
@@ -1230,18 +1172,6 @@
"minimum_mixnode_pledge"
],
"properties": {
"interval_operating_cost": {
"description": "Defines the allowed interval operating cost range of operators. default: 0 - 1'000'000'000'000'000 (1 Billion native tokens - the total supply)",
"default": {
"maximum": "1000000000000000",
"minimum": "0"
},
"allOf": [
{
"$ref": "#/definitions/RangedValue_for_Uint128"
}
]
},
"minimum_gateway_pledge": {
"description": "Minimum amount a gateway must pledge to get into the system.",
"allOf": [
@@ -1268,18 +1198,6 @@
"$ref": "#/definitions/Coin"
}
]
},
"profit_margin": {
"description": "Defines the allowed profit margin range of operators. default: 0% - 100%",
"default": {
"maximum": "1",
"minimum": "0"
},
"allOf": [
{
"$ref": "#/definitions/RangedValue_for_Percent"
}
]
}
},
"additionalProperties": false
@@ -1614,38 +1532,6 @@
}
]
},
"RangedValue_for_Percent": {
"type": "object",
"required": [
"maximum",
"minimum"
],
"properties": {
"maximum": {
"$ref": "#/definitions/Percent"
},
"minimum": {
"$ref": "#/definitions/Percent"
}
},
"additionalProperties": false
},
"RangedValue_for_Uint128": {
"type": "object",
"required": [
"maximum",
"minimum"
],
"properties": {
"maximum": {
"$ref": "#/definitions/Uint128"
},
"minimum": {
"$ref": "#/definitions/Uint128"
}
},
"additionalProperties": false
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
@@ -8177,18 +8063,6 @@
"minimum_mixnode_pledge"
],
"properties": {
"interval_operating_cost": {
"description": "Defines the allowed interval operating cost range of operators. default: 0 - 1'000'000'000'000'000 (1 Billion native tokens - the total supply)",
"default": {
"maximum": "1000000000000000",
"minimum": "0"
},
"allOf": [
{
"$ref": "#/definitions/RangedValue_for_Uint128"
}
]
},
"minimum_gateway_pledge": {
"description": "Minimum amount a gateway must pledge to get into the system.",
"allOf": [
@@ -8215,62 +8089,6 @@
"$ref": "#/definitions/Coin"
}
]
},
"profit_margin": {
"description": "Defines the allowed profit margin range of operators. default: 0% - 100%",
"default": {
"maximum": "1",
"minimum": "0"
},
"allOf": [
{
"$ref": "#/definitions/RangedValue_for_Percent"
}
]
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
"type": "string"
},
"Percent": {
"description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)",
"allOf": [
{
"$ref": "#/definitions/Decimal"
}
]
},
"RangedValue_for_Percent": {
"type": "object",
"required": [
"maximum",
"minimum"
],
"properties": {
"maximum": {
"$ref": "#/definitions/Percent"
},
"minimum": {
"$ref": "#/definitions/Percent"
}
},
"additionalProperties": false
},
"RangedValue_for_Uint128": {
"type": "object",
"required": [
"maximum",
"minimum"
],
"properties": {
"maximum": {
"$ref": "#/definitions/Uint128"
},
"minimum": {
"$ref": "#/definitions/Uint128"
}
},
"additionalProperties": false
@@ -8291,18 +8109,6 @@
"minimum_mixnode_pledge"
],
"properties": {
"interval_operating_cost": {
"description": "Defines the allowed interval operating cost range of operators. default: 0 - 1'000'000'000'000'000 (1 Billion native tokens - the total supply)",
"default": {
"maximum": "1000000000000000",
"minimum": "0"
},
"allOf": [
{
"$ref": "#/definitions/RangedValue_for_Uint128"
}
]
},
"minimum_gateway_pledge": {
"description": "Minimum amount a gateway must pledge to get into the system.",
"allOf": [
@@ -8329,18 +8135,6 @@
"$ref": "#/definitions/Coin"
}
]
},
"profit_margin": {
"description": "Defines the allowed profit margin range of operators. default: 0% - 100%",
"default": {
"maximum": "1",
"minimum": "0"
},
"allOf": [
{
"$ref": "#/definitions/RangedValue_for_Percent"
}
]
}
},
"additionalProperties": false,
@@ -8360,50 +8154,6 @@
}
}
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
"type": "string"
},
"Percent": {
"description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)",
"allOf": [
{
"$ref": "#/definitions/Decimal"
}
]
},
"RangedValue_for_Percent": {
"type": "object",
"required": [
"maximum",
"minimum"
],
"properties": {
"maximum": {
"$ref": "#/definitions/Percent"
},
"minimum": {
"$ref": "#/definitions/Percent"
}
},
"additionalProperties": false
},
"RangedValue_for_Uint128": {
"type": "object",
"required": [
"maximum",
"minimum"
],
"properties": {
"maximum": {
"$ref": "#/definitions/Uint128"
},
"minimum": {
"$ref": "#/definitions/Uint128"
}
},
"additionalProperties": false
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
-56
View File
@@ -1055,18 +1055,6 @@
"minimum_mixnode_pledge"
],
"properties": {
"interval_operating_cost": {
"description": "Defines the allowed interval operating cost range of operators. default: 0 - 1'000'000'000'000'000 (1 Billion native tokens - the total supply)",
"default": {
"maximum": "1000000000000000",
"minimum": "0"
},
"allOf": [
{
"$ref": "#/definitions/RangedValue_for_Uint128"
}
]
},
"minimum_gateway_pledge": {
"description": "Minimum amount a gateway must pledge to get into the system.",
"allOf": [
@@ -1093,18 +1081,6 @@
"$ref": "#/definitions/Coin"
}
]
},
"profit_margin": {
"description": "Defines the allowed profit margin range of operators. default: 0% - 100%",
"default": {
"maximum": "1",
"minimum": "0"
},
"allOf": [
{
"$ref": "#/definitions/RangedValue_for_Percent"
}
]
}
},
"additionalProperties": false
@@ -1439,38 +1415,6 @@
}
]
},
"RangedValue_for_Percent": {
"type": "object",
"required": [
"maximum",
"minimum"
],
"properties": {
"maximum": {
"$ref": "#/definitions/Percent"
},
"minimum": {
"$ref": "#/definitions/Percent"
}
},
"additionalProperties": false
},
"RangedValue_for_Uint128": {
"type": "object",
"required": [
"maximum",
"minimum"
],
"properties": {
"maximum": {
"$ref": "#/definitions/Uint128"
},
"minimum": {
"$ref": "#/definitions/Uint128"
}
},
"additionalProperties": false
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
@@ -22,28 +22,6 @@
"initial_rewarding_params": {
"$ref": "#/definitions/InitialRewardingParams"
},
"interval_operating_cost": {
"default": {
"maximum": "1000000000000000",
"minimum": "0"
},
"allOf": [
{
"$ref": "#/definitions/RangedValue_for_Uint128"
}
]
},
"profit_margin": {
"default": {
"maximum": "1",
"minimum": "0"
},
"allOf": [
{
"$ref": "#/definitions/RangedValue_for_Percent"
}
]
},
"rewarding_denom": {
"type": "string"
},
@@ -130,42 +108,6 @@
"$ref": "#/definitions/Decimal"
}
]
},
"RangedValue_for_Percent": {
"type": "object",
"required": [
"maximum",
"minimum"
],
"properties": {
"maximum": {
"$ref": "#/definitions/Percent"
},
"minimum": {
"$ref": "#/definitions/Percent"
}
},
"additionalProperties": false
},
"RangedValue_for_Uint128": {
"type": "object",
"required": [
"maximum",
"minimum"
],
"properties": {
"maximum": {
"$ref": "#/definitions/Uint128"
},
"minimum": {
"$ref": "#/definitions/Uint128"
}
},
"additionalProperties": false
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
}
}
}
@@ -77,18 +77,6 @@
"minimum_mixnode_pledge"
],
"properties": {
"interval_operating_cost": {
"description": "Defines the allowed interval operating cost range of operators. default: 0 - 1'000'000'000'000'000 (1 Billion native tokens - the total supply)",
"default": {
"maximum": "1000000000000000",
"minimum": "0"
},
"allOf": [
{
"$ref": "#/definitions/RangedValue_for_Uint128"
}
]
},
"minimum_gateway_pledge": {
"description": "Minimum amount a gateway must pledge to get into the system.",
"allOf": [
@@ -115,62 +103,6 @@
"$ref": "#/definitions/Coin"
}
]
},
"profit_margin": {
"description": "Defines the allowed profit margin range of operators. default: 0% - 100%",
"default": {
"maximum": "1",
"minimum": "0"
},
"allOf": [
{
"$ref": "#/definitions/RangedValue_for_Percent"
}
]
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
"type": "string"
},
"Percent": {
"description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)",
"allOf": [
{
"$ref": "#/definitions/Decimal"
}
]
},
"RangedValue_for_Percent": {
"type": "object",
"required": [
"maximum",
"minimum"
],
"properties": {
"maximum": {
"$ref": "#/definitions/Percent"
},
"minimum": {
"$ref": "#/definitions/Percent"
}
},
"additionalProperties": false
},
"RangedValue_for_Uint128": {
"type": "object",
"required": [
"maximum",
"minimum"
],
"properties": {
"maximum": {
"$ref": "#/definitions/Uint128"
},
"minimum": {
"$ref": "#/definitions/Uint128"
}
},
"additionalProperties": false
@@ -8,18 +8,6 @@
"minimum_mixnode_pledge"
],
"properties": {
"interval_operating_cost": {
"description": "Defines the allowed interval operating cost range of operators. default: 0 - 1'000'000'000'000'000 (1 Billion native tokens - the total supply)",
"default": {
"maximum": "1000000000000000",
"minimum": "0"
},
"allOf": [
{
"$ref": "#/definitions/RangedValue_for_Uint128"
}
]
},
"minimum_gateway_pledge": {
"description": "Minimum amount a gateway must pledge to get into the system.",
"allOf": [
@@ -46,18 +34,6 @@
"$ref": "#/definitions/Coin"
}
]
},
"profit_margin": {
"description": "Defines the allowed profit margin range of operators. default: 0% - 100%",
"default": {
"maximum": "1",
"minimum": "0"
},
"allOf": [
{
"$ref": "#/definitions/RangedValue_for_Percent"
}
]
}
},
"additionalProperties": false,
@@ -77,50 +53,6 @@
}
}
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
"type": "string"
},
"Percent": {
"description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)",
"allOf": [
{
"$ref": "#/definitions/Decimal"
}
]
},
"RangedValue_for_Percent": {
"type": "object",
"required": [
"maximum",
"minimum"
],
"properties": {
"maximum": {
"$ref": "#/definitions/Percent"
},
"minimum": {
"$ref": "#/definitions/Percent"
}
},
"additionalProperties": false
},
"RangedValue_for_Uint128": {
"type": "object",
"required": [
"maximum",
"minimum"
],
"properties": {
"maximum": {
"$ref": "#/definitions/Uint128"
},
"minimum": {
"$ref": "#/definitions/Uint128"
}
},
"additionalProperties": false
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
+2 -25
View File
@@ -11,8 +11,7 @@ use cosmwasm_std::{
};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::{
ContractState, ContractStateParams, ExecuteMsg, InstantiateMsg, Interval, MigrateMsg,
OperatingCostRange, ProfitMarginRange, QueryMsg,
ContractState, ContractStateParams, ExecuteMsg, InstantiateMsg, Interval, MigrateMsg, QueryMsg,
};
use nym_contracts_common::set_build_information;
@@ -25,8 +24,6 @@ fn default_initial_state(
rewarding_validator_address: Addr,
rewarding_denom: String,
vesting_contract_address: Addr,
profit_margin: ProfitMarginRange,
interval_operating_cost: OperatingCostRange,
) -> ContractState {
ContractState {
owner,
@@ -43,8 +40,6 @@ fn default_initial_state(
denom: rewarding_denom,
amount: INITIAL_GATEWAY_PLEDGE_AMOUNT,
},
profit_margin,
interval_operating_cost,
},
}
}
@@ -76,8 +71,6 @@ pub fn instantiate(
rewarding_validator_address.clone(),
msg.rewarding_denom,
vesting_contract_address,
msg.profit_margin,
msg.interval_operating_cost,
);
let starting_interval =
Interval::init_interval(msg.epochs_in_interval, msg.epoch_duration, &env);
@@ -638,7 +631,7 @@ pub fn migrate(
mod tests {
use super::*;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{Decimal, Uint128};
use cosmwasm_std::Decimal;
use mixnet_contract_common::reward_params::{IntervalRewardParams, RewardingParams};
use mixnet_contract_common::{InitialRewardingParams, Percent};
use std::time::Duration;
@@ -664,14 +657,6 @@ mod tests {
rewarded_set_size: 543,
active_set_size: 123,
},
profit_margin: ProfitMarginRange {
minimum: "0.05".parse().unwrap(),
maximum: "0.95".parse().unwrap(),
},
interval_operating_cost: OperatingCostRange {
minimum: "1000".parse().unwrap(),
maximum: "10000".parse().unwrap(),
},
};
let sender = mock_info("sender", &[]);
@@ -693,14 +678,6 @@ mod tests {
denom: "uatom".into(),
amount: INITIAL_GATEWAY_PLEDGE_AMOUNT,
},
profit_margin: ProfitMarginRange {
minimum: Percent::from_percentage_value(5).unwrap(),
maximum: Percent::from_percentage_value(95).unwrap(),
},
interval_operating_cost: OperatingCostRange {
minimum: Uint128::new(1000),
maximum: Uint128::new(10000),
},
},
};
@@ -45,8 +45,6 @@ pub(crate) mod tests {
minimum_mixnode_delegation: None,
minimum_mixnode_pledge: coin(123u128, "unym"),
minimum_gateway_pledge: coin(456u128, "unym"),
profit_margin: Default::default(),
interval_operating_cost: Default::default(),
},
};
@@ -6,7 +6,7 @@ use cosmwasm_std::{Addr, Storage};
use cosmwasm_std::{Coin, StdResult};
use cw_storage_plus::Item;
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::{ContractState, OperatingCostRange, ProfitMarginRange};
use mixnet_contract_common::ContractState;
pub(crate) const CONTRACT_STATE: Item<'_, ContractState> = Item::new(CONTRACT_STATE_KEY);
@@ -28,22 +28,6 @@ pub(crate) fn minimum_gateway_pledge(storage: &dyn Storage) -> Result<Coin, Mixn
.map(|state| state.params.minimum_gateway_pledge)?)
}
pub(crate) fn profit_margin_range(
storage: &dyn Storage,
) -> Result<ProfitMarginRange, MixnetContractError> {
Ok(CONTRACT_STATE
.load(storage)
.map(|state| state.params.profit_margin)?)
}
pub(crate) fn interval_oprating_cost_range(
storage: &dyn Storage,
) -> Result<OperatingCostRange, MixnetContractError> {
Ok(CONTRACT_STATE
.load(storage)
.map(|state| state.params.interval_operating_cost)?)
}
#[allow(unused)]
pub(crate) fn minimum_delegation_stake(
storage: &dyn Storage,
@@ -121,8 +121,6 @@ pub mod tests {
denom,
amount: INITIAL_GATEWAY_PLEDGE_AMOUNT + Uint128::new(1234),
},
profit_margin: Default::default(),
interval_operating_cost: Default::default(),
};
let initial_params = storage::CONTRACT_STATE
+1 -14
View File
@@ -25,8 +25,7 @@ use crate::mixnodes::signature_helpers::verify_mixnode_bonding_signature;
use crate::signing::storage as signing_storage;
use crate::support::helpers::{
ensure_bonded, ensure_epoch_in_progress_state, ensure_is_authorized, ensure_no_existing_bond,
ensure_no_pending_pledge_changes, ensure_operating_cost_within_range,
ensure_profit_margin_within_range, ensure_proxy_match, ensure_sent_by_vesting_contract,
ensure_no_pending_pledge_changes, ensure_proxy_match, ensure_sent_by_vesting_contract,
validate_pledge,
};
@@ -122,12 +121,6 @@ fn _try_add_mixnode(
owner_signature: MessageSignature,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// ensure the profit margin is within the defined range
ensure_profit_margin_within_range(deps.storage, cost_params.profit_margin_percent)?;
// ensure the operating cost is within the defined range
ensure_operating_cost_within_range(deps.storage, &cost_params.interval_operating_cost)?;
// check if the pledge contains any funds of the appropriate denomination
let minimum_pledge = mixnet_params_storage::minimum_mixnode_pledge(deps.storage)?;
let pledge = validate_pledge(pledge, minimum_pledge)?;
@@ -484,12 +477,6 @@ pub(crate) fn _try_update_mixnode_cost_params(
ensure_proxy_match(&proxy, &existing_bond.proxy)?;
ensure_bonded(&existing_bond)?;
// ensure the profit margin is within the defined range
ensure_profit_margin_within_range(deps.storage, new_costs.profit_margin_percent)?;
// ensure the operating cost is within the defined range
ensure_operating_cost_within_range(deps.storage, &new_costs.interval_operating_cost)?;
let cosmos_event = new_mixnode_pending_cost_params_update_event(
existing_bond.mix_id,
&owner,
@@ -111,14 +111,6 @@ pub(crate) fn try_reward_mixnode(
);
}
// make sure node's profit margin is within the allowed range,
// if not adjust it accordingly
let params = mixnet_params_storage::CONTRACT_STATE
.load(deps.storage)?
.params;
mix_rewarding.normalise_profit_margin(params.profit_margin);
mix_rewarding.normalise_operating_cost(params.interval_operating_cost);
let rewarding_params = storage::REWARDING_PARAMS.load(deps.storage)?;
let node_reward_params = NodeRewardParams::new(node_performance, node_status.is_active());
-32
View File
@@ -8,7 +8,6 @@ use cosmwasm_std::{wasm_execute, Addr, BankMsg, Coin, CosmosMsg, MessageInfo, Re
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::mixnode::PendingMixNodeChanges;
use mixnet_contract_common::{EpochState, EpochStatus, IdentityKeyRef, MixId, MixNodeBond};
use nym_contracts_common::Percent;
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
// helper trait to attach `Msg` to a response if it's provided
@@ -432,34 +431,3 @@ pub(crate) fn decode_ed25519_identity_key(
Ok(public_key)
}
pub(crate) fn ensure_profit_margin_within_range(
storage: &dyn Storage,
profit_margin: Percent,
) -> Result<(), MixnetContractError> {
let range = mixnet_params_storage::profit_margin_range(storage)?;
if !range.within_range(profit_margin) {
return Err(MixnetContractError::ProfitMarginOutsideRange {
provided: profit_margin,
range,
});
}
Ok(())
}
pub fn ensure_operating_cost_within_range(
storage: &dyn Storage,
operating_cost: &Coin,
) -> Result<(), MixnetContractError> {
let range = mixnet_params_storage::interval_oprating_cost_range(storage)?;
if !range.within_range(operating_cost.amount) {
return Err(MixnetContractError::OperatingCostOutsideRange {
denom: operating_cost.denom.clone(),
provided: operating_cost.amount,
range,
});
}
Ok(())
}
@@ -1258,8 +1258,6 @@ pub mod test_helpers {
epochs_in_interval: 720,
epoch_duration: Duration::from_secs(60 * 60),
initial_rewarding_params: initial_rewarding_params(),
profit_margin: Default::default(),
interval_operating_cost: Default::default(),
};
let env = mock_env();
let info = mock_info("creator", &[]);
+5 -5
View File
@@ -42,17 +42,17 @@ This page displays a full list of all the changes during our release cycle from
### Features
- [Remove the `nym-mixnode` and `nym-gateway` binaries from the CI upload builds action](https://github.com/nymtech/nym/pull/4693):
- [Remove the `nym-mixnode` and `nym-gateway` binaries from the CI upload builds action](https://github.com/nymtech/nym/pull/4693):
- [Add an early return in `parse_raw_str_logs` for empty raw log strings.](https://github.com/nymtech/nym/pull/4686): This accommodates for the v50 + chain upgrade.
- [Bump braces from `3.0.2` to `3.0.3` in `/wasm/mix-fetch/internal-dev`](https://github.com/nymtech/nym/pull/4672): Version update of [braces](https://github.com/micromatch/braces)
- [Bump braces from `3.0.2` to `3.0.3` in `/clients/native/examples/js-examples/websocket`](https://github.com/nymtech/nym/pull/4663): Version update of [braces](https://github.com/micromatch/braces).
- [Bump braces from `3.0.2` to `3.0.3` in `/nym-api/tests`](https://github.com/nymtech/nym/pull/4659): Version update of [braces](https://github.com/micromatch/braces).
- [Bump braces from `3.0.2` to `3.0.3` in `/docker/typescript_client/upload_contract`](https://github.com/nymtech/nym/pull/4658): Version update of [braces](https://github.com/micromatch/braces).
- [Bump `ws` from `8.13.0` to `8.17.1` in `/wasm/client/internal-dev-node`](https://github.com/nymtech/nym/pull/4665): Version update of [`ws`](https://github.com/websockets/ws).
- [Bump `ws` from `8.14.2` to `8.17.1` in `/sdk/typescript/packages/nodejs-client`](https://github.com/nymtech/nym/pull/4662): Version update of [`ws`](https://github.com/websockets/ws).
- [Bump `ws` from `8.13.0` to `8.17.1` in `/wasm/client/internal-dev-node`](https://github.com/nymtech/nym/pull/4665): Version update of [`ws`](https://github.com/websockets/ws).
- [Bump `ws` from `8.14.2` to `8.17.1` in `/sdk/typescript/packages/nodejs-client`](https://github.com/nymtech/nym/pull/4662): Version update of [`ws`](https://github.com/websockets/ws).
- [Add expiry returned on import](https://github.com/nymtech/nym/pull/4670): We need to return the expiry on import for desktop daemon `nym-vpnd`.
- [New clippy lints](https://github.com/nymtech/nym/pull/4660)
- [Remove `nym-connect` directory](https://github.com/nymtech/nym/pull/4643): Since the `nym-vpn` has superseded `nym-connect`, remove `nym-connect` from the repo.
- [Remove `nym-connect` directory](https://github.com/nymtech/nym/pull/4643): Since the `nym-vpn` has superseded `nym-connect`, remove `nym-connect` from the repo.
- [Remove old PR template](https://github.com/nymtech/nym/pull/4639)
### Bugfix
@@ -62,7 +62,7 @@ This page displays a full list of all the changes during our release cycle from
### Operators Guide updates
- [Node description guide](nodes/configuration.md#node-description): Steps to add self-description to `nym-node` and query this information from any node.
- [Web Secure Socket (WSS) guide and reverse proxy update](/nodes/proxy-configuration.md), PR [here](https://github.com/nymtech/nym/pull/4694): A guide to setup `nym-node` in a secure fashion, using WSS via Nginx and Certbot. Landing page (reversed proxy) is updated and simplified.
- [Web Secure Socket (WSS) guide and reverse proxy update](nodes/proxy-configuration.md), PR [here](https://github.com/nymtech/nym/pull/4694): A guide to setup `nym-node` in a secure fashion, using WSS via Nginx and Certbot. Landing page (reversed proxy) is updated and simplified.
---
+2 -1
View File
@@ -34,7 +34,7 @@ You can bond your `nym-node` via the Desktop wallet.
| :-- | :-- | :-- |
| Mixnode | Mix port | `1789` |
| Mixnode | Verloc port | `1790` |
| Mixnode | HTTP api port (picture below) | `8080` |
| Mixnode | HTTP api port (picture below) | `8080` |
| Gateway (entry & exit) | Mix port | `1789` |
| Gateway (entry & exit) | Client WS API port | `9000` |
@@ -59,6 +59,7 @@ You can bond your `nym-node` via the Desktop wallet.
```
![Paste Signature](../images/wallet-screenshots/wallet-sign.png)
*This image is just an example, copy-paste your own base58-encoded signature*
6. Your node will now be bonded and ready to recieve traffic, latest at the beginning of the next epoch (at most 1 hour)
+4 -1
View File
@@ -1,3 +1,6 @@
{
"extends": ["next/core-web-vitals"]
"extends": ["next/core-web-vitals"],
"rules": {
"react-hooks/exhaustive-deps": "off"
}
}
+6 -6
View File
@@ -1,11 +1,11 @@
import React from 'react'
import { Navbar } from './components/Nav/Navbar'
import { Providers } from './providers'
import React from "react";
import { Navbar } from "./components/Nav/Navbar";
import { Providers } from "./providers";
const App = ({ children }: { children: React.ReactNode }) => (
const App = ({ children }: { children: any }) => (
<Providers>
<Navbar>{children}</Navbar>
</Providers>
)
);
export { App }
export { App };
@@ -1,9 +1,9 @@
import { Typography } from '@mui/material';
import * as React from 'react';
import { Typography } from "@mui/material";
import * as React from "react";
export const ComponentError: FCWithChildren<{ text: string }> = ({ text }) => (
export const ComponentError = ({ text }: { text: string }) => (
<Typography
sx={{ marginTop: 2, color: 'primary.main', fontSize: 10 }}
sx={{ marginTop: 2, color: "primary.main", fontSize: 10 }}
variant="body1"
data-testid="delegation-total-amount"
>
+16 -15
View File
@@ -1,16 +1,17 @@
import { Card, CardHeader, CardContent, Typography } from '@mui/material'
import React, { ReactEventHandler } from 'react'
import React, { ReactEventHandler } from "react";
import { Card, CardHeader, CardContent, Typography } from "@mui/material";
type ContentCardProps = {
title?: React.ReactNode
subtitle?: string
Icon?: React.ReactNode
Action?: React.ReactNode
errorMsg?: string
onClick?: ReactEventHandler
}
title?: React.ReactNode;
subtitle?: string;
Icon?: React.ReactNode;
Action?: React.ReactNode;
errorMsg?: string;
children: React.ReactNode;
onClick?: ReactEventHandler;
};
export const ContentCard: FCWithChildren<ContentCardProps> = ({
export const ContentCard = ({
title,
Icon,
Action,
@@ -18,11 +19,11 @@ export const ContentCard: FCWithChildren<ContentCardProps> = ({
errorMsg,
children,
onClick,
}) => (
<Card onClick={onClick} sx={{ height: '100%' }}>
}: ContentCardProps) => (
<Card onClick={onClick} sx={{ height: "100%" }}>
{title && (
<CardHeader
title={title || ''}
title={title || ""}
avatar={Icon}
action={Action}
subheader={subtitle}
@@ -30,9 +31,9 @@ export const ContentCard: FCWithChildren<ContentCardProps> = ({
)}
{children && <CardContent>{children}</CardContent>}
{errorMsg && (
<Typography variant="body2" sx={{ color: 'danger', padding: 2 }}>
<Typography variant="body2" sx={{ color: "danger", padding: 2 }}>
{errorMsg}
</Typography>
)}
</Card>
)
);
@@ -1,13 +1,14 @@
import * as React from 'react'
import { Box, Typography } from '@mui/material'
import { useTheme } from '@mui/material/styles'
import { Tooltip } from '@nymproject/react/tooltip/Tooltip'
import * as React from "react";
import { Box, Typography } from "@mui/material";
import { useTheme } from "@mui/material/styles";
import { Tooltip } from "@nymproject/react";
import { InfoOutlined } from "@mui/icons-material";
export const CustomColumnHeading: FCWithChildren<{
headingTitle: string
tooltipInfo?: string
headingTitle: string;
tooltipInfo?: string;
}> = ({ headingTitle, tooltipInfo }) => {
const theme = useTheme()
const theme = useTheme();
return (
<Box alignItems="center" display="flex">
@@ -20,11 +21,12 @@ export const CustomColumnHeading: FCWithChildren<{
bgColor={theme.palette.nym.networkExplorer.tooltip.background}
maxWidth={230}
arrow
TooltipIcon={<InfoOutlined sx={{ width: 18, height: 18, mr: 1 }} />}
/>
)}
<Typography variant="body2" fontWeight={600} data-testid={headingTitle}>
{headingTitle}
</Typography>
</Box>
)
}
);
};
@@ -1,35 +1,35 @@
'use client'
"use client";
import React, { useState } from 'react'
import { Box, SxProps } from '@mui/material'
import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField'
import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField'
import { CurrencyDenom, DecCoin } from '@nymproject/types'
import { useWalletContext } from '@/app/context/wallet'
import { urls } from '@/app/utils'
import { useDelegationsContext } from '@/app/context/delegations'
import { validateAmount } from '@/app/utils/currency'
import { SimpleModal } from './SimpleModal'
import { ModalListItem } from './ModalListItem'
import { DelegationModalProps } from './DelegationModal'
import React, { useState } from "react";
import { Box, SxProps } from "@mui/material";
import { IdentityKeyFormField } from "@nymproject/react";
import { CurrencyFormField } from "@nymproject/react";
import { CurrencyDenom, DecCoin } from "@nymproject/types";
import { useWalletContext } from "@/app/context/wallet";
import { urls } from "@/app/utils";
import { useDelegationsContext } from "@/app/context/delegations";
import { validateAmount } from "@/app/utils/currency";
import { SimpleModal } from "./SimpleModal";
import { ModalListItem } from "./ModalListItem";
import { DelegationModalProps } from "./DelegationModal";
const MIN_AMOUNT_TO_DELEGATE = 10
const MIN_AMOUNT_TO_DELEGATE = 10;
type Props = {
mixId: number
identityKey: string
header?: string
buttonText?: string
rewardInterval?: string
estimatedReward?: number
profitMarginPercentage?: string | null
nodeUptimePercentage?: number | null
denom: CurrencyDenom
sx?: SxProps
backdropProps?: object
onClose: () => void
onOk?: (delegationModalProps: DelegationModalProps) => void
}
mixId: number;
identityKey: string;
header?: string;
buttonText?: string;
rewardInterval?: string;
estimatedReward?: number;
profitMarginPercentage?: string | null;
nodeUptimePercentage?: number | null;
denom: CurrencyDenom;
sx?: SxProps;
backdropProps?: object;
onClose: () => void;
onOk?: (delegationModalProps: DelegationModalProps) => void;
};
export const DelegateModal = ({
mixId,
@@ -40,106 +40,106 @@ export const DelegateModal = ({
sx,
}: Props) => {
const [amount, setAmount] = useState<DecCoin | undefined>({
amount: '10',
denom: 'nym',
})
const [isValidated, setValidated] = useState<boolean>(false)
const [errorAmount, setErrorAmount] = useState<string | undefined>()
amount: "10",
denom: "nym",
});
const [isValidated, setValidated] = useState<boolean>(false);
const [errorAmount, setErrorAmount] = useState<string | undefined>();
const { address, balance } = useWalletContext()
const { handleDelegate } = useDelegationsContext()
const { address, balance } = useWalletContext();
const { handleDelegate } = useDelegationsContext();
const validate = async () => {
let newValidatedValue = true
let errorAmountMessage
let newValidatedValue = true;
let errorAmountMessage;
if (amount && !(await validateAmount(amount.amount, '0'))) {
newValidatedValue = false
errorAmountMessage = 'Please enter a valid amount'
if (amount && !(await validateAmount(amount.amount, "0"))) {
newValidatedValue = false;
errorAmountMessage = "Please enter a valid amount";
}
if (amount && +amount.amount < MIN_AMOUNT_TO_DELEGATE) {
errorAmountMessage = `Min. delegation amount: ${MIN_AMOUNT_TO_DELEGATE} ${denom.toUpperCase()}`
newValidatedValue = false
errorAmountMessage = `Min. delegation amount: ${MIN_AMOUNT_TO_DELEGATE} ${denom.toUpperCase()}`;
newValidatedValue = false;
}
if (!amount?.amount.length) {
newValidatedValue = false
newValidatedValue = false;
}
if (amount && balance.data && +balance.data - +amount.amount <= 0) {
errorAmountMessage = 'Not enough funds'
newValidatedValue = false
errorAmountMessage = "Not enough funds";
newValidatedValue = false;
}
setErrorAmount(errorAmountMessage)
setValidated(newValidatedValue)
}
setErrorAmount(errorAmountMessage);
setValidated(newValidatedValue);
};
const delegateToMixnode = async ({
delegationMixId,
delegationAmount,
}: {
delegationMixId: number
delegationAmount: string
delegationMixId: number;
delegationAmount: string;
}) => {
try {
const tx = await handleDelegate(delegationMixId, delegationAmount)
return tx
const tx = await handleDelegate(delegationMixId, delegationAmount);
return tx;
} catch (e) {
console.error('Failed to delegate to mixnode', e)
throw e
console.error("Failed to delegate to mixnode", e);
throw e;
}
}
};
const handleConfirm = async () => {
if (mixId && amount && onOk) {
onOk({
status: 'loading',
})
status: "loading",
});
try {
if (!address) {
throw new Error('Please connect your wallet')
throw new Error("Please connect your wallet");
}
const tx = await delegateToMixnode({
delegationMixId: mixId,
delegationAmount: amount.amount,
})
});
if (!tx) {
throw new Error('Failed to delegate')
throw new Error("Failed to delegate");
}
onOk({
status: 'success',
message: 'Delegation can take up to one hour to process',
status: "success",
message: "Delegation can take up to one hour to process",
transactions: [
{
url: `${urls('MAINNET').blockExplorer}/transaction/${
url: `${urls("MAINNET").blockExplorer}/transaction/${
tx.transactionHash
}`,
hash: tx.transactionHash,
},
],
})
});
} catch (e) {
console.error('Failed to delegate', e)
console.error("Failed to delegate", e);
onOk({
status: 'error',
status: "error",
message: (e as Error).message,
})
});
}
}
}
};
const handleAmountChanged = (newAmount: DecCoin) => {
setAmount(newAmount)
}
setAmount(newAmount);
};
React.useEffect(() => {
validate()
}, [amount, identityKey, mixId])
validate();
}, [amount, identityKey, mixId]);
return (
<SimpleModal
@@ -170,7 +170,7 @@ export const DelegateModal = ({
fullWidth
autoFocus
label="Amount"
initialValue={amount?.amount || '10'}
initialValue={amount?.amount || "10"}
onChanged={handleAmountChanged}
denom={denom}
validationError={errorAmount}
@@ -187,5 +187,5 @@ export const DelegateModal = ({
<ModalListItem label="Est. fee for this transaction will be calculated in your connected wallet" />
</SimpleModal>
)
}
);
};
@@ -1,26 +1,26 @@
import React from 'react'
import { Typography, SxProps, Stack } from '@mui/material'
import { Link } from '@nymproject/react/link/Link'
import { LoadingModal } from './LoadingModal'
import { ConfirmationModal } from './ConfirmationModal'
import { ErrorModal } from './ErrorModal'
import React from "react";
import { Typography, SxProps, Stack } from "@mui/material";
import { Link } from "@nymproject/react";
import { LoadingModal } from "./LoadingModal";
import { ConfirmationModal } from "./ConfirmationModal";
import { ErrorModal } from "./ErrorModal";
export type DelegationModalProps = {
status: 'loading' | 'success' | 'error' | 'info'
message?: string
status: "loading" | "success" | "error" | "info";
message?: string;
transactions?: {
url: string
hash: string
}[]
}
url: string;
hash: string;
}[];
};
export const DelegationModal: FCWithChildren<
DelegationModalProps & {
open: boolean
onClose: () => void
sx?: SxProps
backdropProps?: object
children?: React.ReactNode
open: boolean;
onClose: () => void;
sx?: SxProps;
backdropProps?: object;
children?: React.ReactNode;
}
> = ({
status,
@@ -32,18 +32,18 @@ export const DelegationModal: FCWithChildren<
sx,
backdropProps,
}) => {
if (status === 'loading')
return <LoadingModal sx={sx} backdropProps={backdropProps} />
if (status === "loading")
return <LoadingModal sx={sx} backdropProps={backdropProps} />;
if (status === 'error') {
if (status === "error") {
return (
<ErrorModal message={message} sx={sx} open={open} onClose={onClose}>
{children}
</ErrorModal>
)
);
}
if (status === 'info') {
if (status === "info") {
return (
<ConfirmationModal
open={open}
@@ -53,7 +53,7 @@ export const DelegationModal: FCWithChildren<
>
<Typography>{message}</Typography>
</ConfirmationModal>
)
);
}
return (
@@ -91,5 +91,5 @@ export const DelegationModal: FCWithChildren<
)}
</Stack>
</ConfirmationModal>
)
}
);
};
+39 -39
View File
@@ -1,4 +1,4 @@
import * as React from 'react'
import * as React from "react";
import {
Link,
Paper,
@@ -9,49 +9,49 @@ import {
TableHead,
TableRow,
TableCellProps,
} from '@mui/material'
import { useTheme } from '@mui/material/styles'
import { Tooltip } from '@nymproject/react/tooltip/Tooltip'
import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard'
import { Box } from '@mui/system'
import { unymToNym } from '@/app/utils/currency'
import { GatewayEnrichedRowType } from './Gateways/Gateways'
import { MixnodeRowType } from './MixNodes'
import { StakeSaturationProgressBar } from './MixNodes/Economics/StakeSaturationProgressBar'
} from "@mui/material";
import { useTheme } from "@mui/material/styles";
import { Tooltip } from "@nymproject/react";
import { CopyToClipboard } from "@nymproject/react";
import { Box } from "@mui/system";
import { unymToNym } from "@/app/utils/currency";
import { GatewayEnrichedRowType } from "./Gateways/Gateways";
import { MixnodeRowType } from "./MixNodes";
import { StakeSaturationProgressBar } from "./MixNodes/Economics/StakeSaturationProgressBar";
export type ColumnsType = {
field: string
title: string
headerAlign?: TableCellProps['align']
width?: string | number
tooltipInfo?: string
}
field: string;
title: string;
headerAlign?: TableCellProps["align"];
width?: string | number;
tooltipInfo?: string;
};
export interface UniversalTableProps<T = any> {
tableName: string
columnsData: ColumnsType[]
rows: T[]
tableName: string;
columnsData: ColumnsType[];
rows: T[];
}
function formatCellValues(val: string | number, field: string) {
if (field === 'identity_key' && typeof val === 'string') {
if (field === "identity_key" && typeof val === "string") {
return (
<Box display="flex" justifyContent="flex-end">
<CopyToClipboard
sx={{ mr: 1, mt: 0.5, fontSize: '18px' }}
sx={{ mr: 1, mt: 0.5, fontSize: "18px" }}
value={val}
tooltip={`Copy identity key ${val} to clipboard`}
/>
<span>{val}</span>
</Box>
)
);
}
if (field === 'bond') {
return unymToNym(val, 6)
if (field === "bond") {
return unymToNym(val, 6);
}
if (field === 'owner') {
if (field === "owner") {
return (
<Link
underline="none"
@@ -61,22 +61,22 @@ function formatCellValues(val: string | number, field: string) {
>
{val}
</Link>
)
);
}
if (field === 'stake_saturation') {
return <StakeSaturationProgressBar value={Number(val)} threshold={100} />
if (field === "stake_saturation") {
return <StakeSaturationProgressBar value={Number(val)} threshold={100} />;
}
return val
return val;
}
export const DetailTable: FCWithChildren<{
tableName: string
columnsData: ColumnsType[]
rows: MixnodeRowType[] | GatewayEnrichedRowType[]
tableName: string;
columnsData: ColumnsType[];
rows: MixnodeRowType[] | GatewayEnrichedRowType[];
}> = ({ tableName, columnsData, rows }: UniversalTableProps) => {
const theme = useTheme()
const theme = useTheme();
return (
<TableContainer component={Paper}>
<Table sx={{ minWidth: 1080 }} aria-label={tableName}>
@@ -87,9 +87,9 @@ export const DetailTable: FCWithChildren<{
key={field}
sx={{ fontSize: 14, fontWeight: 600, width }}
>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
{tooltipInfo && (
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Tooltip
title={tooltipInfo}
id={field}
@@ -115,7 +115,7 @@ export const DetailTable: FCWithChildren<{
{rows.map((eachRow) => (
<TableRow
key={eachRow.id}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
{columnsData?.map((data, index) => (
<TableCell
@@ -128,7 +128,7 @@ export const DetailTable: FCWithChildren<{
width: 200,
fontSize: 14,
}}
data-testid={`${data.title.replace(/ /g, '-')}-value`}
data-testid={`${data.title.replace(/ /g, "-")}-value`}
>
{formatCellValues(
eachRow[columnsData[index].field],
@@ -141,5 +141,5 @@ export const DetailTable: FCWithChildren<{
</TableBody>
</Table>
</TableContainer>
)
}
);
};
+24 -23
View File
@@ -1,23 +1,24 @@
import React from 'react'
import Box from '@mui/material/Box'
import MuiLink from '@mui/material/Link'
import Typography from '@mui/material/Typography'
import { useIsMobile } from '../hooks/useIsMobile'
import { NymVpnIcon } from '../icons/NymVpn'
import { Socials } from './Socials'
import Link from 'next/link'
import React from "react";
import Box from "@mui/material/Box";
import MuiLink from "@mui/material/Link";
import Typography from "@mui/material/Typography";
import { useIsMobile } from "../hooks/useIsMobile";
import { NymVpnIcon } from "../icons/NymVpn";
import { Socials } from "./Socials";
import Link from "next/link";
export const Footer: FCWithChildren = () => {
const isMobile = useIsMobile()
const isMobile = useIsMobile();
return (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
width: '100%',
height: 'auto',
display: "flex",
flexDirection: "column",
justifyContent: "center",
width: "100%",
height: "auto",
bgcolor: "background.default",
mt: 3,
pt: 3,
pb: 3,
@@ -25,11 +26,11 @@ export const Footer: FCWithChildren = () => {
>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
width: 'auto',
justifyContent: 'center',
alignItems: 'center',
display: "flex",
flexDirection: "row",
width: "auto",
justifyContent: "center",
alignItems: "center",
mb: 2,
}}
>
@@ -45,12 +46,12 @@ export const Footer: FCWithChildren = () => {
<Typography
sx={{
fontSize: 12,
textAlign: isMobile ? 'center' : 'end',
color: 'nym.muted.onDarkBg',
textAlign: isMobile ? "center" : "end",
color: "nym.muted.onDarkBg",
}}
>
© {new Date().getFullYear()} Nym Technologies SA, all rights reserved
</Typography>
</Box>
)
}
);
};
@@ -1,4 +1,4 @@
import * as React from 'react'
import * as React from "react";
import {
Paper,
Table,
@@ -8,26 +8,26 @@ import {
TableHead,
TableRow,
Typography,
} from '@mui/material'
import { Box } from '@mui/system'
import { useTheme } from '@mui/material/styles'
import { Tooltip } from '@nymproject/react/tooltip/Tooltip'
import { EconomicsRowsType, EconomicsInfoRowWithIndex } from './types'
import { UniversalTableProps } from '@/app/components/DetailTable'
import { textColour } from '@/app/utils'
} from "@mui/material";
import { Box } from "@mui/system";
import { useTheme } from "@mui/material/styles";
import { Tooltip } from "@nymproject/react";
import { EconomicsRowsType, EconomicsInfoRowWithIndex } from "./types";
import { UniversalTableProps } from "@/app/components/DetailTable";
import { textColour } from "@/app/utils";
const formatCellValues = (value: EconomicsRowsType, field: string) => (
<Box sx={{ display: 'flex', alignItems: 'center' }} id="field">
<Typography sx={{ mr: 1, fontWeight: '600', fontSize: '12px' }} id={field}>
<Box sx={{ display: "flex", alignItems: "center" }} id="field">
<Typography sx={{ mr: 1, fontWeight: "600", fontSize: "12px" }} id={field}>
{value.value}
</Typography>
</Box>
)
);
export const DelegatorsInfoTable: FCWithChildren<
UniversalTableProps<EconomicsInfoRowWithIndex>
> = ({ tableName, columnsData, rows }) => {
const theme = useTheme()
const theme = useTheme();
return (
<TableContainer component={Paper}>
@@ -39,7 +39,7 @@ export const DelegatorsInfoTable: FCWithChildren<
key={field}
sx={{ fontSize: 14, fontWeight: 600, width }}
>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
{tooltipInfo && (
<Tooltip
title={tooltipInfo}
@@ -65,27 +65,27 @@ export const DelegatorsInfoTable: FCWithChildren<
{rows?.map((eachRow) => (
<TableRow
key={eachRow.id}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
{columnsData?.map((_, index: number) => {
const { field } = columnsData[index]
const value: EconomicsRowsType = (eachRow as any)[field]
const { field } = columnsData[index];
const value: EconomicsRowsType = (eachRow as any)[field];
return (
<TableCell
key={_.title}
sx={{
color: textColour(value, field, theme),
}}
data-testid={`${_.title.replace(/ /g, '-')}-value`}
data-testid={`${_.title.replace(/ /g, "-")}-value`}
>
{formatCellValues(value, columnsData[index].field)}
</TableCell>
)
);
})}
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)
}
);
};
+122 -121
View File
@@ -1,93 +1,94 @@
'use client'
"use client";
import * as React from 'react'
import { ExpandLess, ExpandMore, Menu } from '@mui/icons-material'
import { CSSObject, styled, Theme, useTheme } from '@mui/material/styles'
import { Link as MuiLink } from '@mui/material'
import Button from '@mui/material/Button'
import Box from '@mui/material/Box'
import ListItem from '@mui/material/ListItem'
import MuiDrawer from '@mui/material/Drawer'
import AppBar from '@mui/material/AppBar'
import Toolbar from '@mui/material/Toolbar'
import Typography from '@mui/material/Typography'
import List from '@mui/material/List'
import IconButton from '@mui/material/IconButton'
import ListItemButton from '@mui/material/ListItemButton'
import ListItemIcon from '@mui/material/ListItemIcon'
import ListItemText from '@mui/material/ListItemText'
import { NYM_WEBSITE } from '@/app/api/constants'
import { useMainContext } from '@/app/context/main'
import { MobileDrawerClose } from '@/app/icons/MobileDrawerClose'
import { NavOptionType, originalNavOptions } from '@/app/context/nav'
import { DarkLightSwitchDesktop } from '@/app/components/Switch'
import { Footer } from '@/app/components/Footer'
import { ConnectKeplrWallet } from '@/app/components/Wallet/ConnectKeplrWallet'
import { usePathname, useRouter } from 'next/navigation'
import * as React from "react";
import { Menu } from "@mui/icons-material";
import { CSSObject, styled, Theme, useTheme } from "@mui/material/styles";
import { Link as MuiLink } from "@mui/material";
import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
import ListItem from "@mui/material/ListItem";
import MuiDrawer from "@mui/material/Drawer";
import AppBar from "@mui/material/AppBar";
import Toolbar from "@mui/material/Toolbar";
import Typography from "@mui/material/Typography";
import List from "@mui/material/List";
import IconButton from "@mui/material/IconButton";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemIcon from "@mui/material/ListItemIcon";
import ListItemText from "@mui/material/ListItemText";
import { NYM_WEBSITE } from "@/app/api/constants";
import { useMainContext } from "@/app/context/main";
import { MobileDrawerClose } from "@/app/icons/MobileDrawerClose";
import { NavOptionType, originalNavOptions } from "@/app/context/nav";
import { DarkLightSwitchDesktop } from "@/app/components/Switch";
import { Footer } from "@/app/components/Footer";
import { ConnectKeplrWallet } from "@/app/components/Wallet/ConnectKeplrWallet";
import { usePathname, useRouter } from "next/navigation";
import { NymLogo } from "@nymproject/react";
const drawerWidth = 255
const bannerHeight = 80
const drawerWidth = 255;
const bannerHeight = 80;
const openedMixin = (theme: Theme): CSSObject => ({
width: drawerWidth,
transition: theme.transitions.create('width', {
transition: theme.transitions.create("width", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
overflowX: 'hidden',
})
overflowX: "hidden",
});
const closedMixin = (theme: Theme): CSSObject => ({
transition: theme.transitions.create('width', {
transition: theme.transitions.create("width", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
overflowX: 'hidden',
overflowX: "hidden",
width: `calc(${theme.spacing(7)} + 1px)`,
})
});
const DrawerHeader = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
const DrawerHeader = styled("div")(({ theme }) => ({
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
padding: theme.spacing(0, 1),
height: 64,
}))
}));
const Drawer = styled(MuiDrawer, {
shouldForwardProp: (prop) => prop !== 'open',
shouldForwardProp: (prop) => prop !== "open",
})(({ theme, open }) => ({
width: drawerWidth,
flexShrink: 0,
whiteSpace: 'nowrap',
boxSizing: 'border-box',
whiteSpace: "nowrap",
boxSizing: "border-box",
...(open && {
...openedMixin(theme),
'& .MuiDrawer-paper': openedMixin(theme),
"& .MuiDrawer-paper": openedMixin(theme),
}),
...(!open && {
...closedMixin(theme),
'& .MuiDrawer-paper': closedMixin(theme),
"& .MuiDrawer-paper": closedMixin(theme),
}),
}))
}));
type ExpandableButtonType = {
title: string
url: string
isActive?: boolean
Icon?: React.ReactNode
nested?: NavOptionType[]
isChild?: boolean
isMobile: boolean
drawIsTempOpen: boolean
drawIsFixed: boolean
isExternalLink?: boolean
openDrawer: () => void
closeDrawer?: () => void
fixDrawerClose?: () => void
}
title: string;
url: string;
isActive?: boolean;
Icon?: React.ReactNode;
nested?: NavOptionType[];
isChild?: boolean;
isMobile: boolean;
drawIsTempOpen: boolean;
drawIsFixed: boolean;
isExternalLink?: boolean;
openDrawer: () => void;
closeDrawer?: () => void;
fixDrawerClose?: () => void;
};
export const ExpandableButton: FCWithChildren<ExpandableButtonType> = ({
export const ExpandableButton = ({
title,
url,
drawIsTempOpen,
@@ -100,34 +101,34 @@ export const ExpandableButton: FCWithChildren<ExpandableButtonType> = ({
openDrawer,
closeDrawer,
fixDrawerClose,
}) => {
const { palette } = useTheme()
const pathname = usePathname()
const router = useRouter()
}: ExpandableButtonType) => {
const { palette } = useTheme();
const pathname = usePathname();
const router = useRouter();
const handleClick = () => {
if (title === 'Network Components') {
return undefined
if (title === "Network Components") {
return undefined;
}
if (isExternalLink) {
window.open(url, '_blank')
window.open(url, "_blank");
return undefined
return undefined;
}
if (!isExternalLink) {
router.push(url, {})
router.push(url, {});
}
if (closeDrawer) {
closeDrawer()
closeDrawer();
}
}
};
const selectedStyle = {
background: palette.nym.networkExplorer.nav.selected.main,
borderRight: `3px solid ${palette.nym.highlight}`,
}
};
return (
<>
@@ -135,12 +136,12 @@ export const ExpandableButton: FCWithChildren<ExpandableButtonType> = ({
disablePadding
disableGutters
sx={{
borderBottom: isChild ? 'none' : '1px solid rgba(255, 255, 255, 0.1)',
borderBottom: isChild ? "none" : "1px solid rgba(255, 255, 255, 0.1)",
...(pathname === url
? selectedStyle
: {
background: palette.nym.networkExplorer.nav.background,
borderRight: 'none',
borderRight: "none",
}),
}}
>
@@ -151,10 +152,10 @@ export const ExpandableButton: FCWithChildren<ExpandableButtonType> = ({
pb: 2,
background: isChild
? palette.nym.networkExplorer.nav.selected.nested
: 'none',
: "none",
}}
>
<ListItemIcon sx={{ minWidth: '39px' }}>{Icon}</ListItemIcon>
<ListItemIcon sx={{ minWidth: "39px" }}>{Icon}</ListItemIcon>
<ListItemText
primary={title}
sx={{
@@ -179,52 +180,52 @@ export const ExpandableButton: FCWithChildren<ExpandableButtonType> = ({
/>
))}
</>
)
}
);
};
export const Nav: FCWithChildren = ({ children }) => {
const { environment } = useMainContext()
const [drawerIsOpen, setDrawerToOpen] = React.useState(false)
const [fixedOpen, setFixedOpen] = React.useState(false)
export const Nav = ({ children }: { children: React.ReactNode }) => {
const { environment } = useMainContext();
const [drawerIsOpen, setDrawerToOpen] = React.useState(false);
const [fixedOpen, setFixedOpen] = React.useState(false);
// Set maintenance banner to false by default to don't display it
const [openMaintenance, setOpenMaintenance] = React.useState(false)
const theme = useTheme()
const [openMaintenance, setOpenMaintenance] = React.useState(false);
const theme = useTheme();
const explorerName = environment
? `${environment} Explorer`
: 'Mainnet Explorer'
: "Mainnet Explorer";
const switchNetworkText =
environment === 'mainnet' ? 'Switch to Testnet' : 'Switch to Mainnet'
environment === "mainnet" ? "Switch to Testnet" : "Switch to Mainnet";
const switchNetworkLink =
environment === 'mainnet'
? 'https://sandbox-explorer.nymtech.net'
: 'https://explorer.nymtech.net'
environment === "mainnet"
? "https://sandbox-explorer.nymtech.net"
: "https://explorer.nymtech.net";
const fixDrawerOpen = () => {
setFixedOpen(true)
setDrawerToOpen(true)
}
setFixedOpen(true);
setDrawerToOpen(true);
};
const fixDrawerClose = () => {
setFixedOpen(false)
setDrawerToOpen(false)
}
setFixedOpen(false);
setDrawerToOpen(false);
};
const tempDrawerOpen = () => {
if (!fixedOpen) {
setDrawerToOpen(true)
setDrawerToOpen(true);
}
}
};
const tempDrawerClose = () => {
if (!fixedOpen) {
setDrawerToOpen(false)
setDrawerToOpen(false);
}
}
};
return (
<Box sx={{ display: 'flex' }}>
<Box sx={{ display: "flex" }}>
<AppBar
sx={{
background: theme.palette.nym.networkExplorer.topNav.appBar,
@@ -234,28 +235,28 @@ export const Nav: FCWithChildren = ({ children }) => {
<Toolbar
disableGutters
sx={{
display: 'flex',
justifyContent: 'space-between',
display: "flex",
justifyContent: "space-between",
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
ml: 0.5,
}}
>
<IconButton component="a" href={NYM_WEBSITE} target="_blank">
{/* <NymLogo /> */}
<NymLogo width={25} />
</IconButton>
<Typography
variant="h6"
noWrap
sx={{
color: theme.palette.nym.networkExplorer.nav.text,
fontSize: '18px',
fontSize: "18px",
fontWeight: 600,
}}
>
@@ -274,7 +275,7 @@ export const Nav: FCWithChildren = ({ children }) => {
href={switchNetworkLink}
sx={{
borderRadius: 2,
textTransform: 'none',
textTransform: "none",
width: 150,
ml: 4,
fontSize: 14,
@@ -288,19 +289,19 @@ export const Nav: FCWithChildren = ({ children }) => {
<Box
sx={{
mr: 2,
alignItems: 'center',
display: 'flex',
alignItems: "center",
display: "flex",
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
width: 'auto',
display: "flex",
flexDirection: "row",
width: "auto",
pr: 0,
pl: 2,
justifyContent: 'flex-end',
alignItems: 'center',
justifyContent: "flex-end",
alignItems: "center",
}}
>
<Box sx={{ mr: 1 }}>
@@ -324,10 +325,10 @@ export const Nav: FCWithChildren = ({ children }) => {
>
<DrawerHeader
sx={{
borderBottom: '1px solid rgba(255, 255, 255, 0.1)',
justifyContent: 'flex-start',
borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
justifyContent: "flex-start",
paddingLeft: 0,
display: 'none',
display: "none",
}}
>
<IconButton
@@ -363,11 +364,11 @@ export const Nav: FCWithChildren = ({ children }) => {
</Drawer>
<Box
style={{ width: `calc(100% - ${drawerWidth}px` }}
sx={{ py: 5, px: 6, mt: 7 }}
sx={{ py: 5, px: 6, mt: 7, bgcolor: "background.default" }}
>
{children}
<Footer />
</Box>
</Box>
)
}
);
};
@@ -1,7 +1,7 @@
'use client'
"use client";
import * as React from 'react'
import { useTheme } from '@mui/material/styles'
import * as React from "react";
import { useTheme } from "@mui/material/styles";
import {
AppBar,
Box,
@@ -12,61 +12,58 @@ import {
ListItemButton,
ListItemIcon,
Toolbar,
} from '@mui/material'
import { Menu } from '@mui/icons-material'
import { MaintenanceBanner } from '@nymproject/react/banners/MaintenanceBanner'
import { useIsMobile } from '@/app/hooks/useIsMobile'
import { MobileDrawerClose } from '@/app/icons/MobileDrawerClose'
import { Footer } from '../Footer'
import { ExpandableButton } from './DesktopNav'
import { ConnectKeplrWallet } from '../Wallet/ConnectKeplrWallet'
import { NetworkTitle } from '../NetworkTitle'
import { originalNavOptions } from '@/app/context/nav'
} from "@mui/material";
import { Menu } from "@mui/icons-material";
import { useIsMobile } from "@/app/hooks/useIsMobile";
import { MobileDrawerClose } from "@/app/icons/MobileDrawerClose";
import { Footer } from "../Footer";
import { ExpandableButton } from "./DesktopNav";
import { ConnectKeplrWallet } from "../Wallet/ConnectKeplrWallet";
import { NetworkTitle } from "../NetworkTitle";
import { originalNavOptions } from "@/app/context/nav";
export const MobileNav: FCWithChildren = ({ children }) => {
const theme = useTheme()
const [drawerOpen, setDrawerOpen] = React.useState(false)
export const MobileNav: FCWithChildren<{ children: React.ReactNode }> = ({
children,
}) => {
const theme = useTheme();
const [drawerOpen, setDrawerOpen] = React.useState(false);
// Set maintenance banner to false by default to don't display it
const [openMaintenance, setOpenMaintenance] = React.useState(false)
const isSmallMobile = useIsMobile(400)
const [openMaintenance, setOpenMaintenance] = React.useState(false);
const isSmallMobile = useIsMobile(400);
const toggleDrawer = () => {
setDrawerOpen(!drawerOpen)
}
setDrawerOpen(!drawerOpen);
};
const openDrawer = () => {
setDrawerOpen(true)
}
setDrawerOpen(true);
};
return (
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
<Box sx={{ display: "flex", flexDirection: "column" }}>
<AppBar
sx={{
background: theme.palette.nym.networkExplorer.topNav.appBar,
borderRadius: 0,
}}
>
<MaintenanceBanner
open={openMaintenance}
onClick={() => setOpenMaintenance(false)}
/>
<Toolbar
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
width: '100%',
display: "flex",
justifyContent: "space-between",
alignItems: "center",
width: "100%",
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
display: "flex",
flexDirection: "row",
alignItems: "center",
}}
>
<IconButton onClick={toggleDrawer}>
<Menu sx={{ color: 'primary.contrastText' }} />
<Menu sx={{ color: "primary.contrastText" }} />
</IconButton>
{!isSmallMobile && <NetworkTitle />}
</Box>
@@ -91,7 +88,7 @@ export const MobileNav: FCWithChildren = ({ children }) => {
sx={{
height: 64,
background: theme.palette.nym.networkExplorer.nav.background,
borderBottom: '1px solid rgba(255, 255, 255, 0.1)',
borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
}}
>
<ListItemButton
@@ -100,8 +97,8 @@ export const MobileNav: FCWithChildren = ({ children }) => {
pt: 2,
pb: 2,
background: theme.palette.nym.networkExplorer.nav.background,
display: 'flex',
justifyContent: 'flex-start',
display: "flex",
justifyContent: "flex-start",
}}
>
<ListItemIcon>
@@ -126,11 +123,10 @@ export const MobileNav: FCWithChildren = ({ children }) => {
</List>
</Box>
</Drawer>
<Box sx={{ width: '100%', p: 4, mt: 7 }}>
<Box sx={{ width: "100%", p: 4, mt: 7, bgcolor: "background.default" }}>
{children}
<Footer />
</Box>
</Box>
)
}
);
};
+3 -2
View File
@@ -1,11 +1,12 @@
import * as React from 'react';
import { Typography } from '@mui/material';
import * as React from "react";
import { Typography } from "@mui/material";
export const Title: FCWithChildren<{ text: string }> = ({ text }) => (
<Typography
variant="h5"
sx={{
fontWeight: 600,
color: "text.primary",
}}
data-testid={text}
>
+35 -31
View File
@@ -1,38 +1,42 @@
'use client'
"use client";
import * as React from 'react'
import { scaleLinear } from 'd3-scale'
import * as React from "react";
import { scaleLinear } from "d3-scale";
import {
ComposableMap,
Geographies,
Geography,
Marker,
ZoomableGroup,
} from 'react-simple-maps'
import ReactTooltip from 'react-tooltip'
import { CircularProgress } from '@mui/material'
import { useTheme } from '@mui/material/styles'
import { ApiState, CountryDataResponse } from '../typeDefs/explorer-api'
import MAP_TOPOJSON from '../assets/world-110m.json'
} from "react-simple-maps";
import { Tooltip as ReactTooltip } from "react-tooltip";
import { CircularProgress } from "@mui/material";
import { useTheme } from "@mui/material/styles";
import { ApiState, CountryDataResponse } from "../typeDefs/explorer-api";
import MAP_TOPOJSON from "../assets/world-110m.json";
import "react-tooltip/dist/react-tooltip.css";
type MapProps = {
userLocation?: [number, number]
countryData?: ApiState<CountryDataResponse>
loading: boolean
}
userLocation?: [number, number];
countryData?: ApiState<CountryDataResponse>;
loading: boolean;
};
export const WorldMap: FCWithChildren<MapProps> = ({
countryData,
userLocation,
loading,
}) => {
const { palette } = useTheme()
const { palette } = useTheme();
// tooltip needs fixing
const colorScale = React.useMemo(() => {
if (countryData?.data) {
const heighestNumberOfNodes = Math.max(
...Object.values(countryData.data).map((country) => country.nodes)
)
);
return scaleLinear<string, string>()
.domain([
0,
@@ -42,17 +46,17 @@ export const WorldMap: FCWithChildren<MapProps> = ({
heighestNumberOfNodes,
])
.range(palette.nym.networkExplorer.map.fills)
.unknown(palette.nym.networkExplorer.map.fills[0])
.unknown(palette.nym.networkExplorer.map.fills[0]);
}
return () => palette.nym.networkExplorer.map.fills[0]
}, [countryData, palette])
return () => palette.nym.networkExplorer.map.fills[0];
}, [countryData, palette]);
const [tooltipContent, setTooltipContent] = React.useState<string | null>(
null
)
);
if (loading) {
return <CircularProgress />
return <CircularProgress />;
}
return (
@@ -61,8 +65,8 @@ export const WorldMap: FCWithChildren<MapProps> = ({
data-tip=""
style={{
backgroundColor: palette.nym.networkExplorer.background.tertiary,
width: '100%',
height: 'auto',
width: "100%",
height: "auto",
}}
viewBox="0, 50, 800, 350"
projection="geoMercator"
@@ -75,7 +79,7 @@ export const WorldMap: FCWithChildren<MapProps> = ({
<Geographies geography={MAP_TOPOJSON}>
{({ geographies }) =>
geographies.map((geo) => {
const d = (countryData?.data || {})[geo.properties.ISO_A3]
const d = (countryData?.data || {})[geo.properties.ISO_A3];
return (
<Geography
key={geo.rsmKey}
@@ -84,25 +88,25 @@ export const WorldMap: FCWithChildren<MapProps> = ({
stroke={palette.nym.networkExplorer.map.stroke}
strokeWidth={0.2}
onMouseEnter={() => {
const { NAME_LONG } = geo.properties
const { NAME_LONG } = geo.properties;
if (!userLocation) {
setTooltipContent(`${NAME_LONG} | ${d?.nodes || 0}`)
setTooltipContent(`${NAME_LONG} | ${d?.nodes || 0}`);
}
}}
onMouseLeave={() => {
setTooltipContent('')
setTooltipContent("");
}}
style={{
hover:
!userLocation && countryData
? {
fill: palette.nym.highlight,
outline: 'white',
outline: "white",
}
: undefined,
}}
/>
)
);
})
}
</Geographies>
@@ -123,7 +127,7 @@ export const WorldMap: FCWithChildren<MapProps> = ({
)}
</ZoomableGroup>
</ComposableMap>
<ReactTooltip>{tooltipContent}</ReactTooltip>
<ReactTooltip id="tooltip">{tooltipContent}</ReactTooltip>
</>
)
}
);
};
+95 -93
View File
@@ -1,7 +1,7 @@
'use client'
"use client";
import * as React from 'react'
import { PaletteMode } from '@mui/material'
import * as React from "react";
import { PaletteMode } from "@mui/material";
import {
ApiState,
BlockResponse,
@@ -13,110 +13,112 @@ import {
ValidatorsResponse,
Environment,
DirectoryServiceProvider,
} from '@/app/typeDefs/explorer-api'
import { EnumFilterKey } from '@/app/typeDefs/filters'
import { Api, getEnvironment } from '@/app/api'
import { toPercentIntegerString } from '@/app/utils'
import { NavOptionType, originalNavOptions } from './nav'
} from "@/app/typeDefs/explorer-api";
import { EnumFilterKey } from "@/app/typeDefs/filters";
import { Api, getEnvironment } from "@/app/api";
import { toPercentIntegerString } from "@/app/utils";
import { NavOptionType, originalNavOptions } from "./nav";
interface StateData {
summaryOverview?: ApiState<SummaryOverviewResponse>
block?: ApiState<BlockResponse>
countryData?: ApiState<CountryDataResponse>
gateways?: ApiState<GatewayResponse>
globalError?: string | undefined
mixnodes?: ApiState<MixNodeResponse>
mode: PaletteMode
validators?: ApiState<ValidatorsResponse>
environment?: Environment
serviceProviders?: ApiState<DirectoryServiceProvider[]>
summaryOverview?: ApiState<SummaryOverviewResponse>;
block?: ApiState<BlockResponse>;
countryData?: ApiState<CountryDataResponse>;
gateways?: ApiState<GatewayResponse>;
globalError?: string | undefined;
mixnodes?: ApiState<MixNodeResponse>;
mode: PaletteMode;
validators?: ApiState<ValidatorsResponse>;
environment?: Environment;
serviceProviders?: ApiState<DirectoryServiceProvider[]>;
}
interface StateApi {
fetchMixnodes: (
status?: MixnodeStatus
) => Promise<MixNodeResponse | undefined>
filterMixnodes: (filters: any, status: any) => void
toggleMode: () => void
) => Promise<MixNodeResponse | undefined>;
filterMixnodes: (filters: any, status: any) => void;
toggleMode: () => void;
}
type State = StateData & StateApi
type State = StateData & StateApi;
export const MainContext = React.createContext<State>({
mode: 'dark',
mode: "dark",
toggleMode: () => undefined,
filterMixnodes: () => null,
fetchMixnodes: () => Promise.resolve(undefined),
})
});
export const useMainContext = (): React.ContextType<typeof MainContext> =>
React.useContext<State>(MainContext)
React.useContext<State>(MainContext);
export const MainContextProvider: FCWithChildren = ({ children }) => {
export const MainContextProvider: FCWithChildren<{
children: React.ReactNode;
}> = ({ children }) => {
// network explorer environment
const [environment, setEnvironment] = React.useState<Environment>()
const [environment, setEnvironment] = React.useState<Environment>();
// light/dark mode
const [mode, setMode] = React.useState<PaletteMode>('dark')
const [mode, setMode] = React.useState<PaletteMode>("dark");
// global / banner error messaging
const [globalError] = React.useState<string>()
const [globalError] = React.useState<string>();
// various APIs for Overview page
const [summaryOverview, setSummaryOverview] =
React.useState<ApiState<SummaryOverviewResponse>>()
const [mixnodes, setMixnodes] = React.useState<ApiState<MixNodeResponse>>()
const [gateways, setGateways] = React.useState<ApiState<GatewayResponse>>()
React.useState<ApiState<SummaryOverviewResponse>>();
const [mixnodes, setMixnodes] = React.useState<ApiState<MixNodeResponse>>();
const [gateways, setGateways] = React.useState<ApiState<GatewayResponse>>();
const [validators, setValidators] =
React.useState<ApiState<ValidatorsResponse>>()
const [block, setBlock] = React.useState<ApiState<BlockResponse>>()
React.useState<ApiState<ValidatorsResponse>>();
const [block, setBlock] = React.useState<ApiState<BlockResponse>>();
const [countryData, setCountryData] =
React.useState<ApiState<CountryDataResponse>>()
React.useState<ApiState<CountryDataResponse>>();
const [serviceProviders, setServiceProviders] =
React.useState<ApiState<DirectoryServiceProvider[]>>()
React.useState<ApiState<DirectoryServiceProvider[]>>();
const toggleMode = () => setMode((m) => (m !== 'light' ? 'light' : 'dark'))
const toggleMode = () => setMode((m) => (m !== "light" ? "light" : "dark"));
const fetchOverviewSummary = async () => {
try {
const data = await Api.fetchOverviewSummary()
setSummaryOverview({ data, isLoading: false })
const data = await Api.fetchOverviewSummary();
setSummaryOverview({ data, isLoading: false });
} catch (error) {
setSummaryOverview({
error:
error instanceof Error
? error
: new Error('Overview summary api fail'),
: new Error("Overview summary api fail"),
isLoading: false,
})
});
}
}
};
const fetchMixnodes = async (status?: MixnodeStatus) => {
let data
setMixnodes((d) => ({ ...d, isLoading: true }))
let data;
setMixnodes((d) => ({ ...d, isLoading: true }));
try {
data = status
? await Api.fetchMixnodesActiveSetByStatus(status)
: await Api.fetchMixnodes()
setMixnodes({ data, isLoading: false })
: await Api.fetchMixnodes();
setMixnodes({ data, isLoading: false });
} catch (error) {
setMixnodes({
error: error instanceof Error ? error : new Error('Mixnode api fail'),
error: error instanceof Error ? error : new Error("Mixnode api fail"),
isLoading: false,
})
});
}
return data
}
return data;
};
const filterMixnodes = async (
filters: { [key in EnumFilterKey]: number[] },
status?: MixnodeStatus
) => {
setMixnodes((d) => ({ ...d, isLoading: true }))
setMixnodes((d) => ({ ...d, isLoading: true }));
const mxns = status
? await Api.fetchMixnodesActiveSetByStatus(status)
: await Api.fetchMixnodes()
: await Api.fetchMixnodes();
const filtered = mxns?.filter(
(m) =>
@@ -126,101 +128,101 @@ export const MainContextProvider: FCWithChildren = ({ children }) => {
m.stake_saturation <= filters.stakeSaturation[1] &&
m.avg_uptime >= filters.routingScore[0] &&
m.avg_uptime <= filters.routingScore[1]
)
);
setMixnodes({ data: filtered, isLoading: false })
}
setMixnodes({ data: filtered, isLoading: false });
};
const fetchGateways = async () => {
setGateways((d) => ({ ...d, isLoading: true }))
setGateways((d) => ({ ...d, isLoading: true }));
try {
const data = await Api.fetchGateways()
setGateways({ data, isLoading: false })
const data = await Api.fetchGateways();
setGateways({ data, isLoading: false });
} catch (error) {
setGateways({
error: error instanceof Error ? error : new Error('Gateways api fail'),
error: error instanceof Error ? error : new Error("Gateways api fail"),
isLoading: false,
})
});
}
}
};
const fetchValidators = async () => {
try {
const data = await Api.fetchValidators()
setValidators({ data, isLoading: false })
const data = await Api.fetchValidators();
setValidators({ data, isLoading: false });
} catch (error) {
setValidators({
error:
error instanceof Error ? error : new Error('Validators api fail'),
error instanceof Error ? error : new Error("Validators api fail"),
isLoading: false,
})
});
}
}
};
const fetchBlock = async () => {
try {
const data = await Api.fetchBlock()
setBlock({ data, isLoading: false })
const data = await Api.fetchBlock();
setBlock({ data, isLoading: false });
} catch (error) {
setBlock({
error: error instanceof Error ? error : new Error('Block api fail'),
error: error instanceof Error ? error : new Error("Block api fail"),
isLoading: false,
})
});
}
}
};
const fetchCountryData = async () => {
setCountryData({ data: undefined, isLoading: true })
setCountryData({ data: undefined, isLoading: true });
try {
const res = await Api.fetchCountryData()
setCountryData({ data: res, isLoading: false })
const res = await Api.fetchCountryData();
setCountryData({ data: res, isLoading: false });
} catch (error) {
setCountryData({
error:
error instanceof Error ? error : new Error('Country Data api fail'),
error instanceof Error ? error : new Error("Country Data api fail"),
isLoading: false,
})
});
}
}
};
const fetchServiceProviders = async () => {
setServiceProviders({ data: undefined, isLoading: true })
setServiceProviders({ data: undefined, isLoading: true });
try {
const res = await Api.fetchServiceProviders()
const res = await Api.fetchServiceProviders();
const resWithRoutingScorePercentage = res.map((item) => ({
...item,
routing_score: item.routing_score
? `${toPercentIntegerString(item.routing_score.toString())}%`
: item.routing_score,
}))
}));
setServiceProviders({
data: resWithRoutingScorePercentage,
isLoading: false,
})
});
} catch (error) {
setServiceProviders({
error:
error instanceof Error
? error
: new Error('Service provider api fail'),
: new Error("Service provider api fail"),
isLoading: false,
})
});
}
}
};
React.useEffect(() => {
if (environment === 'mainnet') {
fetchServiceProviders()
if (environment === "mainnet") {
fetchServiceProviders();
}
}, [environment])
}, [environment]);
React.useEffect(() => {
setEnvironment(getEnvironment())
setEnvironment(getEnvironment());
Promise.all([
fetchOverviewSummary(),
fetchGateways(),
fetchValidators(),
fetchBlock(),
fetchCountryData(),
])
}, [])
]);
}, []);
const state = React.useMemo<State>(
() => ({
@@ -250,7 +252,7 @@ export const MainContextProvider: FCWithChildren = ({ children }) => {
validators,
serviceProviders,
]
)
);
return <MainContext.Provider value={state}>{children}</MainContext.Provider>
}
return <MainContext.Provider value={state}>{children}</MainContext.Provider>;
};
@@ -1,8 +1,8 @@
import * as React from 'react'
import { FallbackProps } from 'react-error-boundary'
import { Alert, AlertTitle, Container } from '@mui/material'
import { NymThemeProvider } from '@nymproject/mui-theme'
import { NymLogo } from '@nymproject/react/logo/NymLogo'
import * as React from "react";
import { FallbackProps } from "react-error-boundary";
import { Alert, AlertTitle, Container } from "@mui/material";
import { NymThemeProvider } from "@nymproject/mui-theme";
import { NymLogo } from "@nymproject/react";
export const ErrorBoundaryContent: FCWithChildren<FallbackProps> = ({
error,
@@ -15,7 +15,7 @@ export const ErrorBoundaryContent: FCWithChildren<FallbackProps> = ({
<AlertTitle>{error.name}</AlertTitle>
{error.message}
</Alert>
{process.env.NODE_ENV === 'development' && (
{process.env.NODE_ENV === "development" && (
<Alert severity="info" sx={{ mt: 2 }} data-testid="stack-trace">
<AlertTitle>Stack trace</AlertTitle>
{error.stack}
@@ -23,4 +23,4 @@ export const ErrorBoundaryContent: FCWithChildren<FallbackProps> = ({
)}
</Container>
</NymThemeProvider>
)
);
+9 -9
View File
@@ -1,21 +1,21 @@
import type { Metadata } from 'next'
import '@interchain-ui/react/styles'
import { App } from './App'
import type { Metadata } from "next";
import "@interchain-ui/react/styles";
import { App } from "./App";
export const metadata: Metadata = {
title: 'Nym Network Explorer',
}
title: "Nym Network Explorer",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<App>{children}</App>
</body>
</html>
)
);
}
@@ -1,106 +1,107 @@
'use client'
"use client";
import React, { useMemo } from 'react'
import { Box, Card, Grid, Stack } from '@mui/material'
import { useTheme } from '@mui/material/styles'
import React, { useMemo } from "react";
import { Box, Card, Grid, Stack } from "@mui/material";
import { useTheme } from "@mui/material/styles";
import {
MRT_ColumnDef,
MaterialReactTable,
useMaterialReactTable,
} from 'material-react-table'
import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid'
import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard'
import { Tooltip as InfoTooltip } from '@nymproject/react/tooltip/Tooltip'
import { diff, gte, rcompare } from 'semver'
import { useMainContext } from '@/app/context/main'
import { TableToolbar } from '@/app/components/TableToolbar'
import { CustomColumnHeading } from '@/app/components/CustomColumnHeading'
import { Title } from '@/app/components/Title'
import { unymToNym } from '@/app/utils/currency'
import { Tooltip } from '@/app/components/Tooltip'
import { NYM_BIG_DIPPER } from '@/app/api/constants'
import { splice } from '@/app/utils'
} from "material-react-table";
import { GridColDef, GridRenderCellParams } from "@mui/x-data-grid";
import { CopyToClipboard } from "@nymproject/react";
import { Tooltip as InfoTooltip } from "@nymproject/react";
import { diff, gte, rcompare } from "semver";
import { useMainContext } from "@/app/context/main";
import { TableToolbar } from "@/app/components/TableToolbar";
import { CustomColumnHeading } from "@/app/components/CustomColumnHeading";
import { Title } from "@/app/components/Title";
import { unymToNym } from "@/app/utils/currency";
import { Tooltip } from "@/app/components/Tooltip";
import { NYM_BIG_DIPPER } from "@/app/api/constants";
import { splice } from "@/app/utils";
import {
VersionDisplaySelector,
VersionSelectOptions,
} from '@/app/components/Gateways/VersionDisplaySelector'
import StyledLink from '@/app/components/StyledLink'
} from "@/app/components/Gateways/VersionDisplaySelector";
import StyledLink from "@/app/components/StyledLink";
import {
GatewayRowType,
gatewayToGridRow,
} from '@/app/components/Gateways/Gateways'
} from "@/app/components/Gateways/Gateways";
import { InfoOutlined } from "@mui/icons-material";
const PageGateways = () => {
const { gateways } = useMainContext()
const { gateways } = useMainContext();
const [versionFilter, setVersionFilter] =
React.useState<VersionSelectOptions>(VersionSelectOptions.all)
React.useState<VersionSelectOptions>(VersionSelectOptions.all);
const theme = useTheme()
const theme = useTheme();
const highestVersion = React.useMemo(() => {
if (gateways?.data) {
const versions = gateways.data.reduce(
(a: string[], b) => [...a, b.gateway.version],
[]
)
const [lastestVersion] = versions.sort(rcompare)
return lastestVersion
);
const [lastestVersion] = versions.sort(rcompare);
return lastestVersion;
}
// fallback value
return '2.0.0'
}, [gateways])
return "2.0.0";
}, [gateways]);
const filterByLatestVersions = React.useMemo(() => {
const filtered = gateways?.data?.filter((gw) => {
const versionDiff = diff(highestVersion, gw.gateway.version)
return versionDiff === 'patch' || versionDiff === null
})
if (filtered) return filtered
return []
}, [gateways])
const versionDiff = diff(highestVersion, gw.gateway.version);
return versionDiff === "patch" || versionDiff === null;
});
if (filtered) return filtered;
return [];
}, [gateways]);
const filterByOlderVersions = React.useMemo(() => {
const filtered = gateways?.data?.filter((gw) => {
const versionDiff = diff(highestVersion, gw.gateway.version)
return versionDiff === 'major' || versionDiff === 'minor'
})
if (filtered) return filtered
return []
}, [gateways])
const versionDiff = diff(highestVersion, gw.gateway.version);
return versionDiff === "major" || versionDiff === "minor";
});
if (filtered) return filtered;
return [];
}, [gateways]);
const filteredByVersion = React.useMemo(() => {
switch (versionFilter) {
case VersionSelectOptions.latestVersion:
return filterByLatestVersions
return filterByLatestVersions;
case VersionSelectOptions.olderVersions:
return filterByOlderVersions
return filterByOlderVersions;
case VersionSelectOptions.all:
return gateways?.data || []
return gateways?.data || [];
default:
return []
return [];
}
}, [versionFilter, gateways])
}, [versionFilter, gateways]);
const data = useMemo(() => {
return gatewayToGridRow(filteredByVersion || [])
}, [filteredByVersion])
return gatewayToGridRow(filteredByVersion || []);
}, [filteredByVersion]);
const columns = useMemo<MRT_ColumnDef<GatewayRowType>[]>(() => {
return [
{
id: 'gateway-data',
header: 'Gatewsay Data',
id: "gateway-data",
header: "Gatewsay Data",
columns: [
{
id: 'identity_key',
header: 'Identity Key',
accessorKey: 'identity_key',
id: "identity_key",
header: "Identity Key",
accessorKey: "identity_key",
size: 250,
Cell: ({ row }) => {
return (
<Stack direction="row" alignItems="center" gap={1}>
<CopyToClipboard
sx={{ mr: 0.5, color: 'grey.400' }}
sx={{ mr: 0.5, color: "grey.400" }}
smallIcons
value={row.original.identity_key}
tooltip={`Copy identity key ${row.original.identity_key} to clipboard`}
@@ -113,13 +114,13 @@ const PageGateways = () => {
{splice(7, 29, row.original.identity_key)}
</StyledLink>
</Stack>
)
);
},
},
{
id: 'node_performance',
header: 'Node Performance',
accessorKey: 'node_performance',
id: "node_performance",
header: "Node Performance",
accessorKey: "node_performance",
size: 200,
Header: () => {
return (
@@ -134,10 +135,13 @@ const PageGateways = () => {
}
maxWidth={230}
arrow
TooltipIcon={
<InfoOutlined sx={{ width: 18, height: 18, mr: 1 }} />
}
/>
<CustomColumnHeading headingTitle="Routing Score" />
</Box>
)
);
},
Cell: ({ row }) => {
return (
@@ -148,13 +152,13 @@ const PageGateways = () => {
>
{`${row.original.node_performance}%`}
</StyledLink>
)
);
},
},
{
id: 'version',
header: 'Version',
accessorKey: 'version',
id: "version",
header: "Version",
accessorKey: "version",
size: 150,
Cell: ({ row }) => {
return (
@@ -165,18 +169,18 @@ const PageGateways = () => {
>
{row.original.version}
</StyledLink>
)
);
},
},
{
id: 'location',
header: 'Location',
accessorKey: 'location',
id: "location",
header: "Location",
accessorKey: "location",
size: 150,
Cell: ({ row }) => {
return (
<Box
sx={{ justifyContent: 'flex-start', cursor: 'pointer' }}
sx={{ justifyContent: "flex-start", cursor: "pointer" }}
data-testid="location-button"
>
<Tooltip
@@ -185,22 +189,22 @@ const PageGateways = () => {
>
<Box
sx={{
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: "hidden",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
}}
>
{row.original.location}
</Box>
</Tooltip>
</Box>
)
);
},
},
{
id: 'host',
header: 'IP:Port',
accessorKey: 'host',
id: "host",
header: "IP:Port",
accessorKey: "host",
size: 150,
Cell: ({ row }) => {
return (
@@ -211,13 +215,13 @@ const PageGateways = () => {
>
{row.original.host}
</StyledLink>
)
);
},
},
{
id: 'owner',
header: 'Owner',
accessorKey: 'owner',
id: "owner",
header: "Owner",
accessorKey: "owner",
size: 150,
Cell: ({ row }) => {
return (
@@ -229,18 +233,18 @@ const PageGateways = () => {
>
{splice(7, 29, row.original.owner)}
</StyledLink>
)
);
},
},
],
},
]
}, [])
];
}, []);
const _columns: GridColDef[] = [
{
field: 'node_performance',
align: 'center',
field: "node_performance",
align: "center",
renderHeader: () => (
<>
<InfoTooltip
@@ -251,14 +255,15 @@ const PageGateways = () => {
bgColor={theme.palette.nym.networkExplorer.tooltip.background}
maxWidth={230}
arrow
TooltipIcon={<InfoOutlined sx={{ width: 18, height: 18, mr: 1 }} />}
/>
<CustomColumnHeading headingTitle="Routing Score" />
</>
),
width: 120,
disableColumnMenu: true,
headerAlign: 'center',
headerClassName: 'MuiDataGrid-header-override',
headerAlign: "center",
headerClassName: "MuiDataGrid-header-override",
renderCell: (params: GridRenderCellParams) => (
<StyledLink
to={`/network-components/gateways/${params.row.identity_key}`}
@@ -269,13 +274,13 @@ const PageGateways = () => {
),
},
{
field: 'version',
align: 'center',
field: "version",
align: "center",
renderHeader: () => <CustomColumnHeading headingTitle="Version" />,
width: 150,
disableColumnMenu: true,
headerAlign: 'center',
headerClassName: 'MuiDataGrid-header-override',
headerAlign: "center",
headerClassName: "MuiDataGrid-header-override",
renderCell: (params: GridRenderCellParams) => (
<StyledLink
to={`/network-components/gateways/${params.row.identity_key}`}
@@ -285,28 +290,28 @@ const PageGateways = () => {
</StyledLink>
),
sortComparator: (a, b) => {
if (gte(a, b)) return 1
return -1
if (gte(a, b)) return 1;
return -1;
},
},
{
field: 'location',
field: "location",
renderHeader: () => <CustomColumnHeading headingTitle="Location" />,
width: 180,
disableColumnMenu: true,
headerAlign: 'left',
headerClassName: 'MuiDataGrid-header-override',
headerAlign: "left",
headerClassName: "MuiDataGrid-header-override",
renderCell: (params: GridRenderCellParams) => (
<Box
sx={{ justifyContent: 'flex-start', cursor: 'pointer' }}
sx={{ justifyContent: "flex-start", cursor: "pointer" }}
data-testid="location-button"
>
<Tooltip text={params.value} id="gateway-location-text">
<Box
sx={{
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: "hidden",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
}}
>
{params.value}
@@ -316,12 +321,12 @@ const PageGateways = () => {
),
},
{
field: 'host',
field: "host",
renderHeader: () => <CustomColumnHeading headingTitle="IP:Port" />,
width: 180,
disableColumnMenu: true,
headerAlign: 'left',
headerClassName: 'MuiDataGrid-header-override',
headerAlign: "left",
headerClassName: "MuiDataGrid-header-override",
renderCell: (params: GridRenderCellParams) => (
<StyledLink
to={`/network-components/gateways/${params.row.identity_key}`}
@@ -332,13 +337,13 @@ const PageGateways = () => {
),
},
{
field: 'owner',
headerName: 'Owner',
field: "owner",
headerName: "Owner",
renderHeader: () => <CustomColumnHeading headingTitle="Owner" />,
width: 180,
disableColumnMenu: true,
headerAlign: 'left',
headerClassName: 'MuiDataGrid-header-override',
headerAlign: "left",
headerClassName: "MuiDataGrid-header-override",
renderCell: (params: GridRenderCellParams) => (
<StyledLink
to={`${NYM_BIG_DIPPER}/account/${params.value}`}
@@ -350,13 +355,13 @@ const PageGateways = () => {
),
},
{
field: 'bond',
field: "bond",
width: 150,
disableColumnMenu: true,
type: 'number',
type: "number",
renderHeader: () => <CustomColumnHeading headingTitle="Bond" />,
headerClassName: 'MuiDataGrid-header-override',
headerAlign: 'left',
headerClassName: "MuiDataGrid-header-override",
headerAlign: "left",
renderCell: (params: GridRenderCellParams) => (
<StyledLink
to={`/network-components/gateways/${params.row.identity_key}`}
@@ -366,12 +371,12 @@ const PageGateways = () => {
</StyledLink>
),
},
]
];
const table = useMaterialReactTable({
columns,
data,
})
});
return (
<>
@@ -383,7 +388,7 @@ const PageGateways = () => {
<Card
sx={{
padding: 2,
height: '100%',
height: "100%",
}}
>
<TableToolbar
@@ -399,7 +404,7 @@ const PageGateways = () => {
</Grid>
</Grid>
</>
)
}
);
};
export default PageGateways
export default PageGateways;
@@ -1,13 +1,13 @@
'use client'
"use client";
import React, { useCallback, useMemo } from 'react'
import { useRouter, useSearchParams } from 'next/navigation'
import React, { useCallback, useMemo } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import {
MaterialReactTable,
useMaterialReactTable,
type MRT_ColumnDef,
} from 'material-react-table'
import { Grid, Card, Button, Box, Stack } from '@mui/material'
} from "material-react-table";
import { Grid, Card, Button, Box, Stack } from "@mui/material";
import {
CustomColumnHeading,
DelegateIconButton,
@@ -21,81 +21,81 @@ import {
Title,
Tooltip,
mixnodeToGridRow,
} from '@/app/components'
import { DelegationsProvider } from '@/app/context/delegations'
import { useWalletContext } from '@/app/context/wallet'
import { useGetMixNodeStatusColor, useIsMobile } from '@/app/hooks'
import { useMainContext } from '@/app/context/main'
import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard'
import { splice } from '@/app/utils'
import { currencyToString } from '@/app/utils/currency'
import { NYM_BIG_DIPPER } from '@/app/api/constants'
} from "@/app/components";
import { DelegationsProvider } from "@/app/context/delegations";
import { useWalletContext } from "@/app/context/wallet";
import { useGetMixNodeStatusColor, useIsMobile } from "@/app/hooks";
import { useMainContext } from "@/app/context/main";
import { CopyToClipboard } from "@nymproject/react";
import { splice } from "@/app/utils";
import { currencyToString } from "@/app/utils/currency";
import { NYM_BIG_DIPPER } from "@/app/api/constants";
import {
MixnodeStatusWithAll,
toMixnodeStatus,
} from '@/app/typeDefs/explorer-api'
} from "@/app/typeDefs/explorer-api";
export default function MixnodesPage() {
const isMobile = useIsMobile()
const { isWalletConnected } = useWalletContext()
const { mixnodes, fetchMixnodes } = useMainContext()
const router = useRouter()
const isMobile = useIsMobile();
const { isWalletConnected } = useWalletContext();
const { mixnodes, fetchMixnodes } = useMainContext();
const router = useRouter();
const [itemSelectedForDelegation, setItemSelectedForDelegation] =
React.useState<{
mixId: number
identityKey: string
}>()
mixId: number;
identityKey: string;
}>();
const [confirmationModalProps, setConfirmationModalProps] = React.useState<
DelegationModalProps | undefined
>()
>();
const search = useSearchParams()
const status = search.get('status') as MixnodeStatusWithAll
const search = useSearchParams();
const status = search.get("status") as MixnodeStatusWithAll;
React.useEffect(() => {
// when the status changes, get the mixnodes
fetchMixnodes(toMixnodeStatus(status))
}, [status])
fetchMixnodes(toMixnodeStatus(status));
}, [status]);
const handleMixnodeStatusChanged = (newStatus?: MixnodeStatusWithAll) => {
router.push(
newStatus && newStatus !== 'all'
newStatus && newStatus !== "all"
? `/network-components/mixnodes?status=${newStatus}`
: '/network-components/mixnodes'
)
}
: "/network-components/mixnodes"
);
};
const handleOnDelegate = useCallback(
({ identityKey, mixId }: { identityKey: string; mixId: number }) => {
if (!isWalletConnected) {
setConfirmationModalProps({
status: 'info',
message: 'Please connect your wallet to delegate',
})
status: "info",
message: "Please connect your wallet to delegate",
});
} else {
setItemSelectedForDelegation({ identityKey, mixId })
setItemSelectedForDelegation({ identityKey, mixId });
}
},
[isWalletConnected]
)
);
const handleNewDelegation = (delegationModalProps: DelegationModalProps) => {
setItemSelectedForDelegation(undefined)
setConfirmationModalProps(delegationModalProps)
}
setItemSelectedForDelegation(undefined);
setConfirmationModalProps(delegationModalProps);
};
const columns = useMemo<MRT_ColumnDef<MixnodeRowType>[]>(() => {
return [
{
id: 'mixnode-data',
header: 'Mixnode Data',
id: "mixnode-data",
header: "Mixnode Data",
columns: [
{
id: 'delegate',
accessorKey: 'delegate',
id: "delegate",
accessorKey: "delegate",
size: isMobile ? 50 : 150,
header: '',
header: "",
grow: false,
Cell: ({ row }) => (
<DelegateIconButton
@@ -113,15 +113,15 @@ export default function MixnodesPage() {
Filter: () => null,
},
{
id: 'identity_key',
header: 'Identity Key',
accessorKey: 'identity_key',
id: "identity_key",
header: "Identity Key",
accessorKey: "identity_key",
size: 250,
Cell: ({ row }) => {
return (
<Stack direction="row" alignItems="center" gap={1}>
<CopyToClipboard
sx={{ mr: 0.5, color: 'grey.400' }}
sx={{ mr: 0.5, color: "grey.400" }}
smallIcons
value={row.original.identity_key}
tooltip={`Copy identity key ${row.original.identity_key} to clipboard`}
@@ -134,13 +134,13 @@ export default function MixnodesPage() {
{splice(7, 29, row.original.identity_key)}
</StyledLink>
</Stack>
)
);
},
},
{
id: 'bond',
header: 'Stake',
accessorKey: 'bond',
id: "bond",
header: "Stake",
accessorKey: "bond",
Cell: ({ row }) => (
<StyledLink
to={`/network-components/mixnodes/${row.original.mix_id}`}
@@ -151,9 +151,9 @@ export default function MixnodesPage() {
),
},
{
id: 'stake_saturation',
header: 'Stake Saturation',
accessorKey: 'stake_saturation',
id: "stake_saturation",
header: "Stake Saturation",
accessorKey: "stake_saturation",
size: 225,
Header() {
return (
@@ -161,7 +161,7 @@ export default function MixnodesPage() {
headingTitle="Stake Saturation"
tooltipInfo="Level of stake saturation for this node. Nodes receive more rewards the higher their saturation level, up to 100%. Beyond 100% no additional rewards are granted. The current stake saturation level is 940k NYMs, computed as S/K where S is target amount of tokens staked in the network and K is the number of nodes in the reward set."
/>
)
);
},
Cell: ({ row }) => (
<StyledLink
@@ -171,9 +171,9 @@ export default function MixnodesPage() {
),
},
{
id: 'pledge_amount',
header: 'Bond',
accessorKey: 'pledge_amount',
id: "pledge_amount",
header: "Bond",
accessorKey: "pledge_amount",
size: 185,
Header: () => (
<CustomColumnHeading
@@ -193,9 +193,9 @@ export default function MixnodesPage() {
),
},
{
id: 'profit_percentage',
accessorKey: 'profit_percentage',
header: 'Profit Margin',
id: "profit_percentage",
accessorKey: "profit_percentage",
header: "Profit Margin",
size: 145,
Header: () => (
<CustomColumnHeading
@@ -211,10 +211,10 @@ export default function MixnodesPage() {
),
},
{
id: 'operating_cost',
accessorKey: 'operating_cost',
id: "operating_cost",
accessorKey: "operating_cost",
size: 220,
header: 'Operating Cost',
header: "Operating Cost",
disableColumnMenu: true,
Header: () => (
<CustomColumnHeading
@@ -230,10 +230,10 @@ export default function MixnodesPage() {
),
},
{
id: 'node_performance',
accessorKey: 'node_performance',
id: "node_performance",
accessorKey: "node_performance",
size: 200,
header: 'Routing Score',
header: "Routing Score",
Header: () => (
<CustomColumnHeading
headingTitle="Routing Score"
@@ -248,10 +248,10 @@ export default function MixnodesPage() {
),
},
{
id: 'owner',
accessorKey: 'owner',
id: "owner",
accessorKey: "owner",
size: 150,
header: 'Owner',
header: "Owner",
Header: () => <CustomColumnHeading headingTitle="Owner" />,
Cell: ({ row }) => (
<StyledLink
@@ -265,19 +265,19 @@ export default function MixnodesPage() {
),
},
{
id: 'location',
accessorKey: 'location',
header: 'Location',
id: "location",
accessorKey: "location",
header: "Location",
maxSize: 150,
Header: () => <CustomColumnHeading headingTitle="Location" />,
Cell: ({ row }) => (
<Tooltip text={row.original.location} id="mixnode-location-text">
<Box
sx={{
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
cursor: 'pointer',
overflow: "hidden",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
cursor: "pointer",
color: useGetMixNodeStatusColor(row.original.status),
}}
>
@@ -287,9 +287,9 @@ export default function MixnodesPage() {
),
},
{
id: 'host',
accessorKey: 'host',
header: 'Host',
id: "host",
accessorKey: "host",
header: "Host",
size: 130,
Header: () => <CustomColumnHeading headingTitle="Host" />,
Cell: ({ row }) => (
@@ -303,12 +303,12 @@ export default function MixnodesPage() {
},
],
},
]
}, [handleOnDelegate, isMobile])
];
}, [handleOnDelegate, isMobile]);
const data = useMemo(() => {
return mixnodeToGridRow(mixnodes?.data)
}, [mixnodes?.data])
return mixnodeToGridRow(mixnodes?.data);
}, [mixnodes?.data]);
const table = useMaterialReactTable({
columns,
@@ -317,11 +317,11 @@ export default function MixnodesPage() {
state: {
isLoading: mixnodes?.isLoading,
},
layoutMode: 'grid-no-grow',
layoutMode: "grid-no-grow",
initialState: {
columnPinning: { left: ['delegate'] },
columnPinning: { left: ["delegate"] },
},
})
});
return (
<DelegationsProvider>
@@ -333,7 +333,7 @@ export default function MixnodesPage() {
<Card
sx={{
padding: 2,
height: '100%',
height: "100%",
}}
>
<TableToolbar
@@ -350,7 +350,7 @@ export default function MixnodesPage() {
fullWidth
size="large"
variant="outlined"
onClick={() => router.push('/delegations')}
onClick={() => router.push("/delegations")}
>
Delegations
</Button>
@@ -364,7 +364,7 @@ export default function MixnodesPage() {
{itemSelectedForDelegation && (
<DelegateModal
onClose={() => {
setItemSelectedForDelegation(undefined)
setItemSelectedForDelegation(undefined);
}}
header="Delegate"
buttonText="Delegate stake"
@@ -382,19 +382,19 @@ export default function MixnodesPage() {
{...confirmationModalProps}
open={Boolean(confirmationModalProps)}
onClose={async () => {
setConfirmationModalProps(undefined)
if (confirmationModalProps.status === 'success') {
router.push('/delegations')
setConfirmationModalProps(undefined);
if (confirmationModalProps.status === "success") {
router.push("/delegations");
}
}}
sx={{
width: {
xs: '90%',
xs: "90%",
sm: 600,
},
}}
/>
)}
</DelegationsProvider>
)
);
}
@@ -1,6 +1,6 @@
'use client'
"use client";
import React, { useMemo } from 'react'
import React, { useMemo } from "react";
import {
Box,
Button,
@@ -9,29 +9,28 @@ import {
Grid,
ListItem,
Menu,
Typography,
} from '@mui/material'
import { TableToolbar } from '@/app/components/TableToolbar'
import { Title } from '@/app/components/Title'
import { useMainContext } from '@/app/context/main'
import { CustomColumnHeading } from '@/app/components/CustomColumnHeading'
} from "@mui/material";
import { TableToolbar } from "@/app/components/TableToolbar";
import { Title } from "@/app/components/Title";
import { useMainContext } from "@/app/context/main";
import { CustomColumnHeading } from "@/app/components/CustomColumnHeading";
import {
MRT_ColumnDef,
MaterialReactTable,
useMaterialReactTable,
} from 'material-react-table'
import { DirectoryServiceProvider } from '@/app/typeDefs/explorer-api'
} from "material-react-table";
import { DirectoryServiceProvider } from "@/app/typeDefs/explorer-api";
const SupportedApps = () => {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null)
const open = Boolean(anchorEl)
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget)
}
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null)
}
const anchorRef = React.useRef<HTMLButtonElement>(null)
setAnchorEl(null);
};
const anchorRef = React.useRef<HTMLButtonElement>(null);
return (
<FormControl size="small">
@@ -41,7 +40,7 @@ const SupportedApps = () => {
size="large"
variant="outlined"
color="inherit"
sx={{ mr: 2, textTransform: 'capitalize' }}
sx={{ mr: 2, textTransform: "capitalize" }}
>
Supported Apps
</Button>
@@ -52,67 +51,67 @@ const SupportedApps = () => {
<ListItem>Blockstream Green</ListItem>
</Menu>
</FormControl>
)
}
);
};
const ServiceProviders = () => {
const { serviceProviders } = useMainContext()
const { serviceProviders } = useMainContext();
const columns = useMemo<MRT_ColumnDef<DirectoryServiceProvider>[]>(() => {
return [
{
id: 'service-providers-data',
header: 'Service Providers Data',
id: "service-providers-data",
header: "Service Providers Data",
columns: [
{
id: 'address',
accessorKey: 'address',
header: 'Client ID',
id: "address",
accessorKey: "address",
header: "Client ID",
size: 450,
},
{
id: 'service_type-type',
accessorKey: 'service_type',
header: 'Type',
id: "service_type-type",
accessorKey: "service_type",
header: "Type",
size: 100,
},
{
id: 'routing_score-score',
accessorKey: 'routing_score',
header: 'Routing score',
id: "routing_score-score",
accessorKey: "routing_score",
header: "Routing score",
Header() {
return (
<CustomColumnHeading
headingTitle="Routing score"
tooltipInfo="Routing score is only displayed for the service providers that had a successful ping within the last two hours"
/>
)
);
},
Cell({ row }) {
return row.original.routing_score || '-'
return row.original.routing_score || "-";
},
},
],
},
]
}, [])
];
}, []);
const table = useMaterialReactTable({
columns,
data: serviceProviders?.data || [],
layoutMode: 'grid',
layoutMode: "grid",
state: {
isLoading: serviceProviders?.isLoading,
},
initialState: {
sorting: [
{
id: 'routing_score',
id: "routing_score",
desc: true,
},
],
},
})
});
return (
<>
@@ -134,7 +133,7 @@ const ServiceProviders = () => {
</Grid>
</Grid>
</>
)
}
);
};
export default ServiceProviders
export default ServiceProviders;
-4
View File
@@ -2,12 +2,8 @@
import React, { useMemo } from 'react'
import {
Alert,
Box,
CircularProgress,
Grid,
SelectChangeEvent,
Typography,
} from '@mui/material'
import { ContentCard } from '@/app/components/ContentCard'
import { TableToolbar } from '@/app/components/TableToolbar'
+8 -8
View File
@@ -1,8 +1,8 @@
import React from 'react'
import CosmosKitProvider from '@/app/context/cosmos-kit'
import { WalletProvider } from '@/app/context/wallet'
import { NetworkExplorerThemeProvider } from '@/app/theme'
import { MainContextProvider } from '@/app/context/main'
import React from "react";
import CosmosKitProvider from "@/app/context/cosmos-kit";
import { WalletProvider } from "@/app/context/wallet";
import { NetworkExplorerThemeProvider } from "@/app/theme";
import { MainContextProvider } from "@/app/context/main";
const Providers = ({ children }: { children: React.ReactNode }) => {
return (
@@ -13,7 +13,7 @@ const Providers = ({ children }: { children: React.ReactNode }) => {
</CosmosKitProvider>
</NetworkExplorerThemeProvider>
</MainContextProvider>
)
}
);
};
export { Providers }
export { Providers };
+12 -8
View File
@@ -1,15 +1,19 @@
'use client'
"use client";
import * as React from 'react'
import { NymNetworkExplorerThemeProvider } from '@nymproject/mui-theme'
import { useMainContext } from '../context/main'
import * as React from "react";
import { NymNetworkExplorerThemeProvider } from "@nymproject/mui-theme";
import { useMainContext } from "../context/main";
export const NetworkExplorerThemeProvider: FCWithChildren = ({ children }) => {
const { mode } = useMainContext()
export const NetworkExplorerThemeProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
const { mode } = useMainContext();
return (
<NymNetworkExplorerThemeProvider mode={mode}>
{children}
</NymNetworkExplorerThemeProvider>
)
}
);
};
+1 -1
View File
@@ -1 +1 @@
declare type FCWithChildren<P = {}> = React.FC<React.PropsWithChildren<P>>;
declare type FCWithChildren<P = object> = React.FC<React.PropsWithChildren<P>>;
-1
View File
@@ -1 +0,0 @@
declare module 'react-tooltip';
+56 -37
View File
@@ -1,19 +1,21 @@
/* eslint-disable camelcase */
import { MutableRefObject } from 'react';
import { Theme } from '@mui/material/styles';
import { registerLocale, getName } from 'i18n-iso-countries';
import Big from 'big.js';
import { CountryData } from '@/app/typeDefs/explorer-api';
import { EconomicsRowsType } from '@/app/components/MixNodes/Economics/types';
import { Network } from '../typeDefs/network';
import { MutableRefObject } from "react";
import { Theme } from "@mui/material/styles";
import { registerLocale, getName } from "i18n-iso-countries";
import Big from "big.js";
import { CountryData } from "@/app/typeDefs/explorer-api";
import { EconomicsRowsType } from "@/app/components/MixNodes/Economics/types";
import { Network } from "../typeDefs/network";
registerLocale(require('i18n-iso-countries/langs/en.json'));
registerLocale(require("i18n-iso-countries/langs/en.json"));
export function formatNumber(num: number): string {
return new Intl.NumberFormat().format(num);
}
export function scrollToRef(ref: MutableRefObject<HTMLDivElement | undefined>): void {
export function scrollToRef(
ref: MutableRefObject<HTMLDivElement | undefined>
): void {
if (ref?.current) ref.current.scrollIntoView();
}
@@ -25,13 +27,15 @@ export type CountryDataRowType = {
percentage: string;
};
export function countryDataToGridRow(countriesData: CountryData[]): CountryDataRowType[] {
export function countryDataToGridRow(
countriesData: CountryData[]
): CountryDataRowType[] {
const totalNodes = countriesData.reduce((acc, obj) => acc + obj.nodes, 0);
const formatted = countriesData.map((each: CountryData, index: number) => {
const updatedCountryRecord: CountryDataRowType = {
...each,
id: index,
countryName: getName(each.ISO3, 'en', { select: 'alias' }),
countryName: getName(each.ISO3, "en", { select: "alias" }) || "",
percentage: ((each.nodes * 100) / totalNodes).toFixed(1),
};
return updatedCountryRecord;
@@ -41,16 +45,21 @@ export function countryDataToGridRow(countriesData: CountryData[]): CountryDataR
return sorted;
}
export const splice = (start: number, deleteCount: number, address?: string): string => {
export const splice = (
start: number,
deleteCount: number,
address?: string
): string => {
if (address) {
const array = address.split('');
array.splice(start, deleteCount, '...');
return array.join('');
const array = address.split("");
array.splice(start, deleteCount, "...");
return array.join("");
}
return '';
return "";
};
export const trimAddress = (address = '', trimBy = 6) => `${address.slice(0, trimBy)}...${address.slice(-trimBy)}`;
export const trimAddress = (address = "", trimBy = 6) =>
`${address.slice(0, trimBy)}...${address.slice(-trimBy)}`;
/**
* Converts a stringified percentage float (0.0-1.0) to a stringified integer (0-100).
@@ -58,27 +67,33 @@ export const trimAddress = (address = '', trimBy = 6) => `${address.slice(0, tri
* @param value - the percentage to convert
* @returns A stringified integer
*/
export const toPercentIntegerString = (value: string) => Math.round(Number(value) * 100).toString();
export const toPercentInteger = (value: string) => Math.round(Number(value) * 100);
export const toPercentIntegerString = (value: string) =>
Math.round(Number(value) * 100).toString();
export const toPercentInteger = (value: string) =>
Math.round(Number(value) * 100);
export const textColour = (value: EconomicsRowsType, field: string, theme: Theme) => {
export const textColour = (
value: EconomicsRowsType,
field: string,
theme: Theme
) => {
const progressBarValue = value?.progressBarValue || 0;
const fieldValue = value.value;
if (progressBarValue > 100) {
return theme.palette.warning.main;
}
if (field === 'selectionChance') {
if (field === "selectionChance") {
// TODO: when v2 will be deployed, remove cases: VeryHigh, Moderate and VeryLow
switch (fieldValue) {
case 'High':
case 'VeryHigh':
case "High":
case "VeryHigh":
return theme.palette.nym.networkExplorer.selectionChance.overModerate;
case 'Good':
case 'Moderate':
case "Good":
case "Moderate":
return theme.palette.nym.networkExplorer.selectionChance.moderate;
case 'Low':
case 'VeryLow':
case "Low":
case "VeryLow":
return theme.palette.nym.networkExplorer.selectionChance.underModerate;
default:
return theme.palette.nym.wallet.fee;
@@ -101,8 +116,12 @@ export const isLessThan = (a: number, b: number) => a < b;
*
*/
export const isBalanceEnough = (fee: string, tx: string = '0', balance: string = '0') => {
console.log('balance', balance, fee, tx);
export const isBalanceEnough = (
fee: string,
tx: string = "0",
balance: string = "0"
) => {
console.log("balance", balance, fee, tx);
try {
return Big(balance).gte(Big(fee).plus(Big(tx)));
} catch (e) {
@@ -112,13 +131,13 @@ export const isBalanceEnough = (fee: string, tx: string = '0', balance: string =
};
export const urls = (networkName?: Network) =>
networkName === 'MAINNET'
networkName === "MAINNET"
? {
mixnetExplorer: 'https://mixnet.explorers.guru/',
blockExplorer: 'https://blocks.nymtech.net',
networkExplorer: 'https://explorer.nymtech.net',
}
mixnetExplorer: "https://mixnet.explorers.guru/",
blockExplorer: "https://blocks.nymtech.net",
networkExplorer: "https://explorer.nymtech.net",
}
: {
blockExplorer: `https://${networkName}-blocks.nymtech.net`,
networkExplorer: `https://${networkName}-explorer.nymtech.net`,
};
blockExplorer: `https://${networkName}-blocks.nymtech.net`,
networkExplorer: `https://${networkName}-explorer.nymtech.net`,
};
+37 -6
View File
@@ -4,25 +4,56 @@
"scripts": {
"dev": "next dev",
"build": "next build",
"build:prod": "yarn --cwd .. build && next build",
"build:prod": "pnpm i && pnpm -C .. build && next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@nymproject/react": "^1.0.0",
"@nymproject/nym-validator-client": "0.18.0",
"@chain-registry/types": "^0.45.9",
"@cosmjs/cosmwasm-stargate": "^0.29.5",
"@cosmos-kit/core": "^2.13.1",
"@cosmos-kit/keplr-extension": "^2.12.2",
"@cosmos-kit/react": "^2.17.2",
"@mui/base": "5.0.0-beta.40",
"@mui/icons-material": "^5.2.0",
"@mui/material": "^5.2.2",
"@mui/styles": "^5.2.2",
"@mui/system": "^5.15.20",
"@mui/x-data-grid": "7.1.1",
"@mui/x-date-pickers": "7.1.1",
"@nymproject/contract-clients": "1.2.4-rc.1",
"@nymproject/mui-theme": "workspace:^1.0.0",
"@nymproject/nym-validator-client": "^0.18.0",
"@nymproject/react": "workspace:^1.0.0",
"@nymproject/types": "workspace:^1.0.0",
"@storybook/react": "^6.5.15",
"@types/d3-scale": "^4.0.8",
"big.js": "^6.2.1",
"chain-registry": "^1.63.12",
"d3-scale": "^4.0.2",
"date-fns": "^2.28.0",
"i18n-iso-countries": "^7.11.2",
"lodash": "^4.17.21",
"material-react-table": "^2.12.1",
"next": "14.1.4",
"react": "^18",
"react-dom": "^18",
"react-error-boundary": "^4.0.13",
"material-react-table": "^2.12.1",
"@mui/x-date-pickers": "7.1.1",
"@mui/x-data-grid": "7.1.1"
"react-google-charts": "^4.0.1",
"react-identicons": "^1.2.5",
"react-simple-maps": "^3.0.0",
"react-tooltip": "^5.27.0",
"semver": "^6.3.1",
"use-clipboard-copy": "^0.2.0"
},
"devDependencies": {
"@types/big.js": "^6.1.6",
"@types/lodash": "^4.17.6",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/react-simple-maps": "^3.0.4",
"@types/semver": "^7.3.8",
"eslint": "^8",
"eslint-config-next": "14.1.4",
"typescript": "^5"
+4 -4
View File
@@ -40,11 +40,11 @@
"@mui/system": "^5.0.1",
"@mui/x-data-grid": "^5.0.0-beta.5",
"@nymproject/contract-clients": "1.2.4-rc.1",
"@nymproject/mui-theme": "^1.0.0",
"@nymproject/mui-theme": "workspace: ^1.0.0",
"@nymproject/node-tester": "^1.0.0",
"@nymproject/nym-validator-client": "^0.18.0",
"@nymproject/react": "^1.0.0",
"@nymproject/types": "^1.0.0",
"@nymproject/react": "workspace:^1.0.0",
"@nymproject/types": "workspace:^1.0.0",
"@tauri-apps/api": "^1.5.1",
"big.js": "^6.2.1",
"bs58": "^5.0.0",
@@ -68,7 +68,7 @@
},
"devDependencies": {
"@babel/core": "^7.15.0",
"@nymproject/eslint-config-react-typescript": "^1.0.0",
"@nymproject/eslint-config-react-typescript": "workspace:^1.0.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
"@storybook/addon-actions": "^6.5.8",
"@storybook/addon-essentials": "^6.5.8",
-3
View File
@@ -1,3 +0,0 @@
{
"version": "independent"
}
+1 -1
View File
@@ -36,7 +36,7 @@
"zod": "^3.21.4"
},
"devDependencies": {
"@nymproject/eslint-config-react-typescript": "^1.0.0",
"@nymproject/eslint-config-react-typescript": "workspace:^1.0.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
"@svgr/webpack": "^6.1.1",
"@testing-library/jest-dom": "^5.14.1",
+1 -1
View File
@@ -12,7 +12,7 @@ serde_json = "1.0"
strum = { version = "0.23", features = ["derive"] }
ts-rs = "7.0.0"
cosmwasm-std = "1.4.3"
cosmwasm-std = "1.3.0"
cosmrs = "=0.15.0"
nym-config = { path = "../../common/config" }
+1 -66
View File
@@ -1,11 +1,7 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::Coin;
use nym_mixnet_contract_common::{
ContractStateParams, OperatingCostRange as ContractOperatingCostRange,
ProfitMarginRange as ContractProfitMarginRange,
};
use nym_mixnet_contract_common::ContractStateParams;
use nym_types::currency::{DecCoin, RegisteredCoins};
use nym_types::error::TypesError;
use serde::{Deserialize, Serialize};
@@ -20,31 +16,6 @@ pub struct TauriContractStateParams {
minimum_mixnode_pledge: DecCoin,
minimum_gateway_pledge: DecCoin,
minimum_mixnode_delegation: Option<DecCoin>,
operating_cost: TauriOperatingCostRange,
profit_margin: TauriProfitMarginRange,
}
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
#[cfg_attr(
feature = "generate-ts",
ts(export_to = "nym-wallet/src/types/rust/OperatingCostRange.ts")
)]
#[derive(Serialize, Deserialize, Debug)]
pub struct TauriOperatingCostRange {
minimum: DecCoin,
maximum: DecCoin,
}
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
#[cfg_attr(
feature = "generate-ts",
ts(export_to = "nym-wallet/src/types/rust/ProfitMarginRange.ts")
)]
#[derive(Serialize, Deserialize, Debug)]
pub struct TauriProfitMarginRange {
minimum: String,
maximum: String,
}
impl TauriContractStateParams {
@@ -52,16 +23,6 @@ impl TauriContractStateParams {
state_params: ContractStateParams,
reg: &RegisteredCoins,
) -> Result<Self, TypesError> {
let rewarding_denom = &state_params.minimum_mixnode_pledge.denom;
let min_operating_cost_c = Coin {
denom: rewarding_denom.into(),
amount: state_params.interval_operating_cost.minimum,
};
let max_operating_cost_c = Coin {
denom: rewarding_denom.into(),
amount: state_params.interval_operating_cost.maximum,
};
Ok(TauriContractStateParams {
minimum_mixnode_pledge: reg
.attempt_convert_to_display_dec_coin(state_params.minimum_mixnode_pledge.into())?,
@@ -71,15 +32,6 @@ impl TauriContractStateParams {
.minimum_mixnode_delegation
.map(|min_del| reg.attempt_convert_to_display_dec_coin(min_del.into()))
.transpose()?,
operating_cost: TauriOperatingCostRange {
minimum: reg.attempt_convert_to_display_dec_coin(min_operating_cost_c.into())?,
maximum: reg.attempt_convert_to_display_dec_coin(max_operating_cost_c.into())?,
},
profit_margin: TauriProfitMarginRange {
minimum: state_params.profit_margin.minimum.to_string(),
maximum: state_params.profit_margin.maximum.to_string(),
},
})
}
@@ -87,14 +39,6 @@ impl TauriContractStateParams {
self,
reg: &RegisteredCoins,
) -> Result<ContractStateParams, TypesError> {
assert_eq!(
self.operating_cost.maximum.denom,
self.operating_cost.minimum.denom
);
let min_operating_cost_c = reg.attempt_convert_to_base_coin(self.operating_cost.minimum)?;
let max_operating_cost_c = reg.attempt_convert_to_base_coin(self.operating_cost.maximum)?;
Ok(ContractStateParams {
minimum_mixnode_delegation: self
.minimum_mixnode_delegation
@@ -107,15 +51,6 @@ impl TauriContractStateParams {
minimum_gateway_pledge: reg
.attempt_convert_to_base_coin(self.minimum_gateway_pledge)?
.into(),
profit_margin: ContractProfitMarginRange {
minimum: self.profit_margin.minimum.parse()?,
maximum: self.profit_margin.maximum.parse()?,
},
interval_operating_cost: ContractOperatingCostRange {
minimum: min_operating_cost_c.amount.into(),
maximum: max_operating_cost_c.amount.into(),
},
})
}
}
+16 -15
View File
@@ -5,20 +5,20 @@
"main": "index.js",
"scripts": {
"build": "run-s webpack:prod tauri:build",
"dev": "run-p tauri:dev webpack:dev",
"dev": "pnpm run-p tauri:dev webpack:dev",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"prebuild": "yarn --cwd .. build",
"prestorybook": "yarn --cwd .. build",
"prewebpack:dev": "yarn --cwd .. build",
"prebuild": "pnpm -C .. build",
"prestorybook": "pnpm -C .. build",
"prewebpack:dev": "pnpm -C .. run build",
"storybook": "start-storybook -p 6006",
"storybook:build": "build-storybook",
"tauri:build": "yarn tauri build",
"tauri:dev": "yarn tauri dev",
"tauri:build": "pnpm tauri build",
"tauri:dev": "pnpm tauri dev",
"tsc": "tsc --noEmit true",
"tsc:watch": "tsc --noEmit true --watch",
"webpack:dev": "yarn webpack serve --config webpack.dev.js",
"webpack:prod": "yarn webpack --progress --config webpack.prod.js"
"webpack:dev": "pnpm webpack serve --config webpack.dev.js",
"webpack:prod": "pnpm webpack --progress --config webpack.prod.js"
},
"dependencies": {
"@emotion/react": "^11.7.0",
@@ -28,10 +28,11 @@
"@mui/material": "^5.2.2",
"@mui/styles": "^5.2.2",
"@mui/utils": "^5.7.0",
"@nymproject/mui-theme": "^1.0.0",
"@nymproject/node-tester": "^1.2.3",
"@nymproject/react": "^1.0.0",
"@nymproject/types": "^1.0.0",
"@nymproject/mui-theme": "workspace:^1.0.0",
"@nymproject/node-tester": "1.2.4-rc.1",
"@nymproject/react": "workspace:^1.0.0",
"@nymproject/types": "workspace:^1.0.0",
"@nymproject/webpack": "workspace:^1.0.0",
"@storybook/react": "^6.5.15",
"@tauri-apps/api": "^1.2.0",
"@tauri-apps/tauri-forage": "^1.0.0-beta.2",
@@ -42,7 +43,6 @@
"joi": "^17.11.0",
"lodash": "^4.17.21",
"notistack": "^2.0.3",
"npm-run-all": "^4.1.5",
"qrcode.react": "^1.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -63,7 +63,7 @@
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.14.5",
"@babel/preset-typescript": "^7.15.0",
"@nymproject/eslint-config-react-typescript": "^1.0.0",
"@nymproject/eslint-config-react-typescript": "workspace:^1.0.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
"@storybook/react": "^6.5.15",
"@svgr/webpack": "^6.1.1",
@@ -73,6 +73,7 @@
"@types/big.js": "^6.1.6",
"@types/bs58": "^4.0.1",
"@types/jest": "^27.0.1",
"@types/lodash": "^4.17.6",
"@types/node": "^16.7.13",
"@types/qrcode.react": "^1.0.2",
"@types/react": "^18.0.26",
@@ -125,4 +126,4 @@
"webpack-merge": "^5.8.0"
},
"private": false
}
}
@@ -1,10 +0,0 @@
import * as React from 'react';
import { ComponentMeta } from '@storybook/react';
import { Playground } from '@nymproject/react/playground/Playground';
export default {
title: 'Playground',
component: Playground,
} as ComponentMeta<typeof Playground>;
export const AllControls = () => <Playground />;
+37 -34
View File
@@ -3,55 +3,58 @@
"version": "1.0.0",
"private": true,
"license": "Apache 2.0",
"workspaces": [
"dist/wasm/**",
"dist/node/**",
"dist/ts/**",
"sdk/typescript/packages/mui-theme",
"sdk/typescript/packages/react-components",
"sdk/typescript/packages/validator-client",
"ts-packages/*",
"nym-wallet",
"explorer",
"explorer-nextjs",
"types",
"clients/validator"
],
"scripts": {
"nuke": "npx rimraf **/node_modules node_modules",
"scrub": "npx rimraf **/dist dist",
"clean": "lerna run clean",
"build": "run-s build:types build:packages",
"build": "pnpm build:types && pnpm build:packages",
"build:wasm": "make sdk-wasm-build",
"build:types": "lerna run --scope @nymproject/types build --stream",
"build:types": "pnpm run --filter @nymproject/types --stream build",
"build:packages": "run-s build:packages:theme build:packages:react",
"build:packages:theme": "lerna run --scope @nymproject/mui-theme build",
"build:packages:react": "lerna run --scope @nymproject/react build",
"build:react-example": "lerna run --scope @nymproject/react-webpack-with-theme-example build --stream",
"build:playground": "lerna run --scope @nymproject/react storybook:build --stream",
"build:ci:storybook": "yarn build && yarn dev:on && run-p build:react-example build:playground && yarn build:ci:storybook:collect-artifacts",
"build:packages:theme": "pnpm run --filter @nymproject/mui-theme build",
"build:packages:react": "pnpm run --filter @nymproject/react build",
"build:react-example": "pnpm run --filter @nymproject/react-webpack-with-theme-example --stream build ",
"build:playground": "pnpm run --filter @nymproject/react --stream storybook:build",
"build:ci:storybook": "pnpm run build && pnpm run dev:on && run-p build:react-example build:playground && pnpm build:ci:storybook:collect-artifacts",
"build:ci:storybook:collect-artifacts": "mkdir -p ts-packages/dist && mv sdk/typescript/packages/react-components/storybook-static ts-packages/dist/storybook && mv sdk/typescript/examples/react/mui-theme/dist ts-packages/dist/example",
"prebuild:ci": "yarn dev:on && yarn",
"build:ci": "run-s build:types build:packages build:wasm build:ci:sdk",
"postbuild:ci": "yarn dev:off",
"build:ci:sdk": "lerna run --scope '{@nymproject/sdk,@nymproject/node-tester,@nymproject/contract-clients,@nymproject/sdk-react,@nymproject/mix-fetch,@nymproject/nodejs-client,@nymproject/mix-fetch-node}' build --stream",
"docs:prod:build": "run-s docs:prod:build:ws",
"docs:prod:build:ws": "lerna run docs:prod:build --stream",
"prebuild:ci": "pnpm run dev:on",
"build:ci": "pnpm dlx npm-run-all build:types build:packages build:wasm build:ci:sdk",
"postbuild:ci": "pnpm run dev:off",
"build:ci:sdk": "pnpm run --filter '{@nymproject/sdk,@nymproject/node-tester,@nymproject/contract-clients,@nymproject/sdk-react,@nymproject/mix-fetch,@nymproject/nodejs-client,@nymproject/mix-fetch-node}' --stream build",
"docs:prod:build": "pnpm docs:prod:build:ws",
"docs:prod:build:ws": "pnpm -r docs:prod:build",
"sdk:build": "./sdk/typescript/scripts/build-prod-sdk.sh",
"sdk:publish": "./sdk/typescript/scripts/publish.sh",
"lint": "lerna run lint --stream",
"lint:fix": "lerna run lint:fix --stream",
"tsc": "lerna run tsc --stream",
"types:lint:fix": "lerna run lint:fix --scope @nymproject/types --scope @nymproject/nym-wallet-app",
"lint": "pnpm -r run --stream lint",
"lint:fix": "pnpm -r run --stream lint:fix",
"tsc": "pnpm -r run --stream tsc",
"types:lint:fix": "pnpm run lint:fix --filter @nymproject/types --filter @nymproject/nym-wallet-app",
"audit:fix": "npm_config_yes=true npx yarn-audit-fix -- --dry-run",
"dev:on": "node sdk/typescript/scripts/dev-mode-add.mjs",
"dev:off": "node sdk/typescript/scripts/dev-mode-remove.mjs"
},
"devDependencies": {
"lerna": "^7.3.0",
"npm-run-all": "^4.1.5",
"@npmcli/node-gyp": "^3.0.0",
"js-yaml": "^4.1.0",
"node-gyp": "^9.3.1",
"npm-run-all": "^4.1.5",
"tslog": "3.3.3"
},
"pnpm": {
"overrides": {
"d3-color@<3.1.0": ">=3.1.0",
"trim@<0.0.3": ">=0.0.3",
"sharp@<0.30.5": ">=0.30.5",
"postcss@<8.4.31": ">=8.4.31",
"sharp@<0.32.6": ">=0.32.6",
"trim-newlines@<3.0.1": ">=3.0.1",
"semver@>=7.0.0 <7.5.2": ">=7.5.2",
"xml2js@<0.5.0": ">=0.5.0",
"webpack-dev-middleware@<=5.3.3": ">=5.3.4",
"@babel/traverse@<7.23.2": ">=7.23.2",
"ws@>=8.0.0 <8.17.1": ">=8.17.1",
"axios@>=0.8.1 <0.28.0": ">=0.28.0",
"protobufjs@>=6.10.0 <6.11.4": ">=6.11.4",
"braces@<3.0.3": ">=3.0.3"
}
}
}
+39228
View File
File diff suppressed because it is too large Load Diff
+14
View File
@@ -0,0 +1,14 @@
packages:
- clients/validator
- dist/wasm/**
- dist/node/**
- dist/ts/**
- explorer-nextjs
- sdk/typescript/packages/react-components
- sdk/typescript/packages/validator-client
- sdk/typescript/packages/mui-theme
- sdk/typescript/packages/**
- sdk/typescript/examples/**
- sdk/typescript/codegen/**
- ts-packages/**
- wallet
@@ -15,7 +15,7 @@
"docs:generate:prod": "typedoc --basePath ./docs/tsdoc/nymproject/contract-clients/",
"docs:prod:build": "scripts/build-prod-docs-collect.sh",
"docs:serve": "reload -b -d ./docs -p 3000",
"docs:watch": "nodemon --ext ts --watch './src/**/*' --watch './typedoc.json' --exec \"yarn docs:generate\""
"docs:watch": "nodemon --ext ts --watch './src/**/*' --watch './typedoc.json' --exec \"pnpm docs:generate\""
},
"devDependencies": {
"@cosmwasm/ts-codegen": "^0.35.3",
@@ -26,5 +26,9 @@
"typescript": "^4.6.2"
},
"private": false,
"types": "./dist/index.d.ts"
}
"types": "./dist/index.d.ts",
"dependencies": {
"@cosmjs/amino": "^0.32.4",
"@cosmjs/cosmwasm-stargate": "^0.29.5"
}
}
@@ -7,7 +7,7 @@ set -o pipefail
rm -rf ../../../../dist/ts/docs/tsdoc/nymproject/contract-clients || true
# run the build
yarn docs:generate:prod
pnpm docs:generate:prod
# move the output outside of the yarn/npm workspaces
mkdir -p ../../../../dist/ts/docs/tsdoc/nymproject
@@ -6,7 +6,7 @@
"scripts": {
"build": "npx parcel build",
"build:serve": "npx serve dist",
"lint": "eslint src",
"lint": "echo not implemented",
"lint:fix": "eslint src --fix",
"start": "npx parcel",
"test": "jest",
@@ -41,4 +41,4 @@
"private": false,
"browserslist": "> 0.5%, last 2 versions, not dead",
"source": "src/index.html"
}
}
@@ -7,8 +7,7 @@
"build": "webpack build --progress --config webpack.prod.js",
"build:dev": "webpack build --progress",
"build:serve": "npx serve dist",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"lint": "echo not implemented",
"start": "webpack serve --progress --port 3000",
"test": "jest",
"test:watch": "jest --watch",
@@ -62,4 +61,4 @@
"webpack-merge": "^5.8.0"
},
"private": false
}
}
@@ -4,7 +4,7 @@
"description": "An example project that uses WASM, React, Webpack, Typescript and the Nym theme + components library",
"license": "Apache-2.0",
"scripts": {
"build": "webpack build --progress --config webpack.prod.js",
"build": "echo not implemented",
"build:dev": "webpack build --progress",
"build:serve": "npx serve dist",
"lint": "eslint src",
@@ -78,7 +78,7 @@
"typescript": "^4.6.2",
"url-loader": "^4.1.1",
"webpack": "^5.75.0",
"webpack-cli": "^4.8.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.5.0",
"webpack-favicons": "^1.3.8",
"webpack-merge": "^5.8.0"
@@ -90,4 +90,4 @@
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}
}
@@ -1,5 +1,6 @@
import * as React from 'react';
import { createNymMixnetClient, IWebWorkerEvents, MimeTypes, NymClientConfig, NymMixnetClient } from '@nymproject/sdk';
export interface BinaryMessageHeaders {
filename: string;
mimeType: string;
@@ -1,5 +1,11 @@
import * as React from 'react';
import ReactDOM from 'react-dom';
import { createRoot } from 'react-dom/client';
import { App } from './App';
ReactDOM.render(<App />, document.getElementById('app'));
const element = document.getElementById('app');
if (!element) {
throw new Error('Element not found');
}
createRoot(element).render(<App />);
@@ -4,14 +4,14 @@
"version": "1.0.0",
"license": "Apache-2.0",
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"@mui/material": "^5.0.1",
"@mui/styles": "^5.0.1",
"@mui/icons-material": "^5.5.0",
"@mui/lab": "^5.0.0-alpha.72",
"@nymproject/mui-theme": "^1.0.0",
"@nymproject/react": "^1.0.0"
"@mui/material": "^5.0.1",
"@mui/styles": "^5.0.1",
"@nymproject/mui-theme": "workspace:^1.0.0",
"@nymproject/react": "workspace:^1.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@babel/core": "^7.15.0",
@@ -19,15 +19,16 @@
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.14.5",
"@babel/preset-typescript": "^7.15.0",
"@nymproject/eslint-config-react-typescript": "^1.0.0",
"@nymproject/webpack": "^1.0.0",
"@nymproject/eslint-config-react-typescript": "workspace:^1.0.0",
"@nymproject/webpack": "workspace:^1.0.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
"@svgr/webpack": "^6.1.1",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.0.0",
"@types/jest": "^27.0.1",
"@types/node": "^16.7.13",
"@types/react": "^18.0.26",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"babel-loader": "^8.3.0",
@@ -1,8 +1,7 @@
import * as React from 'react';
import { Box, Container, Grid, Typography } from '@mui/material';
import { NymLogo } from '@nymproject/react/logo/NymLogo';
import { Playground } from '@nymproject/react/playground/Playground';
import { useIsMounted } from '@nymproject/react/hooks/useIsMounted';
import { NymLogo } from '@nymproject/react';
import { useIsMounted } from '@nymproject/react';
import { NymThemeProvider } from '@nymproject/mui-theme';
import { useTheme } from '@mui/material/styles';
import { ThemeToggle } from './ThemeToggle';
@@ -65,8 +64,6 @@ export const Content: FCWithChildren = () => {
))}
</Grid>
</Box>
<h1>Component playground</h1>
<Playground />
</Container>
);
};
@@ -1,13 +0,0 @@
import React, { render } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { App } from '../App';
describe('App', () => {
beforeEach(() => {
render(<App />);
});
it('should render without exploding', () => {
const { container } = render(<App />);
expect(container.firstChild).toBeInTheDocument();
});
});
File diff suppressed because it is too large Load Diff
@@ -37,7 +37,7 @@
"test:watch": "jest --watch",
"tsc": "tsc",
"tsc:watch": "tsc --watch",
"lint": "eslint src",
"lint": "echo not implemented",
"lint:fix": "eslint src --fix"
}
}
@@ -19,11 +19,11 @@
"docs:generate:prod": "typedoc --basePath ./docs/tsdoc/nymproject/sdk/",
"docs:prod:build": "scripts/build-prod-docs-collect.sh",
"docs:serve": "reload -b -d ./docs -p 3000",
"docs:watch": "nodemon --ext ts --watch './src/**/*' --watch './typedoc.json' --exec \"yarn docs:generate\"",
"docs:watch": "nodemon --ext ts --watch './src/**/*' --watch './typedoc.json' --exec \"pnpm docs:generate\"",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"start": "tsc -w",
"start:dev": "nodemon --watch src -e ts,json --exec 'yarn build:dev:esm'",
"start:dev": "nodemon --watch src -e ts,json --exec 'pnpm build:dev:esm'",
"test": "node --experimental-fetch --experimental-vm-modules node_modules/jest/bin/jest.js -c=jest.config.mjs --no-cache",
"tsc": "tsc --noEmit true"
},
@@ -42,6 +42,7 @@
"@rollup/plugin-wasm": "^6.1.1",
"@types/jest": "^27.0.1",
"@types/node": "^16.7.13",
"@types/ws": "^8.5.10",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"eslint": "^8.10.0",
File diff suppressed because it is too large Load Diff
@@ -24,18 +24,19 @@
"docs:generate:prod": "typedoc --basePath ./docs/tsdoc/nymproject/sdk/",
"docs:prod:build": "scripts/build-prod-docs-collect.sh",
"docs:serve": "reload -b -d ./docs -p 3000",
"docs:watch": "nodemon --ext ts --watch './src/**/*' --watch './typedoc.json' --exec \"yarn docs:generate\"",
"docs:watch": "nodemon --ext ts --watch './src/**/*' --watch './typedoc.json' --exec \"pnpm docs:generate\"",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"prebuild": "node scripts/showDependencyLocation.cjs",
"start": "tsc -w",
"start:dev": "nodemon --watch src -e ts,json --exec 'yarn build:dev:esm'",
"start:dev": "nodemon --watch src -e ts,json --exec 'pnpm build:dev:esm'",
"test": "node --experimental-vm-modules --no-warnings node_modules/jest/bin/jest.js -c=jest.config.mjs --no-cache",
"tsc": "tsc --noEmit true"
},
"dependencies": {
"@nymproject/mix-fetch-wasm": ">=1.2.4-rc.2 || ^1",
"comlink": "^4.3.1"
"comlink": "^4.3.1",
"ws": "^8.18.0"
},
"devDependencies": {
"@babel/core": "^7.15.0",
@@ -43,7 +44,7 @@
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.14.5",
"@babel/preset-typescript": "^7.15.0",
"@nymproject/eslint-config-react-typescript": "^1.0.0",
"@nymproject/eslint-config-react-typescript": "workspace: ^1.0.0",
"@rollup/plugin-commonjs": "^24.0.1",
"@rollup/plugin-inject": "^5.0.3",
"@rollup/plugin-json": "^6.0.0",
@@ -54,6 +55,7 @@
"@rollup/plugin-wasm": "^6.1.1",
"@types/jest": "^27.0.1",
"@types/node": "^16.7.13",
"@types/ws": "^8.5.10",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"eslint": "^8.10.0",
@@ -11,7 +11,7 @@
"@mui/styles": "^5.2.2"
},
"devDependencies": {
"@nymproject/eslint-config-react-typescript": "^1.0.0",
"@nymproject/eslint-config-react-typescript": "workspace: jnshhbwshsbsjZ hb ^1.0.0",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"eslint": "^8.10.0",
@@ -20,7 +20,7 @@
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"start": "tsc -w",
"start:dev": "nodemon --watch src -e ts,json --exec 'yarn build:dev:esm'",
"start:dev": "nodemon --watch src -e ts,json --exec 'pnpm build:dev:esm'",
"test": "node --experimental-vm-modules --no-warnings node_modules/jest/bin/jest.js -c=jest.config.mjs --no-cache",
"tsc": "tsc --noEmit true"
},
@@ -34,7 +34,7 @@
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.14.5",
"@babel/preset-typescript": "^7.15.0",
"@nymproject/eslint-config-react-typescript": "^1.0.0",
"@nymproject/eslint-config-react-typescript": "workspace: ^1.0.0",
"@rollup/plugin-commonjs": "^24.0.1",
"@rollup/plugin-inject": "^5.0.3",
"@rollup/plugin-json": "^6.0.0",
@@ -17,11 +17,11 @@
"docs:generate:prod": "typedoc --basePath ./docs/tsdoc/nymproject/sdk/",
"docs:prod:build": "scripts/build-prod-docs-collect.sh",
"docs:serve": "reload -b -d ./docs -p 3000",
"docs:watch": "nodemon --ext ts --watch './src/**/*' --watch './typedoc.json' --exec \"yarn docs:generate\"",
"docs:watch": "nodemon --ext ts --watch './src/**/*' --watch './typedoc.json' --exec \"pnpm docs:generate\"",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"start": "tsc -w",
"start:dev": "nodemon --watch src -e ts,json --exec 'yarn build:dev:esm'",
"start:dev": "nodemon --watch src -e ts,json --exec 'pnpm build:dev:esm'",
"tsc": "tsc --noEmit true"
},
"dependencies": {
@@ -40,6 +40,7 @@
"@rollup/plugin-url": "^8.0.1",
"@rollup/plugin-wasm": "^6.1.1",
"@types/node": "^16.7.13",
"@types/ws": "^8.5.10",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"eslint": "^8.10.0",
File diff suppressed because it is too large Load Diff
@@ -1,5 +1,21 @@
{
"parserOptions": {
"project": "./tsconfig.eslint.json"
"root": true,
"env": {
"browser": true,
"es2020": true
},
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:react-hooks/recommended"],
"ignorePatterns": ["dist", ".eslintrc.cjs"],
"parser": "@typescript-eslint/parser",
"plugins": ["react-refresh"],
"rules": {
"react-refresh/only-export-components": [
"warn",
{
"allowConstantExport": true
}
],
"@typescript-eslint/no-explicit-any": "off",
"react-hooks/exhaustive-deps": "off"
}
}
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
@@ -1,40 +1,25 @@
/* eslint-disable no-param-reassign */
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
export const framework = {
name: '@storybook/react-vite',
options: {},
};
module.exports = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: '@storybook/react',
core: {
builder: 'webpack5',
},
// webpackFinal: async (config, { configType }) => {
// // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
// // You can change the configuration based on that.
// // 'PRODUCTION' is used when building the static version of storybook.
webpackFinal: async (config) => {
config.module.rules.forEach((rule) => {
// look for SVG import rule and replace
// NOTE: the rule before modification is /\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/
if (rule.test?.toString().includes('svg')) {
rule.test = /\.(ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/;
}
});
export const docs = {
autodocs: true,
};
// handle asset loading with this
config.module.rules.unshift({
test: /\.svg(\?.*)?$/i,
issuer: /\.[jt]sx?$/,
use: ['@svgr/webpack'],
});
export const typescript = {
reactDocgen: 'react-docgen-typescript',
};
config.resolve.extensions = ['.tsx', '.ts', '.js'];
config.resolve.plugins = [new TsconfigPathsPlugin()];
const config = {
framework: '@storybook/react-vite',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
// Return the altered config
return config;
},
features: {
emotionAlias: false,
docs: {},
typescript: {
reactDocgen: 'react-docgen-typescript',
},
};
export default config;
@@ -1,58 +1,30 @@
# Nym Shared React Components
# React + TypeScript + Vite
This package contains shared React components that are used in other Nym projects.
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
It uses the following packages:
Currently, two official plugins are available:
- [shared MUI theme](../mui-theme/README.md)
- [webpack config](../webpack/README.md)
- [MUI](https://https://mui.com/)
- Typescript
- React
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## Building
## Expanding the ESLint configuration
```
yarn
yarn build
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
- Configure the top-level `parserOptions` property like this:
```js
export default {
// other rules...
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: ['./tsconfig.json', './tsconfig.node.json'],
tsconfigRootDir: __dirname,
},
}
```
## Development
Run watch mode with:
```
yarn watch
```
Or you can run Storybook with:
```
yarn storybook
```
Or you can run the [example project](../react-webpack-with-theme-example/README.md) in dev mode and this package in watch mode, and test results in the example project's dev server output.
## Playground
There are [playground components](./src/playground/index.tsx) that are intended to be used during development to see the effects are changes to the MUI theme or shared components at a glance.
They are available in Storybook from [src/stories/Playground.stories.tsx](./src/stories/Playground.stories.tsx).
> **Tip**: use the playground to make sure components stay consistent and you don't break other components while making changes
## Shared assets
This project uses [shared asset files](../../assets/README.md) such as favicons and logos.
> **Tip**: use to keep your project consistent with Nym's branding and so that it automatically receives changes when the shared assets change. Please try to avoid duplicating the files in the shared assets directory.
## Publishing
This package is not published to NPM ... yet.
## TODO
- ugprade to React 18
- upgrade Storybook
- upgrade MUI
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Nym React Components</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
@@ -1,12 +1,11 @@
import * as React from 'react';
import { ChangeEvent } from 'react';
import { InputAdornment, TextField } from '@mui/material';
import { InputAdornment, TextField, SxProps } from '@mui/material';
import { TextFieldProps } from '@mui/material/TextField/TextField';
import { validateWalletAddress } from '@nymproject/types';
import DoneIcon from '@mui/icons-material/Done';
import { SxProps } from '@mui/system';
export const WalletAddressFormField: FCWithChildren<{
export type WalletAddressFormFieldProps = {
showTickOnValid?: boolean;
fullWidth?: boolean;
required?: boolean;
@@ -20,7 +19,9 @@ export const WalletAddressFormField: FCWithChildren<{
textFieldProps?: TextFieldProps;
errorText?: string;
sx?: SxProps;
}> = ({
};
export const WalletAddressFormField = ({
required,
fullWidth,
placeholder,
@@ -33,7 +34,7 @@ export const WalletAddressFormField: FCWithChildren<{
onValidate,
textFieldProps,
showTickOnValid = true,
}) => {
}: WalletAddressFormFieldProps) => {
const [value, setValue] = React.useState<string | undefined>(initialValue);
const [validationError, setValidationError] = React.useState<string | undefined>();
@@ -1,14 +1,13 @@
import * as React from 'react';
import { Box, Collapse, Alert, IconButton, Typography, Divider } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import { SxProps } from '@mui/system';
import { SxProps } from '@mui/material';
export interface BannerProps {
export type BannerProps = {
open: boolean;
onClick: () => void;
height?: number;
sx?: SxProps;
}
};
export const MaintenanceBanner = (props: BannerProps) => {
const { open, onClick, height, sx } = props;
@@ -1,22 +1,21 @@
import React from 'react';
import { Box, Typography, Tooltip } from '@mui/material';
import { CopyToClipboard } from '../clipboard/CopyToClipboard';
const AddressTooltip: FCWithChildren<{ visible?: boolean; address?: string }> = ({ visible, address, children }) => {
type AddressTooltipProps = { visible?: boolean; address?: string; children: React.ReactNode };
const AddressTooltip = ({ visible, address, children }: AddressTooltipProps) => {
if (!visible || !address) {
// eslint-disable-next-line react/jsx-no-useless-fragment
return <>{children}</>;
}
return (
<Tooltip title={address} arrow>
{/* eslint-disable-next-line react/jsx-no-useless-fragment */}
<>{children}</>
</Tooltip>
);
};
type ClientAddressProps = {
export type ClientAddressProps = {
address: string;
withLabel?: boolean;
withCopy?: boolean;
@@ -24,7 +23,7 @@ type ClientAddressProps = {
showEntireAddress?: boolean;
};
export const ClientAddressDisplay = ({
export const ClientAddress = ({
withLabel,
withCopy,
smallIcons,
@@ -48,5 +47,3 @@ export const ClientAddressDisplay = ({
{withCopy && <CopyToClipboard smallIcons={smallIcons} value={address} />}
</Box>
);
export const ClientAddress = ({ ...props }: ClientAddressProps) => <ClientAddressDisplay {...props} />;
@@ -4,13 +4,15 @@ import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import DoneIcon from '@mui/icons-material/Done';
import { useClipboard } from 'use-clipboard-copy';
export const CopyToClipboard: FCWithChildren<{
export type CopyToClipboardProps = {
value: string;
tooltip?: React.ReactNode;
onCopy?: (value: string) => void;
smallIcons?: boolean;
sx?: SxProps;
}> = ({ value, tooltip, onCopy, smallIcons, sx }) => {
};
export const CopyToClipboard = ({ value, tooltip, onCopy, smallIcons, sx }: CopyToClipboardProps) => {
const copy = useClipboard();
const [showConfirmation, setShowConfirmation] = React.useState<boolean>(false);
const handleCopy = (e: React.MouseEvent<SVGSVGElement>) => {
@@ -1,13 +1,14 @@
import * as React from 'react';
import { PaletteMode, useTheme } from '@mui/material';
import TokenLight from '@assets/token/token-light.svg';
import TokenDark from '@assets/token/token-dark.svg';
import TokenLight from '@assets/token/token-light.svg?react';
import TokenDark from '@assets/token/token-dark.svg?react';
export const CoinMark: FCWithChildren<{
export type CoinMarkProps = {
mode?: PaletteMode;
width?: number | string;
height?: number | string;
}> = ({ mode, ...props }) => {
};
export const CoinMark = ({ mode, ...props }: CoinMarkProps) => {
const theme = useTheme();
const modeWithTheme = mode || theme.palette.mode;
if (modeWithTheme === 'light') {
@@ -0,0 +1,13 @@
import { useTheme } from '@mui/material';
import TokenLight from '@assets/token/token-light-testnet.svg?react';
import TokenDark from '@assets/token/token-dark-testnet.svg?react';
import { CoinMarkProps } from './CoinMark';
export const CoinMarkTestnet = ({ mode, ...props }: CoinMarkProps) => {
const theme = useTheme();
const modeWithTheme = mode || theme.palette.mode;
if (modeWithTheme === 'light') {
return <TokenLight {...props} />;
}
return <TokenDark {...props} />;
};
@@ -1,16 +1,23 @@
import * as React from 'react';
import type { DecCoin } from '@nymproject/types';
import { Stack, SxProps } from '@mui/material';
import { CurrencyWithCoinMark } from './CurrencyWithCoinMark';
import { CURRENCY_AMOUNT_SPACING, CurrencyAmount } from './CurrencyAmount';
export const Currency: FCWithChildren<{
export type CurrencyProps = {
majorAmount?: DecCoin;
showDenom?: boolean;
showCoinMark?: boolean;
coinMarkPrefix?: boolean;
sx?: SxProps;
}> = ({ majorAmount, sx, showDenom = true, showCoinMark = false, coinMarkPrefix = false }) => {
};
export const Currency = ({
majorAmount,
sx,
showDenom = true,
showCoinMark = false,
coinMarkPrefix = false,
}: CurrencyProps) => {
if (!majorAmount || !majorAmount.amount) {
return (
<Stack direction="row" sx={sx}>
@@ -1,13 +1,11 @@
/* eslint-disable react/no-array-index-key */
import * as React from 'react';
import type { DecCoin } from '@nymproject/types';
import { Stack, SxProps, Typography } from '@mui/material';
export const CURRENCY_AMOUNT_SPACING = 0.35;
const toReverseChunks = (value: String, size: number = 3): Array<string> => {
const toReverseChunks = (value: string, size: number = 3): Array<string> => {
const reversed = value.split('').reverse();
const chunks: Array<Array<String>> = [];
const chunks: Array<Array<string>> = [];
let chunksIndex = 0;
reversed.forEach((char, index) => {
if (index > 0 && index % size === 0) {
@@ -21,8 +19,8 @@ const toReverseChunks = (value: String, size: number = 3): Array<string> => {
return chunks.map((chars) => chars.reverse().join('')).reverse();
};
const toChunks = (value: String, size: number = 3): Array<string> => {
const chunks: Array<Array<String>> = [];
const toChunks = (value: string, size: number = 3): Array<string> => {
const chunks: Array<Array<string>> = [];
let chunksIndex = 0;
value.split('').forEach((char, index) => {
if (index > 0 && index % size === 0) {
@@ -36,11 +34,13 @@ const toChunks = (value: String, size: number = 3): Array<string> => {
return chunks.map((chars) => chars.join(''));
};
export const CurrencyAmountString: FCWithChildren<{
export type CurrencyAmountStringProps = {
majorAmount?: string;
showSeparators?: boolean;
sx?: SxProps;
}> = ({ majorAmount, sx, showSeparators = true }) => {
};
export const CurrencyAmountString = ({ majorAmount, sx, showSeparators = true }: CurrencyAmountStringProps) => {
if (!majorAmount) {
return (
<Stack direction="row" sx={sx}>
@@ -93,8 +93,12 @@ export const CurrencyAmountString: FCWithChildren<{
);
};
export const CurrencyAmount: FCWithChildren<{
export type CurrencyAmountProps = {
majorAmount?: DecCoin;
showSeparators?: boolean;
sx?: SxProps;
}> = ({ majorAmount, ...props }) => <CurrencyAmountString majorAmount={majorAmount?.amount} {...props} />;
};
export const CurrencyAmount = ({ majorAmount, ...props }: CurrencyAmountProps) => (
<CurrencyAmountString majorAmount={majorAmount?.amount} {...props} />
);

Some files were not shown because too many files have changed in this diff Show More