Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 957cbb45b0 | |||
| 8ec074cb1f | |||
| ab5740087f | |||
| 6af59c303e | |||
| 27b384e034 | |||
| 7f5ce3ffeb | |||
| 89b6667c75 | |||
| a94a9aeaf5 | |||
| 6bc8b88a20 | |||
| 21f3991714 | |||
| cd8eba988a | |||
| d2b3841bbd | |||
| de877fb337 | |||
| d4c2b9060f | |||
| 41ac866729 | |||
| a7afd2a1c7 | |||
| df03daf2cc | |||
| b3f5a4f496 | |||
| d080d661f7 | |||
| 6deb481e5d | |||
| 5b98e18a4e | |||
| 506a0da89c | |||
| c7fdcf0a79 | |||
| f7d38a7ec6 | |||
| 8edc762df9 | |||
| 4459aca933 | |||
| 5b84c58985 | |||
| 4301d91f6c | |||
| 03d28c115e | |||
| 7b15f350cd | |||
| 2b4917b8b1 | |||
| de78ca8d9b | |||
| 58d09e382a | |||
| 0cef12d05b | |||
| 30e73ee795 | |||
| d918b69664 | |||
| 921e558660 | |||
| b3b8d2ab46 | |||
| d62638b8e2 | |||
| 67130a1289 | |||
| 0dabff72bd | |||
| e8e2f195e6 | |||
| fa354016e0 | |||
| 935ee765e9 | |||
| 4c8e59e6fc | |||
| 067f3e6f1a | |||
| 6f09d46dce | |||
| bdef48331b | |||
| 51a6936e51 | |||
| fd456d2952 | |||
| eee1abe593 | |||
| fffad43937 | |||
| 3a79f43a8d | |||
| 2e495f87ab | |||
| 57a9f18f5a | |||
| 0c6a0a9cae | |||
| c80d8d354a | |||
| 3f544dbc69 | |||
| d1e1f15db0 | |||
| 651c314182 | |||
| a57545521d | |||
| da60606921 | |||
| 14f9bf7234 | |||
| c1fa92869a | |||
| c8533e3ec8 | |||
| 06c4dd601d | |||
| 4ff80bbab2 | |||
| d7220b1fec | |||
| d92df9ada3 |
@@ -0,0 +1,36 @@
|
||||
name: Daily security audit
|
||||
|
||||
on: workflow_dispatch
|
||||
jobs:
|
||||
security_audit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/audit-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
notification:
|
||||
if: ${{ failure() }}
|
||||
needs: security_audit
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
- name: Keybase - Node Install
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files
|
||||
- name: Keybase - Send Notification
|
||||
env:
|
||||
NYM_NOTIFICATION_KIND: nightly
|
||||
NYM_PROJECT_NAME: "Nym daily audit"
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
|
||||
GIT_BRANCH: "${GITHUB_REF##*/}"
|
||||
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
|
||||
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBTECH_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "test"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
@@ -166,8 +166,8 @@ jobs:
|
||||
GIT_BRANCH: "${GITHUB_REF##*/}"
|
||||
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
|
||||
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "ci-nightly"
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMTECH_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "test"
|
||||
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
|
||||
@@ -45,3 +45,4 @@ jobs:
|
||||
target/release/nym-socks5-client
|
||||
target/release/nym-validator-api
|
||||
target/release/nym-network-requester
|
||||
target/release/nym-network-statistics
|
||||
+7
-255
@@ -2,8 +2,7 @@
|
||||
|
||||
Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
|
||||
## [Unreleased]
|
||||
## [nym-binaries-1.0.2](https://github.com/nymtech/nym/tree/nym-binaries-1.0.2)
|
||||
|
||||
### Added
|
||||
|
||||
@@ -37,6 +36,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- validator: fixed local docker-compose setup to work on Apple M1 ([#1329])
|
||||
- explorer-api: listen out for SIGTERM and SIGQUIT too, making it play nicely as a system service ([#1482]).
|
||||
- network-requester: fix filter for suffix-only domains ([#1487])
|
||||
- validator-api: listen out for SIGTERM and SIGQUIT too, making it play nicely as a system service ([#1496]).
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -51,6 +51,8 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- multisig-contract: Limit the proposal creating functionality to one address (coconut-bandwidth-contract address) ([#1457])
|
||||
- All binaries and cosmwasm blobs are configured at runtime now; binaries are configured using environment variables or .env files and contracts keep the configuration parameters in storage ([#1463])
|
||||
- gateway, network-statistics: include gateway id in the sent statistical data ([#1478])
|
||||
- network explorer: tweak how active set probability is shown ([#1503])
|
||||
- validator-api: rewarder set update fails without panicking on possible nymd queries ([#1520])
|
||||
|
||||
|
||||
[#1249]: https://github.com/nymtech/nym/pull/1249
|
||||
@@ -77,90 +79,9 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
[#1478]: https://github.com/nymtech/nym/pull/1478
|
||||
[#1482]: https://github.com/nymtech/nym/pull/1482
|
||||
[#1487]: https://github.com/nymtech/nym/pull/1487
|
||||
|
||||
## [nym-connect-v1.0.1](https://github.com/nymtech/nym/tree/nym-connect-v1.0.1) (2022-07-22)
|
||||
|
||||
### Added
|
||||
|
||||
- nym-connect: initial proof-of-concept of a UI around the socks5 client was added
|
||||
- nym-connect: add ability to select network requester and gateway ([#1427])
|
||||
- nym-connect: add ability to export gateway keys as JSON
|
||||
- nym-connect: add auto updater
|
||||
|
||||
### Changed
|
||||
|
||||
- nym-connect: reuse config id instead of creating a new id on each connection
|
||||
|
||||
[#1427]: https://github.com/nymtech/nym/pull/1427
|
||||
|
||||
## [nym-wallet-v1.0.7](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.7) (2022-07-11)
|
||||
|
||||
- wallet: dark mode
|
||||
- wallet: when simulating gas costs, an automatic adjustment is being used ([#1388]).
|
||||
|
||||
[#1388]: https://github.com/nymtech/nym/pull/1388
|
||||
|
||||
## [nym-contracts-v1.0.1](https://github.com/nymtech/nym/tree/nym-contracts-v1.0.1) (2022-06-22)
|
||||
|
||||
### Added
|
||||
|
||||
- mixnet-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
|
||||
- mixnet-contract: Replace all naked `-` with `saturating_sub`.
|
||||
- mixnet-contract: Added staking_supply field to ContractStateParams.
|
||||
- mixnet-contract: Added a query to get MixnodeBond by identity key ([#1369]).
|
||||
- mixnet-contract: Added a query to get GatewayBond by identity key ([#1369]).
|
||||
- vesting-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
|
||||
- vesting-contract: Added limit to the amount of tokens one can pledge ([#1331])
|
||||
|
||||
### Fixed
|
||||
|
||||
- mixnet-contract: `estimated_delegator_reward` calculation ([#1284])
|
||||
- mixnet-contract: delegator and operator rewards use lambda and sigma instead of lambda_ticked and sigma_ticked ([#1284])
|
||||
- mixnet-contract: removed `expect` in `query_delegator_reward` and queries containing invalid proxy address should now return a more human-readable error ([#1257])
|
||||
- mixnet-contract: replaced integer division with fixed for performance calculations ([#1284])
|
||||
- mixnet-contract: Under certain circumstances nodes could not be unbonded ([#1255](https://github.com/nymtech/nym/issues/1255)) ([#1258])
|
||||
- mixnet-contract: Using correct staking supply when distributing rewards. ([#1373])
|
||||
- vesting-contract: replaced `checked_sub` with `saturating_sub` to fix the underflow in `get_vesting_tokens` ([#1275])
|
||||
|
||||
|
||||
[#1255]: https://github.com/nymtech/nym/pull/1255
|
||||
[#1257]: https://github.com/nymtech/nym/pull/1257
|
||||
[#1258]: https://github.com/nymtech/nym/pull/1258
|
||||
[#1275]: https://github.com/nymtech/nym/pull/1275
|
||||
[#1284]: https://github.com/nymtech/nym/pull/1284
|
||||
[#1292]: https://github.com/nymtech/nym/pull/1292
|
||||
[#1331]: https://github.com/nymtech/nym/pull/1331
|
||||
[#1369]: https://github.com/nymtech/nym/pull/1369
|
||||
[#1373]: https://github.com/nymtech/nym/pull/1373
|
||||
|
||||
## [nym-wallet-v1.0.6](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.6) (2022-06-21)
|
||||
|
||||
- wallet: undelegating now uses either the mixnet or vesting contract, or both, depending on how delegations were made
|
||||
- wallet: redeeming and compounding now uses both the mixnet and vesting contract
|
||||
- wallet: the wallet backend learned how to archive wallet files
|
||||
- wallet: add ENABLE_QA_MODE environment variable to enable QA mode on built wallet
|
||||
|
||||
## [nym-wallet-v1.0.5](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.5) (2022-06-14)
|
||||
|
||||
- wallet: add simple CLI tool for decrypting and recovering the wallet file.
|
||||
- wallet: added support for multiple accounts ([#1265])
|
||||
- wallet: compound and claim reward endpoints for operators and delegators ([#1302])
|
||||
- wallet: require password to switch accounts
|
||||
- wallet: the wallet backend learned how to keep track of validator name, either hardcoded or by querying the status endpoint.
|
||||
- wallet: new delegation and rewards UI
|
||||
- wallet: show version in nav bar
|
||||
- wallet: contract admin route put back
|
||||
- wallet: staking_supply field to StateParams
|
||||
- wallet: show transaction hash for redeeming or compounding rewards
|
||||
|
||||
[#1265]: https://github.com/nymtech/nym/pull/1265
|
||||
[#1302]: https://github.com/nymtech/nym/pull/1302
|
||||
|
||||
## [nym-wallet-v1.0.4](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.4) (2022-05-04)
|
||||
|
||||
### Changed
|
||||
|
||||
- all: the default behaviour of validator client is changed to use `broadcast_sync` and poll for transaction inclusion instead of using `broadcast_commit` to deal with timeouts ([#1246])
|
||||
[#1496]: https://github.com/nymtech/nym/pull/1496
|
||||
[#1503]: https://github.com/nymtech/nym/pull/1503
|
||||
[#1520]: https://github.com/nymtech/nym/pull/1520
|
||||
|
||||
## [v1.0.1](https://github.com/nymtech/nym/tree/v1.0.1) (2022-05-04)
|
||||
|
||||
@@ -193,77 +114,10 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.3...nym-binaries-1.0.0)
|
||||
|
||||
## [nym-wallet-v1.0.3](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.3) (2022-04-25)
|
||||
|
||||
[Full Changelog](https://github.com/nymtech/nym/compare/nym-binaries-1.0.0-rc.2...nym-wallet-v1.0.3)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- \[Issue\] Wallet 1.0.2 cannot send NYM tokens from a DelayedVestingAccount [\#1215](https://github.com/nymtech/nym/issues/1215)
|
||||
- Main README not showing properly with GitHub dark mode [\#1211](https://github.com/nymtech/nym/issues/1211)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Bugfix - wallet undelegation for vesting accounts [\#1220](https://github.com/nymtech/nym/pull/1220) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Bugfix/delegation reconcile [\#1219](https://github.com/nymtech/nym/pull/1219) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Bugfix/query proxied pending delegations [\#1218](https://github.com/nymtech/nym/pull/1218) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Using custom gas multiplier in the wallet [\#1217](https://github.com/nymtech/nym/pull/1217) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/vesting accounts support [\#1216](https://github.com/nymtech/nym/pull/1216) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Release/1.0.0 rc.2 [\#1214](https://github.com/nymtech/nym/pull/1214) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- chore: fix dark mode rendering [\#1212](https://github.com/nymtech/nym/pull/1212) ([pwnfoo](https://github.com/pwnfoo))
|
||||
- Feature/spend coconut [\#1210](https://github.com/nymtech/nym/pull/1210) ([neacsu](https://github.com/neacsu))
|
||||
- Bugfix/unique sphinx key [\#1207](https://github.com/nymtech/nym/pull/1207) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Add cache read and write timeouts [\#1206](https://github.com/nymtech/nym/pull/1206) ([durch](https://github.com/durch))
|
||||
- Additional, more informative routes [\#1204](https://github.com/nymtech/nym/pull/1204) ([durch](https://github.com/durch))
|
||||
- Feature/aggregated econ dynamics explorer endpoint [\#1203](https://github.com/nymtech/nym/pull/1203) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Debugging validator [\#1198](https://github.com/nymtech/nym/pull/1198) ([durch](https://github.com/durch))
|
||||
- wallet: expose additional validator configuration functionality to the frontend [\#1195](https://github.com/nymtech/nym/pull/1195) ([octol](https://github.com/octol))
|
||||
- Update rewarding validator address [\#1193](https://github.com/nymtech/nym/pull/1193) ([durch](https://github.com/durch))
|
||||
- Crypto part of the Groth's NIDKG [\#1182](https://github.com/nymtech/nym/pull/1182) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- fix unbond page [\#1180](https://github.com/nymtech/nym/pull/1180) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Type safe bounds [\#1179](https://github.com/nymtech/nym/pull/1179) ([durch](https://github.com/durch))
|
||||
- Fix delegation paging [\#1174](https://github.com/nymtech/nym/pull/1174) ([durch](https://github.com/durch))
|
||||
- Update binaries to rc version [\#1172](https://github.com/nymtech/nym/pull/1172) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Bump ansi-regex from 4.1.0 to 4.1.1 in /docker/typescript\_client/upload\_contract [\#1171](https://github.com/nymtech/nym/pull/1171) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
|
||||
## [nym-binaries-1.0.0-rc.2](https://github.com/nymtech/nym/tree/nym-binaries-1.0.0-rc.2) (2022-04-15)
|
||||
|
||||
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.2...nym-binaries-1.0.0-rc.2)
|
||||
|
||||
## [nym-wallet-v1.0.2](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.2) (2022-04-05)
|
||||
|
||||
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.1...nym-wallet-v1.0.2)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Wallet 1.0.2 visual tweaks [\#1197](https://github.com/nymtech/nym/pull/1197) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Password for wallet with routes [\#1196](https://github.com/nymtech/nym/pull/1196) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Add auto-updater to Nym Wallet [\#1194](https://github.com/nymtech/nym/pull/1194) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Fix clippy warnings for beta toolchain [\#1191](https://github.com/nymtech/nym/pull/1191) ([octol](https://github.com/octol))
|
||||
- wallet: expose validator urls to the frontend [\#1190](https://github.com/nymtech/nym/pull/1190) ([octol](https://github.com/octol))
|
||||
- wallet: add test for decrypting stored wallet file [\#1189](https://github.com/nymtech/nym/pull/1189) ([octol](https://github.com/octol))
|
||||
- Fix clippy warnings [\#1188](https://github.com/nymtech/nym/pull/1188) ([octol](https://github.com/octol))
|
||||
- Password for wallet with routes [\#1187](https://github.com/nymtech/nym/pull/1187) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- wallet: add validate\_mnemonic [\#1186](https://github.com/nymtech/nym/pull/1186) ([octol](https://github.com/octol))
|
||||
- wallet: support removing accounts from the wallet file [\#1185](https://github.com/nymtech/nym/pull/1185) ([octol](https://github.com/octol))
|
||||
- Feature/adding discord [\#1184](https://github.com/nymtech/nym/pull/1184) ([gala1234](https://github.com/gala1234))
|
||||
- wallet: config backend for validator selection [\#1183](https://github.com/nymtech/nym/pull/1183) ([octol](https://github.com/octol))
|
||||
- Add storybook to wallet [\#1178](https://github.com/nymtech/nym/pull/1178) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- wallet: connection test nymd and api urls independently [\#1170](https://github.com/nymtech/nym/pull/1170) ([octol](https://github.com/octol))
|
||||
- wallet: wire up account storage [\#1153](https://github.com/nymtech/nym/pull/1153) ([octol](https://github.com/octol))
|
||||
- Feature/signature on deposit [\#1151](https://github.com/nymtech/nym/pull/1151) ([neacsu](https://github.com/neacsu))
|
||||
|
||||
## [nym-wallet-v1.0.1](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.1) (2022-04-05)
|
||||
|
||||
[Full Changelog](https://github.com/nymtech/nym/compare/nym-binaries-1.0.0-rc.1...nym-wallet-v1.0.1)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Check enabling bbbc simultaneously with open access. Estimate what it would take to make this the default compilation target. [\#1175](https://github.com/nymtech/nym/issues/1175)
|
||||
- Get coconut credential for deposited tokens [\#1138](https://github.com/nymtech/nym/issues/1138)
|
||||
- Make payments lazy [\#1135](https://github.com/nymtech/nym/issues/1135)
|
||||
- Uptime on node selection for sets [\#1049](https://github.com/nymtech/nym/issues/1049)
|
||||
|
||||
## [nym-binaries-1.0.0-rc.1](https://github.com/nymtech/nym/tree/nym-binaries-1.0.0-rc.1) (2022-03-28)
|
||||
|
||||
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.0...nym-binaries-1.0.0-rc.1)
|
||||
@@ -342,108 +196,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- feature/pedersen-commitments [\#1048](https://github.com/nymtech/nym/pull/1048) ([danielementary](https://github.com/danielementary))
|
||||
- Feature/reuse init owner [\#970](https://github.com/nymtech/nym/pull/970) ([neacsu](https://github.com/neacsu))
|
||||
|
||||
## [nym-wallet-v1.0.0](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.0) (2022-02-03)
|
||||
|
||||
[Full Changelog](https://github.com/nymtech/nym/compare/v0.12.1...nym-wallet-v1.0.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- \[Feature Request\] Please enable registration without need for Telegram account [\#1016](https://github.com/nymtech/nym/issues/1016)
|
||||
- Fast mixnode launch with a pre-built ISO + VM software [\#1001](https://github.com/nymtech/nym/issues/1001)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- \[Issue\] [\#1000](https://github.com/nymtech/nym/issues/1000)
|
||||
- \[Issue\] `nym-client` requires multiple attempts to run a server [\#869](https://github.com/nymtech/nym/issues/869)
|
||||
- De-'float'-ing `Interval` \(`Display` impl + `serde`\) [\#1065](https://github.com/nymtech/nym/pull/1065) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- display client address on wallet creation [\#1058](https://github.com/nymtech/nym/pull/1058) ([fmtabbara](https://github.com/fmtabbara))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Rewarded set inclusion probability API endpoint [\#1037](https://github.com/nymtech/nym/issues/1037)
|
||||
- Update cw-storage-plus to 0.11 [\#1032](https://github.com/nymtech/nym/issues/1032)
|
||||
- Change `u128` fields in `RewardEstimationResponse` to `u64` [\#1029](https://github.com/nymtech/nym/issues/1029)
|
||||
- Test out the mainnet Gravity Bridge [\#1006](https://github.com/nymtech/nym/issues/1006)
|
||||
- Add vesting contract interface to nym-wallet [\#959](https://github.com/nymtech/nym/issues/959)
|
||||
- Mixnode crash [\#486](https://github.com/nymtech/nym/issues/486)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- create custom urls for mainnet [\#1095](https://github.com/nymtech/nym/pull/1095) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Wallet signing on MacOS [\#1093](https://github.com/nymtech/nym/pull/1093) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Fix rust 2018 idioms warnings [\#1092](https://github.com/nymtech/nym/pull/1092) ([octol](https://github.com/octol))
|
||||
- Prevent contract overwriting [\#1090](https://github.com/nymtech/nym/pull/1090) ([durch](https://github.com/durch))
|
||||
- Logout operation [\#1087](https://github.com/nymtech/nym/pull/1087) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Update to rust edition 2021 everywhere [\#1086](https://github.com/nymtech/nym/pull/1086) ([octol](https://github.com/octol))
|
||||
- Tag contract errors, and print out lines for easier QA [\#1084](https://github.com/nymtech/nym/pull/1084) ([durch](https://github.com/durch))
|
||||
- Feature/flexible vesting + utility queries [\#1083](https://github.com/nymtech/nym/pull/1083) ([durch](https://github.com/durch))
|
||||
- Bump @openzeppelin/contracts from 4.3.1 to 4.4.2 in /contracts/basic-bandwidth-generation [\#1082](https://github.com/nymtech/nym/pull/1082) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump nth-check from 2.0.0 to 2.0.1 in /clients/native/examples/js-examples/websocket [\#1081](https://github.com/nymtech/nym/pull/1081) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump url-parse from 1.5.1 to 1.5.4 in /clients/native/examples/js-examples/websocket [\#1080](https://github.com/nymtech/nym/pull/1080) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump follow-redirects from 1.14.1 to 1.14.7 in /clients/native/examples/js-examples/websocket [\#1079](https://github.com/nymtech/nym/pull/1079) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump nanoid from 3.1.23 to 3.2.0 in /clients/native/examples/js-examples/websocket [\#1078](https://github.com/nymtech/nym/pull/1078) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Setup basic test for mixnode stats reporting [\#1077](https://github.com/nymtech/nym/pull/1077) ([octol](https://github.com/octol))
|
||||
- Make wallet\_address mandatory for mixnode init [\#1076](https://github.com/nymtech/nym/pull/1076) ([octol](https://github.com/octol))
|
||||
- Tidy nym-mixnode module visibility [\#1075](https://github.com/nymtech/nym/pull/1075) ([octol](https://github.com/octol))
|
||||
- Feature/wallet login with password [\#1074](https://github.com/nymtech/nym/pull/1074) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Add trait to mock client dependency in DelayForwarder [\#1073](https://github.com/nymtech/nym/pull/1073) ([octol](https://github.com/octol))
|
||||
- Bump rust-version to latest stable for nym-mixnode [\#1072](https://github.com/nymtech/nym/pull/1072) ([octol](https://github.com/octol))
|
||||
- Fixes CI for our wasm build [\#1069](https://github.com/nymtech/nym/pull/1069) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Add @octol as codeowner [\#1068](https://github.com/nymtech/nym/pull/1068) ([octol](https://github.com/octol))
|
||||
- set-up inclusion probability [\#1067](https://github.com/nymtech/nym/pull/1067) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Feature/wasm client [\#1066](https://github.com/nymtech/nym/pull/1066) ([neacsu](https://github.com/neacsu))
|
||||
- Changed bech32\_prefix from punk to nymt [\#1064](https://github.com/nymtech/nym/pull/1064) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Bump nanoid from 3.1.30 to 3.2.0 in /testnet-faucet [\#1063](https://github.com/nymtech/nym/pull/1063) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump nanoid from 3.1.30 to 3.2.0 in /nym-wallet [\#1062](https://github.com/nymtech/nym/pull/1062) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Rework vesting contract storage [\#1061](https://github.com/nymtech/nym/pull/1061) ([durch](https://github.com/durch))
|
||||
- Mixnet Contract constants extraction [\#1060](https://github.com/nymtech/nym/pull/1060) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- fix: make explorer footer year dynamic [\#1059](https://github.com/nymtech/nym/pull/1059) ([martinyung](https://github.com/martinyung))
|
||||
- Add mnemonic just on creation, to display it [\#1057](https://github.com/nymtech/nym/pull/1057) ([neacsu](https://github.com/neacsu))
|
||||
- Network Explorer: updates to API and UI to show the active set [\#1056](https://github.com/nymtech/nym/pull/1056) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Made contract addresses for query NymdClient construction optional [\#1055](https://github.com/nymtech/nym/pull/1055) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Introduced RPC query for total token supply [\#1053](https://github.com/nymtech/nym/pull/1053) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/tokio console [\#1052](https://github.com/nymtech/nym/pull/1052) ([durch](https://github.com/durch))
|
||||
- Implemented beta clippy lint recommendations [\#1051](https://github.com/nymtech/nym/pull/1051) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- add new function to update profit percentage [\#1050](https://github.com/nymtech/nym/pull/1050) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Upgrade Clap and use declarative argument parsing for nym-mixnode [\#1047](https://github.com/nymtech/nym/pull/1047) ([octol](https://github.com/octol))
|
||||
- Feature/additional bond validation [\#1046](https://github.com/nymtech/nym/pull/1046) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Fix clippy on relevant lints [\#1044](https://github.com/nymtech/nym/pull/1044) ([neacsu](https://github.com/neacsu))
|
||||
- Bump shelljs from 0.8.4 to 0.8.5 in /contracts/basic-bandwidth-generation [\#1043](https://github.com/nymtech/nym/pull/1043) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Endpoint for rewarded set inclusion probabilities [\#1042](https://github.com/nymtech/nym/pull/1042) ([durch](https://github.com/durch))
|
||||
- Bump follow-redirects from 1.14.4 to 1.14.7 in /contracts/basic-bandwidth-generation [\#1041](https://github.com/nymtech/nym/pull/1041) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump follow-redirects from 1.14.5 to 1.14.7 in /testnet-faucet [\#1040](https://github.com/nymtech/nym/pull/1040) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Feature/node settings update [\#1036](https://github.com/nymtech/nym/pull/1036) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Migrate to cw-storage-plus 0.11.1 [\#1035](https://github.com/nymtech/nym/pull/1035) ([durch](https://github.com/durch))
|
||||
- Bump @openzeppelin/contracts from 4.4.1 to 4.4.2 in /contracts/basic-bandwidth-generation [\#1034](https://github.com/nymtech/nym/pull/1034) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Feature/configurable wallet [\#1033](https://github.com/nymtech/nym/pull/1033) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/downcast reward estimation [\#1031](https://github.com/nymtech/nym/pull/1031) ([durch](https://github.com/durch))
|
||||
- Wallet UI updates [\#1028](https://github.com/nymtech/nym/pull/1028) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Remove migration code [\#1027](https://github.com/nymtech/nym/pull/1027) ([neacsu](https://github.com/neacsu))
|
||||
- Chore/stricter dependency requirements [\#1025](https://github.com/nymtech/nym/pull/1025) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/validator api client endpoints [\#1024](https://github.com/nymtech/nym/pull/1024) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Updated cosmrs to 0.4.1 [\#1023](https://github.com/nymtech/nym/pull/1023) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/testnet deploy scripts [\#1022](https://github.com/nymtech/nym/pull/1022) ([mfahampshire](https://github.com/mfahampshire))
|
||||
- Changed wallet's client to a full validator client [\#1021](https://github.com/nymtech/nym/pull/1021) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Fix 404 link [\#1020](https://github.com/nymtech/nym/pull/1020) ([RiccardoMasutti](https://github.com/RiccardoMasutti))
|
||||
- Feature/additional mixnode endpoints [\#1019](https://github.com/nymtech/nym/pull/1019) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Introduced denom check when trying to withdraw vested coins [\#1018](https://github.com/nymtech/nym/pull/1018) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Add network defaults for qa [\#1017](https://github.com/nymtech/nym/pull/1017) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/expanded events [\#1015](https://github.com/nymtech/nym/pull/1015) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- update frontend to use new profit update api [\#1014](https://github.com/nymtech/nym/pull/1014) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Feature/node state endpoint [\#1013](https://github.com/nymtech/nym/pull/1013) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/hourly set updates [\#1012](https://github.com/nymtech/nym/pull/1012) ([durch](https://github.com/durch))
|
||||
- Feature/remove unused profit margin [\#1011](https://github.com/nymtech/nym/pull/1011) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/explorer node status [\#1010](https://github.com/nymtech/nym/pull/1010) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Use serial integer instead of random [\#1009](https://github.com/nymtech/nym/pull/1009) ([durch](https://github.com/durch))
|
||||
- Feature/configure profit [\#1008](https://github.com/nymtech/nym/pull/1008) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/fix gateway sign [\#1004](https://github.com/nymtech/nym/pull/1004) ([neacsu](https://github.com/neacsu))
|
||||
- Fix clippy [\#1003](https://github.com/nymtech/nym/pull/1003) ([neacsu](https://github.com/neacsu))
|
||||
- Update wallet version [\#998](https://github.com/nymtech/nym/pull/998) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Fix wallet build instructions [\#997](https://github.com/nymtech/nym/pull/997) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Make the separation between testnet-mode and erc20 bandwidth mode clearer [\#994](https://github.com/nymtech/nym/pull/994) ([neacsu](https://github.com/neacsu))
|
||||
- Bump @openzeppelin/contracts from 3.4.0 to 4.4.1 in /contracts/basic-bandwidth-generation [\#983](https://github.com/nymtech/nym/pull/983) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Feature/implicit runtime [\#973](https://github.com/nymtech/nym/pull/973) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Differentiate staking and ownership [\#961](https://github.com/nymtech/nym/pull/961) ([durch](https://github.com/durch))
|
||||
|
||||
## [v0.12.1](https://github.com/nymtech/nym/tree/v0.12.1) (2021-12-23)
|
||||
|
||||
|
||||
Generated
+8
-39
@@ -3056,7 +3056,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"clap 3.2.8",
|
||||
"client-core",
|
||||
@@ -3090,7 +3090,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-gateway"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -3138,7 +3138,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnode"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bs58",
|
||||
@@ -3178,7 +3178,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"clap 2.34.0",
|
||||
@@ -3206,7 +3206,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-statistics"
|
||||
version = "0.1.0"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"log",
|
||||
@@ -3222,7 +3222,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"clap 3.2.8",
|
||||
"client-core",
|
||||
@@ -3283,7 +3283,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-validator-api"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -3325,6 +3325,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"task",
|
||||
"thiserror",
|
||||
"time 0.3.9",
|
||||
"tokio",
|
||||
@@ -5397,15 +5398,6 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "streaming-stats"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0d670ce4e348a2081843569e0f79b21c99c91bb9028b3b3ecb0f050306de547"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stringprep"
|
||||
version = "0.1.2"
|
||||
@@ -6293,29 +6285,6 @@ dependencies = [
|
||||
"vesting-contract-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator-client-scripts"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bip39",
|
||||
"bs58",
|
||||
"clap 3.2.8",
|
||||
"csv",
|
||||
"dotenv",
|
||||
"log",
|
||||
"mixnet-contract-common",
|
||||
"network-defaults",
|
||||
"pretty_env_logger",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"streaming-stats",
|
||||
"tokio",
|
||||
"url",
|
||||
"validator-client",
|
||||
"vesting-contract-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.0"
|
||||
|
||||
+2
-2
@@ -68,8 +68,7 @@ members = [
|
||||
"service-providers/network-statistics",
|
||||
"validator-api",
|
||||
"validator-api/validator-api-requests",
|
||||
"tools/ts-rs-cli",
|
||||
"tools/validator-client-scripts"
|
||||
"tools/ts-rs-cli"
|
||||
]
|
||||
|
||||
default-members = [
|
||||
@@ -77,6 +76,7 @@ default-members = [
|
||||
"clients/socks5",
|
||||
"gateway",
|
||||
"service-providers/network-requester",
|
||||
"service-providers/network-statistics",
|
||||
"mixnode",
|
||||
"validator-api",
|
||||
"explorer-api",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
use crate::client::config::{Config, SocketType};
|
||||
use clap::{Parser, Subcommand};
|
||||
use url::Url;
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub(crate) const DEFAULT_ETH_ENDPOINT: &str =
|
||||
@@ -97,28 +96,17 @@ pub(crate) async fn execute(args: &Cli) {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_validators(raw: &str) -> Vec<Url> {
|
||||
raw.split(',')
|
||||
.map(|raw_validator| {
|
||||
raw_validator
|
||||
.trim()
|
||||
.parse()
|
||||
.expect("one of the provided validator api urls is invalid")
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
|
||||
if let Some(raw_validators) = args.validators {
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_validator_apis(parse_validators(&raw_validators));
|
||||
.set_custom_validator_apis(config::parse_validators(&raw_validators));
|
||||
} else if std::env::var(network_defaults::var_names::CONFIGURED).is_ok() {
|
||||
let raw_validators = std::env::var(network_defaults::var_names::API_VALIDATOR)
|
||||
.expect("api validator not set");
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_validator_apis(parse_validators(&raw_validators));
|
||||
.set_custom_validator_apis(config::parse_validators(&raw_validators));
|
||||
}
|
||||
|
||||
if args.disable_socket {
|
||||
|
||||
@@ -82,7 +82,7 @@ fn version_check(cfg: &Config) -> bool {
|
||||
if binary_version == config_version {
|
||||
true
|
||||
} else {
|
||||
warn!("The mixnode binary has different version than what is specified in config file! {} and {}", binary_version, config_version);
|
||||
warn!("The native-client binary has different version than what is specified in config file! {} and {}", binary_version, config_version);
|
||||
if is_minor_version_compatible(binary_version, config_version) {
|
||||
info!("but they are still semver compatible. However, consider running the `upgrade` command");
|
||||
true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
edition = "2021"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::client::config::Config;
|
||||
use clap::{Parser, Subcommand};
|
||||
use url::Url;
|
||||
use config::parse_validators;
|
||||
|
||||
pub mod init;
|
||||
pub(crate) mod run;
|
||||
@@ -96,17 +96,6 @@ pub(crate) async fn execute(args: &Cli) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_validators(raw: &str) -> Vec<Url> {
|
||||
raw.split(',')
|
||||
.map(|raw_validator| {
|
||||
raw_validator
|
||||
.trim()
|
||||
.parse()
|
||||
.expect("one of the provided validator api urls is invalid")
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
|
||||
if let Some(raw_validators) = args.validators {
|
||||
config
|
||||
|
||||
@@ -2,9 +2,4 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod client;
|
||||
// This is only used as we reach into the init functions in nym-connect. We need to refactor the
|
||||
// init functions so that nym-connect can just call the same init function as the regular socks5
|
||||
// client.
|
||||
#[allow(unused)]
|
||||
pub mod commands;
|
||||
pub mod socks;
|
||||
|
||||
@@ -88,3 +88,14 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
|
||||
.map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_validators(raw: &str) -> Vec<url::Url> {
|
||||
raw.split(',')
|
||||
.map(|raw_validator| {
|
||||
raw_validator
|
||||
.trim()
|
||||
.parse()
|
||||
.expect("one of the provided validator api urls is invalid")
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ impl SpendCredentialData {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub enum SpendCredentialStatus {
|
||||
InProgress,
|
||||
Spent,
|
||||
|
||||
@@ -213,7 +213,7 @@ pub enum QueryMsg {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct MigrateMsg {
|
||||
pub mixnet_denom: String,
|
||||
@@ -226,7 +226,7 @@ impl MigrateMsg {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct NodeToRemove {
|
||||
owner: String,
|
||||
proxy: Option<String>,
|
||||
|
||||
@@ -24,7 +24,7 @@ pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("0000000000000000000000000000000000000000");
|
||||
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy";
|
||||
|
||||
pub(crate) const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "http://127.0.0.1:8090";
|
||||
pub(crate) const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "https://mainnet-stats.nymte.ch:8090/";
|
||||
pub const NYMD_VALIDATOR: &str = "https://rpc.nyx.nodes.guru/";
|
||||
pub const API_VALIDATOR: &str = "https://validator.nymtech.net/api/";
|
||||
pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
||||
|
||||
@@ -507,7 +507,7 @@ mod test {
|
||||
|
||||
for (expected, raw_display) in values {
|
||||
let coin = DecCoin {
|
||||
denom: Network::MAINNET.mix_denom().display.into(),
|
||||
denom: Network::MAINNET.mix_denom().display,
|
||||
amount: raw_display.parse().unwrap(),
|
||||
};
|
||||
let base = reg.attempt_convert_to_base_coin(coin).unwrap();
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
## [nym-contracts-v1.0.1](https://github.com/nymtech/nym/tree/nym-contracts-v1.0.1) (2022-06-22)
|
||||
|
||||
### Added
|
||||
|
||||
- mixnet-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
|
||||
- mixnet-contract: Replace all naked `-` with `saturating_sub`.
|
||||
- mixnet-contract: Added staking_supply field to ContractStateParams.
|
||||
- mixnet-contract: Added a query to get MixnodeBond by identity key ([#1369]).
|
||||
- mixnet-contract: Added a query to get GatewayBond by identity key ([#1369]).
|
||||
- vesting-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
|
||||
- vesting-contract: Added limit to the amount of tokens one can pledge ([#1331])
|
||||
|
||||
### Fixed
|
||||
|
||||
- mixnet-contract: `estimated_delegator_reward` calculation ([#1284])
|
||||
- mixnet-contract: delegator and operator rewards use lambda and sigma instead of lambda_ticked and sigma_ticked ([#1284])
|
||||
- mixnet-contract: removed `expect` in `query_delegator_reward` and queries containing invalid proxy address should now return a more human-readable error ([#1257])
|
||||
- mixnet-contract: replaced integer division with fixed for performance calculations ([#1284])
|
||||
- mixnet-contract: Under certain circumstances nodes could not be unbonded ([#1255](https://github.com/nymtech/nym/issues/1255)) ([#1258])
|
||||
- mixnet-contract: Using correct staking supply when distributing rewards. ([#1373])
|
||||
- vesting-contract: replaced `checked_sub` with `saturating_sub` to fix the underflow in `get_vesting_tokens` ([#1275])
|
||||
|
||||
|
||||
[#1255]: https://github.com/nymtech/nym/pull/1255
|
||||
[#1257]: https://github.com/nymtech/nym/pull/1257
|
||||
[#1258]: https://github.com/nymtech/nym/pull/1258
|
||||
[#1275]: https://github.com/nymtech/nym/pull/1275
|
||||
[#1284]: https://github.com/nymtech/nym/pull/1284
|
||||
[#1292]: https://github.com/nymtech/nym/pull/1292
|
||||
[#1331]: https://github.com/nymtech/nym/pull/1331
|
||||
[#1369]: https://github.com/nymtech/nym/pull/1369
|
||||
[#1373]: https://github.com/nymtech/nym/pull/1373
|
||||
+1
-1
@@ -15,6 +15,6 @@ BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
|
||||
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
|
||||
MULTISIG_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
|
||||
REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy
|
||||
STATISTICS_SERVICE_DOMAIN_ADDRESS="http://127.0.0.1:8090"
|
||||
STATISTICS_SERVICE_DOMAIN_ADDRESS="https://mainnet-stats.nymte.ch:8090"
|
||||
NYMD_VALIDATOR="https://rpc.nyx.nodes.guru/"
|
||||
API_VALIDATOR="https://validator.nymtech.net/api/"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
EXPLORER_API_URL=https://explorer.nymtech.net/api/v1
|
||||
VALIDATOR_API_URL=https://validator.nymtech.net
|
||||
VALIDATOR_URL=https://rpc.nyx.nodes.guru
|
||||
BIG_DIPPER_URL=https://blocks.nymtech.net
|
||||
CURRENCY_DENOM=unym
|
||||
CURRENCY_STAKING_DENOM=unyx
|
||||
|
||||
+4
-3
@@ -1,5 +1,6 @@
|
||||
EXPLORER_API_URL=https://qa-explorer.nymtech.net/api/v1
|
||||
VALIDATOR_API_URL=https://qa-validator.nymtech.net
|
||||
VALIDATOR_API_URL=https://qa-validator-api.nymtech.net
|
||||
VALIDATOR_URL=https://qa-validator.nymtech.net
|
||||
BIG_DIPPER_URL=https://qa-blocks.nymtech.net
|
||||
CURRENCY_DENOM=unymt
|
||||
CURRENCY_STAKING_DENOM=unyxt
|
||||
CURRENCY_DENOM=unym
|
||||
CURRENCY_STAKING_DENOM=unyx
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// master APIs
|
||||
export const API_BASE_URL = process.env.EXPLORER_API_URL;
|
||||
export const VALIDATOR_API_BASE_URL = process.env.VALIDATOR_API_URL;
|
||||
export const VALIDATOR_URL = process.env.VALIDATOR_URL;
|
||||
export const BIG_DIPPER = process.env.BIG_DIPPER_URL;
|
||||
|
||||
// specific API routes
|
||||
@@ -9,7 +10,7 @@ export const MIXNODE_PING = `${API_BASE_URL}/ping`;
|
||||
export const MIXNODES_API = `${API_BASE_URL}/mix-nodes`;
|
||||
export const MIXNODE_API = `${API_BASE_URL}/mix-node`;
|
||||
export const GATEWAYS_API = `${VALIDATOR_API_BASE_URL}/api/v1/gateways`;
|
||||
export const VALIDATORS_API = `${VALIDATOR_API_BASE_URL}/validators`;
|
||||
export const VALIDATORS_API = `${VALIDATOR_URL}/validators`;
|
||||
export const BLOCK_API = `${VALIDATOR_API_BASE_URL}/block`;
|
||||
export const COUNTRY_DATA_API = `${API_BASE_URL}/countries`;
|
||||
export const UPTIME_STORY_API = `${VALIDATOR_API_BASE_URL}/api/v1/status/mixnode`; // add ID then '/history' to this.
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
DialogTitle,
|
||||
IconButton,
|
||||
Slider,
|
||||
Typography,
|
||||
Box,
|
||||
@@ -25,7 +24,9 @@ import { useIsMobile } from '../../hooks/useIsMobile';
|
||||
const FilterItem = ({
|
||||
label,
|
||||
id,
|
||||
tooltipInfo,
|
||||
value,
|
||||
isSmooth,
|
||||
marks,
|
||||
scale,
|
||||
min,
|
||||
@@ -36,12 +37,13 @@ const FilterItem = ({
|
||||
}) => (
|
||||
<Box sx={{ p: 2 }}>
|
||||
<Typography gutterBottom>{label}</Typography>
|
||||
<Typography fontSize={12}>{tooltipInfo}</Typography>
|
||||
<Slider
|
||||
value={value}
|
||||
onChange={(e: Event, newValue: number | number[]) => onChange(id, newValue as number[])}
|
||||
valueLabelDisplay="off"
|
||||
valueLabelDisplay={isSmooth ? 'auto' : 'off'}
|
||||
marks={marks}
|
||||
step={null}
|
||||
step={isSmooth ? 1 : null}
|
||||
scale={scale}
|
||||
min={min}
|
||||
max={max}
|
||||
@@ -50,7 +52,7 @@ const FilterItem = ({
|
||||
);
|
||||
|
||||
export const Filters = () => {
|
||||
const { filterMixnodes, fetchMixnodes } = useMainContext();
|
||||
const { filterMixnodes, fetchMixnodes, mixnodes } = useMainContext();
|
||||
const { status } = useParams<{ status: MixnodeStatusWithAll | undefined }>();
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
@@ -127,19 +129,26 @@ export const Filters = () => {
|
||||
<Alert
|
||||
severity="info"
|
||||
variant={isMobile ? 'standard' : 'outlined'}
|
||||
sx={{ color: (t) => t.palette.info.light }}
|
||||
action={
|
||||
<Button size="small" onClick={onClearFilters}>
|
||||
Clear
|
||||
CLEAR FILTERS
|
||||
</Button>
|
||||
}
|
||||
sx={{ width: 300 }}
|
||||
>
|
||||
Filters applied
|
||||
{mixnodes?.data?.length} mixnodes matched your criteria
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
<IconButton size="large" onClick={handleToggleShowFilters}>
|
||||
<Tune />
|
||||
</IconButton>
|
||||
<Button
|
||||
size="large"
|
||||
variant="text"
|
||||
color="inherit"
|
||||
endIcon={<Tune />}
|
||||
onClick={handleToggleShowFilters}
|
||||
sx={{ textTransform: 'none' }}
|
||||
>
|
||||
Advanced filters
|
||||
</Button>
|
||||
<Dialog open={showFilters} onClose={handleToggleShowFilters} maxWidth="md" fullWidth>
|
||||
<DialogTitle>Mixnode filters</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
|
||||
@@ -5,6 +5,7 @@ export const generateFilterSchema = (upperSaturationValue?: number) => ({
|
||||
label: 'Profit margin (%)',
|
||||
id: EnumFilterKey.profitMargin,
|
||||
value: [0, 100],
|
||||
isSmooth: true,
|
||||
marks: [
|
||||
{ label: '0', value: 0 },
|
||||
{ label: '10', value: 10 },
|
||||
@@ -18,6 +19,8 @@ export const generateFilterSchema = (upperSaturationValue?: number) => ({
|
||||
{ label: '90', value: 90 },
|
||||
{ label: '100', value: 100 },
|
||||
],
|
||||
tooltipInfo:
|
||||
'As a delegator you want to chose nodes with lower profit margin, meaning more payout for their delegators',
|
||||
},
|
||||
stakeSaturation: {
|
||||
label: 'Stake saturation (%)',
|
||||
@@ -43,65 +46,29 @@ export const generateFilterSchema = (upperSaturationValue?: number) => ({
|
||||
},
|
||||
],
|
||||
max: upperSaturationValue,
|
||||
tooltipInfo: "Select nodes with <100% saturation. Any additional stake above 100% saturation won't get rewards",
|
||||
},
|
||||
stake: {
|
||||
label: 'Stake (NYM)',
|
||||
id: EnumFilterKey.stake,
|
||||
value: [20, 90],
|
||||
min: 20,
|
||||
max: 90,
|
||||
routingScore: {
|
||||
label: 'Routing score (%)',
|
||||
id: EnumFilterKey.routingScore,
|
||||
value: [0, 100],
|
||||
marks: [
|
||||
{
|
||||
value: 0,
|
||||
label: '1',
|
||||
},
|
||||
{
|
||||
value: 10,
|
||||
label: '10',
|
||||
},
|
||||
{
|
||||
value: 20,
|
||||
label: '100',
|
||||
},
|
||||
{
|
||||
value: 30,
|
||||
label: '1k',
|
||||
},
|
||||
{
|
||||
value: 40,
|
||||
label: '10k',
|
||||
},
|
||||
{
|
||||
value: 50,
|
||||
label: '100k',
|
||||
},
|
||||
{
|
||||
value: 60,
|
||||
label: '1M',
|
||||
},
|
||||
{
|
||||
value: 70,
|
||||
label: '10M',
|
||||
},
|
||||
{
|
||||
value: 80,
|
||||
label: '100M',
|
||||
},
|
||||
{
|
||||
value: 90,
|
||||
label: '1B',
|
||||
},
|
||||
{ label: '0', value: 0 },
|
||||
{ label: '10', value: 10 },
|
||||
{ label: '20', value: 20 },
|
||||
{ label: '30', value: 30 },
|
||||
{ label: '40', value: 40 },
|
||||
{ label: '50', value: 50 },
|
||||
{ label: '60', value: 60 },
|
||||
{ label: '70', value: 70 },
|
||||
{ label: '80', value: 80 },
|
||||
{ label: '90', value: 90 },
|
||||
{ label: '100', value: 100 },
|
||||
],
|
||||
tooltipInfo: 'The higher the routing score the better the performance of the node and so its rewards',
|
||||
},
|
||||
});
|
||||
|
||||
const formatStakeValuesToMinorDenom = ([value_1, value_2]: number[]) => {
|
||||
const lowerValue = 10 ** (value_1 / 10) * 1_000_000;
|
||||
const upperValue = 10 ** (value_2 / 10) * 1_000_000;
|
||||
|
||||
return [lowerValue, upperValue];
|
||||
};
|
||||
|
||||
const formatStakeSaturationValues = ([value_1, value_2]: number[]) => {
|
||||
const lowerValue = value_1 / 100;
|
||||
const upperValue = value_2 / 100;
|
||||
@@ -110,7 +77,7 @@ const formatStakeSaturationValues = ([value_1, value_2]: number[]) => {
|
||||
};
|
||||
|
||||
export const formatOnSave = (filters: TFilters) => ({
|
||||
stake: formatStakeValuesToMinorDenom(filters.stake.value),
|
||||
routingScore: filters.routingScore.value,
|
||||
profitMargin: filters.profitMargin.value,
|
||||
stakeSaturation: formatStakeSaturationValues(filters.stakeSaturation.value),
|
||||
});
|
||||
|
||||
@@ -102,8 +102,8 @@ export const MainContextProvider: React.FC = ({ children }) => {
|
||||
m.mix_node.profit_margin_percent <= filters.profitMargin[1] &&
|
||||
m.stake_saturation >= filters.stakeSaturation[0] &&
|
||||
m.stake_saturation <= filters.stakeSaturation[1] &&
|
||||
+m.pledge_amount.amount + +m.total_delegation.amount >= filters.stake[0] &&
|
||||
+m.pledge_amount.amount + +m.total_delegation.amount <= filters.stake[1],
|
||||
m.avg_uptime >= filters.routingScore[0] &&
|
||||
m.avg_uptime <= filters.routingScore[1],
|
||||
);
|
||||
setMixnodes({ data: filtered, isLoading: false });
|
||||
};
|
||||
|
||||
@@ -4,17 +4,19 @@ import { Mark } from '@mui/base';
|
||||
export enum EnumFilterKey {
|
||||
profitMargin = 'profitMargin',
|
||||
stakeSaturation = 'stakeSaturation',
|
||||
stake = 'stake',
|
||||
routingScore = 'routingScore',
|
||||
}
|
||||
|
||||
export type TFilterItem = {
|
||||
label: string;
|
||||
id: EnumFilterKey;
|
||||
value: number[];
|
||||
isSmooth?: boolean;
|
||||
marks: Mark[];
|
||||
min?: number;
|
||||
max?: number;
|
||||
scale?: (value: number) => number;
|
||||
tooltipInfo?: string;
|
||||
};
|
||||
|
||||
export type TFilters = { [key in EnumFilterKey]: TFilterItem };
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-gateway"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Mixnet Gateway"
|
||||
edition = "2021"
|
||||
|
||||
@@ -6,11 +6,11 @@ use std::{process, str::FromStr};
|
||||
use crate::{config::Config, Cli};
|
||||
use clap::Subcommand;
|
||||
use colored::Colorize;
|
||||
use config::parse_validators;
|
||||
use crypto::bech32_address_validation;
|
||||
use network_defaults::var_names::{
|
||||
API_VALIDATOR, BECH32_PREFIX, CONFIGURED, NYMD_VALIDATOR, STATISTICS_SERVICE_DOMAIN_ADDRESS,
|
||||
};
|
||||
use url::Url;
|
||||
|
||||
pub(crate) mod init;
|
||||
pub(crate) mod node_details;
|
||||
@@ -69,17 +69,6 @@ pub(crate) async fn execute(args: Cli) {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_validators(raw: &str) -> Vec<Url> {
|
||||
raw.split(',')
|
||||
.map(|raw_validator| {
|
||||
raw_validator
|
||||
.trim()
|
||||
.parse()
|
||||
.expect("one of the provided validator api urls is invalid")
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
|
||||
let mut was_host_overridden = false;
|
||||
if let Some(host) = args.host {
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-mixnode"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
|
||||
@@ -6,9 +6,11 @@ use std::process;
|
||||
use crate::{config::Config, Cli};
|
||||
use clap::Subcommand;
|
||||
use colored::Colorize;
|
||||
use config::defaults::var_names::{API_VALIDATOR, BECH32_PREFIX, CONFIGURED};
|
||||
use config::{
|
||||
defaults::var_names::{API_VALIDATOR, BECH32_PREFIX, CONFIGURED},
|
||||
parse_validators,
|
||||
};
|
||||
use crypto::bech32_address_validation;
|
||||
use url::Url;
|
||||
|
||||
mod describe;
|
||||
mod init;
|
||||
@@ -61,17 +63,6 @@ pub(crate) async fn execute(args: Cli) {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_validators(raw: &str) -> Vec<Url> {
|
||||
raw.split(',')
|
||||
.map(|raw_validator| {
|
||||
raw_validator
|
||||
.trim()
|
||||
.parse()
|
||||
.expect("one of the provided validator api urls is invalid")
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn override_config(mut config: Config, args: OverrideConfig) -> Config {
|
||||
let mut was_host_overridden = false;
|
||||
if let Some(host) = args.host {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
## [nym-connect-v1.0.1](https://github.com/nymtech/nym/tree/nym-connect-v1.0.1) (2022-07-22)
|
||||
|
||||
### Added
|
||||
|
||||
- nym-connect: initial proof-of-concept of a UI around the socks5 client was added
|
||||
- nym-connect: add ability to select network requester and gateway ([#1427])
|
||||
- nym-connect: add ability to export gateway keys as JSON
|
||||
- nym-connect: add auto updater
|
||||
|
||||
### Changed
|
||||
|
||||
- nym-connect: reuse config id instead of creating a new id on each connection
|
||||
|
||||
[#1427]: https://github.com/nymtech/nym/pull/1427
|
||||
@@ -7,7 +7,7 @@ use tokio::sync::RwLock;
|
||||
|
||||
use client_core::config::Config as BaseConfig;
|
||||
use config_common::NymConfig;
|
||||
use nym_socks5::{client::config::Config as Socks5Config, commands::parse_validators};
|
||||
use nym_socks5::client::config::Config as Socks5Config;
|
||||
|
||||
use crate::{
|
||||
error::{BackendError, Result},
|
||||
@@ -134,10 +134,11 @@ pub async fn init_socks5_config(provider_address: String, chosen_gateway_id: Str
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY);
|
||||
|
||||
if let Ok(raw_validators) = std::env::var(config_common::defaults::var_names::API_VALIDATOR) {
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_validator_apis(parse_validators(&raw_validators));
|
||||
.set_custom_validator_apis(config_common::parse_validators(&raw_validators));
|
||||
}
|
||||
|
||||
let gateway = setup_gateway(
|
||||
|
||||
@@ -1 +1 @@
|
||||
ADMIN_ADDRESS={"MAINNET":[],"SANDBOX":[],"QA":["n1c8te4wlc25re97qw5nh8urtpek494zqx30lza2","n177krl498arsfupd2p9097kwfmuf07lkj6crmj9"]}
|
||||
ADMIN_ADDRESS={\"MAINNET\":[],\"SANDBOX\":[],\"QA\":[\"n1c8te4wlc25re97qw5nh8urtpek494zqx30lza2\",\"n177krl498arsfupd2p9097kwfmuf07lkj6crmj9\"]}
|
||||
@@ -0,0 +1,213 @@
|
||||
## [nym-wallet-v1.0.8](https://github.com/nymtech/nym/releases/tag/nym-wallet-v1.0.8) (2022-08-11)
|
||||
|
||||
- wallet: new bonding flow and screen for bonded node
|
||||
- wallet: compound and redeem functionalities for operator rewards
|
||||
- wallet: a few minor touch ups and bug fixes
|
||||
|
||||
|
||||
## [nym-wallet-v1.0.7](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.7) (2022-07-11)
|
||||
|
||||
- wallet: dark mode
|
||||
- wallet: when simulating gas costs, an automatic adjustment is being used ([#1388]).
|
||||
|
||||
[#1388]: https://github.com/nymtech/nym/pull/1388
|
||||
|
||||
|
||||
## [nym-wallet-v1.0.6](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.6) (2022-06-21)
|
||||
|
||||
- wallet: undelegating now uses either the mixnet or vesting contract, or both, depending on how delegations were made
|
||||
- wallet: redeeming and compounding now uses both the mixnet and vesting contract
|
||||
- wallet: the wallet backend learned how to archive wallet files
|
||||
- wallet: add ENABLE_QA_MODE environment variable to enable QA mode on built wallet
|
||||
|
||||
## [nym-wallet-v1.0.5](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.5) (2022-06-14)
|
||||
|
||||
- wallet: add simple CLI tool for decrypting and recovering the wallet file.
|
||||
- wallet: added support for multiple accounts ([#1265])
|
||||
- wallet: compound and claim reward endpoints for operators and delegators ([#1302])
|
||||
- wallet: require password to switch accounts
|
||||
- wallet: the wallet backend learned how to keep track of validator name, either hardcoded or by querying the status endpoint.
|
||||
- wallet: new delegation and rewards UI
|
||||
- wallet: show version in nav bar
|
||||
- wallet: contract admin route put back
|
||||
- wallet: staking_supply field to StateParams
|
||||
- wallet: show transaction hash for redeeming or compounding rewards
|
||||
|
||||
[#1265]: https://github.com/nymtech/nym/pull/1265
|
||||
[#1302]: https://github.com/nymtech/nym/pull/1302
|
||||
|
||||
## [nym-wallet-v1.0.4](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.4) (2022-05-04)
|
||||
|
||||
### Changed
|
||||
|
||||
- all: the default behaviour of validator client is changed to use `broadcast_sync` and poll for transaction inclusion instead of using `broadcast_commit` to deal with timeouts ([#1246])
|
||||
|
||||
## [nym-wallet-v1.0.3](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.3) (2022-04-25)
|
||||
|
||||
[Full Changelog](https://github.com/nymtech/nym/compare/nym-binaries-1.0.0-rc.2...nym-wallet-v1.0.3)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- \[Issue\] Wallet 1.0.2 cannot send NYM tokens from a DelayedVestingAccount [\#1215](https://github.com/nymtech/nym/issues/1215)
|
||||
- Main README not showing properly with GitHub dark mode [\#1211](https://github.com/nymtech/nym/issues/1211)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Bugfix - wallet undelegation for vesting accounts [\#1220](https://github.com/nymtech/nym/pull/1220) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Bugfix/delegation reconcile [\#1219](https://github.com/nymtech/nym/pull/1219) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Bugfix/query proxied pending delegations [\#1218](https://github.com/nymtech/nym/pull/1218) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Using custom gas multiplier in the wallet [\#1217](https://github.com/nymtech/nym/pull/1217) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/vesting accounts support [\#1216](https://github.com/nymtech/nym/pull/1216) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Release/1.0.0 rc.2 [\#1214](https://github.com/nymtech/nym/pull/1214) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- chore: fix dark mode rendering [\#1212](https://github.com/nymtech/nym/pull/1212) ([pwnfoo](https://github.com/pwnfoo))
|
||||
- Feature/spend coconut [\#1210](https://github.com/nymtech/nym/pull/1210) ([neacsu](https://github.com/neacsu))
|
||||
- Bugfix/unique sphinx key [\#1207](https://github.com/nymtech/nym/pull/1207) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Add cache read and write timeouts [\#1206](https://github.com/nymtech/nym/pull/1206) ([durch](https://github.com/durch))
|
||||
- Additional, more informative routes [\#1204](https://github.com/nymtech/nym/pull/1204) ([durch](https://github.com/durch))
|
||||
- Feature/aggregated econ dynamics explorer endpoint [\#1203](https://github.com/nymtech/nym/pull/1203) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Debugging validator [\#1198](https://github.com/nymtech/nym/pull/1198) ([durch](https://github.com/durch))
|
||||
- wallet: expose additional validator configuration functionality to the frontend [\#1195](https://github.com/nymtech/nym/pull/1195) ([octol](https://github.com/octol))
|
||||
- Update rewarding validator address [\#1193](https://github.com/nymtech/nym/pull/1193) ([durch](https://github.com/durch))
|
||||
- Crypto part of the Groth's NIDKG [\#1182](https://github.com/nymtech/nym/pull/1182) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- fix unbond page [\#1180](https://github.com/nymtech/nym/pull/1180) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Type safe bounds [\#1179](https://github.com/nymtech/nym/pull/1179) ([durch](https://github.com/durch))
|
||||
- Fix delegation paging [\#1174](https://github.com/nymtech/nym/pull/1174) ([durch](https://github.com/durch))
|
||||
- Update binaries to rc version [\#1172](https://github.com/nymtech/nym/pull/1172) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Bump ansi-regex from 4.1.0 to 4.1.1 in /docker/typescript\_client/upload\_contract [\#1171](https://github.com/nymtech/nym/pull/1171) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
|
||||
## [nym-wallet-v1.0.2](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.2) (2022-04-05)
|
||||
|
||||
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.1...nym-wallet-v1.0.2)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Wallet 1.0.2 visual tweaks [\#1197](https://github.com/nymtech/nym/pull/1197) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Password for wallet with routes [\#1196](https://github.com/nymtech/nym/pull/1196) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Add auto-updater to Nym Wallet [\#1194](https://github.com/nymtech/nym/pull/1194) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Fix clippy warnings for beta toolchain [\#1191](https://github.com/nymtech/nym/pull/1191) ([octol](https://github.com/octol))
|
||||
- wallet: expose validator urls to the frontend [\#1190](https://github.com/nymtech/nym/pull/1190) ([octol](https://github.com/octol))
|
||||
- wallet: add test for decrypting stored wallet file [\#1189](https://github.com/nymtech/nym/pull/1189) ([octol](https://github.com/octol))
|
||||
- Fix clippy warnings [\#1188](https://github.com/nymtech/nym/pull/1188) ([octol](https://github.com/octol))
|
||||
- Password for wallet with routes [\#1187](https://github.com/nymtech/nym/pull/1187) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- wallet: add validate\_mnemonic [\#1186](https://github.com/nymtech/nym/pull/1186) ([octol](https://github.com/octol))
|
||||
- wallet: support removing accounts from the wallet file [\#1185](https://github.com/nymtech/nym/pull/1185) ([octol](https://github.com/octol))
|
||||
- Feature/adding discord [\#1184](https://github.com/nymtech/nym/pull/1184) ([gala1234](https://github.com/gala1234))
|
||||
- wallet: config backend for validator selection [\#1183](https://github.com/nymtech/nym/pull/1183) ([octol](https://github.com/octol))
|
||||
- Add storybook to wallet [\#1178](https://github.com/nymtech/nym/pull/1178) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- wallet: connection test nymd and api urls independently [\#1170](https://github.com/nymtech/nym/pull/1170) ([octol](https://github.com/octol))
|
||||
- wallet: wire up account storage [\#1153](https://github.com/nymtech/nym/pull/1153) ([octol](https://github.com/octol))
|
||||
- Feature/signature on deposit [\#1151](https://github.com/nymtech/nym/pull/1151) ([neacsu](https://github.com/neacsu))
|
||||
|
||||
## [nym-wallet-v1.0.1](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.1) (2022-04-05)
|
||||
|
||||
[Full Changelog](https://github.com/nymtech/nym/compare/nym-binaries-1.0.0-rc.1...nym-wallet-v1.0.1)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Check enabling bbbc simultaneously with open access. Estimate what it would take to make this the default compilation target. [\#1175](https://github.com/nymtech/nym/issues/1175)
|
||||
- Get coconut credential for deposited tokens [\#1138](https://github.com/nymtech/nym/issues/1138)
|
||||
- Make payments lazy [\#1135](https://github.com/nymtech/nym/issues/1135)
|
||||
- Uptime on node selection for sets [\#1049](https://github.com/nymtech/nym/issues/1049)
|
||||
|
||||
## [nym-wallet-v1.0.0](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.0) (2022-02-03)
|
||||
|
||||
[Full Changelog](https://github.com/nymtech/nym/compare/v0.12.1...nym-wallet-v1.0.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- \[Feature Request\] Please enable registration without need for Telegram account [\#1016](https://github.com/nymtech/nym/issues/1016)
|
||||
- Fast mixnode launch with a pre-built ISO + VM software [\#1001](https://github.com/nymtech/nym/issues/1001)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- \[Issue\] [\#1000](https://github.com/nymtech/nym/issues/1000)
|
||||
- \[Issue\] `nym-client` requires multiple attempts to run a server [\#869](https://github.com/nymtech/nym/issues/869)
|
||||
- De-'float'-ing `Interval` \(`Display` impl + `serde`\) [\#1065](https://github.com/nymtech/nym/pull/1065) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- display client address on wallet creation [\#1058](https://github.com/nymtech/nym/pull/1058) ([fmtabbara](https://github.com/fmtabbara))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Rewarded set inclusion probability API endpoint [\#1037](https://github.com/nymtech/nym/issues/1037)
|
||||
- Update cw-storage-plus to 0.11 [\#1032](https://github.com/nymtech/nym/issues/1032)
|
||||
- Change `u128` fields in `RewardEstimationResponse` to `u64` [\#1029](https://github.com/nymtech/nym/issues/1029)
|
||||
- Test out the mainnet Gravity Bridge [\#1006](https://github.com/nymtech/nym/issues/1006)
|
||||
- Add vesting contract interface to nym-wallet [\#959](https://github.com/nymtech/nym/issues/959)
|
||||
- Mixnode crash [\#486](https://github.com/nymtech/nym/issues/486)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- create custom urls for mainnet [\#1095](https://github.com/nymtech/nym/pull/1095) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Wallet signing on MacOS [\#1093](https://github.com/nymtech/nym/pull/1093) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Fix rust 2018 idioms warnings [\#1092](https://github.com/nymtech/nym/pull/1092) ([octol](https://github.com/octol))
|
||||
- Prevent contract overwriting [\#1090](https://github.com/nymtech/nym/pull/1090) ([durch](https://github.com/durch))
|
||||
- Logout operation [\#1087](https://github.com/nymtech/nym/pull/1087) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Update to rust edition 2021 everywhere [\#1086](https://github.com/nymtech/nym/pull/1086) ([octol](https://github.com/octol))
|
||||
- Tag contract errors, and print out lines for easier QA [\#1084](https://github.com/nymtech/nym/pull/1084) ([durch](https://github.com/durch))
|
||||
- Feature/flexible vesting + utility queries [\#1083](https://github.com/nymtech/nym/pull/1083) ([durch](https://github.com/durch))
|
||||
- Bump @openzeppelin/contracts from 4.3.1 to 4.4.2 in /contracts/basic-bandwidth-generation [\#1082](https://github.com/nymtech/nym/pull/1082) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump nth-check from 2.0.0 to 2.0.1 in /clients/native/examples/js-examples/websocket [\#1081](https://github.com/nymtech/nym/pull/1081) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump url-parse from 1.5.1 to 1.5.4 in /clients/native/examples/js-examples/websocket [\#1080](https://github.com/nymtech/nym/pull/1080) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump follow-redirects from 1.14.1 to 1.14.7 in /clients/native/examples/js-examples/websocket [\#1079](https://github.com/nymtech/nym/pull/1079) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump nanoid from 3.1.23 to 3.2.0 in /clients/native/examples/js-examples/websocket [\#1078](https://github.com/nymtech/nym/pull/1078) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Setup basic test for mixnode stats reporting [\#1077](https://github.com/nymtech/nym/pull/1077) ([octol](https://github.com/octol))
|
||||
- Make wallet\_address mandatory for mixnode init [\#1076](https://github.com/nymtech/nym/pull/1076) ([octol](https://github.com/octol))
|
||||
- Tidy nym-mixnode module visibility [\#1075](https://github.com/nymtech/nym/pull/1075) ([octol](https://github.com/octol))
|
||||
- Feature/wallet login with password [\#1074](https://github.com/nymtech/nym/pull/1074) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Add trait to mock client dependency in DelayForwarder [\#1073](https://github.com/nymtech/nym/pull/1073) ([octol](https://github.com/octol))
|
||||
- Bump rust-version to latest stable for nym-mixnode [\#1072](https://github.com/nymtech/nym/pull/1072) ([octol](https://github.com/octol))
|
||||
- Fixes CI for our wasm build [\#1069](https://github.com/nymtech/nym/pull/1069) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Add @octol as codeowner [\#1068](https://github.com/nymtech/nym/pull/1068) ([octol](https://github.com/octol))
|
||||
- set-up inclusion probability [\#1067](https://github.com/nymtech/nym/pull/1067) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Feature/wasm client [\#1066](https://github.com/nymtech/nym/pull/1066) ([neacsu](https://github.com/neacsu))
|
||||
- Changed bech32\_prefix from punk to nymt [\#1064](https://github.com/nymtech/nym/pull/1064) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Bump nanoid from 3.1.30 to 3.2.0 in /testnet-faucet [\#1063](https://github.com/nymtech/nym/pull/1063) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump nanoid from 3.1.30 to 3.2.0 in /nym-wallet [\#1062](https://github.com/nymtech/nym/pull/1062) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Rework vesting contract storage [\#1061](https://github.com/nymtech/nym/pull/1061) ([durch](https://github.com/durch))
|
||||
- Mixnet Contract constants extraction [\#1060](https://github.com/nymtech/nym/pull/1060) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- fix: make explorer footer year dynamic [\#1059](https://github.com/nymtech/nym/pull/1059) ([martinyung](https://github.com/martinyung))
|
||||
- Add mnemonic just on creation, to display it [\#1057](https://github.com/nymtech/nym/pull/1057) ([neacsu](https://github.com/neacsu))
|
||||
- Network Explorer: updates to API and UI to show the active set [\#1056](https://github.com/nymtech/nym/pull/1056) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Made contract addresses for query NymdClient construction optional [\#1055](https://github.com/nymtech/nym/pull/1055) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Introduced RPC query for total token supply [\#1053](https://github.com/nymtech/nym/pull/1053) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/tokio console [\#1052](https://github.com/nymtech/nym/pull/1052) ([durch](https://github.com/durch))
|
||||
- Implemented beta clippy lint recommendations [\#1051](https://github.com/nymtech/nym/pull/1051) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- add new function to update profit percentage [\#1050](https://github.com/nymtech/nym/pull/1050) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Upgrade Clap and use declarative argument parsing for nym-mixnode [\#1047](https://github.com/nymtech/nym/pull/1047) ([octol](https://github.com/octol))
|
||||
- Feature/additional bond validation [\#1046](https://github.com/nymtech/nym/pull/1046) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Fix clippy on relevant lints [\#1044](https://github.com/nymtech/nym/pull/1044) ([neacsu](https://github.com/neacsu))
|
||||
- Bump shelljs from 0.8.4 to 0.8.5 in /contracts/basic-bandwidth-generation [\#1043](https://github.com/nymtech/nym/pull/1043) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Endpoint for rewarded set inclusion probabilities [\#1042](https://github.com/nymtech/nym/pull/1042) ([durch](https://github.com/durch))
|
||||
- Bump follow-redirects from 1.14.4 to 1.14.7 in /contracts/basic-bandwidth-generation [\#1041](https://github.com/nymtech/nym/pull/1041) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump follow-redirects from 1.14.5 to 1.14.7 in /testnet-faucet [\#1040](https://github.com/nymtech/nym/pull/1040) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Feature/node settings update [\#1036](https://github.com/nymtech/nym/pull/1036) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Migrate to cw-storage-plus 0.11.1 [\#1035](https://github.com/nymtech/nym/pull/1035) ([durch](https://github.com/durch))
|
||||
- Bump @openzeppelin/contracts from 4.4.1 to 4.4.2 in /contracts/basic-bandwidth-generation [\#1034](https://github.com/nymtech/nym/pull/1034) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Feature/configurable wallet [\#1033](https://github.com/nymtech/nym/pull/1033) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/downcast reward estimation [\#1031](https://github.com/nymtech/nym/pull/1031) ([durch](https://github.com/durch))
|
||||
- Wallet UI updates [\#1028](https://github.com/nymtech/nym/pull/1028) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Remove migration code [\#1027](https://github.com/nymtech/nym/pull/1027) ([neacsu](https://github.com/neacsu))
|
||||
- Chore/stricter dependency requirements [\#1025](https://github.com/nymtech/nym/pull/1025) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/validator api client endpoints [\#1024](https://github.com/nymtech/nym/pull/1024) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Updated cosmrs to 0.4.1 [\#1023](https://github.com/nymtech/nym/pull/1023) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/testnet deploy scripts [\#1022](https://github.com/nymtech/nym/pull/1022) ([mfahampshire](https://github.com/mfahampshire))
|
||||
- Changed wallet's client to a full validator client [\#1021](https://github.com/nymtech/nym/pull/1021) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Fix 404 link [\#1020](https://github.com/nymtech/nym/pull/1020) ([RiccardoMasutti](https://github.com/RiccardoMasutti))
|
||||
- Feature/additional mixnode endpoints [\#1019](https://github.com/nymtech/nym/pull/1019) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Introduced denom check when trying to withdraw vested coins [\#1018](https://github.com/nymtech/nym/pull/1018) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Add network defaults for qa [\#1017](https://github.com/nymtech/nym/pull/1017) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/expanded events [\#1015](https://github.com/nymtech/nym/pull/1015) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- update frontend to use new profit update api [\#1014](https://github.com/nymtech/nym/pull/1014) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Feature/node state endpoint [\#1013](https://github.com/nymtech/nym/pull/1013) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/hourly set updates [\#1012](https://github.com/nymtech/nym/pull/1012) ([durch](https://github.com/durch))
|
||||
- Feature/remove unused profit margin [\#1011](https://github.com/nymtech/nym/pull/1011) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/explorer node status [\#1010](https://github.com/nymtech/nym/pull/1010) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Use serial integer instead of random [\#1009](https://github.com/nymtech/nym/pull/1009) ([durch](https://github.com/durch))
|
||||
- Feature/configure profit [\#1008](https://github.com/nymtech/nym/pull/1008) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/fix gateway sign [\#1004](https://github.com/nymtech/nym/pull/1004) ([neacsu](https://github.com/neacsu))
|
||||
- Fix clippy [\#1003](https://github.com/nymtech/nym/pull/1003) ([neacsu](https://github.com/neacsu))
|
||||
- Update wallet version [\#998](https://github.com/nymtech/nym/pull/998) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Fix wallet build instructions [\#997](https://github.com/nymtech/nym/pull/997) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Make the separation between testnet-mode and erc20 bandwidth mode clearer [\#994](https://github.com/nymtech/nym/pull/994) ([neacsu](https://github.com/neacsu))
|
||||
- Bump @openzeppelin/contracts from 3.4.0 to 4.4.1 in /contracts/basic-bandwidth-generation [\#983](https://github.com/nymtech/nym/pull/983) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Feature/implicit runtime [\#973](https://github.com/nymtech/nym/pull/973) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Differentiate staking and ownership [\#961](https://github.com/nymtech/nym/pull/961) ([durch](https://github.com/durch))
|
||||
Generated
+87
-10
@@ -3273,7 +3273,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
"parking_lot_core 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3290,6 +3300,19 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec 1.8.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.3.2"
|
||||
@@ -4550,6 +4573,15 @@ dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "1.3.2"
|
||||
@@ -4635,9 +4667,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "state"
|
||||
version = "0.5.2"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87cf4f5369e6d3044b5e365c9690f451516ac8f0954084622b49ea3fde2f6de5"
|
||||
checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b"
|
||||
dependencies = [
|
||||
"loom",
|
||||
]
|
||||
@@ -4656,7 +4688,7 @@ checksum = "33994d0838dc2d152d17a62adf608a869b5e846b65b389af7f3dbc1de45c5b26"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"new_debug_unreachable",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.2",
|
||||
"phf_shared 0.10.0",
|
||||
"precomputed-hash",
|
||||
"serde",
|
||||
@@ -4838,7 +4870,7 @@ dependencies = [
|
||||
"ndk-glue",
|
||||
"ndk-sys",
|
||||
"objc",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.2",
|
||||
"raw-window-handle",
|
||||
"scopeguard",
|
||||
"serde",
|
||||
@@ -5241,7 +5273,9 @@ dependencies = [
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"once_cell",
|
||||
"parking_lot 0.12.1",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"winapi",
|
||||
@@ -5898,11 +5932,11 @@ version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b749ebd2304aa012c5992d11a25d07b406bdbe5f79d371cb7a918ce501a19eb0"
|
||||
dependencies = [
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_msvc",
|
||||
"windows_aarch64_msvc 0.30.0",
|
||||
"windows_i686_gnu 0.30.0",
|
||||
"windows_i686_msvc 0.30.0",
|
||||
"windows_x86_64_gnu 0.30.0",
|
||||
"windows_x86_64_msvc 0.30.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5915,12 +5949,31 @@ dependencies = [
|
||||
"windows_reader",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
||||
dependencies = [
|
||||
"windows_aarch64_msvc 0.36.1",
|
||||
"windows_i686_gnu 0.36.1",
|
||||
"windows_i686_msvc 0.36.1",
|
||||
"windows_x86_64_gnu 0.36.1",
|
||||
"windows_x86_64_msvc 0.36.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||
|
||||
[[package]]
|
||||
name = "windows_gen"
|
||||
version = "0.30.0"
|
||||
@@ -5937,12 +5990,24 @@ version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||
|
||||
[[package]]
|
||||
name = "windows_macros"
|
||||
version = "0.30.0"
|
||||
@@ -5973,12 +6038,24 @@ version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.7.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym_wallet"
|
||||
version = "1.0.7"
|
||||
version = "1.0.8"
|
||||
description = "Nym Native Wallet"
|
||||
authors = ["Nym Technologies SA"]
|
||||
license = ""
|
||||
@@ -32,14 +32,14 @@ log = "0.4"
|
||||
once_cell = "1.7.2"
|
||||
pretty_env_logger = "0.4"
|
||||
rand = "0.6.5"
|
||||
reqwest = "0.11.9"
|
||||
reqwest = {version = "0.11.9", features = ["json"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
strum = { version = "0.23", features = ["derive"] }
|
||||
tauri = { version = "=1.0.0-rc.2", features = ["clipboard-all", "shell-open", "updater", "window-maximize"] }
|
||||
tendermint-rpc = "0.23.0"
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1.10", features = ["sync", "time"] }
|
||||
tokio = { version = "1.10", features = ["full"] }
|
||||
toml = "0.5.8"
|
||||
url = "2.2"
|
||||
|
||||
|
||||
@@ -58,6 +58,8 @@ fn main() {
|
||||
mixnet::bond::unbond_gateway,
|
||||
mixnet::bond::unbond_mixnode,
|
||||
mixnet::bond::update_mixnode,
|
||||
mixnet::bond::get_number_of_mixnode_delegators,
|
||||
mixnet::bond::get_mix_node_description,
|
||||
mixnet::delegate::delegate_to_mixnode,
|
||||
mixnet::delegate::get_delegator_rewards,
|
||||
mixnet::delegate::get_pending_delegation_events,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::error::BackendError;
|
||||
use crate::state::WalletState;
|
||||
use crate::{Gateway, MixNode};
|
||||
@@ -5,8 +7,18 @@ use nym_types::currency::DecCoin;
|
||||
use nym_types::gateway::GatewayBond;
|
||||
use nym_types::mixnode::MixNodeBond;
|
||||
use nym_types::transaction::TransactionExecuteResult;
|
||||
use reqwest::Error as ReqwestError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use validator_client::nymd::{Coin, Fee};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct NodeDescription {
|
||||
name: String,
|
||||
description: String,
|
||||
link: String,
|
||||
location: String,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn bond_gateway(
|
||||
gateway: Gateway,
|
||||
@@ -198,3 +210,51 @@ pub async fn get_operator_rewards(
|
||||
);
|
||||
Ok(display_coin)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_number_of_mixnode_delegators(
|
||||
identity: String,
|
||||
state: tauri::State<'_, WalletState>,
|
||||
) -> Result<usize, BackendError> {
|
||||
let guard = state.read().await;
|
||||
let client = guard.current_client()?;
|
||||
let paged_delegations = client
|
||||
.nymd
|
||||
.get_mix_delegations_paged(identity, None, Some(20))
|
||||
.await?;
|
||||
|
||||
Ok(paged_delegations.delegations.len())
|
||||
}
|
||||
|
||||
async fn fetch_mix_node_description(
|
||||
host: &str,
|
||||
port: u16,
|
||||
) -> Result<NodeDescription, ReqwestError> {
|
||||
let milli_second = Duration::from_millis(1000);
|
||||
let client = reqwest::Client::builder().timeout(milli_second).build()?;
|
||||
let response = client
|
||||
.get(format!("http://{}:{}/description", host, port))
|
||||
.send()
|
||||
.await;
|
||||
|
||||
match response {
|
||||
Ok(res) => {
|
||||
let json = res.json::<NodeDescription>().await;
|
||||
match json {
|
||||
Ok(json) => Ok(json),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_mix_node_description(
|
||||
host: &str,
|
||||
port: u16,
|
||||
) -> Result<NodeDescription, BackendError> {
|
||||
fetch_mix_node_description(host, port)
|
||||
.await
|
||||
.map_err(|e| BackendError::ReqwestError { source: e })
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"package": {
|
||||
"productName": "nym-wallet",
|
||||
"version": "1.0.7"
|
||||
"version": "1.0.8"
|
||||
},
|
||||
"build": {
|
||||
"distDir": "../dist",
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import React from 'react';
|
||||
import { Avatar } from '@mui/material';
|
||||
import { Avatar, Typography } from '@mui/material';
|
||||
import stc from 'string-to-color';
|
||||
import { TAccount } from 'src/types';
|
||||
|
||||
export const AccountAvatar = ({ name }: Pick<TAccount, 'name'>) => (
|
||||
<Avatar sx={{ bgcolor: stc(name), width: 20, height: 20 }}>{name?.split('')[0]}</Avatar>
|
||||
export const AccountAvatar = ({ name, small }: { name: TAccount['name']; small?: boolean }) => (
|
||||
<Avatar sx={{ bgcolor: stc(name), ...(small ? { width: 25, height: 25 } : {}) }}>
|
||||
<Typography fontSize={small ? 14 : 'inherit'} fontWeight={600}>
|
||||
{name?.split('')[0]}
|
||||
</Typography>
|
||||
</Avatar>
|
||||
);
|
||||
|
||||
@@ -5,10 +5,10 @@ import { AccountAvatar } from './AccountAvatar';
|
||||
|
||||
export const AccountOverview = ({ account, onClick }: { account: AccountEntry; onClick: () => void }) => (
|
||||
<Button
|
||||
startIcon={<AccountAvatar name={account.id} />}
|
||||
startIcon={<AccountAvatar name={account.id} small />}
|
||||
sx={{ color: 'text.primary' }}
|
||||
color="inherit"
|
||||
onClick={onClick}
|
||||
disableRipple
|
||||
>
|
||||
{account.id}
|
||||
</Button>
|
||||
|
||||
@@ -20,7 +20,7 @@ export const MultiAccountHowTo = ({ show, handleClose }: { show: boolean; handle
|
||||
<Close />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Typography variant="body1" sx={{ color: (t) => t.palette.nym.text.muted }}>
|
||||
<Typography variant="body1" sx={{ color: (theme) => theme.palette.nym.text.muted }}>
|
||||
How to set up multiple accounts
|
||||
</Typography>
|
||||
</DialogTitle>
|
||||
@@ -29,7 +29,7 @@ export const MultiAccountHowTo = ({ show, handleClose }: { show: boolean; handle
|
||||
<Alert
|
||||
severity="warning"
|
||||
icon={false}
|
||||
sx={(t) => (t.palette.mode === 'dark' ? { bgcolor: (t) => t.palette.background.paper } : {})}
|
||||
sx={(t) => (t.palette.mode === 'dark' ? { bgcolor: (theme) => theme.palette.background.paper } : {})}
|
||||
>
|
||||
<Typography>In order to create multiple accounts your wallet needs a password.</Typography>
|
||||
<Typography>Follow steps below to create password.</Typography>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
DialogTitle,
|
||||
IconButton,
|
||||
Typography,
|
||||
Divider,
|
||||
} from '@mui/material';
|
||||
import { Add, ArrowDownwardSharp, Close } from '@mui/icons-material';
|
||||
import { AccountsContext } from 'src/context';
|
||||
@@ -51,7 +52,7 @@ export const AccountsModal = () => {
|
||||
<Close />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Typography variant="body1" sx={{ color: (theme) => theme.palette.text.disabled }}>
|
||||
<Typography fontSize="small" sx={{ color: 'grey.600' }}>
|
||||
Switch between accounts
|
||||
</Typography>
|
||||
</DialogTitle>
|
||||
@@ -69,6 +70,7 @@ export const AccountsModal = () => {
|
||||
/>
|
||||
))}
|
||||
</DialogContent>
|
||||
<Divider variant="middle" sx={{ mt: 3 }} />
|
||||
<DialogActions sx={{ p: 3 }}>
|
||||
<Button startIcon={<ArrowDownwardSharp />} onClick={() => setDialogToDisplay('Import')}>
|
||||
Import account
|
||||
|
||||
@@ -7,17 +7,16 @@ import {
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
IconButton,
|
||||
TextField,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { ArrowBackSharp } from '@mui/icons-material';
|
||||
import { useClipboard } from 'use-clipboard-copy';
|
||||
import { createMnemonic, validateMnemonic } from 'src/requests';
|
||||
import { Console } from 'src/utils/console';
|
||||
import { AccountsContext } from 'src/context';
|
||||
import { ConfirmPassword, Mnemonic } from 'src/components';
|
||||
import { MnemonicInput } from 'src/components/textfields';
|
||||
import { StyledBackButton } from 'src/components/StyledBackButton';
|
||||
|
||||
const createAccountSteps = [
|
||||
'Copy and save mnemonic for your new account',
|
||||
@@ -30,14 +29,15 @@ const importAccountSteps = [
|
||||
'Confirm the password used to login to your wallet',
|
||||
];
|
||||
|
||||
const MnemonicStep = ({ mnemonic, onNext }: { mnemonic: string; onNext: () => void }) => {
|
||||
const MnemonicStep = ({ mnemonic, onNext, onBack }: { mnemonic: string; onNext: () => void; onBack: () => void }) => {
|
||||
const { copy, copied } = useClipboard({ copiedTimeout: 5000 });
|
||||
return (
|
||||
<Box sx={{ mt: 1 }}>
|
||||
<DialogContent>
|
||||
<Mnemonic mnemonic={mnemonic} handleCopy={copy} copied={copied} />
|
||||
</DialogContent>
|
||||
<DialogActions sx={{ p: 3, pt: 0 }}>
|
||||
<DialogActions sx={{ p: 3, pt: 0, gap: 2 }}>
|
||||
<StyledBackButton onBack={onBack} />
|
||||
<Button disabled={!copied} fullWidth disableElevation variant="contained" size="large" onClick={onNext}>
|
||||
I saved my mnemonic
|
||||
</Button>
|
||||
@@ -50,10 +50,12 @@ const ImportMnemonic = ({
|
||||
value,
|
||||
onChange,
|
||||
onNext,
|
||||
onBack,
|
||||
}: {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
onNext: () => void;
|
||||
onBack: () => void;
|
||||
}) => {
|
||||
const [error, setError] = useState<string>();
|
||||
|
||||
@@ -77,7 +79,8 @@ const ImportMnemonic = ({
|
||||
}}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions sx={{ p: 3, pt: 0 }}>
|
||||
<DialogActions sx={{ p: 3, pt: 0, gap: 2 }}>
|
||||
<StyledBackButton onBack={onBack} />
|
||||
<Button
|
||||
disabled={value.length === 0}
|
||||
fullWidth
|
||||
@@ -93,7 +96,7 @@ const ImportMnemonic = ({
|
||||
);
|
||||
};
|
||||
|
||||
const NameAccount = ({ onNext }: { onNext: (value: string) => void }) => {
|
||||
const NameAccount = ({ onNext, onBack }: { onNext: (value: string) => void; onBack: () => void }) => {
|
||||
const [value, setValue] = useState('');
|
||||
const [error, setError] = useState<string>();
|
||||
|
||||
@@ -121,7 +124,8 @@ const NameAccount = ({ onNext }: { onNext: (value: string) => void }) => {
|
||||
fullWidth
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions sx={{ p: 3, pt: 0 }}>
|
||||
<DialogActions sx={{ p: 3, pt: 0, gap: 2 }}>
|
||||
<StyledBackButton onBack={onBack} />
|
||||
<Button
|
||||
disabled={!value.length}
|
||||
fullWidth
|
||||
@@ -178,9 +182,6 @@ export const AddAccountModal = () => {
|
||||
<DialogTitle sx={{ pb: 0 }}>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="h6">{`${dialogToDisplay} new account`}</Typography>
|
||||
<IconButton onClick={() => (step === 0 ? handleClose() : setStep((s) => s - 1))}>
|
||||
<ArrowBackSharp />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Typography sx={{ mt: 2 }}>
|
||||
{dialogToDisplay === 'Add' ? createAccountSteps[step] : importAccountSteps[step]}
|
||||
@@ -190,12 +191,17 @@ export const AddAccountModal = () => {
|
||||
switch (step) {
|
||||
case 0:
|
||||
return dialogToDisplay === 'Add' ? (
|
||||
<MnemonicStep mnemonic={data.mnemonic} onNext={() => setStep((s) => s + 1)} />
|
||||
<MnemonicStep
|
||||
mnemonic={data.mnemonic}
|
||||
onNext={() => setStep((s) => s + 1)}
|
||||
onBack={() => (step === 0 ? handleClose() : setStep((s) => s - 1))}
|
||||
/>
|
||||
) : (
|
||||
<ImportMnemonic
|
||||
value={data.mnemonic}
|
||||
onChange={(value) => setData((d) => ({ ...d, mnemonic: value }))}
|
||||
onNext={() => setStep((s) => s + 1)}
|
||||
onBack={() => (step === 0 ? handleClose() : setStep((s) => s - 1))}
|
||||
/>
|
||||
);
|
||||
case 1:
|
||||
@@ -205,6 +211,7 @@ export const AddAccountModal = () => {
|
||||
setData((d) => ({ ...d, accountName }));
|
||||
setStep((s) => s + 1);
|
||||
}}
|
||||
onBack={() => setStep((s) => s - 1)}
|
||||
/>
|
||||
);
|
||||
case 2:
|
||||
@@ -222,6 +229,7 @@ export const AddAccountModal = () => {
|
||||
}
|
||||
}
|
||||
}}
|
||||
onCancel={() => setStep((s) => s - 1)}
|
||||
isLoading={isLoading}
|
||||
error={error}
|
||||
/>
|
||||
|
||||
@@ -19,17 +19,18 @@ export const ConfirmPasswordModal = ({
|
||||
<Dialog open={Boolean(accountName)} onClose={onClose} fullWidth>
|
||||
<Paper>
|
||||
<DialogTitle>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="h6">Switch account</Typography>
|
||||
<IconButton onClick={onClose}>
|
||||
<ArrowBack />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Typography variant="body1" sx={{ color: (theme) => theme.palette.text.disabled }}>
|
||||
<Typography variant="h6">Switch account</Typography>
|
||||
<Typography fontSize="small" sx={{ color: 'grey.600' }}>
|
||||
Confirm password
|
||||
</Typography>
|
||||
</DialogTitle>
|
||||
<ConfirmPassword onConfirm={onConfirm} error={error} isLoading={isLoading} buttonTitle="Switch account" />
|
||||
<ConfirmPassword
|
||||
onConfirm={onConfirm}
|
||||
error={error}
|
||||
isLoading={isLoading}
|
||||
buttonTitle="Switch account"
|
||||
onCancel={onClose}
|
||||
/>
|
||||
</Paper>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
@@ -33,7 +33,7 @@ export const EditAccountModal = () => {
|
||||
<Close />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Typography variant="body1" sx={{ color: (theme) => theme.palette.text.disabled }}>
|
||||
<Typography fontSize="small" sx={{ color: 'grey.600' }}>
|
||||
New wallet address
|
||||
</Typography>
|
||||
</DialogTitle>
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
import React, { useContext, useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Paper,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
IconButton,
|
||||
TextField,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { Close } from '@mui/icons-material';
|
||||
import { AccountsContext } from 'src/context';
|
||||
|
||||
export const ImportAccountModal = () => {
|
||||
const [mnemonic, setMnemonic] = useState('');
|
||||
|
||||
const { dialogToDisplay, setDialogToDisplay, handleImportAccount } = useContext(AccountsContext);
|
||||
|
||||
const handleClose = () => {
|
||||
setMnemonic('');
|
||||
setDialogToDisplay('Accounts');
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={dialogToDisplay === 'Import'} onClose={handleClose} fullWidth>
|
||||
<Paper>
|
||||
<DialogTitle>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="h6">Import account</Typography>
|
||||
<IconButton onClick={handleClose}>
|
||||
<Close />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Typography variant="body1" sx={{ color: (theme) => theme.palette.text.disabled }}>
|
||||
Provide mnemonic of account you want to import
|
||||
</Typography>
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ p: 0 }}>
|
||||
<Box sx={{ px: 3, mt: 1 }}>
|
||||
<TextField
|
||||
placeholder="Paste or type your mnemonic here"
|
||||
fullWidth
|
||||
value={mnemonic}
|
||||
onChange={(e) => setMnemonic(e.target.value)}
|
||||
autoFocus
|
||||
multiline
|
||||
rows={3}
|
||||
/>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions sx={{ p: 3 }}>
|
||||
<Button
|
||||
fullWidth
|
||||
disableElevation
|
||||
variant="contained"
|
||||
size="large"
|
||||
onClick={() => handleImportAccount({ id: '', address: '' })}
|
||||
disabled={!mnemonic.length}
|
||||
>
|
||||
Import account
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Paper>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { MoreVertSharp } from '@mui/icons-material';
|
||||
import { IconButton, ListItemIcon, ListItemText, Menu, MenuItem } from '@mui/material';
|
||||
|
||||
export const ActionsMenu: React.FC<{ open: boolean; onOpen: () => void; onClose: () => void }> = ({
|
||||
children,
|
||||
open,
|
||||
onOpen,
|
||||
onClose,
|
||||
}) => {
|
||||
const anchorEl: any = useRef<HTMLElement>();
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton ref={anchorEl} onClick={onOpen}>
|
||||
<MoreVertSharp />
|
||||
</IconButton>
|
||||
<Menu anchorEl={anchorEl.current} open={open} onClose={onClose}>
|
||||
{children}
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const ActionsMenuItem = ({
|
||||
title,
|
||||
description,
|
||||
onClick,
|
||||
Icon,
|
||||
disabled,
|
||||
}: {
|
||||
title: string;
|
||||
description?: string;
|
||||
onClick?: () => void;
|
||||
Icon?: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
}) => (
|
||||
<MenuItem sx={{ p: 2 }} onClick={onClick} disabled={disabled}>
|
||||
<ListItemIcon sx={{ color: 'text.primary' }}>{Icon}</ListItemIcon>
|
||||
<ListItemText sx={{ color: 'text.primary' }} primary={title} secondary={description} />
|
||||
</MenuItem>
|
||||
);
|
||||
@@ -7,13 +7,11 @@ import ModeNightOutlinedIcon from '@mui/icons-material/ModeNightOutlined';
|
||||
import LightModeOutlinedIcon from '@mui/icons-material/LightModeOutlined';
|
||||
import { AppContext } from '../context/main';
|
||||
import { NetworkSelector } from './NetworkSelector';
|
||||
import { Node as NodeIcon } from '../svg-icons/node';
|
||||
import { MultiAccounts } from './Accounts';
|
||||
import { config } from '../config';
|
||||
|
||||
export const AppBar = () => {
|
||||
const { logOut, handleShowTerminal, appEnv, handleShowSettings, showSettings, mode, handleSwitchMode } =
|
||||
useContext(AppContext);
|
||||
const { logOut, handleShowTerminal, appEnv, mode, handleSwitchMode } = useContext(AppContext);
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
@@ -31,7 +29,7 @@ export const AppBar = () => {
|
||||
<Grid item container justifyContent="flex-end" md={12} lg={5} spacing={2}>
|
||||
<Grid item>
|
||||
<IconButton size="small" onClick={handleSwitchMode} sx={{ color: 'text.primary' }}>
|
||||
{mode === 'light' ? (
|
||||
{mode === 'dark' ? (
|
||||
<LightModeOutlinedIcon fontSize="small" />
|
||||
) : (
|
||||
<ModeNightOutlinedIcon fontSize="small" sx={{ transform: 'rotate(180deg)' }} />
|
||||
@@ -45,15 +43,6 @@ export const AppBar = () => {
|
||||
</IconButton>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid item>
|
||||
<IconButton
|
||||
onClick={handleShowSettings}
|
||||
sx={{ color: showSettings ? 'primary.main' : 'text.primary' }}
|
||||
size="small"
|
||||
>
|
||||
<NodeIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<IconButton
|
||||
size="small"
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { Logout } from '@mui/icons-material';
|
||||
import TerminalIcon from '@mui/icons-material/Terminal';
|
||||
import ModeNightOutlinedIcon from '@mui/icons-material/ModeNightOutlined';
|
||||
import LightModeOutlinedIcon from '@mui/icons-material/LightModeOutlined';
|
||||
import { AppBar as MuiAppBar, Grid, IconButton, Toolbar } from '@mui/material';
|
||||
import { Node } from 'src/svg-icons/node';
|
||||
import { config } from '../../config';
|
||||
import { AppContext } from '../../context/main';
|
||||
import { MultiAccounts } from '../Accounts';
|
||||
import { NetworkSelector } from '../NetworkSelector';
|
||||
|
||||
export const AppBar = () => {
|
||||
const { showSettings, handleShowTerminal, appEnv, handleShowSettings, logOut, mode, handleSwitchMode } =
|
||||
useContext(AppContext);
|
||||
|
||||
return (
|
||||
<MuiAppBar position="sticky" sx={{ boxShadow: 'none', bgcolor: 'transparent', backgroundImage: 'none' }}>
|
||||
<Toolbar disableGutters>
|
||||
<Grid container justifyContent="space-between" alignItems="center" flexWrap="nowrap">
|
||||
<Grid item container alignItems="center" spacing={1}>
|
||||
<Grid item>
|
||||
<MultiAccounts />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<NetworkSelector />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item container justifyContent="flex-end" md={12} lg={5} spacing={2}>
|
||||
<Grid item>
|
||||
<IconButton size="small" onClick={handleSwitchMode} sx={{ color: 'text.primary' }}>
|
||||
{mode === 'light' ? (
|
||||
<ModeNightOutlinedIcon fontSize="small" />
|
||||
) : (
|
||||
<LightModeOutlinedIcon fontSize="small" />
|
||||
)}
|
||||
</IconButton>
|
||||
</Grid>
|
||||
{(appEnv?.SHOW_TERMINAL || config.IS_DEV_MODE) && (
|
||||
<Grid item>
|
||||
<IconButton size="small" onClick={handleShowTerminal} sx={{ color: 'text.primary' }}>
|
||||
<TerminalIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid item>
|
||||
<IconButton
|
||||
onClick={handleShowSettings}
|
||||
sx={{ color: showSettings ? 'primary.main' : 'text.primary' }}
|
||||
size="small"
|
||||
>
|
||||
<Node fontSize="small" />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<IconButton size="small" onClick={logOut} sx={{ color: 'text.primary' }}>
|
||||
<Logout fontSize="small" />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Toolbar>
|
||||
</MuiAppBar>
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export * from './AppBar';
|
||||
@@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import { Box, Button, Typography } from '@mui/material';
|
||||
import { NymCard } from '../NymCard';
|
||||
|
||||
export const Bond = ({
|
||||
onBond,
|
||||
disabled,
|
||||
}: {
|
||||
onBond: () => void;
|
||||
|
||||
disabled: boolean;
|
||||
}) => (
|
||||
<NymCard title="Bonding" borderless>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<Typography>Bond a mixnode or a gateway</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'flex-end',
|
||||
justifyContent: 'space-between',
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
size="large"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
type="button"
|
||||
disableElevation
|
||||
onClick={onBond}
|
||||
disabled={disabled}
|
||||
>
|
||||
Bond
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</NymCard>
|
||||
);
|
||||
@@ -0,0 +1,84 @@
|
||||
import React from 'react';
|
||||
import { Stack, Typography } from '@mui/material';
|
||||
import { Link } from '@nymproject/react/link/Link';
|
||||
import { TBondedGateway, urls } from 'src/context';
|
||||
import { NymCard } from 'src/components';
|
||||
import { Network } from 'src/types';
|
||||
import { IdentityKey } from 'src/components/IdentityKey';
|
||||
import { Cell, Header, NodeTable } from './NodeTable';
|
||||
import { BondedGatewayActions, TBondedGatwayActions } from './BondedGatewayAction';
|
||||
|
||||
const headers: Header[] = [
|
||||
{
|
||||
header: 'IP',
|
||||
id: 'ip',
|
||||
sx: { pl: 0 },
|
||||
},
|
||||
{
|
||||
header: 'Bond',
|
||||
id: 'bond',
|
||||
},
|
||||
{
|
||||
id: 'menu-button',
|
||||
sx: { width: 34, maxWidth: 34 },
|
||||
},
|
||||
];
|
||||
|
||||
export const BondedGateway = ({
|
||||
gateway,
|
||||
network,
|
||||
onActionSelect,
|
||||
}: {
|
||||
gateway: TBondedGateway;
|
||||
network?: Network;
|
||||
onActionSelect: (action: TBondedGatwayActions) => void;
|
||||
}) => {
|
||||
const { name, bond, ip, identityKey } = gateway;
|
||||
const cells: Cell[] = [
|
||||
{
|
||||
cell: ip,
|
||||
id: 'stake-saturation-cell',
|
||||
},
|
||||
{
|
||||
cell: `${bond.amount} ${bond.denom}`,
|
||||
id: 'stake-cell',
|
||||
sx: { pl: 0 },
|
||||
},
|
||||
|
||||
{
|
||||
cell: <BondedGatewayActions onActionSelect={onActionSelect} />,
|
||||
id: 'actions-cell',
|
||||
align: 'right',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<NymCard
|
||||
borderless
|
||||
title={
|
||||
<Stack gap={2}>
|
||||
<Typography variant="h5" fontWeight={600}>
|
||||
Gateway
|
||||
</Typography>
|
||||
|
||||
{name && (
|
||||
<Typography fontWeight="regular" variant="h6">
|
||||
{name}
|
||||
</Typography>
|
||||
)}
|
||||
<IdentityKey identityKey={identityKey} />
|
||||
</Stack>
|
||||
}
|
||||
>
|
||||
<NodeTable headers={headers} cells={cells} />
|
||||
{network && (
|
||||
<Typography sx={{ mt: 2, fontSize: 'small' }}>
|
||||
Check more stats of your node on the{' '}
|
||||
<Link href={`${urls(network).networkExplorer}/network-components/gateways`} target="_blank">
|
||||
explorer
|
||||
</Link>
|
||||
</Typography>
|
||||
)}
|
||||
</NymCard>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
import React, { useState } from 'react';
|
||||
import { ActionsMenu, ActionsMenuItem } from 'src/components/ActionsMenu';
|
||||
import { Unbond as UnbondIcon } from '../../svg-icons';
|
||||
|
||||
export type TBondedGatwayActions = 'unbond';
|
||||
|
||||
export const BondedGatewayActions = ({
|
||||
onActionSelect,
|
||||
}: {
|
||||
onActionSelect: (action: TBondedGatwayActions) => void;
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const handleOpen = () => setIsOpen(true);
|
||||
const handleClose = () => setIsOpen(false);
|
||||
|
||||
const handleActionClick = (action: TBondedGatwayActions) => {
|
||||
onActionSelect(action);
|
||||
handleClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<ActionsMenu open={isOpen} onOpen={handleOpen} onClose={handleClose}>
|
||||
<ActionsMenuItem
|
||||
title="Unbond"
|
||||
Icon={<UnbondIcon fontSize="inherit" />}
|
||||
onClick={() => handleActionClick('unbond')}
|
||||
/>
|
||||
</ActionsMenu>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,138 @@
|
||||
import React from 'react';
|
||||
import { Box, Button, Stack, Typography } from '@mui/material';
|
||||
import { Link } from '@nymproject/react/link/Link';
|
||||
import { TBondedMixnode, urls } from 'src/context';
|
||||
import { NymCard } from 'src/components';
|
||||
import { Network } from 'src/types';
|
||||
import { IdentityKey } from 'src/components/IdentityKey';
|
||||
import { NodeStatus } from 'src/components/NodeStatus';
|
||||
import { Node as NodeIcon } from '../../svg-icons/node';
|
||||
import { Cell, Header, NodeTable } from './NodeTable';
|
||||
import { BondedMixnodeActions, TBondedMixnodeActions } from './BondedMixnodeActions';
|
||||
|
||||
const headers: Header[] = [
|
||||
{
|
||||
header: 'Stake',
|
||||
id: 'stake',
|
||||
sx: { pl: 0 },
|
||||
},
|
||||
{
|
||||
header: 'Bond',
|
||||
id: 'bond',
|
||||
},
|
||||
{
|
||||
header: 'Stake saturation',
|
||||
id: 'stake-saturation',
|
||||
},
|
||||
{
|
||||
header: 'PM',
|
||||
id: 'profit-margin',
|
||||
tooltipText:
|
||||
'The percentage of the node rewards that you as the node operator will take before the rest of the reward is shared between you and the delegators.',
|
||||
},
|
||||
{
|
||||
header: 'Operator rewards',
|
||||
id: 'operator-rewards',
|
||||
tooltipText:
|
||||
'This is your (operator) new rewards including the PM and cost. You can compound your rewards manually every epoch or unbond your node to redeem them.',
|
||||
},
|
||||
{
|
||||
header: 'No. delegators',
|
||||
id: 'delegators',
|
||||
},
|
||||
{
|
||||
id: 'menu-button',
|
||||
sx: { width: 34, maxWidth: 34 },
|
||||
},
|
||||
];
|
||||
|
||||
export const BondedMixnode = ({
|
||||
mixnode,
|
||||
network,
|
||||
onActionSelect,
|
||||
}: {
|
||||
mixnode: TBondedMixnode;
|
||||
network?: Network;
|
||||
onActionSelect: (action: TBondedMixnodeActions) => void;
|
||||
}) => {
|
||||
const { name, stake, bond, stakeSaturation, profitMargin, operatorRewards, delegators, status, identityKey } =
|
||||
mixnode;
|
||||
const cells: Cell[] = [
|
||||
{
|
||||
cell: `${stake.amount} ${stake.denom}`,
|
||||
id: 'stake-cell',
|
||||
},
|
||||
{
|
||||
cell: `${bond.amount} ${bond.denom}`,
|
||||
id: 'bond-cell',
|
||||
},
|
||||
{
|
||||
cell: `${stakeSaturation}%`,
|
||||
id: 'stake-saturation-cell',
|
||||
},
|
||||
{
|
||||
cell: `${profitMargin}%`,
|
||||
id: 'pm-cell',
|
||||
},
|
||||
{
|
||||
cell: `${operatorRewards.amount} ${operatorRewards.denom}`,
|
||||
id: 'operator-rewards-cell',
|
||||
},
|
||||
{
|
||||
cell: delegators,
|
||||
id: 'delegators-cell',
|
||||
},
|
||||
{
|
||||
cell: (
|
||||
<BondedMixnodeActions
|
||||
onActionSelect={onActionSelect}
|
||||
disabledRedeemAndCompound={Number(mixnode.operatorRewards.amount) === 0}
|
||||
/>
|
||||
),
|
||||
id: 'actions-cell',
|
||||
align: 'right',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<NymCard
|
||||
borderless
|
||||
title={
|
||||
<Stack gap={2}>
|
||||
<Box display="flex" alignItems="center" gap={2}>
|
||||
<Typography variant="h5" fontWeight={600}>
|
||||
Mix node
|
||||
</Typography>
|
||||
<NodeStatus status={status} />
|
||||
</Box>
|
||||
{name && (
|
||||
<Typography fontWeight="regular" variant="h6">
|
||||
{name}
|
||||
</Typography>
|
||||
)}
|
||||
<IdentityKey identityKey={identityKey} />
|
||||
</Stack>
|
||||
}
|
||||
Action={
|
||||
<Button
|
||||
variant="text"
|
||||
color="secondary"
|
||||
onClick={() => onActionSelect('nodeSettings')}
|
||||
startIcon={<NodeIcon />}
|
||||
>
|
||||
Settings
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<NodeTable headers={headers} cells={cells} />
|
||||
{network && (
|
||||
<Typography sx={{ mt: 2, fontSize: 'small' }}>
|
||||
Check more stats of your node on the{' '}
|
||||
<Link href={`${urls(network).networkExplorer}/network-components/mixnodes`} target="_blank">
|
||||
explorer
|
||||
</Link>
|
||||
</Typography>
|
||||
)}
|
||||
</NymCard>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Typography } from '@mui/material';
|
||||
import { ActionsMenu, ActionsMenuItem } from 'src/components/ActionsMenu';
|
||||
import { Unbond as UnbondIcon } from '../../svg-icons';
|
||||
|
||||
export type TBondedMixnodeActions = 'nodeSettings' | 'bondMore' | 'unbond' | 'redeem' | 'compound';
|
||||
|
||||
export const BondedMixnodeActions = ({
|
||||
onActionSelect,
|
||||
disabledRedeemAndCompound,
|
||||
}: {
|
||||
onActionSelect: (action: TBondedMixnodeActions) => void;
|
||||
disabledRedeemAndCompound: boolean;
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const handleOpen = () => setIsOpen(true);
|
||||
const handleClose = () => setIsOpen(false);
|
||||
|
||||
const handleActionClick = (action: TBondedMixnodeActions) => {
|
||||
onActionSelect(action);
|
||||
handleClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<ActionsMenu open={isOpen} onOpen={handleOpen} onClose={handleClose}>
|
||||
<ActionsMenuItem
|
||||
title="Unbond"
|
||||
Icon={<UnbondIcon fontSize="inherit" />}
|
||||
onClick={() => handleActionClick('unbond')}
|
||||
/>
|
||||
<ActionsMenuItem
|
||||
title="Compound rewards"
|
||||
Icon={<Typography sx={{ pl: 1 }}>C</Typography>}
|
||||
description={disabledRedeemAndCompound ? 'No rewards to compound' : 'Add your rewards to you balance'}
|
||||
onClick={() => handleActionClick('compound')}
|
||||
disabled={disabledRedeemAndCompound}
|
||||
/>
|
||||
<ActionsMenuItem
|
||||
title="Redeem rewards"
|
||||
Icon={<Typography sx={{ pl: 1 }}>R</Typography>}
|
||||
description={disabledRedeemAndCompound ? 'No rewards to redeem' : 'Add your rewards to you balance'}
|
||||
onClick={() => handleActionClick('redeem')}
|
||||
disabled={disabledRedeemAndCompound}
|
||||
/>
|
||||
</ActionsMenu>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Stack,
|
||||
SxProps,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableCellProps,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { InfoTooltip } from '../InfoToolTip';
|
||||
|
||||
export type Header = { header?: string; id: string; tooltipText?: string; sx?: SxProps };
|
||||
export type Cell = { cell: string | React.ReactNode; id: string; align?: TableCellProps['align']; sx?: SxProps };
|
||||
|
||||
export interface TableProps {
|
||||
headers: Header[];
|
||||
cells: Cell[];
|
||||
}
|
||||
|
||||
export const NodeTable = ({ headers, cells }: TableProps) => (
|
||||
<TableContainer>
|
||||
<Table aria-label="node-table">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
{headers.map(({ header, id, tooltipText }) => (
|
||||
<TableCell key={id}>
|
||||
<Stack direction="row" gap={1}>
|
||||
{tooltipText && <InfoTooltip title={tooltipText} />}
|
||||
<Typography>{header}</Typography>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
<TableRow key="node-data">
|
||||
{cells.map(({ cell, id, align }) => (
|
||||
<TableCell key={id} align={align} sx={{ textTransform: 'uppercase' }}>
|
||||
{cell}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
);
|
||||
@@ -0,0 +1,214 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField';
|
||||
import { Box, Checkbox, FormControlLabel, Stack, TextField } from '@mui/material';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField';
|
||||
import { NodeTypeSelector, TokenPoolSelector } from 'src/components';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { checkHasEnoughFunds, checkHasEnoughLockedTokens } from 'src/utils';
|
||||
import { CurrencyDenom, TNodeType } from '@nymproject/types';
|
||||
import { GatewayAmount, GatewayData } from 'src/pages/bonding/types';
|
||||
import { gatewayValidationSchema, amountSchema } from './gatewayValidationSchema';
|
||||
|
||||
const NodeFormData = ({ gatewayData, onNext }: { gatewayData: GatewayData; onNext: (data: GatewayData) => void }) => {
|
||||
const [showAdvancedOptions, setShowAdvancedOptions] = useState(false);
|
||||
|
||||
const {
|
||||
register,
|
||||
formState: { errors },
|
||||
handleSubmit,
|
||||
setValue,
|
||||
} = useForm({ resolver: yupResolver(gatewayValidationSchema), defaultValues: gatewayData });
|
||||
|
||||
const handleRequestValidation = (event: { detail: { step: number } }) => {
|
||||
if (event.detail.step === 1) {
|
||||
handleSubmit(onNext)();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('validate_bond_gateway_step' as any, handleRequestValidation);
|
||||
return () => window.removeEventListener('validate_bond_gateway_step' as any, handleRequestValidation);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Stack gap={2}>
|
||||
<IdentityKeyFormField
|
||||
required
|
||||
fullWidth
|
||||
label="Identity Key"
|
||||
initialValue={gatewayData?.identityKey}
|
||||
errorText={errors.identityKey?.message}
|
||||
onChanged={(value) => setValue('identityKey', value)}
|
||||
/>
|
||||
<TextField
|
||||
{...register('sphinxKey')}
|
||||
name="sphinxKey"
|
||||
label="Sphinx key"
|
||||
error={Boolean(errors.sphinxKey)}
|
||||
helperText={errors.sphinxKey?.message}
|
||||
/>
|
||||
<TextField
|
||||
{...register('ownerSignature')}
|
||||
name="ownerSignature"
|
||||
label="Owner signature"
|
||||
error={Boolean(errors.ownerSignature)}
|
||||
helperText={errors.ownerSignature?.message}
|
||||
/>
|
||||
<TextField
|
||||
{...register('location')}
|
||||
name="location"
|
||||
label="Location"
|
||||
error={Boolean(errors.location)}
|
||||
helperText={errors.location?.message}
|
||||
required
|
||||
sx={{ flexBasis: '50%' }}
|
||||
/>
|
||||
<Stack direction="row" gap={2}>
|
||||
<TextField
|
||||
{...register('host')}
|
||||
name="host"
|
||||
label="Host"
|
||||
error={Boolean(errors.host)}
|
||||
helperText={errors.host?.message}
|
||||
required
|
||||
sx={{ flexBasis: '50%' }}
|
||||
/>
|
||||
<TextField
|
||||
{...register('version')}
|
||||
name="version"
|
||||
label="Version"
|
||||
error={Boolean(errors.version)}
|
||||
helperText={errors.version?.message}
|
||||
required
|
||||
sx={{ flexBasis: '50%' }}
|
||||
/>
|
||||
</Stack>
|
||||
<FormControlLabel
|
||||
control={<Checkbox onChange={() => setShowAdvancedOptions((show) => !show)} checked={showAdvancedOptions} />}
|
||||
label="Show advanced options"
|
||||
/>
|
||||
{showAdvancedOptions && (
|
||||
<Stack direction="row" gap={2} sx={{ mb: 2 }}>
|
||||
<TextField
|
||||
{...register('mixPort')}
|
||||
name="mixPort"
|
||||
label="Mix port"
|
||||
error={Boolean(errors.mixPort)}
|
||||
helperText={errors.mixPort?.message}
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
{...register('clientsPort')}
|
||||
name="clientsPort"
|
||||
label="Client WS API port"
|
||||
error={Boolean(errors.clientsPort)}
|
||||
helperText={errors.clientsPort?.message}
|
||||
fullWidth
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const AmountFormData = ({
|
||||
denom,
|
||||
amountData,
|
||||
hasVestingTokens,
|
||||
onNext,
|
||||
}: {
|
||||
denom: CurrencyDenom;
|
||||
amountData: GatewayAmount;
|
||||
hasVestingTokens: boolean;
|
||||
onNext: (data: any) => void;
|
||||
}) => {
|
||||
const {
|
||||
formState: { errors },
|
||||
handleSubmit,
|
||||
setValue,
|
||||
getValues,
|
||||
setError,
|
||||
} = useForm({ resolver: yupResolver(amountSchema), defaultValues: amountData });
|
||||
|
||||
const handleRequestValidation = async (event: { detail: { step: number } }) => {
|
||||
let hasSufficientTokens = true;
|
||||
const values = getValues();
|
||||
|
||||
if (values.tokenPool === 'balance') {
|
||||
hasSufficientTokens = await checkHasEnoughFunds(values.amount.amount);
|
||||
}
|
||||
|
||||
if (values.tokenPool === 'locked') {
|
||||
hasSufficientTokens = await checkHasEnoughLockedTokens(values.amount.amount);
|
||||
}
|
||||
|
||||
if (event.detail.step === 2 && hasSufficientTokens) {
|
||||
handleSubmit(onNext)();
|
||||
} else {
|
||||
setError('amount.amount', { message: 'Not enough tokens' });
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('validate_bond_gateway_step' as any, handleRequestValidation);
|
||||
return () => window.removeEventListener('validate_bond_gateway_step' as any, handleRequestValidation);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Stack gap={2}>
|
||||
<Box display="flex" gap={2} justifyContent="center" sx={{ mt: 2 }}>
|
||||
{hasVestingTokens && <TokenPoolSelector disabled={false} onSelect={(pool) => setValue('tokenPool', pool)} />}
|
||||
<CurrencyFormField
|
||||
required
|
||||
fullWidth
|
||||
label="Amount"
|
||||
autoFocus
|
||||
onChanged={(newValue) => setValue('amount', newValue, { shouldValidate: true })}
|
||||
validationError={errors.amount?.amount?.message}
|
||||
denom={denom}
|
||||
initialValue={amountData.amount.amount}
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export const BondGatewayForm = ({
|
||||
step,
|
||||
denom,
|
||||
gatewayData,
|
||||
amountData,
|
||||
hasVestingTokens,
|
||||
onValidateGatewayData,
|
||||
onValidateAmountData,
|
||||
onSelectNodeType,
|
||||
}: {
|
||||
step: 1 | 2 | 3;
|
||||
gatewayData: GatewayData;
|
||||
amountData: GatewayAmount;
|
||||
denom: CurrencyDenom;
|
||||
hasVestingTokens: boolean;
|
||||
onValidateGatewayData: (data: GatewayData) => void;
|
||||
onValidateAmountData: (data: GatewayAmount) => Promise<void>;
|
||||
onSelectNodeType: (nodeType: TNodeType) => void;
|
||||
}) => (
|
||||
<>
|
||||
{step === 1 && (
|
||||
<>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<NodeTypeSelector disabled={false} setNodeType={onSelectNodeType} nodeType="gateway" />
|
||||
</Box>
|
||||
<NodeFormData onNext={onValidateGatewayData} gatewayData={gatewayData} />
|
||||
</>
|
||||
)}
|
||||
{step === 2 && (
|
||||
<AmountFormData
|
||||
denom={denom}
|
||||
amountData={amountData}
|
||||
hasVestingTokens={hasVestingTokens}
|
||||
onNext={onValidateAmountData}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
@@ -0,0 +1,223 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { Box, Checkbox, FormControlLabel, Stack, TextField } from '@mui/material';
|
||||
import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField';
|
||||
import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField';
|
||||
import { CurrencyDenom, TNodeType } from '@nymproject/types';
|
||||
import { checkHasEnoughFunds, checkHasEnoughLockedTokens } from 'src/utils';
|
||||
import { NodeTypeSelector, TokenPoolSelector } from 'src/components';
|
||||
import { MixnodeAmount, MixnodeData } from 'src/pages/bonding/types';
|
||||
import { amountSchema, mixnodeValidationSchema } from './mixnodeValidationSchema';
|
||||
|
||||
const NodeFormData = ({ mixnodeData, onNext }: { mixnodeData: MixnodeData; onNext: (data: any) => void }) => {
|
||||
const [showAdvancedOptions, setShowAdvancedOptions] = useState(false);
|
||||
|
||||
const {
|
||||
register,
|
||||
formState: { errors },
|
||||
handleSubmit,
|
||||
setValue,
|
||||
} = useForm({ resolver: yupResolver(mixnodeValidationSchema), defaultValues: mixnodeData });
|
||||
|
||||
const handleRequestValidation = (event: { detail: { step: number } }) => {
|
||||
if (event.detail.step === 1) {
|
||||
handleSubmit(onNext)();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('validate_bond_mixnode_step' as any, handleRequestValidation);
|
||||
return () => window.removeEventListener('validate_bond_mixnode_step' as any, handleRequestValidation);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Stack gap={2}>
|
||||
<IdentityKeyFormField
|
||||
required
|
||||
fullWidth
|
||||
label="Identity Key"
|
||||
initialValue={mixnodeData?.identityKey}
|
||||
errorText={errors.identityKey?.message}
|
||||
onChanged={(value) => setValue('identityKey', value)}
|
||||
/>
|
||||
<TextField
|
||||
{...register('sphinxKey')}
|
||||
name="sphinxKey"
|
||||
label="Sphinx key"
|
||||
error={Boolean(errors.sphinxKey)}
|
||||
helperText={errors.sphinxKey?.message}
|
||||
/>
|
||||
<TextField
|
||||
{...register('ownerSignature')}
|
||||
name="ownerSignature"
|
||||
label="Owner signature"
|
||||
error={Boolean(errors.ownerSignature)}
|
||||
helperText={errors.ownerSignature?.message}
|
||||
/>
|
||||
<Stack direction="row" gap={2}>
|
||||
<TextField
|
||||
{...register('host')}
|
||||
name="host"
|
||||
label="Host"
|
||||
error={Boolean(errors.host)}
|
||||
helperText={errors.host?.message}
|
||||
required
|
||||
sx={{ flexBasis: '50%' }}
|
||||
/>
|
||||
<TextField
|
||||
{...register('version')}
|
||||
name="version"
|
||||
label="Version"
|
||||
error={Boolean(errors.version)}
|
||||
helperText={errors.version?.message}
|
||||
required
|
||||
sx={{ flexBasis: '50%' }}
|
||||
/>
|
||||
</Stack>
|
||||
<FormControlLabel
|
||||
control={<Checkbox onChange={() => setShowAdvancedOptions((show) => !show)} checked={showAdvancedOptions} />}
|
||||
label="Show advanced options"
|
||||
/>
|
||||
{showAdvancedOptions && (
|
||||
<Stack direction="row" gap={2} sx={{ mb: 2 }}>
|
||||
<TextField
|
||||
{...register('mixPort')}
|
||||
name="mixPort"
|
||||
label="Mix port"
|
||||
error={Boolean(errors.mixPort)}
|
||||
helperText={errors.mixPort?.message}
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
{...register('verlocPort')}
|
||||
name="verlocPort"
|
||||
label="Verloc port"
|
||||
error={Boolean(errors.verlocPort)}
|
||||
helperText={errors.verlocPort?.message}
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
{...register('httpApiPort')}
|
||||
name="httpApiPort"
|
||||
label="HTTP api port"
|
||||
error={Boolean(errors.httpApiPort)}
|
||||
helperText={errors.httpApiPort?.message}
|
||||
fullWidth
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const AmountFormData = ({
|
||||
amountData,
|
||||
hasVestingTokens,
|
||||
denom,
|
||||
onNext,
|
||||
}: {
|
||||
amountData: MixnodeAmount;
|
||||
hasVestingTokens: boolean;
|
||||
denom: CurrencyDenom;
|
||||
onNext: (data: MixnodeAmount) => void;
|
||||
}) => {
|
||||
const {
|
||||
register,
|
||||
formState: { errors },
|
||||
handleSubmit,
|
||||
setValue,
|
||||
getValues,
|
||||
setError,
|
||||
} = useForm({ resolver: yupResolver(amountSchema), defaultValues: amountData });
|
||||
|
||||
const handleRequestValidation = async (event: { detail: { step: number } }) => {
|
||||
let hasSufficientTokens = true;
|
||||
const values = getValues();
|
||||
|
||||
if (values.tokenPool === 'balance') {
|
||||
hasSufficientTokens = await checkHasEnoughFunds(values.amount.amount);
|
||||
}
|
||||
|
||||
if (values.tokenPool === 'locked') {
|
||||
hasSufficientTokens = await checkHasEnoughLockedTokens(values.amount.amount);
|
||||
}
|
||||
|
||||
if (event.detail.step === 2 && hasSufficientTokens) {
|
||||
handleSubmit(onNext)();
|
||||
} else {
|
||||
setError('amount.amount', { message: 'Not enough tokens' });
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('validate_bond_mixnode_step' as any, handleRequestValidation);
|
||||
return () => window.removeEventListener('validate_bond_mixnode_step' as any, handleRequestValidation);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Stack gap={2}>
|
||||
<Box display="flex" gap={2} justifyContent="center" sx={{ mt: 2 }}>
|
||||
{hasVestingTokens && <TokenPoolSelector disabled={false} onSelect={(pool) => setValue('tokenPool', pool)} />}
|
||||
<CurrencyFormField
|
||||
required
|
||||
fullWidth
|
||||
label="Amount"
|
||||
autoFocus
|
||||
onChanged={(newValue) => {
|
||||
setValue('amount', newValue, { shouldValidate: true });
|
||||
}}
|
||||
validationError={errors.amount?.amount?.message}
|
||||
denom={denom}
|
||||
initialValue={amountData.amount.amount}
|
||||
/>
|
||||
</Box>
|
||||
<TextField
|
||||
{...register('profitMargin')}
|
||||
name="profitMargin"
|
||||
label="Profit margin"
|
||||
error={Boolean(errors.profitMargin)}
|
||||
helperText={errors.profitMargin?.message}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export const BondMixnodeForm = ({
|
||||
step,
|
||||
denom,
|
||||
mixnodeData,
|
||||
amountData,
|
||||
hasVestingTokens,
|
||||
onValidateMixnodeData,
|
||||
onValidateAmountData,
|
||||
onSelectNodeType,
|
||||
}: {
|
||||
step: 1 | 2 | 3;
|
||||
mixnodeData: MixnodeData;
|
||||
amountData: MixnodeAmount;
|
||||
denom: CurrencyDenom;
|
||||
hasVestingTokens: boolean;
|
||||
onValidateMixnodeData: (data: MixnodeData) => void;
|
||||
onValidateAmountData: (data: MixnodeAmount) => Promise<void>;
|
||||
onSelectNodeType: (nodeType: TNodeType) => void;
|
||||
}) => (
|
||||
<>
|
||||
{step === 1 && (
|
||||
<>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<NodeTypeSelector disabled={false} setNodeType={onSelectNodeType} nodeType="mixnode" />
|
||||
</Box>
|
||||
<NodeFormData onNext={onValidateMixnodeData} mixnodeData={mixnodeData} />
|
||||
</>
|
||||
)}
|
||||
{step === 2 && (
|
||||
<AmountFormData
|
||||
denom={denom}
|
||||
amountData={amountData}
|
||||
hasVestingTokens={hasVestingTokens}
|
||||
onNext={onValidateAmountData}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
@@ -0,0 +1,59 @@
|
||||
import * as Yup from 'yup';
|
||||
import {
|
||||
isValidHostname,
|
||||
validateAmount,
|
||||
validateKey,
|
||||
validateLocation,
|
||||
validateRawPort,
|
||||
validateVersion,
|
||||
} from 'src/utils';
|
||||
|
||||
export const gatewayValidationSchema = Yup.object().shape({
|
||||
identityKey: Yup.string()
|
||||
.required('An indentity key is required')
|
||||
.test('valid-id-key', 'A valid identity key is required', (value) => validateKey(value || '', 32)),
|
||||
|
||||
sphinxKey: Yup.string()
|
||||
.required('A sphinx key is required')
|
||||
.test('valid-sphinx-key', 'A valid sphinx key is required', (value) => validateKey(value || '', 32)),
|
||||
|
||||
ownerSignature: Yup.string()
|
||||
.required('Signature is required')
|
||||
.test('valid-signature', 'A valid signature is required', (value) => validateKey(value || '', 64)),
|
||||
|
||||
host: Yup.string()
|
||||
.required('A host is required')
|
||||
.test('valid-host', 'A valid host is required', (value) => (value ? isValidHostname(value) : false)),
|
||||
|
||||
version: Yup.string()
|
||||
.required('A version is required')
|
||||
.test('valid-version', 'A valid version is required', (value) => (value ? validateVersion(value) : false)),
|
||||
|
||||
location: Yup.string()
|
||||
.required('A location is required')
|
||||
.test('valid-location', 'A valid version is required', (locationValueTest) =>
|
||||
locationValueTest ? validateLocation(locationValueTest) : false,
|
||||
),
|
||||
|
||||
mixPort: Yup.number()
|
||||
.required('A mixport is required')
|
||||
.test('valid-mixport', 'A valid mixport is required', (value) => (value ? validateRawPort(value) : false)),
|
||||
|
||||
clientsPort: Yup.number()
|
||||
.required('A clients port is required')
|
||||
.test('valid-clients', 'A valid clients port is required', (value) => (value ? validateRawPort(value) : false)),
|
||||
});
|
||||
|
||||
export const amountSchema = Yup.object().shape({
|
||||
amount: Yup.object().shape({
|
||||
amount: Yup.string()
|
||||
.required('An amount is required')
|
||||
.test('valid-amount', 'Pledge error', async function isValidAmount(this, value) {
|
||||
const isValid = await validateAmount(value || '', '100');
|
||||
if (!isValid) {
|
||||
return this.createError({ message: 'A valid amount is required (min 100)' });
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
import * as Yup from 'yup';
|
||||
import { isValidHostname, validateAmount, validateKey, validateRawPort, validateVersion } from 'src/utils';
|
||||
|
||||
export const mixnodeValidationSchema = Yup.object().shape({
|
||||
identityKey: Yup.string()
|
||||
.required('An identity key is required')
|
||||
.test('valid-id-key', 'A valid identity key is required', (value) => validateKey(value || '', 32)),
|
||||
|
||||
sphinxKey: Yup.string()
|
||||
.required('A sphinx key is required')
|
||||
.test('valid-sphinx-key', 'A valid sphinx key is required', (value) => validateKey(value || '', 32)),
|
||||
|
||||
ownerSignature: Yup.string()
|
||||
.required('Signature is required')
|
||||
.test('valid-signature', 'A valid signature is required', (value) => validateKey(value || '', 64)),
|
||||
|
||||
host: Yup.string()
|
||||
.required('A host is required')
|
||||
.test('valid-host', 'A valid host is required', (value) => (value ? isValidHostname(value) : false)),
|
||||
|
||||
version: Yup.string()
|
||||
.required('A version is required')
|
||||
.test('valid-version', 'A valid version is required', (value) => (value ? validateVersion(value) : false)),
|
||||
|
||||
mixPort: Yup.number()
|
||||
.required('A mixport is required')
|
||||
.test('valid-mixport', 'A valid mixport is required', (value) => (value ? validateRawPort(value) : false)),
|
||||
|
||||
verlocPort: Yup.number()
|
||||
.required('A verloc port is required')
|
||||
.test('valid-verloc', 'A valid verloc port is required', (value) => (value ? validateRawPort(value) : false)),
|
||||
|
||||
httpApiPort: Yup.number()
|
||||
.required('A http-api port is required')
|
||||
.test('valid-http', 'A valid http-api port is required', (value) => (value ? validateRawPort(value) : false)),
|
||||
});
|
||||
|
||||
export const amountSchema = Yup.object().shape({
|
||||
amount: Yup.object().shape({
|
||||
amount: Yup.string()
|
||||
.required('An amount is required')
|
||||
.test('valid-amount', 'Pledge error', async function isValidAmount(this, value) {
|
||||
const isValid = await validateAmount(value || '', '100');
|
||||
if (!isValid) {
|
||||
return this.createError({ message: 'A valid amount is required (min 100)' });
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
}),
|
||||
profitMargin: Yup.number().required('Profit Percentage is required').min(0).max(100),
|
||||
});
|
||||
@@ -0,0 +1,161 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Box } from '@mui/material';
|
||||
import { CurrencyDenom, TNodeType } from '@nymproject/types';
|
||||
import { ConfirmTx } from 'src/components/ConfirmTX';
|
||||
import { ModalListItem } from 'src/components/Modals/ModalListItem';
|
||||
import { SimpleModal } from 'src/components/Modals/SimpleModal';
|
||||
import { TPoolOption } from 'src/components/TokenPoolSelector';
|
||||
import { useGetFee } from 'src/hooks/useGetFee';
|
||||
import { GatewayAmount, GatewayData } from 'src/pages/bonding/types';
|
||||
import { simulateBondGateway, simulateVestingBondGateway } from 'src/requests';
|
||||
import { TBondGatewayArgs } from 'src/types';
|
||||
import { BondGatewayForm } from '../forms/BondGatewayForm';
|
||||
|
||||
const defaultMixnodeValues: GatewayData = {
|
||||
identityKey: '',
|
||||
sphinxKey: '',
|
||||
ownerSignature: '',
|
||||
location: '',
|
||||
host: '',
|
||||
version: '',
|
||||
mixPort: 1789,
|
||||
clientsPort: 1790,
|
||||
};
|
||||
|
||||
const defaultAmountValues = (denom: CurrencyDenom) => ({
|
||||
amount: { amount: '100', denom },
|
||||
tokenPool: 'balance',
|
||||
});
|
||||
|
||||
export const BondGatewayModal = ({
|
||||
denom,
|
||||
hasVestingTokens,
|
||||
onBondGateway,
|
||||
onSelectNodeType,
|
||||
onClose,
|
||||
onError,
|
||||
}: {
|
||||
denom: CurrencyDenom;
|
||||
hasVestingTokens: boolean;
|
||||
onBondGateway: (data: TBondGatewayArgs, tokenPool: TPoolOption) => void;
|
||||
onSelectNodeType: (type: TNodeType) => void;
|
||||
onClose: () => void;
|
||||
onError: (e: string) => void;
|
||||
}) => {
|
||||
const [step, setStep] = useState<1 | 2 | 3>(1);
|
||||
const [gatewayData, setGatewayData] = useState<GatewayData>(defaultMixnodeValues);
|
||||
const [amountData, setAmountData] = useState<GatewayAmount>(defaultAmountValues(denom));
|
||||
|
||||
const { fee, getFee, resetFeeState, feeError } = useGetFee();
|
||||
|
||||
useEffect(() => {
|
||||
if (feeError) {
|
||||
onError(feeError);
|
||||
}
|
||||
}, [feeError]);
|
||||
|
||||
const validateStep = async (s: number) => {
|
||||
const event = new CustomEvent('validate_bond_gateway_step', { detail: { step: s } });
|
||||
window.dispatchEvent(event);
|
||||
};
|
||||
|
||||
const handleBack = () => {
|
||||
setStep(1);
|
||||
};
|
||||
|
||||
const handleUpdateGatwayData = (data: GatewayData) => {
|
||||
setGatewayData(data);
|
||||
setStep(2);
|
||||
};
|
||||
|
||||
const handleUpdateAmountData = async (data: GatewayAmount) => {
|
||||
setAmountData(data);
|
||||
const payload = {
|
||||
pledge: data.amount,
|
||||
ownerSignature: gatewayData.ownerSignature,
|
||||
gateway: {
|
||||
...gatewayData,
|
||||
host: gatewayData.host,
|
||||
version: gatewayData.version,
|
||||
mix_port: gatewayData.mixPort,
|
||||
clients_port: gatewayData.clientsPort,
|
||||
sphinx_key: gatewayData.sphinxKey,
|
||||
identity_key: gatewayData.identityKey,
|
||||
location: gatewayData.location,
|
||||
},
|
||||
};
|
||||
|
||||
if (data.tokenPool === 'balance') {
|
||||
await getFee<TBondGatewayArgs>(simulateBondGateway, payload);
|
||||
} else {
|
||||
await getFee<TBondGatewayArgs>(simulateVestingBondGateway, payload);
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirm = async () => {
|
||||
await onBondGateway(
|
||||
{
|
||||
pledge: amountData.amount,
|
||||
ownerSignature: gatewayData.ownerSignature,
|
||||
gateway: {
|
||||
...gatewayData,
|
||||
host: gatewayData.host,
|
||||
version: gatewayData.version,
|
||||
mix_port: gatewayData.mixPort,
|
||||
clients_port: gatewayData.clientsPort,
|
||||
sphinx_key: gatewayData.sphinxKey,
|
||||
identity_key: gatewayData.identityKey,
|
||||
location: gatewayData.location,
|
||||
},
|
||||
},
|
||||
amountData.tokenPool as TPoolOption,
|
||||
);
|
||||
};
|
||||
|
||||
if (fee) {
|
||||
return (
|
||||
<ConfirmTx
|
||||
open
|
||||
header="Bond details"
|
||||
fee={fee}
|
||||
onClose={onClose}
|
||||
onPrev={resetFeeState}
|
||||
onConfirm={handleConfirm}
|
||||
>
|
||||
<ModalListItem label="Node identity key" value={gatewayData.identityKey} divider />
|
||||
<ModalListItem
|
||||
label="Amount"
|
||||
value={`${amountData.amount.amount} ${amountData.amount.denom.toUpperCase()}`}
|
||||
divider
|
||||
/>
|
||||
</ConfirmTx>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SimpleModal
|
||||
open
|
||||
onOk={async () => {
|
||||
await validateStep(step);
|
||||
}}
|
||||
onBack={step === 2 ? handleBack : undefined}
|
||||
onClose={onClose}
|
||||
header="Bond gateway"
|
||||
subHeader={`Step ${step}/2`}
|
||||
okLabel="Next"
|
||||
>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<BondGatewayForm
|
||||
step={step}
|
||||
denom={denom}
|
||||
gatewayData={gatewayData}
|
||||
amountData={amountData}
|
||||
hasVestingTokens={hasVestingTokens}
|
||||
onValidateGatewayData={handleUpdateGatwayData}
|
||||
onValidateAmountData={handleUpdateAmountData}
|
||||
onSelectNodeType={onSelectNodeType}
|
||||
/>
|
||||
</Box>
|
||||
</SimpleModal>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,160 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Box } from '@mui/material';
|
||||
import { CurrencyDenom, TNodeType } from '@nymproject/types';
|
||||
import { ConfirmTx } from 'src/components/ConfirmTX';
|
||||
import { ModalListItem } from 'src/components/Modals/ModalListItem';
|
||||
import { SimpleModal } from 'src/components/Modals/SimpleModal';
|
||||
import { TPoolOption } from 'src/components/TokenPoolSelector';
|
||||
import { useGetFee } from 'src/hooks/useGetFee';
|
||||
import { MixnodeAmount, MixnodeData } from 'src/pages/bonding/types';
|
||||
import { simulateBondMixnode, simulateVestingBondMixnode } from 'src/requests';
|
||||
import { TBondMixNodeArgs } from 'src/types';
|
||||
import { BondMixnodeForm } from '../forms/BondMixnodeForm';
|
||||
|
||||
const defaultMixnodeValues: MixnodeData = {
|
||||
identityKey: '',
|
||||
sphinxKey: '',
|
||||
ownerSignature: '',
|
||||
host: '',
|
||||
version: '',
|
||||
mixPort: 1789,
|
||||
verlocPort: 1790,
|
||||
httpApiPort: 8000,
|
||||
};
|
||||
|
||||
const defaultAmountValues = (denom: CurrencyDenom) => ({
|
||||
amount: { amount: '100', denom },
|
||||
profitMargin: 10,
|
||||
tokenPool: 'balance',
|
||||
});
|
||||
|
||||
export const BondMixnodeModal = ({
|
||||
denom,
|
||||
hasVestingTokens,
|
||||
onBondMixnode,
|
||||
onSelectNodeType,
|
||||
onClose,
|
||||
onError,
|
||||
}: {
|
||||
denom: CurrencyDenom;
|
||||
hasVestingTokens: boolean;
|
||||
onBondMixnode: (data: TBondMixNodeArgs, tokenPool: TPoolOption) => void;
|
||||
onSelectNodeType: (type: TNodeType) => void;
|
||||
onClose: () => void;
|
||||
onError: (e: string) => void;
|
||||
}) => {
|
||||
const [step, setStep] = useState<1 | 2 | 3>(1);
|
||||
const [mixnodeData, setMixnodeData] = useState<MixnodeData>(defaultMixnodeValues);
|
||||
const [amountData, setAmountData] = useState<MixnodeAmount>(defaultAmountValues(denom));
|
||||
|
||||
const { fee, getFee, resetFeeState, feeError } = useGetFee();
|
||||
|
||||
useEffect(() => {
|
||||
if (feeError) {
|
||||
onError(feeError);
|
||||
}
|
||||
}, [feeError]);
|
||||
|
||||
const validateStep = async (s: number) => {
|
||||
const event = new CustomEvent('validate_bond_mixnode_step', { detail: { step: s } });
|
||||
window.dispatchEvent(event);
|
||||
};
|
||||
|
||||
const handleBack = () => {
|
||||
setStep(1);
|
||||
};
|
||||
|
||||
const handleUpdateMixnodeData = (data: MixnodeData) => {
|
||||
setMixnodeData(data);
|
||||
setStep(2);
|
||||
};
|
||||
|
||||
const handleUpdateAmountData = async (data: MixnodeAmount) => {
|
||||
setAmountData(data);
|
||||
const payload = {
|
||||
pledge: data.amount,
|
||||
ownerSignature: mixnodeData.ownerSignature,
|
||||
mixnode: {
|
||||
...mixnodeData,
|
||||
mix_port: mixnodeData.mixPort,
|
||||
http_api_port: mixnodeData.httpApiPort,
|
||||
verloc_port: mixnodeData.verlocPort,
|
||||
sphinx_key: mixnodeData.sphinxKey,
|
||||
identity_key: mixnodeData.identityKey,
|
||||
profit_margin_percent: data.profitMargin,
|
||||
},
|
||||
};
|
||||
|
||||
if (data.tokenPool === 'balance') {
|
||||
await getFee<TBondMixNodeArgs>(simulateBondMixnode, payload);
|
||||
} else {
|
||||
await getFee<TBondMixNodeArgs>(simulateVestingBondMixnode, payload);
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirm = async () => {
|
||||
await onBondMixnode(
|
||||
{
|
||||
pledge: amountData.amount,
|
||||
ownerSignature: mixnodeData.ownerSignature,
|
||||
mixnode: {
|
||||
...mixnodeData,
|
||||
mix_port: mixnodeData.mixPort,
|
||||
http_api_port: mixnodeData.httpApiPort,
|
||||
verloc_port: mixnodeData.verlocPort,
|
||||
sphinx_key: mixnodeData.sphinxKey,
|
||||
identity_key: mixnodeData.identityKey,
|
||||
profit_margin_percent: amountData.profitMargin,
|
||||
},
|
||||
},
|
||||
amountData.tokenPool as TPoolOption,
|
||||
);
|
||||
};
|
||||
|
||||
if (fee) {
|
||||
return (
|
||||
<ConfirmTx
|
||||
open
|
||||
header="Bond details"
|
||||
fee={fee}
|
||||
onClose={onClose}
|
||||
onPrev={resetFeeState}
|
||||
onConfirm={handleConfirm}
|
||||
>
|
||||
<ModalListItem label="Node identity key" value={mixnodeData.identityKey} divider />
|
||||
<ModalListItem
|
||||
label="Amount"
|
||||
value={`${amountData.amount.amount} ${amountData.amount.denom.toUpperCase()}`}
|
||||
divider
|
||||
/>
|
||||
</ConfirmTx>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SimpleModal
|
||||
open
|
||||
onOk={async () => {
|
||||
await validateStep(step);
|
||||
}}
|
||||
onBack={step === 2 ? handleBack : undefined}
|
||||
onClose={onClose}
|
||||
header="Bond mixnode"
|
||||
subHeader={`Step ${step}/2`}
|
||||
okLabel="Next"
|
||||
>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<BondMixnodeForm
|
||||
step={step}
|
||||
denom={denom}
|
||||
mixnodeData={mixnodeData}
|
||||
amountData={amountData}
|
||||
hasVestingTokens={hasVestingTokens}
|
||||
onValidateMixnodeData={handleUpdateMixnodeData}
|
||||
onValidateAmountData={handleUpdateAmountData}
|
||||
onSelectNodeType={onSelectNodeType}
|
||||
/>
|
||||
</Box>
|
||||
</SimpleModal>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,115 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Box, FormHelperText, Stack, TextField } from '@mui/material';
|
||||
import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField';
|
||||
import { ModalListItem } from 'src/components/Modals/ModalListItem';
|
||||
import { SimpleModal } from 'src/components/Modals/SimpleModal';
|
||||
import { DecCoin } from '@nymproject/types';
|
||||
import { TokenPoolSelector, TPoolOption } from 'src/components/TokenPoolSelector';
|
||||
import { ConfirmTx } from 'src/components/ConfirmTX';
|
||||
import { useGetFee } from 'src/hooks/useGetFee';
|
||||
import { validateAmount, validateKey } from 'src/utils';
|
||||
|
||||
export const BondMoreModal = ({
|
||||
currentBond,
|
||||
userBalance,
|
||||
hasVestingTokens,
|
||||
onConfirm,
|
||||
onClose,
|
||||
}: {
|
||||
currentBond: DecCoin;
|
||||
userBalance?: string;
|
||||
hasVestingTokens: boolean;
|
||||
onConfirm: (args: { additionalBond: DecCoin; signature: string; tokenPool: TPoolOption }) => Promise<void>;
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
const { fee, resetFeeState } = useGetFee();
|
||||
const [additionalBond, setAdditionalBond] = useState<DecCoin>({ amount: '0', denom: currentBond.denom });
|
||||
const [signature, setSignature] = useState<string>('');
|
||||
const [tokenPool, setTokenPool] = useState<TPoolOption>('balance');
|
||||
const [errorAmount, setErrorAmount] = useState(false);
|
||||
const [errorSignature, setErrorSignature] = useState(false);
|
||||
|
||||
const handleOnOk = async () => {
|
||||
const errors = {
|
||||
amount: false,
|
||||
signature: false,
|
||||
};
|
||||
|
||||
if (!validateKey(signature || '', 64)) {
|
||||
errors.signature = true;
|
||||
}
|
||||
|
||||
if (!additionalBond?.amount) {
|
||||
errors.amount = true;
|
||||
}
|
||||
|
||||
if (additionalBond && !(await validateAmount(additionalBond.amount, '1'))) {
|
||||
errors.amount = true;
|
||||
}
|
||||
|
||||
if (!errors.amount && !errors.signature) {
|
||||
onConfirm({ additionalBond, signature, tokenPool });
|
||||
} else {
|
||||
setErrorAmount(errors.amount);
|
||||
setErrorSignature(errors.signature);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setErrorAmount(false);
|
||||
}, [additionalBond]);
|
||||
|
||||
if (fee)
|
||||
return (
|
||||
<ConfirmTx
|
||||
header="Bond more details"
|
||||
open
|
||||
fee={fee}
|
||||
onConfirm={async () => onConfirm({ additionalBond, signature, tokenPool })}
|
||||
onPrev={resetFeeState}
|
||||
>
|
||||
<ModalListItem label="Current bond" value={`${currentBond.amount} ${currentBond.denom}`} divider />
|
||||
<ModalListItem label="Additional bond" value={`${additionalBond?.amount} ${additionalBond?.denom}`} divider />
|
||||
</ConfirmTx>
|
||||
);
|
||||
|
||||
return (
|
||||
<SimpleModal
|
||||
open
|
||||
header="Bond more"
|
||||
subHeader="Bond more tokens on your node and receive more rewards"
|
||||
okLabel="Next"
|
||||
onOk={handleOnOk}
|
||||
okDisabled={errorAmount || errorSignature}
|
||||
onClose={onClose}
|
||||
>
|
||||
<Stack gap={2}>
|
||||
<Box display="flex" gap={1}>
|
||||
{hasVestingTokens && <TokenPoolSelector disabled={false} onSelect={(pool) => setTokenPool(pool)} />}
|
||||
<CurrencyFormField
|
||||
autoFocus
|
||||
label="Bond amount"
|
||||
denom={currentBond.denom}
|
||||
onChanged={(value) => {
|
||||
setAdditionalBond(value);
|
||||
setErrorSignature(false);
|
||||
}}
|
||||
fullWidth
|
||||
validationError={errorAmount ? 'Please enter a valid amount' : undefined}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<TextField fullWidth label="Signature" value={signature} onChange={(e) => setSignature(e.target.value)} />
|
||||
{errorSignature && <FormHelperText sx={{ color: 'error.main' }}>Invalid signature</FormHelperText>}
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<ModalListItem label="Account balance" value={userBalance?.toUpperCase() || '-'} divider />
|
||||
<ModalListItem label="Current bond" value={`${currentBond.amount} ${currentBond.denom}`} divider />
|
||||
<ModalListItem label="Est. fee for this operation will be calculated in the next page" value="" divider />
|
||||
</Box>
|
||||
</Stack>
|
||||
</SimpleModal>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { FeeDetails } from '@nymproject/types';
|
||||
import { ModalFee } from 'src/components/Modals/ModalFee';
|
||||
import { ModalListItem } from 'src/components/Modals/ModalListItem';
|
||||
import { SimpleModal } from 'src/components/Modals/SimpleModal';
|
||||
import { TBondedMixnode } from 'src/context';
|
||||
import { useGetFee } from 'src/hooks/useGetFee';
|
||||
import { simulateCompoundOperatorReward, simulateVestingCompoundOperatorReward } from 'src/requests';
|
||||
|
||||
export const CompoundRewardsModal = ({
|
||||
node,
|
||||
onConfirm,
|
||||
onClose,
|
||||
onError,
|
||||
}: {
|
||||
node: TBondedMixnode;
|
||||
onClose: () => void;
|
||||
onConfirm: (fee?: FeeDetails) => void;
|
||||
onError: (err: string) => void;
|
||||
}) => {
|
||||
const { fee, getFee, feeError, isFeeLoading } = useGetFee();
|
||||
|
||||
useEffect(() => {
|
||||
if (feeError) onError(feeError);
|
||||
}, [feeError]);
|
||||
|
||||
useEffect(() => {
|
||||
if (node.proxy) getFee(simulateVestingCompoundOperatorReward, {});
|
||||
else getFee(simulateCompoundOperatorReward, {});
|
||||
}, []);
|
||||
|
||||
const handleOnOK = async () => onConfirm(fee);
|
||||
|
||||
return (
|
||||
<SimpleModal
|
||||
open
|
||||
header="Compound rewards"
|
||||
subHeader="Get more rewards by compounding"
|
||||
okLabel="Compound"
|
||||
okDisabled={isFeeLoading}
|
||||
onOk={handleOnOK}
|
||||
onClose={onClose}
|
||||
>
|
||||
<ModalListItem
|
||||
label="Rewards to redeem"
|
||||
value={`${node.operatorRewards.amount} ${node.operatorRewards.denom.toUpperCase()}`}
|
||||
divider
|
||||
/>
|
||||
<ModalFee fee={fee} isLoading={isFeeLoading} divider />
|
||||
<ModalListItem label="Rewards will be transferred to the account you are logged in with" value="" />
|
||||
</SimpleModal>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
import React from 'react';
|
||||
import { Stack, Typography, SxProps } from '@mui/material';
|
||||
import { Link } from '@nymproject/react/link/Link';
|
||||
import { ConfirmationModal } from 'src/components/Modals/ConfirmationModal';
|
||||
import { ErrorModal } from 'src/components/Modals/ErrorModal';
|
||||
|
||||
export type ConfirmationDetailProps = {
|
||||
status: 'success' | 'error';
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
txUrl?: string;
|
||||
};
|
||||
|
||||
export const ConfirmationDetailsModal = ({
|
||||
title,
|
||||
subtitle,
|
||||
txUrl,
|
||||
status,
|
||||
onClose,
|
||||
sx,
|
||||
backdropProps,
|
||||
}: ConfirmationDetailProps & {
|
||||
onClose: () => void;
|
||||
sx?: SxProps;
|
||||
backdropProps?: object;
|
||||
}) => {
|
||||
if (status === 'error') {
|
||||
<ErrorModal open message={subtitle} onClose={onClose} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<ConfirmationModal
|
||||
open
|
||||
onConfirm={onClose}
|
||||
onClose={onClose}
|
||||
title=""
|
||||
confirmButton="Done"
|
||||
maxWidth="xs"
|
||||
fullWidth
|
||||
sx={sx}
|
||||
backdropProps={backdropProps}
|
||||
>
|
||||
<Stack alignItems="center" spacing={2}>
|
||||
<Typography variant="h6" fontWeight={600}>
|
||||
{title}
|
||||
</Typography>
|
||||
<Typography>{subtitle}</Typography>
|
||||
{txUrl && <Link href={txUrl} target="_blank" sx={{ ml: 1 }} text="View on blockchain" />}
|
||||
</Stack>
|
||||
</ConfirmationModal>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,128 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Box, Button, FormHelperText, TextField, Typography } from '@mui/material';
|
||||
import { SimpleModal } from 'src/components/Modals/SimpleModal';
|
||||
import { Node as NodeIcon } from 'src/svg-icons/node';
|
||||
import { TBondedMixnode } from 'src/context';
|
||||
import { Tabs } from 'src/components/Tabs';
|
||||
import { ModalListItem } from 'src/components/Modals/ModalListItem';
|
||||
import { isDecimal } from 'src/utils';
|
||||
import { useGetFee } from 'src/hooks/useGetFee';
|
||||
import { ConfirmTx } from 'src/components/ConfirmTX';
|
||||
import { simulateUpdateMixnode, simulateVestingUpdateMixnode } from 'src/requests';
|
||||
import { LoadingModal } from 'src/components/Modals/LoadingModal';
|
||||
import { FeeDetails } from '@nymproject/types';
|
||||
|
||||
export const NodeSettings = ({
|
||||
currentPm,
|
||||
isVesting,
|
||||
onConfirm,
|
||||
onClose,
|
||||
onError,
|
||||
}: {
|
||||
currentPm: TBondedMixnode['profitMargin'];
|
||||
isVesting: boolean;
|
||||
onConfirm: (profitMargin: number, fee?: FeeDetails) => Promise<void>;
|
||||
onClose: () => void;
|
||||
onError: (err: string) => void;
|
||||
}) => {
|
||||
const [pm, setPm] = useState(currentPm.toString());
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
const { fee, getFee, resetFeeState, isFeeLoading, feeError } = useGetFee();
|
||||
|
||||
const handleValidate = async () => {
|
||||
let isValid = true;
|
||||
const pmAsNumber = Number(pm);
|
||||
|
||||
if (!pmAsNumber) {
|
||||
isValid = false;
|
||||
}
|
||||
if (isDecimal(pmAsNumber)) {
|
||||
isValid = false;
|
||||
}
|
||||
if (pmAsNumber > 100) {
|
||||
isValid = false;
|
||||
}
|
||||
if (pmAsNumber < 0) {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
setError(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isVesting) {
|
||||
await getFee(simulateVestingUpdateMixnode, { profitMarginPercent: pmAsNumber });
|
||||
} else {
|
||||
await getFee(simulateUpdateMixnode, { profitMarginPercent: pmAsNumber });
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setError(false);
|
||||
}, [pm]);
|
||||
|
||||
useEffect(() => {
|
||||
if (feeError) {
|
||||
onError(feeError);
|
||||
}
|
||||
}, [feeError]);
|
||||
|
||||
if (isFeeLoading) return <LoadingModal />;
|
||||
|
||||
if (fee)
|
||||
return (
|
||||
<ConfirmTx
|
||||
open
|
||||
header="Profit margin change"
|
||||
fee={fee}
|
||||
onPrev={resetFeeState}
|
||||
onClose={onClose}
|
||||
onConfirm={() => onConfirm(Number(pm), fee)}
|
||||
>
|
||||
<ModalListItem label="Current profit margin" value={`${currentPm}%`} divider />
|
||||
<ModalListItem label="New profit margin" value={`${pm}%`} divider />
|
||||
</ConfirmTx>
|
||||
);
|
||||
|
||||
return (
|
||||
<SimpleModal
|
||||
open
|
||||
hideCloseIcon
|
||||
sx={{ p: 0 }}
|
||||
header={
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, p: 3 }}>
|
||||
<NodeIcon />
|
||||
<Typography variant="h6" fontWeight={600}>
|
||||
Node Settings
|
||||
</Typography>
|
||||
</Box>
|
||||
}
|
||||
okLabel="Next"
|
||||
onClose={onClose}
|
||||
>
|
||||
<Tabs tabs={['System variables']} selectedTab={0} disableActiveTabHighlight />
|
||||
<Box sx={{ p: 3 }}>
|
||||
<Typography fontWeight={600} sx={{ mb: 1 }}>
|
||||
Set profit margin
|
||||
</Typography>
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<TextField placeholder="Profit margin" value={pm} onChange={(e) => setPm(e.target.value)} fullWidth />
|
||||
{error && (
|
||||
<FormHelperText sx={{ color: 'error.main' }}>
|
||||
Profit margin should be a whole number between 0 and 100
|
||||
</FormHelperText>
|
||||
)}
|
||||
<FormHelperText>Your new profit margin will be applied in the next epoch</FormHelperText>
|
||||
</Box>
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<ModalListItem label="Est. fee for this operation will be caculated in the next page" value="" />
|
||||
</Box>
|
||||
<Button variant="contained" fullWidth size="large" onClick={handleValidate} disabled={error}>
|
||||
Next
|
||||
</Button>
|
||||
</Box>
|
||||
</SimpleModal>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { FeeDetails } from '@nymproject/types';
|
||||
import { ModalListItem } from 'src/components/Modals/ModalListItem';
|
||||
import { SimpleModal } from 'src/components/Modals/SimpleModal';
|
||||
import { ModalFee } from 'src/components/Modals/ModalFee';
|
||||
import { useGetFee } from 'src/hooks/useGetFee';
|
||||
import { simulateClaimOperatorReward, simulateVestingClaimOperatorReward } from 'src/requests';
|
||||
import { TBondedMixnode } from 'src/context';
|
||||
|
||||
export const RedeemRewardsModal = ({
|
||||
node,
|
||||
onConfirm,
|
||||
onError,
|
||||
onClose,
|
||||
}: {
|
||||
node: TBondedMixnode;
|
||||
onConfirm: (fee?: FeeDetails) => Promise<void>;
|
||||
onError: (err: string) => void;
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
const { fee, getFee, isFeeLoading, feeError } = useGetFee();
|
||||
|
||||
useEffect(() => {
|
||||
if (feeError) onError(feeError);
|
||||
}, [feeError]);
|
||||
|
||||
useEffect(() => {
|
||||
if (node.proxy) getFee(simulateVestingClaimOperatorReward, {});
|
||||
else getFee(simulateClaimOperatorReward, {});
|
||||
}, []);
|
||||
|
||||
const handleOnOK = async () => onConfirm(fee);
|
||||
|
||||
return (
|
||||
<SimpleModal
|
||||
open
|
||||
header="Redeem rewards"
|
||||
subHeader="Claim you rewards"
|
||||
okLabel="Redeem"
|
||||
okDisabled={isFeeLoading}
|
||||
onOk={handleOnOK}
|
||||
onClose={onClose}
|
||||
>
|
||||
<ModalListItem
|
||||
label="Rewards to redeem"
|
||||
value={`${node.operatorRewards.amount} ${node.operatorRewards.denom.toUpperCase()}`}
|
||||
divider
|
||||
/>
|
||||
<ModalFee fee={fee} isLoading={isFeeLoading} divider />
|
||||
<ModalListItem label="Rewards will be transferred to the account you are logged in with" value="" />
|
||||
</SimpleModal>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
import * as React from 'react';
|
||||
import { Typography } from '@mui/material';
|
||||
import { useEffect } from 'react';
|
||||
import { TBondedGateway, TBondedMixnode } from 'src/context';
|
||||
import { useGetFee } from 'src/hooks/useGetFee';
|
||||
import { isGateway, isMixnode } from 'src/types';
|
||||
import { ModalFee } from '../../Modals/ModalFee';
|
||||
import { ModalListItem } from '../../Modals/ModalListItem';
|
||||
import { SimpleModal } from '../../Modals/SimpleModal';
|
||||
import {
|
||||
simulateUnbondGateway,
|
||||
simulateUnbondMixnode,
|
||||
simulateVestingUnbondGateway,
|
||||
simulateVestingUnbondMixnode,
|
||||
} from '../../../requests';
|
||||
|
||||
interface Props {
|
||||
node: TBondedMixnode | TBondedGateway;
|
||||
onConfirm: () => Promise<void>;
|
||||
onClose: () => void;
|
||||
onError: (e: string) => void;
|
||||
}
|
||||
|
||||
export const UnbondModal = ({ node, onConfirm, onClose, onError }: Props) => {
|
||||
const { fee, isFeeLoading, getFee, feeError } = useGetFee();
|
||||
|
||||
useEffect(() => {
|
||||
if (feeError) {
|
||||
onError(feeError);
|
||||
}
|
||||
}, [feeError]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isMixnode(node) && !node.proxy) {
|
||||
getFee(simulateUnbondMixnode, {});
|
||||
}
|
||||
|
||||
if (isMixnode(node) && node.proxy) {
|
||||
getFee(simulateVestingUnbondMixnode, {});
|
||||
}
|
||||
|
||||
if (isGateway(node) && !node.proxy) {
|
||||
getFee(simulateUnbondGateway, {});
|
||||
}
|
||||
|
||||
if (isGateway(node) && node.proxy) {
|
||||
getFee(simulateVestingUnbondGateway, {});
|
||||
}
|
||||
}, [node]);
|
||||
|
||||
return (
|
||||
<SimpleModal
|
||||
open
|
||||
header="Unbond"
|
||||
subHeader="Unbond and remove your node from the mixnet"
|
||||
okLabel="Unbond"
|
||||
onOk={onConfirm}
|
||||
onClose={onClose}
|
||||
>
|
||||
<ModalListItem label="Amount to unbond" value={`${node.bond.amount} ${node.bond.denom.toUpperCase()}`} divider />
|
||||
{isMixnode(node) && (
|
||||
<ModalListItem
|
||||
label="Operator rewards"
|
||||
value={`${node.operatorRewards.amount} ${node.operatorRewards.denom.toUpperCase()}`}
|
||||
divider
|
||||
/>
|
||||
)}
|
||||
<ModalFee isLoading={isFeeLoading} fee={fee} divider />
|
||||
<Typography fontSize="small">Tokens will be transferred to the account you are logged in with now</Typography>
|
||||
</SimpleModal>
|
||||
);
|
||||
};
|
||||
@@ -2,16 +2,19 @@ import React, { useEffect, useState } from 'react';
|
||||
import { Button, CircularProgress, DialogActions, DialogContent, Typography } from '@mui/material';
|
||||
import { useKeyPress } from 'src/hooks/useKeyPress';
|
||||
import { PasswordInput } from './textfields';
|
||||
import { StyledBackButton } from './StyledBackButton';
|
||||
|
||||
export const ConfirmPassword = ({
|
||||
error,
|
||||
isLoading,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
buttonTitle,
|
||||
}: {
|
||||
error?: string;
|
||||
isLoading?: boolean;
|
||||
buttonTitle: string;
|
||||
onCancel?: () => void;
|
||||
onConfirm: (password: string) => void;
|
||||
}) => {
|
||||
const [value, setValue] = useState('');
|
||||
@@ -39,7 +42,8 @@ export const ConfirmPassword = ({
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions sx={{ p: 3, pt: 0 }}>
|
||||
<DialogActions sx={{ p: 3, pt: 0, gap: 2 }}>
|
||||
{onCancel && <StyledBackButton onBack={onCancel} />}
|
||||
<Button
|
||||
disabled={!value.length || isLoading}
|
||||
fullWidth
|
||||
|
||||
@@ -10,9 +10,9 @@ export default {
|
||||
|
||||
const Template: ComponentStory<typeof ConfirmTx> = (args) => (
|
||||
<ConfirmTx {...args}>
|
||||
<ModalListItem label="Transaction type" value="Bond" divider />
|
||||
<ModalListItem label="Current bond" value="100 NYM" divider />
|
||||
<ModalListItem label="Additional bond" value="50 NYM" divider />
|
||||
<ModalListItem label="Transaction type:" value="Bond" divider />
|
||||
<ModalListItem label="Current bond:" value="100 NYM" divider />
|
||||
<ModalListItem label="Additional bond:" value="50 NYM" divider />
|
||||
</ConfirmTx>
|
||||
);
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ export const CopyToClipboard = ({ text = '', iconButton }: { text?: string; icon
|
||||
color: 'text.primary',
|
||||
}}
|
||||
>
|
||||
{!copied ? <ContentCopy fontSize="small" /> : <Check color="success" />}
|
||||
{!copied ? <ContentCopy sx={{ fontSize: 14 }} /> : <Check color="success" sx={{ fontSize: 14 }} />}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -13,12 +13,13 @@ import { TokenPoolSelector, TPoolOption } from '../TokenPoolSelector';
|
||||
import { ConfirmTx } from '../ConfirmTX';
|
||||
|
||||
import { getMixnodeStakeSaturation } from '../../requests';
|
||||
import { ErrorModal } from '../Modals/ErrorModal';
|
||||
|
||||
const MIN_AMOUNT_TO_DELEGATE = 10;
|
||||
|
||||
export const DelegateModal: React.FC<{
|
||||
open: boolean;
|
||||
onClose?: () => void;
|
||||
onClose: () => void;
|
||||
onOk?: (identityKey: string, amount: DecCoin, tokenPool: TPoolOption, fee?: FeeDetails) => Promise<void>;
|
||||
identityKey?: string;
|
||||
onIdentityKeyChanged?: (identityKey: string) => void;
|
||||
@@ -62,7 +63,7 @@ export const DelegateModal: React.FC<{
|
||||
const [tokenPool, setTokenPool] = useState<TPoolOption>('balance');
|
||||
const [errorIdentityKey, setErrorIdentityKey] = useState<string>();
|
||||
|
||||
const { fee, getFee, resetFeeState } = useGetFee();
|
||||
const { fee, getFee, resetFeeState, feeError } = useGetFee();
|
||||
|
||||
const handleCheckStakeSaturation = async (identity: string) => {
|
||||
try {
|
||||
@@ -168,12 +169,24 @@ export const DelegateModal: React.FC<{
|
||||
onPrev={resetFeeState}
|
||||
onConfirm={handleOk}
|
||||
>
|
||||
<ModalListItem label="Node identity key" value={identityKey} divider />
|
||||
<ModalListItem label="Amount" value={`${amount} ${denom.toUpperCase()}`} divider />
|
||||
<ModalListItem label="Node identity key:" value={identityKey} divider />
|
||||
<ModalListItem label="Amount:" value={`${amount} ${denom.toUpperCase()}`} divider />
|
||||
</ConfirmTx>
|
||||
);
|
||||
}
|
||||
|
||||
if (feeError) {
|
||||
return (
|
||||
<ErrorModal
|
||||
title="Something went wrong while calculating fee. Are you sure you entered a valid node address?"
|
||||
message={feeError}
|
||||
sx={sx}
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SimpleModal
|
||||
open={open}
|
||||
@@ -184,23 +197,24 @@ export const DelegateModal: React.FC<{
|
||||
}
|
||||
}}
|
||||
header={header || 'Delegate'}
|
||||
subHeader="Delegate to mixnode"
|
||||
okLabel={buttonText || 'Delegate stake'}
|
||||
okDisabled={!isValidated}
|
||||
sx={sx}
|
||||
backdropProps={backdropProps}
|
||||
>
|
||||
<IdentityKeyFormField
|
||||
required
|
||||
fullWidth
|
||||
placeholder="Node identity key"
|
||||
onChanged={handleIdentityKeyChanged}
|
||||
initialValue={identityKey}
|
||||
readOnly={Boolean(initialIdentityKey)}
|
||||
textFieldProps={{
|
||||
autoFocus: !initialIdentityKey,
|
||||
}}
|
||||
/>
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<IdentityKeyFormField
|
||||
required
|
||||
fullWidth
|
||||
label="Node identity key"
|
||||
onChanged={handleIdentityKeyChanged}
|
||||
initialValue={identityKey}
|
||||
readOnly={Boolean(initialIdentityKey)}
|
||||
textFieldProps={{
|
||||
autoFocus: !initialIdentityKey,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Typography
|
||||
component="div"
|
||||
textAlign="left"
|
||||
@@ -214,7 +228,7 @@ export const DelegateModal: React.FC<{
|
||||
<CurrencyFormField
|
||||
required
|
||||
fullWidth
|
||||
placeholder="Amount"
|
||||
label="Amount"
|
||||
initialValue={amount}
|
||||
autoFocus={Boolean(initialIdentityKey)}
|
||||
onChanged={handleAmountChanged}
|
||||
@@ -230,7 +244,7 @@ export const DelegateModal: React.FC<{
|
||||
{errorAmount}
|
||||
</Typography>
|
||||
<Box sx={{ mt: 3 }}>
|
||||
<ModalListItem label="Account balance" value={accountBalance} divider />
|
||||
<ModalListItem label="Account balance" value={accountBalance?.toUpperCase()} divider fontWeight={600} />
|
||||
</Box>
|
||||
|
||||
<ModalListItem label="Rewards payout interval" value={rewardInterval} hidden divider />
|
||||
@@ -241,7 +255,7 @@ export const DelegateModal: React.FC<{
|
||||
divider
|
||||
/>
|
||||
<ModalListItem
|
||||
label="Node uptime"
|
||||
label="Node avg. uptime"
|
||||
value={`${nodeUptimePercentage ? `${nodeUptimePercentage}%` : '-'}`}
|
||||
hidden={nodeUptimePercentage === undefined}
|
||||
divider
|
||||
@@ -253,6 +267,7 @@ export const DelegateModal: React.FC<{
|
||||
hidden
|
||||
divider
|
||||
/>
|
||||
<ModalListItem label="Est. fee for this transaction will be calculated in the next page" />
|
||||
</SimpleModal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,19 +1,8 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
IconButton,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Stack,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { MoreVertSharp } from '@mui/icons-material';
|
||||
import React, { useState } from 'react';
|
||||
import { Box, Button, Stack, Tooltip, Typography } from '@mui/material';
|
||||
import { DelegationEventKind } from '@nymproject/types';
|
||||
import { Delegate, Undelegate } from '../../svg-icons';
|
||||
import { ActionsMenu, ActionsMenuItem } from '../ActionsMenu';
|
||||
import { DelegateListItemPending } from './types';
|
||||
|
||||
export type DelegationListItemActions = 'delegate' | 'undelegate' | 'redeem' | 'compound';
|
||||
@@ -75,42 +64,20 @@ export const DelegationActions: React.FC<{
|
||||
);
|
||||
};
|
||||
|
||||
const DelegationActionsMenuItem = ({
|
||||
title,
|
||||
description,
|
||||
onClick,
|
||||
Icon,
|
||||
disabled,
|
||||
}: {
|
||||
title: string;
|
||||
description?: string;
|
||||
onClick?: () => void;
|
||||
Icon?: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
}) => (
|
||||
<MenuItem sx={{ p: 2 }} onClick={onClick} disabled={disabled}>
|
||||
<ListItemIcon sx={{ color: 'text.primary' }}>{Icon}</ListItemIcon>
|
||||
<ListItemText sx={{ color: 'text.primary' }} primary={title} secondary={description} />
|
||||
</MenuItem>
|
||||
);
|
||||
|
||||
export const DelegationsActionsMenu: React.FC<{
|
||||
onActionClick?: (action: DelegationListItemActions) => void;
|
||||
isPending?: DelegationEventKind;
|
||||
disableRedeemingRewards?: boolean;
|
||||
disableCompoundRewards?: boolean;
|
||||
}> = ({ disableRedeemingRewards, disableCompoundRewards, onActionClick, isPending }) => {
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const open = Boolean(anchorEl);
|
||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const handleClose = () => setAnchorEl(null);
|
||||
const handleOpenMenu = () => setIsOpen(true);
|
||||
const handleOnClose = () => setIsOpen(false);
|
||||
|
||||
const handleActionSelect = (action: DelegationListItemActions) => {
|
||||
handleClose();
|
||||
onActionClick?.(action);
|
||||
handleOnClose();
|
||||
};
|
||||
|
||||
if (isPending) {
|
||||
@@ -126,37 +93,23 @@ export const DelegationsActionsMenu: React.FC<{
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton onClick={handleClick}>
|
||||
<MoreVertSharp />
|
||||
</IconButton>
|
||||
<Menu anchorEl={anchorEl} open={open} onClose={handleClose}>
|
||||
<DelegationActionsMenuItem
|
||||
title="Delegate more"
|
||||
Icon={<Delegate />}
|
||||
onClick={() => handleActionSelect?.('delegate')}
|
||||
/>
|
||||
<DelegationActionsMenuItem
|
||||
title="Undelegate"
|
||||
Icon={<Undelegate />}
|
||||
onClick={() => handleActionSelect?.('undelegate')}
|
||||
disabled={false}
|
||||
/>
|
||||
<DelegationActionsMenuItem
|
||||
title="Redeem"
|
||||
description="Transfer your rewards to your balance"
|
||||
Icon={<Typography sx={{ pl: 1 }}>R</Typography>}
|
||||
onClick={() => handleActionSelect?.('redeem')}
|
||||
disabled={disableRedeemingRewards}
|
||||
/>
|
||||
<DelegationActionsMenuItem
|
||||
title="Compound"
|
||||
description="Add your rewards to this delegation"
|
||||
Icon={<Typography sx={{ pl: 1 }}>C</Typography>}
|
||||
onClick={() => handleActionSelect?.('compound')}
|
||||
disabled={disableCompoundRewards}
|
||||
/>
|
||||
</Menu>
|
||||
</>
|
||||
<ActionsMenu open={isOpen} onOpen={handleOpenMenu} onClose={handleOnClose}>
|
||||
<ActionsMenuItem title="Delegate more" Icon={<Delegate />} onClick={() => handleActionSelect('delegate')} />
|
||||
<ActionsMenuItem title="Undelegate" Icon={<Undelegate />} onClick={() => handleActionSelect('undelegate')} />
|
||||
<ActionsMenuItem
|
||||
title="Redeem"
|
||||
description="Transfer your rewards to your balance"
|
||||
Icon={<Typography sx={{ pl: 1 }}>R</Typography>}
|
||||
onClick={() => handleActionSelect('redeem')}
|
||||
disabled={disableRedeemingRewards}
|
||||
/>
|
||||
<ActionsMenuItem
|
||||
title="Compound"
|
||||
description="Add your rewards to this delegation"
|
||||
Icon={<Typography sx={{ pl: 1 }}>C</Typography>}
|
||||
onClick={() => handleActionSelect('compound')}
|
||||
disabled={disableCompoundRewards}
|
||||
/>
|
||||
</ActionsMenu>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -29,9 +29,6 @@ const transactionForDarkTheme = {
|
||||
url: 'https://sandbox-blocks.nymtech.net/transactions/11ED7B9E21534A9421834F52FED5103DC6E982949C06335F5E12EFC71DAF0CFO',
|
||||
hash: '11ED7B9E21534A9421834F52FED5103DC6E982949C06335F5E12EFC71DAF0CF0',
|
||||
};
|
||||
const balance = '104 NYMT';
|
||||
const balanceVested = '12 NYMT';
|
||||
const recipient = 'nymt1923pujepxfnv8dqyxqrl078s4ysf3xn2p7z2xa';
|
||||
|
||||
const Content: React.FC<{ children: React.ReactElement<any, any>; handleClick: () => void }> = ({
|
||||
children,
|
||||
@@ -78,8 +75,6 @@ export const DelegateSuccess = () => {
|
||||
status="success"
|
||||
action="delegate"
|
||||
message="You delegated 5 NYM"
|
||||
recipient={recipient}
|
||||
balance={balance}
|
||||
transactions={theme.palette.mode === 'light' ? [transaction] : [transactionForDarkTheme]}
|
||||
{...storybookStyles(theme)}
|
||||
/>
|
||||
@@ -99,8 +94,6 @@ export const UndelegateSuccess = () => {
|
||||
status="success"
|
||||
action="undelegate"
|
||||
message="You undelegated 5 NYM"
|
||||
recipient={recipient}
|
||||
balance={balance}
|
||||
transactions={theme.palette.mode === 'light' ? [transaction] : [transactionForDarkTheme]}
|
||||
{...storybookStyles(theme)}
|
||||
/>
|
||||
@@ -120,8 +113,6 @@ export const RedeemSuccess = () => {
|
||||
status="success"
|
||||
action="redeem"
|
||||
message="42 NYM"
|
||||
recipient={recipient}
|
||||
balance={balance}
|
||||
transactions={
|
||||
theme.palette.mode === 'light'
|
||||
? [transaction, transaction]
|
||||
@@ -145,9 +136,6 @@ export const RedeemWithVestedSuccess = () => {
|
||||
status="success"
|
||||
action="redeem"
|
||||
message="42 NYM"
|
||||
recipient={recipient}
|
||||
balance={balance}
|
||||
balanceVested={balanceVested}
|
||||
transactions={
|
||||
theme.palette.mode === 'light'
|
||||
? [transaction, transaction]
|
||||
@@ -171,8 +159,6 @@ export const RedeemAllSuccess = () => {
|
||||
status="success"
|
||||
action="redeem-all"
|
||||
message="42 NYM"
|
||||
recipient={recipient}
|
||||
balance={balance}
|
||||
transactions={
|
||||
theme.palette.mode === 'light'
|
||||
? [transaction, transaction]
|
||||
@@ -196,8 +182,6 @@ export const Error = () => {
|
||||
status="error"
|
||||
action="redeem-all"
|
||||
message="Minim esse veniam Lorem id velit Lorem eu eu est. Excepteur labore sunt do proident proident sint aliquip consequat Lorem sint non nulla ad excepteur."
|
||||
recipient={recipient}
|
||||
balance={balance}
|
||||
transactions={theme.palette.mode === 'light' ? [transaction] : [transactionForDarkTheme]}
|
||||
{...storybookStyles(theme)}
|
||||
/>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React from 'react';
|
||||
import { Box, Button, Modal, Typography, SxProps } from '@mui/material';
|
||||
import { Typography, SxProps, Stack } from '@mui/material';
|
||||
import { Link } from '@nymproject/react/link/Link';
|
||||
import { Console } from 'src/utils/console';
|
||||
import { modalStyle } from '../Modals/styles';
|
||||
import { LoadingModal } from '../Modals/LoadingModal';
|
||||
import { ConfirmationModal } from '../Modals/ConfirmationModal';
|
||||
import { ErrorModal } from '../Modals/ErrorModal';
|
||||
|
||||
export type ActionType = 'delegate' | 'undelegate' | 'redeem' | 'redeem-all' | 'compound';
|
||||
|
||||
@@ -15,9 +16,9 @@ const actionToHeader = (action: ActionType): string => {
|
||||
case 'redeem-all':
|
||||
return 'All rewards redeemed successfully';
|
||||
case 'delegate':
|
||||
return 'Delegation complete';
|
||||
return 'Delegation successful';
|
||||
case 'undelegate':
|
||||
return 'Undelegation complete';
|
||||
return 'Undelegation successful';
|
||||
case 'compound':
|
||||
return 'Rewards compounded successfully';
|
||||
default:
|
||||
@@ -29,9 +30,6 @@ export type DelegationModalProps = {
|
||||
status: 'loading' | 'success' | 'error';
|
||||
action: ActionType;
|
||||
message?: string;
|
||||
recipient?: string;
|
||||
balance?: string;
|
||||
balanceVested?: string;
|
||||
transactions?: {
|
||||
url: string;
|
||||
hash: string;
|
||||
@@ -41,94 +39,44 @@ export type DelegationModalProps = {
|
||||
export const DelegationModal: React.FC<
|
||||
DelegationModalProps & {
|
||||
open: boolean;
|
||||
onClose?: () => void;
|
||||
onClose: () => void;
|
||||
sx?: SxProps;
|
||||
backdropProps?: object;
|
||||
}
|
||||
> = ({
|
||||
status,
|
||||
action,
|
||||
message,
|
||||
recipient,
|
||||
balance,
|
||||
balanceVested,
|
||||
transactions,
|
||||
open,
|
||||
onClose,
|
||||
children,
|
||||
sx,
|
||||
backdropProps,
|
||||
}) => {
|
||||
> = ({ status, action, message, transactions, open, onClose, children, sx, backdropProps }) => {
|
||||
if (status === 'loading') return <LoadingModal sx={sx} backdropProps={backdropProps} />;
|
||||
|
||||
if (status === 'error') {
|
||||
return (
|
||||
<Modal open={open} onClose={onClose} BackdropProps={backdropProps}>
|
||||
<Box sx={{ ...modalStyle, ...sx }} textAlign="center">
|
||||
<Typography color={(theme) => theme.palette.error.main} mb={1}>
|
||||
Oh no! Something went wrong...
|
||||
</Typography>
|
||||
<Typography my={5} color="text.primary">
|
||||
{message}
|
||||
</Typography>
|
||||
{children}
|
||||
<Button variant="contained" onClick={onClose}>
|
||||
Close
|
||||
</Button>
|
||||
</Box>
|
||||
</Modal>
|
||||
<ErrorModal message={message} sx={sx} open={open} onClose={onClose}>
|
||||
{children}
|
||||
</ErrorModal>
|
||||
);
|
||||
}
|
||||
|
||||
transactions?.map((transaction) => Console.log('action', action, 'status', status, 'key', transaction.hash));
|
||||
return (
|
||||
<Modal open={open} onClose={onClose} BackdropProps={backdropProps}>
|
||||
<Box sx={{ ...modalStyle, ...sx }} textAlign="center">
|
||||
<Typography color={(theme) => theme.palette.success.main} mb={1}>
|
||||
{actionToHeader(action)}
|
||||
</Typography>
|
||||
<Typography mb={3} color="text.primary">
|
||||
{message}
|
||||
</Typography>
|
||||
|
||||
{recipient && (
|
||||
<Typography mb={1} fontSize="small" color={(theme) => theme.palette.text.secondary}>
|
||||
Recipient: {recipient}
|
||||
</Typography>
|
||||
return (
|
||||
<ConfirmationModal
|
||||
open={open}
|
||||
onConfirm={onClose || (() => {})}
|
||||
title={actionToHeader(action)}
|
||||
confirmButton="Done"
|
||||
>
|
||||
<Stack alignItems="center" spacing={2} mb={0}>
|
||||
{message && <Typography>{message}</Typography>}
|
||||
{transactions?.length === 1 && (
|
||||
<Link href={transactions[0].url} target="_blank" sx={{ ml: 1 }} text="View on blockchain" noIcon />
|
||||
)}
|
||||
{balanceVested ? (
|
||||
<>
|
||||
<Typography mb={1} fontSize="small" color={(theme) => theme.palette.text.secondary}>
|
||||
Your current balance: {balance?.toUpperCase()}
|
||||
</Typography>
|
||||
<Typography mb={1} fontSize="small" color={(theme) => theme.palette.text.secondary}>
|
||||
({balanceVested.toUpperCase()} is unlocked in your vesting account)
|
||||
</Typography>
|
||||
</>
|
||||
) : (
|
||||
<Typography mb={1} fontSize="small" color={(theme) => theme.palette.text.secondary}>
|
||||
Your current balance: {balance?.toUpperCase()}
|
||||
</Typography>
|
||||
)}
|
||||
{transactions && (
|
||||
<Typography mb={1} fontSize="small" color={(theme) => theme.palette.text.secondary}>
|
||||
Check the transaction {transactions.length > 1 ? 'hashes' : 'hash'}:
|
||||
{transactions.map((transaction) => (
|
||||
<Link
|
||||
key={transaction.hash}
|
||||
href={transaction.url}
|
||||
target="_blank"
|
||||
sx={{ ml: 1 }}
|
||||
text={transaction.hash.slice(0, 6)}
|
||||
/>
|
||||
{transactions && transactions.length > 1 && (
|
||||
<Stack alignItems="center" spacing={1}>
|
||||
<Typography>View the transactions on blockchain:</Typography>
|
||||
{transactions.map(({ url, hash }) => (
|
||||
<Link href={url} target="_blank" sx={{ ml: 1 }} text={hash.slice(0, 6)} key={hash} noIcon />
|
||||
))}
|
||||
</Typography>
|
||||
</Stack>
|
||||
)}
|
||||
{children}
|
||||
<Button variant="contained" sx={{ mt: 3 }} size="large" onClick={onClose}>
|
||||
Finish
|
||||
</Button>
|
||||
</Box>
|
||||
</Modal>
|
||||
</Stack>
|
||||
</ConfirmationModal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -40,7 +40,6 @@ export const UndelegateModal: React.FC<{
|
||||
onClose={onClose}
|
||||
onOk={handleOk}
|
||||
header="Undelegate"
|
||||
subHeader="Undelegate from mixnode"
|
||||
okLabel="Undelegate stake"
|
||||
okDisabled={!fee}
|
||||
sx={sx}
|
||||
@@ -55,14 +54,10 @@ export const UndelegateModal: React.FC<{
|
||||
/>
|
||||
|
||||
<Box sx={{ mt: 3 }}>
|
||||
<ModalListItem label="Delegation amount" value={`${amount} ${currency}`} divider />
|
||||
<ModalListItem label="Delegation amount" value={`${amount} ${currency.toUpperCase()}`} divider />
|
||||
<ModalFee fee={fee} isLoading={isFeeLoading} error={feeError} divider />
|
||||
<ModalListItem label=" Tokens will be transferred to account you are logged in with now" value="" divider />
|
||||
</Box>
|
||||
|
||||
<Typography mb={5} fontSize="smaller" sx={{ color: 'text.primary' }}>
|
||||
Tokens will be transferred to account you are logged in with now
|
||||
</Typography>
|
||||
|
||||
<ModalFee fee={fee} isLoading={isFeeLoading} error={feeError} />
|
||||
</SimpleModal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import { Stack, Typography } from '@mui/material';
|
||||
import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard';
|
||||
import { splice } from 'src/utils';
|
||||
|
||||
export const IdentityKey = ({ identityKey }: { identityKey: string }) => (
|
||||
<Stack direction="row">
|
||||
<Typography variant="body2" component="span" fontWeight={400} sx={{ mr: 1, color: 'text.primary' }}>
|
||||
{splice(6, identityKey)}
|
||||
</Typography>
|
||||
<CopyToClipboard value={identityKey} sx={{ fontSize: 18 }} />
|
||||
</Stack>
|
||||
);
|
||||
@@ -41,7 +41,7 @@ export const ConfirmationModal = ({
|
||||
backdropProps,
|
||||
}: ConfirmationModalProps) => {
|
||||
const Title = (
|
||||
<DialogTitle id="responsive-dialog-title" sx={{ py: 3, pb: 2, fontWeight: 600 }} color="text.primary">
|
||||
<DialogTitle id="responsive-dialog-title" sx={{ pb: 2 }}>
|
||||
{title}
|
||||
{subTitle &&
|
||||
(typeof subTitle === 'string' ? (
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
import { Box, Button, Modal, SxProps, Typography } from '@mui/material';
|
||||
import { modalStyle } from './styles';
|
||||
|
||||
export const ErrorModal: React.FC<{
|
||||
open: boolean;
|
||||
title?: string;
|
||||
message?: string;
|
||||
sx?: SxProps;
|
||||
backdropProps?: object;
|
||||
onClose: () => void;
|
||||
}> = ({ children, open, title, message, sx, backdropProps, onClose }) => (
|
||||
<Modal open={open} onClose={onClose} BackdropProps={backdropProps}>
|
||||
<Box sx={{ ...modalStyle, ...sx }} textAlign="center">
|
||||
<Typography color={(theme) => theme.palette.error.main} mb={1}>
|
||||
{title || 'Oh no! Something went wrong...'}
|
||||
</Typography>
|
||||
<Typography my={5} color="text.primary">
|
||||
{message}
|
||||
</Typography>
|
||||
{children}
|
||||
<Button variant="contained" onClick={onClose}>
|
||||
Close
|
||||
</Button>
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
@@ -2,16 +2,20 @@ import React from 'react';
|
||||
import { FeeDetails } from '@nymproject/types';
|
||||
import { CircularProgress } from '@mui/material';
|
||||
import { ModalListItem } from './ModalListItem';
|
||||
import { ModalDivider } from './ModalDivider';
|
||||
|
||||
type TFeeProps = { fee?: FeeDetails; isLoading: boolean; error?: string };
|
||||
type TFeeProps = { fee?: FeeDetails; isLoading: boolean; error?: string; divider?: boolean };
|
||||
|
||||
const getValue = ({ fee, isLoading, error }: TFeeProps) => {
|
||||
if (isLoading) return <CircularProgress size={15} />;
|
||||
if (error && !isLoading) return 'n/a';
|
||||
if (fee) return `${fee.amount?.amount} ${fee.amount?.denom}`;
|
||||
if (fee) return `${fee.amount?.amount} ${fee.amount?.denom.toUpperCase()}`;
|
||||
return '-';
|
||||
};
|
||||
|
||||
export const ModalFee = ({ fee, isLoading, error }: TFeeProps) => (
|
||||
<ModalListItem label="Estimated fee for this operation" value={getValue({ fee, isLoading, error })} />
|
||||
export const ModalFee = ({ fee, isLoading, error, divider }: TFeeProps) => (
|
||||
<>
|
||||
<ModalListItem label="Fee for this operation" value={getValue({ fee, isLoading, error })} />
|
||||
{divider && <ModalDivider />}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
import React from 'react';
|
||||
import { Box, Stack, Typography } from '@mui/material';
|
||||
import { Box, Stack, Typography, TypographyProps } from '@mui/material';
|
||||
import { ModalDivider } from './ModalDivider';
|
||||
import { fontWeight } from '@mui/system';
|
||||
|
||||
type TFontWeight = 'strong' | 'light';
|
||||
|
||||
export const ModalListItem: React.FC<{
|
||||
label: string;
|
||||
divider?: boolean;
|
||||
hidden?: boolean;
|
||||
strong?: boolean;
|
||||
value: React.ReactNode;
|
||||
}> = ({ label, value, hidden, divider, strong }) => (
|
||||
fontWeight?: TypographyProps['fontWeight'];
|
||||
light?: boolean;
|
||||
value?: React.ReactNode;
|
||||
}> = ({ label, value, hidden, fontWeight, divider }) => (
|
||||
<Box sx={{ display: hidden ? 'none' : 'block' }}>
|
||||
<Stack direction="row" justifyContent="space-between">
|
||||
<Typography fontSize="smaller" fontWeight={strong ? 600 : undefined} sx={{ color: 'text.primary' }}>
|
||||
{label}:
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="smaller"
|
||||
fontWeight={strong ? 600 : undefined}
|
||||
sx={{ color: 'text.primary', textTransform: 'uppercase' }}
|
||||
>
|
||||
{value}
|
||||
<Typography fontSize="smaller" fontWeight={fontWeight} sx={{ color: 'text.primary' }}>
|
||||
{label}
|
||||
</Typography>
|
||||
{value && (
|
||||
<Typography fontSize="smaller" fontWeight={fontWeight} sx={{ color: 'text.primary' }}>
|
||||
{value}
|
||||
</Typography>
|
||||
)}
|
||||
</Stack>
|
||||
{divider && <ModalDivider />}
|
||||
</Box>
|
||||
|
||||
@@ -14,7 +14,7 @@ export const SimpleModal: React.FC<{
|
||||
onClose?: () => void;
|
||||
onOk?: () => Promise<void>;
|
||||
onBack?: () => void;
|
||||
header: string;
|
||||
header: string | React.ReactNode;
|
||||
subHeader?: string;
|
||||
okLabel: string;
|
||||
okDisabled?: boolean;
|
||||
@@ -41,22 +41,25 @@ export const SimpleModal: React.FC<{
|
||||
<Box sx={{ ...modalStyle, ...sx }}>
|
||||
{displayErrorIcon && <ErrorOutline color="error" sx={{ mb: 3 }} />}
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Typography fontSize={20} fontWeight={600} sx={{ color: 'text.primary', ...headerStyles }}>
|
||||
{header}
|
||||
</Typography>
|
||||
{typeof header === 'string' ? (
|
||||
<Typography fontSize={20} fontWeight={600} sx={{ color: 'text.primary', ...headerStyles }}>
|
||||
{header}
|
||||
</Typography>
|
||||
) : (
|
||||
header
|
||||
)}
|
||||
{!hideCloseIcon && <CloseIcon onClick={onClose} cursor="pointer" />}
|
||||
</Stack>
|
||||
{subHeader && (
|
||||
<Typography
|
||||
mt={0.5}
|
||||
mb={3}
|
||||
fontSize={12}
|
||||
color={(theme) => theme.palette.text.secondary}
|
||||
sx={{ color: (theme) => theme.palette.nym.nymWallet.text.muted, ...subHeaderStyles }}
|
||||
>
|
||||
{subHeader}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
<Typography
|
||||
mt={0.5}
|
||||
mb={3}
|
||||
fontSize={12}
|
||||
color={(theme) => theme.palette.text.secondary}
|
||||
sx={{ color: (theme) => theme.palette.nym.nymWallet.text.muted, ...subHeaderStyles }}
|
||||
>
|
||||
{subHeader}
|
||||
</Typography>
|
||||
|
||||
{children}
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ import React, { useState, useContext } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { List, ListItem, ListItemIcon, ListItemText } from '@mui/material';
|
||||
import { AccountBalanceWalletOutlined, ArrowBack, ArrowForward, Description, Settings } from '@mui/icons-material';
|
||||
import { AppContext } from '../context';
|
||||
import { Bond, Delegate, Unbond } from '../svg-icons';
|
||||
import { AppContext } from '../context/main';
|
||||
import { Delegate, Bonding } from '../svg-icons';
|
||||
|
||||
export const Nav = () => {
|
||||
const location = useLocation();
|
||||
@@ -29,16 +29,10 @@ export const Nav = () => {
|
||||
onClick: handleShowReceiveModal,
|
||||
},
|
||||
{
|
||||
label: 'Bond',
|
||||
route: '/bond',
|
||||
Icon: Bond,
|
||||
onClick: () => navigate('/bond'),
|
||||
},
|
||||
{
|
||||
label: 'Unbond',
|
||||
route: '/unbond',
|
||||
Icon: Unbond,
|
||||
onClick: () => navigate('/unbond'),
|
||||
label: 'Bonding',
|
||||
route: '/bonding',
|
||||
Icon: Bonding,
|
||||
onClick: () => navigate('/bonding'),
|
||||
},
|
||||
{
|
||||
label: 'Delegation',
|
||||
@@ -68,9 +62,16 @@ export const Nav = () => {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
marginLeft: 12,
|
||||
marginRight: 12,
|
||||
}}
|
||||
>
|
||||
<List disablePadding>
|
||||
<List
|
||||
disablePadding
|
||||
sx={{
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
{routesSchema
|
||||
.filter(({ mode }) => {
|
||||
if (!mode) {
|
||||
@@ -86,17 +87,35 @@ export const Nav = () => {
|
||||
}
|
||||
})
|
||||
.map(({ Icon, onClick, label, route }) => (
|
||||
<ListItem disableGutters key={label} onClick={onClick} sx={{ cursor: 'pointer' }}>
|
||||
<ListItem
|
||||
disableGutters
|
||||
key={label}
|
||||
onClick={onClick}
|
||||
sx={{
|
||||
cursor: 'pointer',
|
||||
py: 2,
|
||||
paddingLeft: 3.5,
|
||||
borderRadius: 1,
|
||||
'&:hover': { backgroundColor: (theme) => theme.palette.nym.nymWallet.hover.background },
|
||||
}}
|
||||
>
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
height: '20px',
|
||||
minWidth: 30,
|
||||
color: location.pathname === route ? 'primary.main' : 'text.primary',
|
||||
}}
|
||||
>
|
||||
<Icon sx={{ fontSize: 20 }} />
|
||||
<Icon
|
||||
sx={{
|
||||
fontSize: 20,
|
||||
}}
|
||||
/>
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
sx={{
|
||||
height: '20px',
|
||||
margin: 0,
|
||||
color: location.pathname === route ? 'primary.main' : 'text.primary',
|
||||
'& .MuiListItemText-primary': {
|
||||
fontSize: 14,
|
||||
|
||||
@@ -39,7 +39,7 @@ export const NetworkSelector = () => {
|
||||
<>
|
||||
<Button
|
||||
variant="text"
|
||||
color="primary"
|
||||
color="inherit"
|
||||
sx={{ color: 'text.primary' }}
|
||||
onClick={handleClick}
|
||||
disableElevation
|
||||
|
||||
@@ -26,7 +26,7 @@ export const NymCard: React.FC<{
|
||||
subheader={subheader}
|
||||
data-testid={dataTestid || title}
|
||||
subheaderTypographyProps={{ variant: 'subtitle1' }}
|
||||
action={<Box sx={{ mt: 1, mr: 1 }}>{Action}</Box>}
|
||||
action={Action}
|
||||
/>
|
||||
{noPadding ? (
|
||||
<CardContentNoPadding>{children}</CardContentNoPadding>
|
||||
|
||||
@@ -1,42 +1,33 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { AppContext } from 'src/context';
|
||||
import { Box, Stack, Typography, SxProps } from '@mui/material';
|
||||
import { Box, Stack, Typography, SxProps, Dialog, DialogTitle, DialogContent } from '@mui/material';
|
||||
import QRCode from 'qrcode.react';
|
||||
import { SimpleModal } from '../Modals/SimpleModal';
|
||||
import { ClientAddress } from '../ClientAddress';
|
||||
import { ModalListItem } from '../Modals/ModalListItem';
|
||||
import { Close as CloseIcon } from '@mui/icons-material';
|
||||
|
||||
export const ReceiveModal = ({
|
||||
onClose,
|
||||
open,
|
||||
sx,
|
||||
backdropProps,
|
||||
}: {
|
||||
onClose: () => void;
|
||||
open: boolean;
|
||||
sx?: SxProps;
|
||||
backdropProps?: object;
|
||||
}) => {
|
||||
export const ReceiveModal = ({ onClose }: { onClose: () => void; sx?: SxProps; backdropProps?: object }) => {
|
||||
const { clientDetails } = useContext(AppContext);
|
||||
return (
|
||||
<SimpleModal
|
||||
header="Receive"
|
||||
okLabel="Ok"
|
||||
onClose={onClose}
|
||||
open={open}
|
||||
sx={{ width: 'small', ...sx }}
|
||||
backdropProps={backdropProps}
|
||||
>
|
||||
<Stack spacing={3} sx={{ mt: 1.6 }}>
|
||||
<Stack direction="row" alignItems="center" gap={4}>
|
||||
<Typography>Your address:</Typography>
|
||||
<ClientAddress withCopy showEntireAddress />
|
||||
</Stack>
|
||||
<Stack alignItems="center">
|
||||
<Box sx={{ border: (t) => `1px solid ${t.palette.nym.highlight}`, borderRadius: 2, p: 2 }}>
|
||||
<Dialog open maxWidth="sm" fullWidth onClose={onClose}>
|
||||
<DialogTitle>
|
||||
<Box sx={{ mt: 1, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<Typography fontSize={20} fontWeight={600}>
|
||||
Receive
|
||||
</Typography>
|
||||
<CloseIcon onClick={onClose} cursor="pointer" />
|
||||
</Box>
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ p: 0 }}>
|
||||
<Box sx={{ px: 3 }}>
|
||||
<ModalListItem label="Your address:" value={<ClientAddress withCopy showEntireAddress />} />
|
||||
</Box>
|
||||
<Stack alignItems="center" sx={{ px: 0, py: 3, mt: 3, bgcolor: 'rgba(251, 110, 78, 5%)' }}>
|
||||
<Box sx={{ border: (t) => `1px solid ${t.palette.nym.highlight}`, bgcolor: 'white', borderRadius: 2, p: 3 }}>
|
||||
{clientDetails && <QRCode data-testid="qr-code" value={clientDetails?.client_address} />}
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</SimpleModal>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,8 +5,7 @@ import { ReceiveModal } from './ReceiveModal';
|
||||
export const Receive = ({ hasStorybookStyles }: { hasStorybookStyles?: {} }) => {
|
||||
const { showReceiveModal, handleShowReceiveModal } = useContext(AppContext);
|
||||
|
||||
if (showReceiveModal)
|
||||
return <ReceiveModal onClose={handleShowReceiveModal} open={showReceiveModal} {...hasStorybookStyles} />;
|
||||
if (showReceiveModal) return <ReceiveModal onClose={handleShowReceiveModal} {...hasStorybookStyles} />;
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Stack, Typography } from '@mui/material';
|
||||
import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField';
|
||||
import { CurrencyDenom, FeeDetails } from '@nymproject/types';
|
||||
import { simulateCompoundDelgatorReward, simulateVestingCompoundDelgatorReward } from 'src/requests';
|
||||
@@ -7,6 +6,7 @@ import { useGetFee } from 'src/hooks/useGetFee';
|
||||
import { SimpleModal } from '../Modals/SimpleModal';
|
||||
import { ModalFee } from '../Modals/ModalFee';
|
||||
import { FeeWarning } from '../FeeWarning';
|
||||
import { ModalListItem } from '../Modals/ModalListItem';
|
||||
|
||||
export const CompoundModal: React.FC<{
|
||||
open: boolean;
|
||||
@@ -42,20 +42,13 @@ export const CompoundModal: React.FC<{
|
||||
subHeader="Compound rewards from delegations"
|
||||
okLabel="Compound rewards"
|
||||
>
|
||||
{identityKey && <IdentityKeyFormField readOnly fullWidth initialValue={identityKey} showTickOnValid={false} />}
|
||||
|
||||
<Stack direction="row" justifyContent="space-between" mb={4} mt={identityKey && 4}>
|
||||
<Typography>Rewards amount:</Typography>
|
||||
<Typography>
|
||||
{amount} {denom.toUpperCase()}
|
||||
</Typography>
|
||||
</Stack>
|
||||
|
||||
<Typography mb={5} fontSize="smaller">
|
||||
Rewards will be transferred to account you are logged in with now
|
||||
</Typography>
|
||||
{identityKey && (
|
||||
<IdentityKeyFormField readOnly fullWidth initialValue={identityKey} showTickOnValid={false} sx={{ mb: 2 }} />
|
||||
)}
|
||||
<ModalListItem label="Rewards amount" value={` ${amount} ${denom.toUpperCase()}`} divider />
|
||||
<ModalFee fee={fee} isLoading={isFeeLoading} error={feeError} divider />
|
||||
<ModalListItem label="Rewards will be added to this delegation" value="" divider />
|
||||
{fee && <FeeWarning amount={amount} fee={fee} />}
|
||||
<ModalFee fee={fee} isLoading={isFeeLoading} error={feeError} />
|
||||
</SimpleModal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Stack, Typography, SxProps } from '@mui/material';
|
||||
import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField';
|
||||
import { CurrencyDenom, FeeDetails } from '@nymproject/types';
|
||||
import { SxProps } from '@mui/material';
|
||||
import { useGetFee } from 'src/hooks/useGetFee';
|
||||
import { simulateClaimDelgatorReward, simulateVestingClaimDelgatorReward } from 'src/requests';
|
||||
import { ModalFee } from '../Modals/ModalFee';
|
||||
import { SimpleModal } from '../Modals/SimpleModal';
|
||||
import { FeeWarning } from '../FeeWarning';
|
||||
import { ModalListItem } from '../Modals/ModalListItem';
|
||||
|
||||
export const RedeemModal: React.FC<{
|
||||
open: boolean;
|
||||
@@ -47,20 +48,13 @@ export const RedeemModal: React.FC<{
|
||||
sx={sx}
|
||||
backdropProps={backdropProps}
|
||||
>
|
||||
{identityKey && <IdentityKeyFormField readOnly fullWidth initialValue={identityKey} showTickOnValid={false} />}
|
||||
|
||||
<Stack direction="row" justifyContent="space-between" mb={4} mt={identityKey && 4}>
|
||||
<Typography sx={{ color: 'text.primary' }}>Rewards amount:</Typography>
|
||||
<Typography sx={{ color: 'text.primary' }}>
|
||||
{amount} {denom.toUpperCase()}
|
||||
</Typography>
|
||||
</Stack>
|
||||
|
||||
<Typography mb={5} fontSize="smaller" sx={{ color: 'text.primary' }}>
|
||||
Rewards will be transferred to account you are logged in with now
|
||||
</Typography>
|
||||
{identityKey && (
|
||||
<IdentityKeyFormField readOnly fullWidth initialValue={identityKey} showTickOnValid={false} sx={{ mb: 2 }} />
|
||||
)}
|
||||
<ModalListItem label="Rewards amount" value={` ${amount} ${denom.toUpperCase()}`} divider />
|
||||
<ModalFee fee={fee} isLoading={isFeeLoading} error={feeError} divider />
|
||||
<ModalListItem label="Rewards will be transferred to account you are logged in with now" value="" divider />
|
||||
{fee && <FeeWarning amount={amount} fee={fee} />}
|
||||
<ModalFee fee={fee} isLoading={isFeeLoading} error={feeError} />
|
||||
</SimpleModal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { CircularProgress, Stack, Typography } from '@mui/material';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { InfoTooltip } from '../InfoToolTip';
|
||||
|
||||
export const RewardsSummary: React.FC<{
|
||||
isLoading?: boolean;
|
||||
@@ -9,15 +10,17 @@ export const RewardsSummary: React.FC<{
|
||||
}> = ({ isLoading, totalDelegation, totalRewards }) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Stack direction="row" justifyContent="space-between">
|
||||
<Stack direction="row" spacing={4}>
|
||||
<Stack direction="row" spacing={2}>
|
||||
<Stack direction="row" spacing={1} alignItems="center">
|
||||
<InfoTooltip title="This is the total amount you have delgated across multiple nodes" />
|
||||
<Typography>Total delegations:</Typography>
|
||||
<Typography fontWeight={600} fontSize={16} textTransform="uppercase">
|
||||
{isLoading ? <CircularProgress size={theme.typography.fontSize} /> : totalDelegation || '-'}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Stack direction="row" spacing={2}>
|
||||
<Stack direction="row" spacing={1} alignItems="center">
|
||||
<InfoTooltip title="Awaiting rewards accrue per epoch (hourly). You can redeem or compound them" />
|
||||
<Typography>New rewards:</Typography>
|
||||
<Typography fontWeight={600} fontSize={16} textTransform="uppercase">
|
||||
{isLoading ? <CircularProgress size={theme.typography.fontSize} /> : totalRewards || '-'}
|
||||
|
||||
@@ -30,6 +30,7 @@ export const SendInput = () => {
|
||||
<SendInputModal
|
||||
toAddress=""
|
||||
fromAddress="nymt1w8qp7zsxggvtxhpqpt6e329j42wtv07dm5ts8u"
|
||||
denom="nym"
|
||||
onNext={() => {}}
|
||||
onClose={() => {}}
|
||||
onAddressChange={() => {}}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import { Stack, SxProps } from '@mui/material';
|
||||
import { CurrencyDenom } from '@nymproject/types';
|
||||
import { FeeDetails, DecCoin } from '@nymproject/types';
|
||||
import { FeeDetails, DecCoin, CurrencyDenom } from '@nymproject/types';
|
||||
import { SimpleModal } from '../Modals/SimpleModal';
|
||||
import { ModalListItem } from '../Modals/ModalListItem';
|
||||
import { ModalFee } from '../Modals/ModalFee';
|
||||
|
||||
export const SendDetailsModal = ({
|
||||
amount,
|
||||
@@ -39,14 +39,10 @@ export const SendDetailsModal = ({
|
||||
backdropProps={backdropProps}
|
||||
>
|
||||
<Stack gap={0.5} sx={{ mt: 4 }}>
|
||||
<ModalListItem label="From" value={fromAddress} divider />
|
||||
<ModalListItem label="To" value={toAddress} divider />
|
||||
<ModalListItem label="Amount" value={`${amount?.amount} ${denom.toUpperCase()}`} divider />
|
||||
<ModalListItem
|
||||
label="Fee for this transaction"
|
||||
value={!fee ? 'n/a' : `${fee.amount?.amount} ${fee.amount?.denom}`}
|
||||
divider
|
||||
/>
|
||||
<ModalListItem label="From:" value={fromAddress} divider />
|
||||
<ModalListItem label="To:" value={toAddress} divider />
|
||||
<ModalListItem label="Amount:" value={`${amount?.amount} ${denom.toUpperCase()}`} divider />
|
||||
<ModalFee fee={fee} divider isLoading={false} />
|
||||
</Stack>
|
||||
</SimpleModal>
|
||||
);
|
||||
|
||||
@@ -56,6 +56,7 @@ export const SendInputModal = ({
|
||||
backdropProps={backdropProps}
|
||||
>
|
||||
<Stack gap={2} sx={{ mt: 2 }}>
|
||||
<ModalListItem label="Your address:" value={fromAddress} fontWeight="light" />
|
||||
<TextField
|
||||
placeholder="Recipient address"
|
||||
fullWidth
|
||||
@@ -76,9 +77,8 @@ export const SendInputModal = ({
|
||||
{error}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Stack gap={0.5} sx={{ mt: 2 }}>
|
||||
<ModalListItem label="Account balance" value={balance} divider strong />
|
||||
<ModalListItem label="Your address" value={fromAddress} divider />
|
||||
<Stack gap={0.5} sx={{ mt: 1 }}>
|
||||
<ModalListItem label="Account balance:" value={balance?.toUpperCase()} divider fontWeight={600} />
|
||||
<Typography fontSize="smaller" sx={{ color: 'text.primary' }}>
|
||||
Est. fee for this transaction will be show on the next page
|
||||
</Typography>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import { Tab, Tabs as MuiTabs } from '@mui/material';
|
||||
|
||||
export const Tabs: React.FC<{
|
||||
tabs: string[];
|
||||
selectedTab: number;
|
||||
disabled?: boolean;
|
||||
onChange?: (event: React.SyntheticEvent, tab: number) => void;
|
||||
disableActiveTabHighlight?: boolean;
|
||||
}> = ({ tabs, selectedTab, disabled, disableActiveTabHighlight, onChange }) => (
|
||||
<MuiTabs
|
||||
value={selectedTab}
|
||||
onChange={onChange}
|
||||
sx={{
|
||||
bgcolor: (theme) => theme.palette.nym.nymWallet.background.grey,
|
||||
borderTop: '1px solid',
|
||||
borderBottom: '1px solid',
|
||||
borderColor: (theme) => theme.palette.nym.nymWallet.background.greyStroke,
|
||||
}}
|
||||
textColor="inherit"
|
||||
TabIndicatorProps={
|
||||
disableActiveTabHighlight
|
||||
? {
|
||||
style: {
|
||||
opacity: 0,
|
||||
},
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>
|
||||
{tabs.map((tabName) => (
|
||||
<Tab key={tabName} label={tabName} sx={{ textTransform: 'capitalize' }} disabled={disabled} />
|
||||
))}
|
||||
</MuiTabs>
|
||||
);
|
||||
@@ -4,7 +4,7 @@ import { Box, Typography } from '@mui/material';
|
||||
export const Title: React.FC<{ title: string | React.ReactNode; Icon?: React.ReactNode }> = ({ title, Icon }) => (
|
||||
<Box width="100%" display="flex" alignItems="center">
|
||||
{Icon}
|
||||
<Typography width="100%" variant="h6" sx={{ fontWeight: 600 }}>
|
||||
<Typography width="100%" variant="h5" sx={{ fontWeight: 600 }}>
|
||||
{title}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
@@ -14,11 +14,13 @@ export const TokenPoolSelector: React.FC<{ disabled: boolean; onSelect: (pool: T
|
||||
clientDetails,
|
||||
} = useContext(AppContext);
|
||||
|
||||
const fetchBalances = async () => {
|
||||
await fetchBalance();
|
||||
await fetchTokenAllocation();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
await fetchBalance();
|
||||
await fetchTokenAllocation();
|
||||
})();
|
||||
fetchBalances();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -0,0 +1,358 @@
|
||||
import { FeeDetails, DecCoin, MixnodeStatus, TransactionExecuteResult } from '@nymproject/types';
|
||||
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { isGateway, isMixnode, Network, TBondGatewayArgs, TBondMixNodeArgs } from 'src/types';
|
||||
import { Console } from 'src/utils/console';
|
||||
import {
|
||||
bondGateway as bondGatewayRequest,
|
||||
bondMixNode as bondMixNodeRequest,
|
||||
claimOperatorReward,
|
||||
compoundOperatorReward,
|
||||
unbondGateway as unbondGatewayRequest,
|
||||
unbondMixNode as unbondMixnodeRequest,
|
||||
vestingBondGateway,
|
||||
vestingBondMixNode,
|
||||
vestingUnbondGateway,
|
||||
vestingUnbondMixnode,
|
||||
updateMixnode as updateMixnodeRequest,
|
||||
vestingUpdateMixnode as updateMixnodeVestingRequest,
|
||||
getNodeDescription as getNodeDescriptioRequest,
|
||||
getGatewayBondDetails,
|
||||
getMixnodeBondDetails,
|
||||
getMixnodeStatus,
|
||||
getOperatorRewards,
|
||||
getMixnodeStakeSaturation,
|
||||
getNumberOfMixnodeDelegators,
|
||||
vestingClaimOperatorReward,
|
||||
vestingCompoundOperatorReward,
|
||||
} from '../requests';
|
||||
import { useCheckOwnership } from '../hooks/useCheckOwnership';
|
||||
import { AppContext } from './main';
|
||||
|
||||
const bonded: TBondedMixnode = {
|
||||
name: 'Monster node',
|
||||
identityKey: 'B2Xx4haarLWMajX8w259oHjtRZsC7nHwagbWrJNiA3QC',
|
||||
bond: { denom: 'nym', amount: '1234' },
|
||||
delegators: 123,
|
||||
operatorRewards: { denom: 'nym', amount: '12' },
|
||||
profitMargin: 10,
|
||||
stake: { denom: 'nym', amount: '99' },
|
||||
stakeSaturation: 99,
|
||||
status: 'active',
|
||||
};
|
||||
|
||||
// TODO add relevant data
|
||||
export type TBondedMixnode = {
|
||||
name?: string;
|
||||
identityKey: string;
|
||||
stake: DecCoin;
|
||||
bond: DecCoin;
|
||||
stakeSaturation: number;
|
||||
profitMargin: number;
|
||||
operatorRewards: DecCoin;
|
||||
delegators: number;
|
||||
status: MixnodeStatus;
|
||||
proxy?: string;
|
||||
};
|
||||
|
||||
// TODO add relevant data
|
||||
export interface TBondedGateway {
|
||||
name: string;
|
||||
identityKey: string;
|
||||
ip: string;
|
||||
bond: DecCoin;
|
||||
location?: string; // TODO not yet available, only available in Network Explorer API
|
||||
proxy?: string;
|
||||
}
|
||||
|
||||
export type TokenPool = 'locked' | 'balance';
|
||||
|
||||
export type TBondingContext = {
|
||||
isLoading: boolean;
|
||||
error?: string;
|
||||
bondedNode?: TBondedMixnode | TBondedGateway;
|
||||
refresh: () => Promise<void>;
|
||||
bondMixnode: (data: TBondMixNodeArgs, tokenPool: TokenPool) => Promise<TransactionExecuteResult | undefined>;
|
||||
bondGateway: (data: TBondGatewayArgs, tokenPool: TokenPool) => Promise<TransactionExecuteResult | undefined>;
|
||||
unbond: (fee?: FeeDetails) => Promise<TransactionExecuteResult | undefined>;
|
||||
redeemRewards: (fee?: FeeDetails) => Promise<TransactionExecuteResult | undefined>;
|
||||
compoundRewards: (fee?: FeeDetails) => Promise<TransactionExecuteResult | undefined>;
|
||||
updateMixnode: (pm: number, fee?: FeeDetails) => Promise<TransactionExecuteResult | undefined>;
|
||||
checkOwnership: () => Promise<void>;
|
||||
};
|
||||
|
||||
const calculateStake = (pledge: DecCoin, delegations: DecCoin) => {
|
||||
const total = Number(pledge.amount) + Number(delegations.amount);
|
||||
return { amount: total.toString(), denom: pledge.denom };
|
||||
};
|
||||
|
||||
export const BondingContext = createContext<TBondingContext>({
|
||||
isLoading: true,
|
||||
refresh: async () => undefined,
|
||||
bondMixnode: async () => {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
bondGateway: async () => {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
unbond: async () => {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
redeemRewards: async () => {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
compoundRewards: async () => {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
updateMixnode: async () => {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
checkOwnership(): Promise<void> {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
});
|
||||
|
||||
export const BondingContextProvider = ({ children }: { children?: React.ReactNode }): JSX.Element => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string>();
|
||||
const [bondedNode, setBondedNode] = useState<TBondedMixnode | TBondedGateway>();
|
||||
|
||||
const { userBalance, clientDetails } = useContext(AppContext);
|
||||
const { ownership, isLoading: isOwnershipLoading, checkOwnership } = useCheckOwnership();
|
||||
|
||||
const isVesting = Boolean(ownership.vestingPledge);
|
||||
|
||||
const resetState = () => {
|
||||
setError(undefined);
|
||||
setBondedNode(undefined);
|
||||
};
|
||||
|
||||
const getAdditionalMixnodeDetails = async (identityKey: string) => {
|
||||
const additionalDetails: { status: MixnodeStatus; stakeSaturation: number; numberOfDelegators: number } = {
|
||||
status: 'not_found',
|
||||
stakeSaturation: 0,
|
||||
numberOfDelegators: 0,
|
||||
};
|
||||
|
||||
try {
|
||||
const statusResponse = await getMixnodeStatus(identityKey);
|
||||
additionalDetails.status = statusResponse.status;
|
||||
} catch (e) {
|
||||
Console.log(e);
|
||||
}
|
||||
|
||||
try {
|
||||
const stakeSaturationResponse = await getMixnodeStakeSaturation(identityKey);
|
||||
additionalDetails.stakeSaturation = Math.round(stakeSaturationResponse.saturation * 100);
|
||||
} catch (e) {
|
||||
Console.log(e);
|
||||
}
|
||||
|
||||
try {
|
||||
const numberOfDelegators = await getNumberOfMixnodeDelegators(identityKey);
|
||||
additionalDetails.numberOfDelegators = numberOfDelegators;
|
||||
} catch (e) {
|
||||
Console.log(e);
|
||||
}
|
||||
|
||||
return additionalDetails;
|
||||
};
|
||||
|
||||
const getNodeDescription = async (host: string, port: number) => {
|
||||
try {
|
||||
const nodeDescription = await getNodeDescriptioRequest(host, port);
|
||||
return nodeDescription;
|
||||
} catch (e) {
|
||||
Console.log(e);
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const refresh = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
|
||||
if (ownership.hasOwnership && ownership.nodeType === 'mixnode' && clientDetails) {
|
||||
try {
|
||||
const data = await getMixnodeBondDetails();
|
||||
const operatorRewards = await getOperatorRewards(clientDetails?.client_address);
|
||||
if (data) {
|
||||
const { status, stakeSaturation, numberOfDelegators } = await getAdditionalMixnodeDetails(
|
||||
data.mix_node.identity_key,
|
||||
);
|
||||
const nodeDescription = await getNodeDescription(data.mix_node.host, data.mix_node.http_api_port);
|
||||
setBondedNode({
|
||||
name: nodeDescription?.name,
|
||||
identityKey: data.mix_node.identity_key,
|
||||
ip: '',
|
||||
stake: calculateStake(data.pledge_amount, data.total_delegation),
|
||||
bond: data.pledge_amount,
|
||||
profitMargin: data.mix_node.profit_margin_percent,
|
||||
nodeRewards: data.accumulated_rewards,
|
||||
delegators: numberOfDelegators,
|
||||
proxy: data.proxy,
|
||||
operatorRewards,
|
||||
status,
|
||||
stakeSaturation,
|
||||
} as TBondedMixnode);
|
||||
}
|
||||
} catch (e: any) {
|
||||
setError(`While fetching current bond state, an error occurred: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (ownership.hasOwnership && ownership.nodeType === 'gateway') {
|
||||
try {
|
||||
const data = await getGatewayBondDetails();
|
||||
if (data) {
|
||||
const nodeDescription = await getNodeDescription(data.gateway.host, data.gateway.clients_port);
|
||||
|
||||
setBondedNode({
|
||||
name: nodeDescription?.name,
|
||||
identityKey: data.gateway.identity_key,
|
||||
ip: data.gateway.host,
|
||||
location: data.gateway.location,
|
||||
bond: data.pledge_amount,
|
||||
delegators: bonded.delegators,
|
||||
proxy: data.proxy,
|
||||
} as TBondedGateway);
|
||||
}
|
||||
} catch (e: any) {
|
||||
setError(`While fetching current bond state, an error occurred: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ownership.hasOwnership) {
|
||||
resetState();
|
||||
}
|
||||
setIsLoading(false);
|
||||
}, [ownership]);
|
||||
|
||||
useEffect(() => {
|
||||
refresh();
|
||||
}, [ownership, refresh]);
|
||||
|
||||
const bondMixnode = async (data: TBondMixNodeArgs, tokenPool: TokenPool) => {
|
||||
let tx: TransactionExecuteResult | undefined;
|
||||
setIsLoading(true);
|
||||
try {
|
||||
if (tokenPool === 'balance') {
|
||||
tx = await bondMixNodeRequest(data);
|
||||
await userBalance.fetchBalance();
|
||||
}
|
||||
if (tokenPool === 'locked') {
|
||||
tx = await vestingBondMixNode(data);
|
||||
await userBalance.fetchTokenAllocation();
|
||||
}
|
||||
return tx;
|
||||
} catch (e: any) {
|
||||
setError(`an error occurred: ${e}`);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const bondGateway = async (data: TBondGatewayArgs, tokenPool: TokenPool) => {
|
||||
let tx: TransactionExecuteResult | undefined;
|
||||
setIsLoading(true);
|
||||
try {
|
||||
if (tokenPool === 'balance') {
|
||||
tx = await bondGatewayRequest(data);
|
||||
await userBalance.fetchBalance();
|
||||
}
|
||||
if (tokenPool === 'locked') {
|
||||
tx = await vestingBondGateway(data);
|
||||
await userBalance.fetchTokenAllocation();
|
||||
}
|
||||
return tx;
|
||||
} catch (e: any) {
|
||||
setError(`an error occurred: ${e}`);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const unbond = async (fee?: FeeDetails) => {
|
||||
let tx;
|
||||
setIsLoading(true);
|
||||
try {
|
||||
if (bondedNode && isMixnode(bondedNode) && bondedNode.proxy) tx = await vestingUnbondMixnode(fee?.fee);
|
||||
if (bondedNode && isMixnode(bondedNode) && !bondedNode.proxy) tx = await unbondMixnodeRequest(fee?.fee);
|
||||
if (bondedNode && isGateway(bondedNode) && bondedNode.proxy) tx = await vestingUnbondGateway(fee?.fee);
|
||||
if (bondedNode && isGateway(bondedNode) && !bondedNode.proxy) tx = await unbondGatewayRequest(fee?.fee);
|
||||
} catch (e) {
|
||||
setError(`an error occurred: ${e as string}`);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
return tx;
|
||||
};
|
||||
|
||||
const updateMixnode = async (pm: number, fee?: FeeDetails) => {
|
||||
let tx;
|
||||
setIsLoading(true);
|
||||
try {
|
||||
if (bondedNode?.proxy) tx = await updateMixnodeVestingRequest(pm, fee?.fee);
|
||||
else tx = await updateMixnodeRequest(pm, fee?.fee);
|
||||
} catch (e: any) {
|
||||
setError(`an error occurred: ${e}`);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
return tx;
|
||||
};
|
||||
|
||||
const redeemRewards = async (fee?: FeeDetails) => {
|
||||
let tx;
|
||||
setIsLoading(true);
|
||||
try {
|
||||
if (bondedNode?.proxy) tx = await vestingClaimOperatorReward(fee?.fee);
|
||||
else tx = await claimOperatorReward(fee?.fee);
|
||||
} catch (e: any) {
|
||||
setError(`an error occurred: ${e}`);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
return tx;
|
||||
};
|
||||
|
||||
const compoundRewards = async (fee?: FeeDetails) => {
|
||||
let tx;
|
||||
setIsLoading(true);
|
||||
try {
|
||||
if (bondedNode?.proxy) tx = await vestingCompoundOperatorReward(fee?.fee);
|
||||
else tx = await compoundOperatorReward(fee?.fee);
|
||||
} catch (e: any) {
|
||||
setError(`an error occurred: ${e}`);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
return tx;
|
||||
};
|
||||
|
||||
const bondMore = async (_signature: string, _additionalBond: DecCoin) =>
|
||||
// TODO to implement
|
||||
undefined;
|
||||
|
||||
const memoizedValue = useMemo(
|
||||
() => ({
|
||||
isLoading: isLoading || isOwnershipLoading,
|
||||
error,
|
||||
bondMixnode,
|
||||
bondedNode,
|
||||
bondGateway,
|
||||
unbond,
|
||||
updateMixnode,
|
||||
refresh,
|
||||
redeemRewards,
|
||||
compoundRewards,
|
||||
bondMore,
|
||||
checkOwnership,
|
||||
}),
|
||||
[isLoading, isOwnershipLoading, error, bondedNode, isVesting],
|
||||
);
|
||||
|
||||
return <BondingContext.Provider value={memoizedValue}>{children}</BondingContext.Provider>;
|
||||
};
|
||||
|
||||
export const useBondingContext = () => useContext<TBondingContext>(BondingContext);
|
||||
@@ -81,6 +81,7 @@ export const DelegationContextProvider: FC<{
|
||||
};
|
||||
|
||||
const refresh = useCallback(async () => {
|
||||
resetState();
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const data = await getDelegationSummary();
|
||||
@@ -94,12 +95,11 @@ export const DelegationContextProvider: FC<{
|
||||
setError((e as Error).message);
|
||||
}
|
||||
setIsLoading(false);
|
||||
}, [network]);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
resetState();
|
||||
refresh();
|
||||
}, [network]);
|
||||
}, []);
|
||||
|
||||
const memoizedValue = useMemo(
|
||||
() => ({
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './main';
|
||||
export * from './auth';
|
||||
export * from './accounts';
|
||||
export * from './bonding';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user